Compare commits
60 Commits
v1.10.2
...
feature/Ca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6db85bfa39 | ||
|
|
03c9ac1e11 | ||
|
|
1ee0063b62 | ||
|
|
3e889d8c08 | ||
|
|
a01395aed3 | ||
|
|
7b3459abff | ||
|
|
9a88f798fc | ||
|
|
256fd1cce2 | ||
|
|
a8cb332232 | ||
|
|
89ea2b23a2 | ||
|
|
9b214a66c8 | ||
|
|
4c4689ec8d | ||
|
|
c4e561dee6 | ||
|
|
69bfe5b750 | ||
|
|
137b3dc2ea | ||
|
|
ea5f879181 | ||
|
|
25d5f34f68 | ||
|
|
c8a6df77ac | ||
|
|
7b6ac46b6a | ||
|
|
d77c648d54 | ||
|
|
c3f47c5fa1 | ||
|
|
f37a51b46f | ||
|
|
9feb3f35c3 | ||
|
|
5b44cf03ce | ||
|
|
86a6382463 | ||
|
|
df991a3829 | ||
|
|
f243c86b50 | ||
|
|
b77be0a5e9 | ||
|
|
83be587c1a | ||
|
|
c6048aea80 | ||
|
|
13b495b0f6 | ||
|
|
ac64c35efa | ||
|
|
127b58601f | ||
|
|
1f795b45e9 | ||
|
|
d26e35ee9a | ||
|
|
70e69e9dac | ||
|
|
3d88f4212d | ||
|
|
ad90a9c8f3 | ||
|
|
b43176764b | ||
|
|
77f24282e0 | ||
|
|
533f1f1102 | ||
|
|
92c5d8bd44 | ||
|
|
d754ecb486 | ||
|
|
b18987a95c | ||
|
|
0daec61f31 | ||
|
|
8ecf301eb8 | ||
|
|
6080646e89 | ||
|
|
970a521b66 | ||
|
|
9b5a92f942 | ||
|
|
c4e0f13d67 | ||
|
|
b6821746d0 | ||
|
|
b98fc91a99 | ||
|
|
bd7f7b867e | ||
|
|
32a3fea8d7 | ||
|
|
3561beab1d | ||
|
|
1d1fd52cae | ||
|
|
c4ba438150 | ||
|
|
37f0ee08b1 | ||
|
|
240b02c94e | ||
|
|
e8142ff3df |
@@ -1,65 +1,67 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="ColorHashSharp" Version="1.0.0" />
|
||||
<PackageVersion Include="CommunityToolkit.Common" Version="8.4.0" />
|
||||
<PackageVersion Include="CommunityToolkit.Diagnostics" Version="8.4.0" />
|
||||
<PackageVersion Include="CommunityToolkit.Labs.Uwp.Controls.MarkdownTextBlock" Version="0.1.250206-build.2040" />
|
||||
<PackageVersion Include="CommunityToolkit.Labs.Uwp.DependencyPropertyGenerator" Version="0.1.250206-build.2040" />
|
||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Animations" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Behaviors" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Controls.Segmented" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Controls.SettingsControls" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Controls.Sizers" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Controls.TabbedCommandBar" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Controls.TokenizingTextBox" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Extensions" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="EmailValidation" Version="1.2.0" />
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.11.72" />
|
||||
<PackageVersion Include="Ical.Net" Version="4.3.1" />
|
||||
<PackageVersion Include="IsExternalInit" Version="1.0.3" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.2" />
|
||||
<PackageVersion Include="Microsoft.Graph" Version="5.69.0" />
|
||||
<PackageVersion Include="Microsoft.Identity.Client" Version="4.68.0" />
|
||||
<PackageVersion Include="Microsoft.Identity.Client.Broker" Version="4.68.0" />
|
||||
<PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.68.0" />
|
||||
<PackageVersion Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.14" />
|
||||
<PackageVersion Include="Microsoft.UI.Xaml" Version="2.8.7" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.Uwp.Managed" Version="3.0.0" />
|
||||
<PackageVersion Include="MimeKit" Version="4.10.0" />
|
||||
<PackageVersion Include="morelinq" Version="4.4.0" />
|
||||
<PackageVersion Include="Nito.AsyncEx" Version="5.1.2" />
|
||||
<PackageVersion Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
|
||||
<PackageVersion Include="NodaTime" Version="3.2.1" />
|
||||
<PackageVersion Include="Serilog" Version="4.2.0" />
|
||||
<PackageVersion Include="Serilog.Exceptions" Version="8.4.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Debug" Version="3.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.ApplicationInsights" Version="4.0.0" />
|
||||
<PackageVersion Include="SkiaSharp" Version="3.116.1" />
|
||||
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
||||
<PackageVersion Include="SqlKata" Version="4.0.1" />
|
||||
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.2" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.2" />
|
||||
<PackageVersion Include="Win2D.uwp" Version="1.28.2" />
|
||||
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.2.0" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||
<PackageVersion Include="Google.Apis.Auth" Version="1.69.0" />
|
||||
<PackageVersion Include="Google.Apis.Calendar.v3" Version="1.69.0.3667" />
|
||||
<PackageVersion Include="Google.Apis.Gmail.v1" Version="1.68.0.3427" />
|
||||
<PackageVersion Include="Google.Apis.PeopleService.v1" Version="1.68.0.3359" />
|
||||
<PackageVersion Include="HtmlKit" Version="1.2.0" />
|
||||
<PackageVersion Include="MailKit" Version="4.10.0" />
|
||||
<PackageVersion Include="TimePeriodLibrary.NET" Version="2.1.5" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.2" />
|
||||
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="ColorHashSharp" Version="1.0.0" />
|
||||
<PackageVersion Include="CommunityToolkit.Common" Version="8.4.0" />
|
||||
<PackageVersion Include="CommunityToolkit.Diagnostics" Version="8.4.0" />
|
||||
<PackageVersion Include="CommunityToolkit.Labs.Uwp.Controls.MarkdownTextBlock" Version="0.1.250206-build.2040" />
|
||||
<PackageVersion Include="CommunityToolkit.Labs.Uwp.DependencyPropertyGenerator" Version="0.1.250206-build.2040" />
|
||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Animations" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Behaviors" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Controls.Segmented" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Controls.SettingsControls" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Controls.Sizers" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Controls.TabbedCommandBar" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Controls.TokenizingTextBox" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Extensions" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.Uwp.Controls.Primitives" Version="8.2.250129-preview2" />
|
||||
<PackageVersion Include="EmailValidation" Version="1.3.0" />
|
||||
<PackageVersion Include="gravatar-dotnet" Version="0.1.3" />
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.12.0" />
|
||||
<PackageVersion Include="Ical.Net" Version="4.3.1" />
|
||||
<PackageVersion Include="IsExternalInit" Version="1.0.3" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Graph" Version="5.75.0" />
|
||||
<PackageVersion Include="Microsoft.Identity.Client" Version="4.70.1" />
|
||||
<PackageVersion Include="Microsoft.Identity.Client.Broker" Version="4.70.1" />
|
||||
<PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.70.1" />
|
||||
<PackageVersion Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.14" />
|
||||
<PackageVersion Include="Microsoft.UI.Xaml" Version="2.8.7" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.Uwp.Managed" Version="3.0.0" />
|
||||
<PackageVersion Include="MimeKit" Version="4.11.0" />
|
||||
<PackageVersion Include="morelinq" Version="4.4.0" />
|
||||
<PackageVersion Include="Nito.AsyncEx" Version="5.1.2" />
|
||||
<PackageVersion Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
|
||||
<PackageVersion Include="NodaTime" Version="3.2.2" />
|
||||
<PackageVersion Include="Serilog" Version="4.2.0" />
|
||||
<PackageVersion Include="Serilog.Exceptions" Version="8.4.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Debug" Version="3.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.ApplicationInsights" Version="4.0.0" />
|
||||
<PackageVersion Include="SkiaSharp" Version="3.116.1" />
|
||||
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
||||
<PackageVersion Include="SqlKata" Version="4.0.1" />
|
||||
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.4" />
|
||||
<PackageVersion Include="Win2D.uwp" Version="1.28.2" />
|
||||
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||
<PackageVersion Include="Google.Apis.Auth" Version="1.69.0" />
|
||||
<PackageVersion Include="Google.Apis.Calendar.v3" Version="1.69.0.3667" />
|
||||
<PackageVersion Include="Google.Apis.Gmail.v1" Version="1.68.0.3427" />
|
||||
<PackageVersion Include="Google.Apis.PeopleService.v1" Version="1.68.0.3359" />
|
||||
<PackageVersion Include="HtmlKit" Version="1.2.0" />
|
||||
<PackageVersion Include="MailKit" Version="4.11.0" />
|
||||
<PackageVersion Include="TimePeriodLibrary.NET" Version="2.1.6" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -67,7 +67,8 @@ public class OutlookAuthenticator : BaseAuthenticator, IOutlookAuthenticator
|
||||
{
|
||||
await EnsureTokenCacheAttachedAsync();
|
||||
|
||||
var storedAccount = (await _publicClientApplication.GetAccountsAsync()).FirstOrDefault(a => a.Username == account.Address);
|
||||
var storedAccount = (await _publicClientApplication.GetAccountsAsync()).FirstOrDefault(
|
||||
a => string.Equals(a.Username?.Trim(), account.Address?.Trim(), StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (storedAccount == null)
|
||||
return await GenerateTokenInformationAsync(account);
|
||||
@@ -107,7 +108,7 @@ public class OutlookAuthenticator : BaseAuthenticator, IOutlookAuthenticator
|
||||
|
||||
if (account?.Address != null && !account.Address.Equals(authResult.Account.Username, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new AuthenticationException("Authenticated address does not match with your account address.");
|
||||
throw new AuthenticationException("Authenticated address does not match with your account address. If you are signing with a Office365, it is not officially supported yet.");
|
||||
}
|
||||
|
||||
return new TokenInformationEx(authResult.AccessToken, authResult.Account.Username);
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,90 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap rescap">
|
||||
|
||||
<Identity
|
||||
Name="58272BurakKSE.WinoCalendar"
|
||||
Publisher="CN=51FBDAF3-E212-4149-89A2-A2636B3BC911"
|
||||
Version="1.0.0.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Wino Calendar</DisplayName>
|
||||
<PublisherDisplayName>Burak KÖSE</PublisherDisplayName>
|
||||
<Logo>Images\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14393.0" MaxVersionTested="10.0.14393.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements
|
||||
DisplayName="Wino Calendar"
|
||||
Description="Wino.Calendar.Packaging"
|
||||
BackgroundColor="transparent"
|
||||
Square150x150Logo="Images\Square150x150Logo.png"
|
||||
Square44x44Logo="Images\Square44x44Logo.png">
|
||||
<uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png" />
|
||||
<uap:SplashScreen Image="Images\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
|
||||
<Extensions>
|
||||
<!-- Registration of full trust backend application. -->
|
||||
<uap:Extension Category="windows.appService">
|
||||
<uap:AppService Name="WinoInteropService" />
|
||||
</uap:Extension>
|
||||
|
||||
<!-- Protocol activation: Google OAuth -->
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="google.pw.oauth2">
|
||||
<uap:DisplayName>Wino Google Authentication Protocol</uap:DisplayName>
|
||||
</uap:Protocol>
|
||||
</uap:Extension>
|
||||
|
||||
<!-- Protocol activation: Launch UWP app from Full Trust Process -->
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="wino.calendar.launch">
|
||||
<uap:DisplayName>Wino Calendara Launcher Protocol</uap:DisplayName>
|
||||
</uap:Protocol>
|
||||
</uap:Extension>
|
||||
|
||||
<!-- Startup Task -->
|
||||
<uap5:Extension
|
||||
Category="windows.startupTask"
|
||||
Executable="Wino.Server\Wino.Server.exe"
|
||||
EntryPoint="Windows.FullTrustApplication">
|
||||
<uap5:StartupTask
|
||||
TaskId="WinoServer"
|
||||
Enabled="false"
|
||||
DisplayName="Wino Mail" />
|
||||
</uap5:Extension>
|
||||
|
||||
<desktop:Extension Category="windows.fullTrustProcess" Executable="Wino.Server\Wino.Server.exe">
|
||||
<desktop:FullTrustProcess>
|
||||
<desktop:ParameterGroup GroupId="WinoServer" Parameters="Calendar" />
|
||||
</desktop:FullTrustProcess>
|
||||
</desktop:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
<rescap:Capability Name="confirmAppClose" />
|
||||
</Capabilities>
|
||||
</Package>
|
||||
@@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '15.0'">
|
||||
<VisualStudioVersion>15.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x86">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x86</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x86">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x86</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|AnyCPU">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>AnyCPU</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|AnyCPU">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>AnyCPU</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<WapProjPath Condition="'$(WapProjPath)'==''">$(MSBuildExtensionsPath)\Microsoft\DesktopBridge\</WapProjPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.props" />
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>7485b18c-f5ab-4abe-ba7f-05b6623c67c8</ProjectGuid>
|
||||
<TargetPlatformVersion>10.0.22621.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<AppxPackageSigningEnabled>false</AppxPackageSigningEnabled>
|
||||
<NoWarn>$(NoWarn);NU1702</NoWarn>
|
||||
<EntryPointProjectUniqueName>..\Wino.Calendar\Wino.Calendar.csproj</EntryPointProjectUniqueName>
|
||||
<GenerateTemporaryStoreCertificate>True</GenerateTemporaryStoreCertificate>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Images\SplashScreen.scale-200.png" />
|
||||
<Content Include="Images\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="Images\Square150x150Logo.scale-200.png" />
|
||||
<Content Include="Images\Square44x44Logo.scale-200.png" />
|
||||
<Content Include="Images\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="Images\StoreLogo.png" />
|
||||
<Content Include="Images\Wide310x150Logo.scale-200.png" />
|
||||
<None Include="Package.StoreAssociation.xml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Wino.Calendar\Wino.Calendar.csproj" />
|
||||
<ProjectReference Include="..\Wino.Server\Wino.Server.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,48 +1,37 @@
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Calendar.ViewModels.Interfaces;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.ViewModels;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Messaging.UI;
|
||||
using Wino.Messaging.Client.Navigation;
|
||||
|
||||
namespace Wino.Calendar.ViewModels
|
||||
namespace Wino.Calendar.ViewModels;
|
||||
|
||||
public partial class AccountDetailsPageViewModel : CalendarBaseViewModel
|
||||
{
|
||||
public partial class AccountDetailsPageViewModel : CalendarBaseViewModel
|
||||
private readonly IAccountService _accountService;
|
||||
|
||||
public AccountProviderDetailViewModel Account { get; private set; }
|
||||
public ICalendarDialogService CalendarDialogService { get; }
|
||||
public IAccountCalendarStateService AccountCalendarStateService { get; }
|
||||
|
||||
public AccountDetailsPageViewModel(ICalendarDialogService calendarDialogService, IAccountService accountService, IAccountCalendarStateService accountCalendarStateService)
|
||||
{
|
||||
private readonly IAccountService _accountService;
|
||||
CalendarDialogService = calendarDialogService;
|
||||
_accountService = accountService;
|
||||
AccountCalendarStateService = accountCalendarStateService;
|
||||
}
|
||||
|
||||
public AccountProviderDetailViewModel Account { get; private set; }
|
||||
public ICalendarDialogService CalendarDialogService { get; }
|
||||
public IAccountCalendarStateService AccountCalendarStateService { get; }
|
||||
[RelayCommand]
|
||||
private void EditAccountDetails()
|
||||
=> Messenger.Send(new BreadcrumbNavigationRequested(Translator.SettingsEditAccountDetails_Title, WinoPage.EditAccountDetailsPage, Account));
|
||||
|
||||
public AccountDetailsPageViewModel(ICalendarDialogService calendarDialogService, IAccountService accountService, IAccountCalendarStateService accountCalendarStateService)
|
||||
{
|
||||
CalendarDialogService = calendarDialogService;
|
||||
_accountService = accountService;
|
||||
AccountCalendarStateService = accountCalendarStateService;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RenameAccount()
|
||||
{
|
||||
if (Account == null)
|
||||
return;
|
||||
|
||||
var updatedAccount = await CalendarDialogService.ShowEditAccountDialogAsync(Account.Account);
|
||||
|
||||
if (updatedAccount != null)
|
||||
{
|
||||
await _accountService.UpdateAccountAsync(updatedAccount);
|
||||
|
||||
ReportUIChange(new AccountUpdatedMessage(updatedAccount));
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
}
|
||||
public override void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -13,135 +14,144 @@ using Wino.Core.Domain.Models.Synchronization;
|
||||
using Wino.Core.ViewModels;
|
||||
using Wino.Messaging.Server;
|
||||
|
||||
namespace Wino.Calendar.ViewModels
|
||||
namespace Wino.Calendar.ViewModels;
|
||||
|
||||
public partial class AccountManagementViewModel : AccountManagementPageViewModelBase
|
||||
{
|
||||
public partial class AccountManagementViewModel : AccountManagementPageViewModelBase
|
||||
private readonly IProviderService _providerService;
|
||||
|
||||
public AccountManagementViewModel(ICalendarDialogService dialogService,
|
||||
IWinoServerConnectionManager winoServerConnectionManager,
|
||||
INavigationService navigationService,
|
||||
IAccountService accountService,
|
||||
IProviderService providerService,
|
||||
IStoreManagementService storeManagementService,
|
||||
IAuthenticationProvider authenticationProvider,
|
||||
IPreferencesService preferencesService) : base(dialogService, winoServerConnectionManager, navigationService, accountService, providerService, storeManagementService, authenticationProvider, preferencesService)
|
||||
{
|
||||
private readonly IProviderService _providerService;
|
||||
CalendarDialogService = dialogService;
|
||||
_providerService = providerService;
|
||||
}
|
||||
|
||||
public AccountManagementViewModel(ICalendarDialogService dialogService,
|
||||
IWinoServerConnectionManager winoServerConnectionManager,
|
||||
INavigationService navigationService,
|
||||
IAccountService accountService,
|
||||
IProviderService providerService,
|
||||
IStoreManagementService storeManagementService,
|
||||
IAuthenticationProvider authenticationProvider,
|
||||
IPreferencesService preferencesService) : base(dialogService, winoServerConnectionManager, navigationService, accountService, providerService, storeManagementService, authenticationProvider, preferencesService)
|
||||
public ICalendarDialogService CalendarDialogService { get; }
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
await InitializeAccountsAsync();
|
||||
}
|
||||
|
||||
public override async Task InitializeAccountsAsync()
|
||||
{
|
||||
Accounts.Clear();
|
||||
|
||||
var accounts = await AccountService.GetAccountsAsync().ConfigureAwait(false);
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
CalendarDialogService = dialogService;
|
||||
_providerService = providerService;
|
||||
}
|
||||
|
||||
public ICalendarDialogService CalendarDialogService { get; }
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
await InitializeAccountsAsync();
|
||||
}
|
||||
|
||||
public override async Task InitializeAccountsAsync()
|
||||
{
|
||||
Accounts.Clear();
|
||||
|
||||
var accounts = await AccountService.GetAccountsAsync().ConfigureAwait(false);
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
foreach (var account in accounts)
|
||||
{
|
||||
foreach (var account in accounts)
|
||||
{
|
||||
var accountDetails = GetAccountProviderDetails(account);
|
||||
var accountDetails = GetAccountProviderDetails(account);
|
||||
|
||||
Accounts.Add(accountDetails);
|
||||
}
|
||||
});
|
||||
|
||||
await ManageStorePurchasesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AddNewAccountAsync()
|
||||
{
|
||||
if (IsAccountCreationBlocked)
|
||||
{
|
||||
var isPurchaseClicked = await DialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_AccountLimitMessage, Translator.DialogMessage_AccountLimitTitle, Translator.Buttons_Purchase);
|
||||
|
||||
if (!isPurchaseClicked) return;
|
||||
|
||||
await PurchaseUnlimitedAccountAsync();
|
||||
|
||||
return;
|
||||
Accounts.Add(accountDetails);
|
||||
}
|
||||
});
|
||||
|
||||
var availableProviders = _providerService.GetAvailableProviders();
|
||||
await ManageStorePurchasesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var accountCreationDialogResult = await DialogService.ShowAccountProviderSelectionDialogAsync(availableProviders);
|
||||
[RelayCommand]
|
||||
private async Task AddNewAccountAsync()
|
||||
{
|
||||
if (IsAccountCreationBlocked)
|
||||
{
|
||||
var isPurchaseClicked = await DialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_AccountLimitMessage, Translator.DialogMessage_AccountLimitTitle, Translator.Buttons_Purchase);
|
||||
|
||||
if (accountCreationDialogResult == null) return;
|
||||
if (!isPurchaseClicked) return;
|
||||
|
||||
var accountCreationCancellationTokenSource = new CancellationTokenSource();
|
||||
var accountCreationDialog = CalendarDialogService.GetAccountCreationDialog(accountCreationDialogResult);
|
||||
await PurchaseUnlimitedAccountAsync();
|
||||
|
||||
accountCreationDialog.ShowDialog(accountCreationCancellationTokenSource);
|
||||
accountCreationDialog.State = AccountCreationDialogState.SigningIn;
|
||||
return;
|
||||
}
|
||||
|
||||
// For OAuth authentications, we just generate token and assign it to the MailAccount.
|
||||
var availableProviders = _providerService.GetAvailableProviders();
|
||||
|
||||
var createdAccount = new MailAccount()
|
||||
{
|
||||
ProviderType = accountCreationDialogResult.ProviderType,
|
||||
Name = accountCreationDialogResult.AccountName,
|
||||
Id = Guid.NewGuid()
|
||||
};
|
||||
var accountCreationDialogResult = await DialogService.ShowAccountProviderSelectionDialogAsync(availableProviders);
|
||||
|
||||
var tokenInformationResponse = await WinoServerConnectionManager
|
||||
.GetResponseAsync<TokenInformationEx, AuthorizationRequested>(new AuthorizationRequested(accountCreationDialogResult.ProviderType,
|
||||
createdAccount,
|
||||
createdAccount.ProviderType == MailProviderType.Gmail), accountCreationCancellationTokenSource.Token);
|
||||
if (accountCreationDialogResult == null) return;
|
||||
|
||||
if (accountCreationDialog.State == AccountCreationDialogState.Canceled)
|
||||
throw new AccountSetupCanceledException();
|
||||
var accountCreationCancellationTokenSource = new CancellationTokenSource();
|
||||
var accountCreationDialog = CalendarDialogService.GetAccountCreationDialog(accountCreationDialogResult);
|
||||
|
||||
tokenInformationResponse.ThrowIfFailed();
|
||||
await accountCreationDialog.ShowDialogAsync(accountCreationCancellationTokenSource);
|
||||
await Task.Delay(500);
|
||||
|
||||
await AccountService.CreateAccountAsync(createdAccount, null);
|
||||
// For OAuth authentications, we just generate token and assign it to the MailAccount.
|
||||
|
||||
// Sync profile information if supported.
|
||||
if (createdAccount.IsProfileInfoSyncSupported)
|
||||
{
|
||||
// Start profile information synchronization.
|
||||
// It's only available for Outlook and Gmail synchronizers.
|
||||
var createdAccount = new MailAccount()
|
||||
{
|
||||
ProviderType = accountCreationDialogResult.ProviderType,
|
||||
Name = accountCreationDialogResult.AccountName,
|
||||
Id = Guid.NewGuid()
|
||||
};
|
||||
|
||||
var profileSyncOptions = new MailSynchronizationOptions()
|
||||
{
|
||||
AccountId = createdAccount.Id,
|
||||
Type = MailSynchronizationType.UpdateProfile
|
||||
};
|
||||
var tokenInformationResponse = await WinoServerConnectionManager
|
||||
.GetResponseAsync<TokenInformationEx, AuthorizationRequested>(new AuthorizationRequested(accountCreationDialogResult.ProviderType,
|
||||
createdAccount,
|
||||
createdAccount.ProviderType == MailProviderType.Gmail), accountCreationCancellationTokenSource.Token);
|
||||
|
||||
var profileSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewMailSynchronizationRequested>(new NewMailSynchronizationRequested(profileSyncOptions, SynchronizationSource.Client));
|
||||
if (accountCreationDialog.State == AccountCreationDialogState.Canceled)
|
||||
throw new AccountSetupCanceledException();
|
||||
|
||||
var profileSynchronizationResult = profileSynchronizationResponse.Data;
|
||||
tokenInformationResponse.ThrowIfFailed();
|
||||
|
||||
if (profileSynchronizationResult.CompletedState != SynchronizationCompletedState.Success)
|
||||
throw new Exception(Translator.Exception_FailedToSynchronizeProfileInformation);
|
||||
await AccountService.CreateAccountAsync(createdAccount, null);
|
||||
|
||||
createdAccount.SenderName = profileSynchronizationResult.ProfileInformation.SenderName;
|
||||
createdAccount.Base64ProfilePictureData = profileSynchronizationResult.ProfileInformation.Base64ProfilePictureData;
|
||||
// Sync profile information if supported.
|
||||
if (createdAccount.IsProfileInfoSyncSupported)
|
||||
{
|
||||
// Start profile information synchronization.
|
||||
// It's only available for Outlook and Gmail synchronizers.
|
||||
|
||||
await AccountService.UpdateProfileInformationAsync(createdAccount.Id, profileSynchronizationResult.ProfileInformation);
|
||||
}
|
||||
|
||||
accountCreationDialog.State = AccountCreationDialogState.FetchingEvents;
|
||||
|
||||
// Start synchronizing events.
|
||||
var synchronizationOptions = new CalendarSynchronizationOptions()
|
||||
var profileSyncOptions = new MailSynchronizationOptions()
|
||||
{
|
||||
AccountId = createdAccount.Id,
|
||||
Type = CalendarSynchronizationType.CalendarMetadata
|
||||
Type = MailSynchronizationType.UpdateProfile
|
||||
};
|
||||
|
||||
var synchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<CalendarSynchronizationResult, NewCalendarSynchronizationRequested>(new NewCalendarSynchronizationRequested(synchronizationOptions, SynchronizationSource.Client));
|
||||
var profileSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewMailSynchronizationRequested>(new NewMailSynchronizationRequested(profileSyncOptions, SynchronizationSource.Client));
|
||||
|
||||
var profileSynchronizationResult = profileSynchronizationResponse.Data;
|
||||
|
||||
if (profileSynchronizationResult.CompletedState != SynchronizationCompletedState.Success)
|
||||
throw new Exception(Translator.Exception_FailedToSynchronizeProfileInformation);
|
||||
|
||||
createdAccount.SenderName = profileSynchronizationResult.ProfileInformation.SenderName;
|
||||
createdAccount.Base64ProfilePictureData = profileSynchronizationResult.ProfileInformation.Base64ProfilePictureData;
|
||||
|
||||
await AccountService.UpdateProfileInformationAsync(createdAccount.Id, profileSynchronizationResult.ProfileInformation);
|
||||
}
|
||||
|
||||
accountCreationDialog.State = AccountCreationDialogState.FetchingEvents;
|
||||
|
||||
// Start synchronizing events.
|
||||
var synchronizationOptions = new CalendarSynchronizationOptions()
|
||||
{
|
||||
AccountId = createdAccount.Id,
|
||||
Type = CalendarSynchronizationType.CalendarMetadata
|
||||
};
|
||||
|
||||
var timer = new Stopwatch();
|
||||
|
||||
var synchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<CalendarSynchronizationResult, NewCalendarSynchronizationRequested>(new NewCalendarSynchronizationRequested(synchronizationOptions, SynchronizationSource.Client));
|
||||
|
||||
timer.Stop();
|
||||
|
||||
Debug.WriteLine("Synchronization completed in {timer.ElapsedMilliseconds} ms");
|
||||
|
||||
// TODO: Properly handle synchronization errors.
|
||||
|
||||
accountCreationDialog.Complete(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,347 +21,346 @@ using Wino.Messaging.Client.Calendar;
|
||||
using Wino.Messaging.Client.Navigation;
|
||||
using Wino.Messaging.Server;
|
||||
|
||||
namespace Wino.Calendar.ViewModels
|
||||
namespace Wino.Calendar.ViewModels;
|
||||
|
||||
public partial class AppShellViewModel : CalendarBaseViewModel,
|
||||
IRecipient<VisibleDateRangeChangedMessage>,
|
||||
IRecipient<CalendarEnableStatusChangedMessage>,
|
||||
IRecipient<NavigateManageAccountsRequested>,
|
||||
IRecipient<CalendarDisplayTypeChangedMessage>,
|
||||
IRecipient<DetailsPageStateChangedMessage>
|
||||
{
|
||||
public partial class AppShellViewModel : CalendarBaseViewModel,
|
||||
IRecipient<VisibleDateRangeChangedMessage>,
|
||||
IRecipient<CalendarEnableStatusChangedMessage>,
|
||||
IRecipient<NavigateManageAccountsRequested>,
|
||||
IRecipient<CalendarDisplayTypeChangedMessage>,
|
||||
IRecipient<DetailsPageStateChangedMessage>
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
public IStatePersistanceService StatePersistenceService { get; }
|
||||
public IAccountCalendarStateService AccountCalendarStateService { get; }
|
||||
public INavigationService NavigationService { get; }
|
||||
public IWinoServerConnectionManager ServerConnectionManager { get; }
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isEventDetailsPageActive;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _selectedMenuItemIndex = -1;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isCalendarEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the active connection status of the Wino server.
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private WinoServerConnectionStatus activeConnectionStatus;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display date of the calendar.
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private DateTimeOffset _displayDate;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the highlighted range in the CalendarView and displayed date range in FlipView.
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private DateRange highlightedDateRange;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableRangeCollection<string> dateNavigationHeaderItems = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private int _selectedDateNavigationHeaderIndex;
|
||||
|
||||
public bool IsVerticalCalendar => StatePersistenceService.CalendarDisplayType == CalendarDisplayType.Month;
|
||||
|
||||
// For updating account calendars asynchronously.
|
||||
private SemaphoreSlim _accountCalendarUpdateSemaphoreSlim = new(1);
|
||||
|
||||
public AppShellViewModel(IPreferencesService preferencesService,
|
||||
IStatePersistanceService statePersistanceService,
|
||||
IAccountService accountService,
|
||||
ICalendarService calendarService,
|
||||
IAccountCalendarStateService accountCalendarStateService,
|
||||
INavigationService navigationService,
|
||||
IWinoServerConnectionManager serverConnectionManager)
|
||||
{
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
public IStatePersistanceService StatePersistenceService { get; }
|
||||
public IAccountCalendarStateService AccountCalendarStateService { get; }
|
||||
public INavigationService NavigationService { get; }
|
||||
public IWinoServerConnectionManager ServerConnectionManager { get; }
|
||||
_accountService = accountService;
|
||||
_calendarService = calendarService;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isEventDetailsPageActive;
|
||||
AccountCalendarStateService = accountCalendarStateService;
|
||||
AccountCalendarStateService.AccountCalendarSelectionStateChanged += UpdateAccountCalendarRequested;
|
||||
AccountCalendarStateService.CollectiveAccountGroupSelectionStateChanged += AccountCalendarStateCollectivelyChanged;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _selectedMenuItemIndex = -1;
|
||||
NavigationService = navigationService;
|
||||
ServerConnectionManager = serverConnectionManager;
|
||||
PreferencesService = preferencesService;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isCalendarEnabled;
|
||||
StatePersistenceService = statePersistanceService;
|
||||
StatePersistenceService.StatePropertyChanged += PrefefencesChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the active connection status of the Wino server.
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private WinoServerConnectionStatus activeConnectionStatus;
|
||||
private void SelectedCalendarItemsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display date of the calendar.
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private DateTimeOffset _displayDate;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the highlighted range in the CalendarView and displayed date range in FlipView.
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private DateRange highlightedDateRange;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableRangeCollection<string> dateNavigationHeaderItems = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private int _selectedDateNavigationHeaderIndex;
|
||||
|
||||
public bool IsVerticalCalendar => StatePersistenceService.CalendarDisplayType == CalendarDisplayType.Month;
|
||||
|
||||
// For updating account calendars asynchronously.
|
||||
private SemaphoreSlim _accountCalendarUpdateSemaphoreSlim = new(1);
|
||||
|
||||
public AppShellViewModel(IPreferencesService preferencesService,
|
||||
IStatePersistanceService statePersistanceService,
|
||||
IAccountService accountService,
|
||||
ICalendarService calendarService,
|
||||
IAccountCalendarStateService accountCalendarStateService,
|
||||
INavigationService navigationService,
|
||||
IWinoServerConnectionManager serverConnectionManager)
|
||||
private void PrefefencesChanged(object sender, string e)
|
||||
{
|
||||
if (e == nameof(StatePersistenceService.CalendarDisplayType))
|
||||
{
|
||||
_accountService = accountService;
|
||||
_calendarService = calendarService;
|
||||
Messenger.Send(new CalendarDisplayTypeChangedMessage(StatePersistenceService.CalendarDisplayType));
|
||||
|
||||
AccountCalendarStateService = accountCalendarStateService;
|
||||
AccountCalendarStateService.AccountCalendarSelectionStateChanged += UpdateAccountCalendarRequested;
|
||||
AccountCalendarStateService.CollectiveAccountGroupSelectionStateChanged += AccountCalendarStateCollectivelyChanged;
|
||||
|
||||
NavigationService = navigationService;
|
||||
ServerConnectionManager = serverConnectionManager;
|
||||
PreferencesService = preferencesService;
|
||||
|
||||
StatePersistenceService = statePersistanceService;
|
||||
StatePersistenceService.StatePropertyChanged += PrefefencesChanged;
|
||||
// Change the calendar.
|
||||
DateClicked(new CalendarViewDayClickedEventArgs(GetDisplayTypeSwitchDate()));
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectedCalendarItemsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
private void PrefefencesChanged(object sender, string e)
|
||||
UpdateDateNavigationHeaderItems();
|
||||
|
||||
await InitializeAccountCalendarsAsync();
|
||||
|
||||
TodayClicked();
|
||||
}
|
||||
|
||||
private async void AccountCalendarStateCollectivelyChanged(object sender, GroupedAccountCalendarViewModel e)
|
||||
{
|
||||
// When using three-state checkbox, multiple accounts will be selected/unselected at the same time.
|
||||
// Reporting all these changes one by one to the UI is not efficient and may cause problems in the future.
|
||||
|
||||
// Update all calendar states at once.
|
||||
try
|
||||
{
|
||||
if (e == nameof(StatePersistenceService.CalendarDisplayType))
|
||||
await _accountCalendarUpdateSemaphoreSlim.WaitAsync();
|
||||
|
||||
foreach (var calendar in e.AccountCalendars)
|
||||
{
|
||||
Messenger.Send(new CalendarDisplayTypeChangedMessage(StatePersistenceService.CalendarDisplayType));
|
||||
|
||||
|
||||
// Change the calendar.
|
||||
DateClicked(new CalendarViewDayClickedEventArgs(GetDisplayTypeSwitchDate()));
|
||||
await _calendarService.UpdateAccountCalendarAsync(calendar.AccountCalendar).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
catch (Exception ex)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
UpdateDateNavigationHeaderItems();
|
||||
|
||||
await InitializeAccountCalendarsAsync();
|
||||
|
||||
TodayClicked();
|
||||
Log.Error(ex, "Error while waiting for account calendar update semaphore.");
|
||||
}
|
||||
|
||||
private async void AccountCalendarStateCollectivelyChanged(object sender, GroupedAccountCalendarViewModel e)
|
||||
finally
|
||||
{
|
||||
// When using three-state checkbox, multiple accounts will be selected/unselected at the same time.
|
||||
// Reporting all these changes one by one to the UI is not efficient and may cause problems in the future.
|
||||
|
||||
// Update all calendar states at once.
|
||||
try
|
||||
{
|
||||
await _accountCalendarUpdateSemaphoreSlim.WaitAsync();
|
||||
|
||||
foreach (var calendar in e.AccountCalendars)
|
||||
{
|
||||
await _calendarService.UpdateAccountCalendarAsync(calendar.AccountCalendar).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error while waiting for account calendar update semaphore.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_accountCalendarUpdateSemaphoreSlim.Release();
|
||||
}
|
||||
_accountCalendarUpdateSemaphoreSlim.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private async void UpdateAccountCalendarRequested(object sender, AccountCalendarViewModel e)
|
||||
=> await _calendarService.UpdateAccountCalendarAsync(e.AccountCalendar).ConfigureAwait(false);
|
||||
private async void UpdateAccountCalendarRequested(object sender, AccountCalendarViewModel e)
|
||||
=> await _calendarService.UpdateAccountCalendarAsync(e.AccountCalendar).ConfigureAwait(false);
|
||||
|
||||
private async Task InitializeAccountCalendarsAsync()
|
||||
private async Task InitializeAccountCalendarsAsync()
|
||||
{
|
||||
await Dispatcher.ExecuteOnUIThread(() => AccountCalendarStateService.ClearGroupedAccountCalendar());
|
||||
|
||||
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
|
||||
|
||||
foreach (var account in accounts)
|
||||
{
|
||||
await Dispatcher.ExecuteOnUIThread(() => AccountCalendarStateService.ClearGroupedAccountCalendar());
|
||||
var accountCalendars = await _calendarService.GetAccountCalendarsAsync(account.Id).ConfigureAwait(false);
|
||||
var calendarViewModels = new List<AccountCalendarViewModel>();
|
||||
|
||||
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
|
||||
|
||||
foreach (var account in accounts)
|
||||
foreach (var calendar in accountCalendars)
|
||||
{
|
||||
var accountCalendars = await _calendarService.GetAccountCalendarsAsync(account.Id).ConfigureAwait(false);
|
||||
var calendarViewModels = new List<AccountCalendarViewModel>();
|
||||
var calendarViewModel = new AccountCalendarViewModel(account, calendar);
|
||||
|
||||
foreach (var calendar in accountCalendars)
|
||||
{
|
||||
var calendarViewModel = new AccountCalendarViewModel(account, calendar);
|
||||
|
||||
calendarViewModels.Add(calendarViewModel);
|
||||
}
|
||||
|
||||
var groupedAccountCalendarViewModel = new GroupedAccountCalendarViewModel(account, calendarViewModels);
|
||||
|
||||
await Dispatcher.ExecuteOnUIThread(() =>
|
||||
{
|
||||
AccountCalendarStateService.AddGroupedAccountCalendar(groupedAccountCalendarViewModel);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void ForceNavigateCalendarDate()
|
||||
{
|
||||
if (SelectedMenuItemIndex == -1)
|
||||
{
|
||||
var args = new CalendarPageNavigationArgs()
|
||||
{
|
||||
NavigationDate = _navigationDate ?? DateTime.Now.Date
|
||||
};
|
||||
|
||||
// Already on calendar. Just navigate.
|
||||
NavigationService.Navigate(WinoPage.CalendarPage, args);
|
||||
|
||||
_navigationDate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedMenuItemIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnSelectedMenuItemIndexChanged(int oldValue, int newValue)
|
||||
{
|
||||
switch (newValue)
|
||||
{
|
||||
case -1:
|
||||
ForceNavigateCalendarDate();
|
||||
break;
|
||||
case 0:
|
||||
NavigationService.Navigate(WinoPage.ManageAccountsPage);
|
||||
break;
|
||||
case 1:
|
||||
NavigationService.Navigate(WinoPage.SettingsPage);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task Sync()
|
||||
{
|
||||
// Sync all calendars.
|
||||
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
|
||||
|
||||
foreach (var account in accounts)
|
||||
{
|
||||
var t = new NewCalendarSynchronizationRequested(new CalendarSynchronizationOptions()
|
||||
{
|
||||
AccountId = account.Id,
|
||||
Type = CalendarSynchronizationType.CalendarMetadata
|
||||
}, SynchronizationSource.Client);
|
||||
|
||||
Messenger.Send(t);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When calendar type switches, we need to navigate to the most ideal date.
|
||||
/// This method returns that date.
|
||||
/// </summary>
|
||||
private DateTime GetDisplayTypeSwitchDate()
|
||||
{
|
||||
var settings = PreferencesService.GetCurrentCalendarSettings();
|
||||
switch (StatePersistenceService.CalendarDisplayType)
|
||||
{
|
||||
case CalendarDisplayType.Day:
|
||||
if (HighlightedDateRange.IsInRange(DateTime.Now)) return DateTime.Now.Date;
|
||||
|
||||
return HighlightedDateRange.StartDate;
|
||||
case CalendarDisplayType.Week:
|
||||
if (HighlightedDateRange == null || HighlightedDateRange.IsInRange(DateTime.Now))
|
||||
{
|
||||
return DateTime.Now.Date.GetWeekStartDateForDate(settings.FirstDayOfWeek);
|
||||
}
|
||||
|
||||
return HighlightedDateRange.StartDate.GetWeekStartDateForDate(settings.FirstDayOfWeek);
|
||||
case CalendarDisplayType.WorkWeek:
|
||||
break;
|
||||
case CalendarDisplayType.Month:
|
||||
break;
|
||||
case CalendarDisplayType.Year:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
calendarViewModels.Add(calendarViewModel);
|
||||
}
|
||||
|
||||
return DateTime.Today.Date;
|
||||
}
|
||||
var groupedAccountCalendarViewModel = new GroupedAccountCalendarViewModel(account, calendarViewModels);
|
||||
|
||||
private DateTime? _navigationDate;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly ICalendarService _calendarService;
|
||||
|
||||
#region Commands
|
||||
|
||||
[RelayCommand]
|
||||
private void TodayClicked()
|
||||
{
|
||||
_navigationDate = DateTime.Now.Date;
|
||||
|
||||
ForceNavigateCalendarDate();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public void ManageAccounts() => NavigationService.Navigate(WinoPage.AccountManagementPage);
|
||||
|
||||
[RelayCommand]
|
||||
private Task ReconnectServerAsync() => ServerConnectionManager.ConnectAsync();
|
||||
|
||||
[RelayCommand]
|
||||
private void DateClicked(CalendarViewDayClickedEventArgs clickedDateArgs)
|
||||
{
|
||||
_navigationDate = clickedDateArgs.ClickedDate;
|
||||
|
||||
ForceNavigateCalendarDate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Receive(VisibleDateRangeChangedMessage message) => HighlightedDateRange = message.DateRange;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the header navigation items based on visible date range and calendar type.
|
||||
/// </summary>
|
||||
private void UpdateDateNavigationHeaderItems()
|
||||
{
|
||||
DateNavigationHeaderItems.Clear();
|
||||
|
||||
// TODO: From settings
|
||||
var testInfo = new CultureInfo("en-US");
|
||||
|
||||
switch (StatePersistenceService.CalendarDisplayType)
|
||||
await Dispatcher.ExecuteOnUIThread(() =>
|
||||
{
|
||||
case CalendarDisplayType.Day:
|
||||
case CalendarDisplayType.Week:
|
||||
case CalendarDisplayType.WorkWeek:
|
||||
case CalendarDisplayType.Month:
|
||||
DateNavigationHeaderItems.ReplaceRange(testInfo.DateTimeFormat.MonthNames);
|
||||
break;
|
||||
case CalendarDisplayType.Year:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
SetDateNavigationHeaderItems();
|
||||
}
|
||||
|
||||
partial void OnHighlightedDateRangeChanged(DateRange value) => SetDateNavigationHeaderItems();
|
||||
|
||||
private void SetDateNavigationHeaderItems()
|
||||
{
|
||||
if (HighlightedDateRange == null) return;
|
||||
|
||||
if (DateNavigationHeaderItems.Count == 0)
|
||||
{
|
||||
UpdateDateNavigationHeaderItems();
|
||||
}
|
||||
|
||||
// TODO: Year view
|
||||
var monthIndex = HighlightedDateRange.GetMostVisibleMonthIndex();
|
||||
|
||||
SelectedDateNavigationHeaderIndex = Math.Max(monthIndex - 1, -1);
|
||||
}
|
||||
|
||||
public async void Receive(CalendarEnableStatusChangedMessage message)
|
||||
=> await ExecuteUIThread(() => IsCalendarEnabled = message.IsEnabled);
|
||||
|
||||
public void Receive(NavigateManageAccountsRequested message) => SelectedMenuItemIndex = 1;
|
||||
|
||||
public void Receive(CalendarDisplayTypeChangedMessage message) => OnPropertyChanged(nameof(IsVerticalCalendar));
|
||||
|
||||
public async void Receive(DetailsPageStateChangedMessage message)
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
IsEventDetailsPageActive = message.IsActivated;
|
||||
|
||||
// TODO: This is for Wino Mail. Generalize this later on.
|
||||
StatePersistenceService.IsReaderNarrowed = message.IsActivated;
|
||||
StatePersistenceService.IsReadingMail = message.IsActivated;
|
||||
AccountCalendarStateService.AddGroupedAccountCalendar(groupedAccountCalendarViewModel);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void ForceNavigateCalendarDate()
|
||||
{
|
||||
if (SelectedMenuItemIndex == -1)
|
||||
{
|
||||
var args = new CalendarPageNavigationArgs()
|
||||
{
|
||||
NavigationDate = _navigationDate ?? DateTime.Now.Date
|
||||
};
|
||||
|
||||
// Already on calendar. Just navigate.
|
||||
NavigationService.Navigate(WinoPage.CalendarPage, args);
|
||||
|
||||
_navigationDate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedMenuItemIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnSelectedMenuItemIndexChanged(int oldValue, int newValue)
|
||||
{
|
||||
switch (newValue)
|
||||
{
|
||||
case -1:
|
||||
ForceNavigateCalendarDate();
|
||||
break;
|
||||
case 0:
|
||||
NavigationService.Navigate(WinoPage.ManageAccountsPage);
|
||||
break;
|
||||
case 1:
|
||||
NavigationService.Navigate(WinoPage.SettingsPage);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task Sync()
|
||||
{
|
||||
// Sync all calendars.
|
||||
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
|
||||
|
||||
foreach (var account in accounts)
|
||||
{
|
||||
var t = new NewCalendarSynchronizationRequested(new CalendarSynchronizationOptions()
|
||||
{
|
||||
AccountId = account.Id,
|
||||
Type = CalendarSynchronizationType.CalendarMetadata
|
||||
}, SynchronizationSource.Client);
|
||||
|
||||
Messenger.Send(t);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When calendar type switches, we need to navigate to the most ideal date.
|
||||
/// This method returns that date.
|
||||
/// </summary>
|
||||
private DateTime GetDisplayTypeSwitchDate()
|
||||
{
|
||||
var settings = PreferencesService.GetCurrentCalendarSettings();
|
||||
switch (StatePersistenceService.CalendarDisplayType)
|
||||
{
|
||||
case CalendarDisplayType.Day:
|
||||
if (HighlightedDateRange.IsInRange(DateTime.Now)) return DateTime.Now.Date;
|
||||
|
||||
return HighlightedDateRange.StartDate;
|
||||
case CalendarDisplayType.Week:
|
||||
if (HighlightedDateRange == null || HighlightedDateRange.IsInRange(DateTime.Now))
|
||||
{
|
||||
return DateTime.Now.Date.GetWeekStartDateForDate(settings.FirstDayOfWeek);
|
||||
}
|
||||
|
||||
return HighlightedDateRange.StartDate.GetWeekStartDateForDate(settings.FirstDayOfWeek);
|
||||
case CalendarDisplayType.WorkWeek:
|
||||
break;
|
||||
case CalendarDisplayType.Month:
|
||||
break;
|
||||
case CalendarDisplayType.Year:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return DateTime.Today.Date;
|
||||
}
|
||||
|
||||
private DateTime? _navigationDate;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly ICalendarService _calendarService;
|
||||
|
||||
#region Commands
|
||||
|
||||
[RelayCommand]
|
||||
private void TodayClicked()
|
||||
{
|
||||
_navigationDate = DateTime.Now.Date;
|
||||
|
||||
ForceNavigateCalendarDate();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public void ManageAccounts() => NavigationService.Navigate(WinoPage.AccountManagementPage);
|
||||
|
||||
[RelayCommand]
|
||||
private Task ReconnectServerAsync() => ServerConnectionManager.ConnectAsync();
|
||||
|
||||
[RelayCommand]
|
||||
private void DateClicked(CalendarViewDayClickedEventArgs clickedDateArgs)
|
||||
{
|
||||
_navigationDate = clickedDateArgs.ClickedDate;
|
||||
|
||||
ForceNavigateCalendarDate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Receive(VisibleDateRangeChangedMessage message) => HighlightedDateRange = message.DateRange;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the header navigation items based on visible date range and calendar type.
|
||||
/// </summary>
|
||||
private void UpdateDateNavigationHeaderItems()
|
||||
{
|
||||
DateNavigationHeaderItems.Clear();
|
||||
|
||||
// TODO: From settings
|
||||
var testInfo = new CultureInfo("en-US");
|
||||
|
||||
switch (StatePersistenceService.CalendarDisplayType)
|
||||
{
|
||||
case CalendarDisplayType.Day:
|
||||
case CalendarDisplayType.Week:
|
||||
case CalendarDisplayType.WorkWeek:
|
||||
case CalendarDisplayType.Month:
|
||||
DateNavigationHeaderItems.ReplaceRange(testInfo.DateTimeFormat.MonthNames);
|
||||
break;
|
||||
case CalendarDisplayType.Year:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
SetDateNavigationHeaderItems();
|
||||
}
|
||||
|
||||
partial void OnHighlightedDateRangeChanged(DateRange value) => SetDateNavigationHeaderItems();
|
||||
|
||||
private void SetDateNavigationHeaderItems()
|
||||
{
|
||||
if (HighlightedDateRange == null) return;
|
||||
|
||||
if (DateNavigationHeaderItems.Count == 0)
|
||||
{
|
||||
UpdateDateNavigationHeaderItems();
|
||||
}
|
||||
|
||||
// TODO: Year view
|
||||
var monthIndex = HighlightedDateRange.GetMostVisibleMonthIndex();
|
||||
|
||||
SelectedDateNavigationHeaderIndex = Math.Max(monthIndex - 1, -1);
|
||||
}
|
||||
|
||||
public async void Receive(CalendarEnableStatusChangedMessage message)
|
||||
=> await ExecuteUIThread(() => IsCalendarEnabled = message.IsEnabled);
|
||||
|
||||
public void Receive(NavigateManageAccountsRequested message) => SelectedMenuItemIndex = 1;
|
||||
|
||||
public void Receive(CalendarDisplayTypeChangedMessage message) => OnPropertyChanged(nameof(IsVerticalCalendar));
|
||||
|
||||
public async void Receive(DetailsPageStateChangedMessage message)
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
IsEventDetailsPageActive = message.IsActivated;
|
||||
|
||||
// TODO: This is for Wino Mail. Generalize this later on.
|
||||
StatePersistenceService.IsReaderNarrowed = message.IsActivated;
|
||||
StatePersistenceService.IsReadingMail = message.IsActivated;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,120 +8,119 @@ using Wino.Core.Domain.Translations;
|
||||
using Wino.Core.ViewModels;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Calendar.ViewModels
|
||||
namespace Wino.Calendar.ViewModels;
|
||||
|
||||
public partial class CalendarSettingsPageViewModel : CalendarBaseViewModel
|
||||
{
|
||||
public partial class CalendarSettingsPageViewModel : CalendarBaseViewModel
|
||||
[ObservableProperty]
|
||||
private double _cellHourHeight;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _selectedFirstDayOfWeekIndex;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _is24HourHeaders;
|
||||
|
||||
[ObservableProperty]
|
||||
private TimeSpan _workingHourStart;
|
||||
|
||||
[ObservableProperty]
|
||||
private TimeSpan _workingHourEnd;
|
||||
|
||||
[ObservableProperty]
|
||||
private List<string> _dayNames = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private int _workingDayStartIndex;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _workingDayEndIndex;
|
||||
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
|
||||
private readonly bool _isLoaded = false;
|
||||
|
||||
public CalendarSettingsPageViewModel(IPreferencesService preferencesService)
|
||||
{
|
||||
[ObservableProperty]
|
||||
private double _cellHourHeight;
|
||||
PreferencesService = preferencesService;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _selectedFirstDayOfWeekIndex;
|
||||
var currentLanguageLanguageCode = WinoTranslationDictionary.GetLanguageFileNameRelativePath(preferencesService.CurrentLanguage);
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _is24HourHeaders;
|
||||
var cultureInfo = new CultureInfo(currentLanguageLanguageCode);
|
||||
|
||||
[ObservableProperty]
|
||||
private TimeSpan _workingHourStart;
|
||||
|
||||
[ObservableProperty]
|
||||
private TimeSpan _workingHourEnd;
|
||||
|
||||
[ObservableProperty]
|
||||
private List<string> _dayNames = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private int _workingDayStartIndex;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _workingDayEndIndex;
|
||||
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
|
||||
private readonly bool _isLoaded = false;
|
||||
|
||||
public CalendarSettingsPageViewModel(IPreferencesService preferencesService)
|
||||
// Populate the day names list
|
||||
for (var i = 0; i < 7; i++)
|
||||
{
|
||||
PreferencesService = preferencesService;
|
||||
|
||||
var currentLanguageLanguageCode = WinoTranslationDictionary.GetLanguageFileNameRelativePath(preferencesService.CurrentLanguage);
|
||||
|
||||
var cultureInfo = new CultureInfo(currentLanguageLanguageCode);
|
||||
|
||||
// Populate the day names list
|
||||
for (var i = 0; i < 7; i++)
|
||||
{
|
||||
_dayNames.Add(cultureInfo.DateTimeFormat.DayNames[i]);
|
||||
}
|
||||
|
||||
var cultureFirstDayName = cultureInfo.DateTimeFormat.GetDayName(preferencesService.FirstDayOfWeek);
|
||||
|
||||
_selectedFirstDayOfWeekIndex = _dayNames.IndexOf(cultureFirstDayName);
|
||||
_is24HourHeaders = preferencesService.Prefer24HourTimeFormat;
|
||||
_workingHourStart = preferencesService.WorkingHourStart;
|
||||
_workingHourEnd = preferencesService.WorkingHourEnd;
|
||||
_cellHourHeight = preferencesService.HourHeight;
|
||||
|
||||
_workingDayStartIndex = _dayNames.IndexOf(cultureInfo.DateTimeFormat.GetDayName(preferencesService.WorkingDayStart));
|
||||
_workingDayEndIndex = _dayNames.IndexOf(cultureInfo.DateTimeFormat.GetDayName(preferencesService.WorkingDayEnd));
|
||||
|
||||
_isLoaded = true;
|
||||
_dayNames.Add(cultureInfo.DateTimeFormat.DayNames[i]);
|
||||
}
|
||||
|
||||
partial void OnCellHourHeightChanged(double oldValue, double newValue) => SaveSettings();
|
||||
partial void OnIs24HourHeadersChanged(bool value) => SaveSettings();
|
||||
partial void OnSelectedFirstDayOfWeekIndexChanged(int value) => SaveSettings();
|
||||
partial void OnWorkingHourStartChanged(TimeSpan value) => SaveSettings();
|
||||
partial void OnWorkingHourEndChanged(TimeSpan value) => SaveSettings();
|
||||
partial void OnWorkingDayStartIndexChanged(int value) => SaveSettings();
|
||||
partial void OnWorkingDayEndIndexChanged(int value) => SaveSettings();
|
||||
var cultureFirstDayName = cultureInfo.DateTimeFormat.GetDayName(preferencesService.FirstDayOfWeek);
|
||||
|
||||
public void SaveSettings()
|
||||
_selectedFirstDayOfWeekIndex = _dayNames.IndexOf(cultureFirstDayName);
|
||||
_is24HourHeaders = preferencesService.Prefer24HourTimeFormat;
|
||||
_workingHourStart = preferencesService.WorkingHourStart;
|
||||
_workingHourEnd = preferencesService.WorkingHourEnd;
|
||||
_cellHourHeight = preferencesService.HourHeight;
|
||||
|
||||
_workingDayStartIndex = _dayNames.IndexOf(cultureInfo.DateTimeFormat.GetDayName(preferencesService.WorkingDayStart));
|
||||
_workingDayEndIndex = _dayNames.IndexOf(cultureInfo.DateTimeFormat.GetDayName(preferencesService.WorkingDayEnd));
|
||||
|
||||
_isLoaded = true;
|
||||
}
|
||||
|
||||
partial void OnCellHourHeightChanged(double oldValue, double newValue) => SaveSettings();
|
||||
partial void OnIs24HourHeadersChanged(bool value) => SaveSettings();
|
||||
partial void OnSelectedFirstDayOfWeekIndexChanged(int value) => SaveSettings();
|
||||
partial void OnWorkingHourStartChanged(TimeSpan value) => SaveSettings();
|
||||
partial void OnWorkingHourEndChanged(TimeSpan value) => SaveSettings();
|
||||
partial void OnWorkingDayStartIndexChanged(int value) => SaveSettings();
|
||||
partial void OnWorkingDayEndIndexChanged(int value) => SaveSettings();
|
||||
|
||||
public void SaveSettings()
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
|
||||
PreferencesService.FirstDayOfWeek = SelectedFirstDayOfWeekIndex switch
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
0 => DayOfWeek.Sunday,
|
||||
1 => DayOfWeek.Monday,
|
||||
2 => DayOfWeek.Tuesday,
|
||||
3 => DayOfWeek.Wednesday,
|
||||
4 => DayOfWeek.Thursday,
|
||||
5 => DayOfWeek.Friday,
|
||||
6 => DayOfWeek.Saturday,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
PreferencesService.FirstDayOfWeek = SelectedFirstDayOfWeekIndex switch
|
||||
{
|
||||
0 => DayOfWeek.Sunday,
|
||||
1 => DayOfWeek.Monday,
|
||||
2 => DayOfWeek.Tuesday,
|
||||
3 => DayOfWeek.Wednesday,
|
||||
4 => DayOfWeek.Thursday,
|
||||
5 => DayOfWeek.Friday,
|
||||
6 => DayOfWeek.Saturday,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
PreferencesService.WorkingDayStart = WorkingDayStartIndex switch
|
||||
{
|
||||
0 => DayOfWeek.Sunday,
|
||||
1 => DayOfWeek.Monday,
|
||||
2 => DayOfWeek.Tuesday,
|
||||
3 => DayOfWeek.Wednesday,
|
||||
4 => DayOfWeek.Thursday,
|
||||
5 => DayOfWeek.Friday,
|
||||
6 => DayOfWeek.Saturday,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
PreferencesService.WorkingDayStart = WorkingDayStartIndex switch
|
||||
{
|
||||
0 => DayOfWeek.Sunday,
|
||||
1 => DayOfWeek.Monday,
|
||||
2 => DayOfWeek.Tuesday,
|
||||
3 => DayOfWeek.Wednesday,
|
||||
4 => DayOfWeek.Thursday,
|
||||
5 => DayOfWeek.Friday,
|
||||
6 => DayOfWeek.Saturday,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
PreferencesService.WorkingDayEnd = WorkingDayEndIndex switch
|
||||
{
|
||||
0 => DayOfWeek.Sunday,
|
||||
1 => DayOfWeek.Monday,
|
||||
2 => DayOfWeek.Tuesday,
|
||||
3 => DayOfWeek.Wednesday,
|
||||
4 => DayOfWeek.Thursday,
|
||||
5 => DayOfWeek.Friday,
|
||||
6 => DayOfWeek.Saturday,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
PreferencesService.WorkingDayEnd = WorkingDayEndIndex switch
|
||||
{
|
||||
0 => DayOfWeek.Sunday,
|
||||
1 => DayOfWeek.Monday,
|
||||
2 => DayOfWeek.Tuesday,
|
||||
3 => DayOfWeek.Wednesday,
|
||||
4 => DayOfWeek.Thursday,
|
||||
5 => DayOfWeek.Friday,
|
||||
6 => DayOfWeek.Saturday,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
PreferencesService.Prefer24HourTimeFormat = Is24HourHeaders;
|
||||
PreferencesService.WorkingHourStart = WorkingHourStart;
|
||||
PreferencesService.WorkingHourEnd = WorkingHourEnd;
|
||||
PreferencesService.HourHeight = CellHourHeight;
|
||||
|
||||
PreferencesService.Prefer24HourTimeFormat = Is24HourHeaders;
|
||||
PreferencesService.WorkingHourStart = WorkingHourStart;
|
||||
PreferencesService.WorkingHourEnd = WorkingHourEnd;
|
||||
PreferencesService.HourHeight = CellHourHeight;
|
||||
|
||||
Messenger.Send(new CalendarSettingsUpdatedMessage());
|
||||
}
|
||||
Messenger.Send(new CalendarSettingsUpdatedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Wino.Core;
|
||||
|
||||
namespace Wino.Calendar.ViewModels
|
||||
namespace Wino.Calendar.ViewModels;
|
||||
|
||||
public static class CalendarViewModelContainerSetup
|
||||
{
|
||||
public static class CalendarViewModelContainerSetup
|
||||
public static void RegisterCalendarViewModelServices(this IServiceCollection services)
|
||||
{
|
||||
public static void RegisterCalendarViewModelServices(this IServiceCollection services)
|
||||
{
|
||||
services.RegisterCoreServices();
|
||||
}
|
||||
services.RegisterCoreServices();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,67 +4,66 @@ using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Calendar.ViewModels.Data
|
||||
namespace Wino.Calendar.ViewModels.Data;
|
||||
|
||||
public partial class AccountCalendarViewModel : ObservableObject, IAccountCalendar
|
||||
{
|
||||
public partial class AccountCalendarViewModel : ObservableObject, IAccountCalendar
|
||||
public MailAccount Account { get; }
|
||||
public AccountCalendar AccountCalendar { get; }
|
||||
|
||||
public AccountCalendarViewModel(MailAccount account, AccountCalendar accountCalendar)
|
||||
{
|
||||
public MailAccount Account { get; }
|
||||
public AccountCalendar AccountCalendar { get; }
|
||||
Account = account;
|
||||
AccountCalendar = accountCalendar;
|
||||
|
||||
public AccountCalendarViewModel(MailAccount account, AccountCalendar accountCalendar)
|
||||
{
|
||||
Account = account;
|
||||
AccountCalendar = accountCalendar;
|
||||
|
||||
IsChecked = accountCalendar.IsExtended;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isChecked;
|
||||
|
||||
partial void OnIsCheckedChanged(bool value) => IsExtended = value;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => AccountCalendar.Name;
|
||||
set => SetProperty(AccountCalendar.Name, value, AccountCalendar, (u, n) => u.Name = n);
|
||||
}
|
||||
|
||||
public string TextColorHex
|
||||
{
|
||||
get => AccountCalendar.TextColorHex;
|
||||
set => SetProperty(AccountCalendar.TextColorHex, value, AccountCalendar, (u, t) => u.TextColorHex = t);
|
||||
}
|
||||
|
||||
public string BackgroundColorHex
|
||||
{
|
||||
get => AccountCalendar.BackgroundColorHex;
|
||||
set => SetProperty(AccountCalendar.BackgroundColorHex, value, AccountCalendar, (u, b) => u.BackgroundColorHex = b);
|
||||
}
|
||||
|
||||
public bool IsExtended
|
||||
{
|
||||
get => AccountCalendar.IsExtended;
|
||||
set => SetProperty(AccountCalendar.IsExtended, value, AccountCalendar, (u, i) => u.IsExtended = i);
|
||||
}
|
||||
|
||||
public bool IsPrimary
|
||||
{
|
||||
get => AccountCalendar.IsPrimary;
|
||||
set => SetProperty(AccountCalendar.IsPrimary, value, AccountCalendar, (u, i) => u.IsPrimary = i);
|
||||
}
|
||||
|
||||
public Guid AccountId
|
||||
{
|
||||
get => AccountCalendar.AccountId;
|
||||
set => SetProperty(AccountCalendar.AccountId, value, AccountCalendar, (u, a) => u.AccountId = a);
|
||||
}
|
||||
|
||||
public string RemoteCalendarId
|
||||
{
|
||||
get => AccountCalendar.RemoteCalendarId;
|
||||
set => SetProperty(AccountCalendar.RemoteCalendarId, value, AccountCalendar, (u, r) => u.RemoteCalendarId = r);
|
||||
}
|
||||
public Guid Id { get => ((IAccountCalendar)AccountCalendar).Id; set => ((IAccountCalendar)AccountCalendar).Id = value; }
|
||||
IsChecked = accountCalendar.IsExtended;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isChecked;
|
||||
|
||||
partial void OnIsCheckedChanged(bool value) => IsExtended = value;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => AccountCalendar.Name;
|
||||
set => SetProperty(AccountCalendar.Name, value, AccountCalendar, (u, n) => u.Name = n);
|
||||
}
|
||||
|
||||
public string TextColorHex
|
||||
{
|
||||
get => AccountCalendar.TextColorHex;
|
||||
set => SetProperty(AccountCalendar.TextColorHex, value, AccountCalendar, (u, t) => u.TextColorHex = t);
|
||||
}
|
||||
|
||||
public string BackgroundColorHex
|
||||
{
|
||||
get => AccountCalendar.BackgroundColorHex;
|
||||
set => SetProperty(AccountCalendar.BackgroundColorHex, value, AccountCalendar, (u, b) => u.BackgroundColorHex = b);
|
||||
}
|
||||
|
||||
public bool IsExtended
|
||||
{
|
||||
get => AccountCalendar.IsExtended;
|
||||
set => SetProperty(AccountCalendar.IsExtended, value, AccountCalendar, (u, i) => u.IsExtended = i);
|
||||
}
|
||||
|
||||
public bool IsPrimary
|
||||
{
|
||||
get => AccountCalendar.IsPrimary;
|
||||
set => SetProperty(AccountCalendar.IsPrimary, value, AccountCalendar, (u, i) => u.IsPrimary = i);
|
||||
}
|
||||
|
||||
public Guid AccountId
|
||||
{
|
||||
get => AccountCalendar.AccountId;
|
||||
set => SetProperty(AccountCalendar.AccountId, value, AccountCalendar, (u, a) => u.AccountId = a);
|
||||
}
|
||||
|
||||
public string RemoteCalendarId
|
||||
{
|
||||
get => AccountCalendar.RemoteCalendarId;
|
||||
set => SetProperty(AccountCalendar.RemoteCalendarId, value, AccountCalendar, (u, r) => u.RemoteCalendarId = r);
|
||||
}
|
||||
public Guid Id { get => ((IAccountCalendar)AccountCalendar).Id; set => ((IAccountCalendar)AccountCalendar).Id = value; }
|
||||
}
|
||||
|
||||
@@ -1,46 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Itenso.TimePeriod;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Calendar.ViewModels.Data
|
||||
namespace Wino.Calendar.ViewModels.Data;
|
||||
|
||||
public partial class CalendarItemViewModel : ObservableObject, ICalendarItem, ICalendarItemViewModel
|
||||
{
|
||||
public partial class CalendarItemViewModel : ObservableObject, ICalendarItem, ICalendarItemViewModel
|
||||
public CalendarItem CalendarItem { get; }
|
||||
|
||||
public string Title => CalendarItem.Title;
|
||||
|
||||
public Guid Id => CalendarItem.Id;
|
||||
|
||||
public IAccountCalendar AssignedCalendar => CalendarItem.AssignedCalendar;
|
||||
|
||||
public DateTime StartDateTime { get => CalendarItem.StartDateTime; set => CalendarItem.StartDateTime = value; }
|
||||
|
||||
public DateTime EndDateTime => CalendarItem.EndDateTime;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the start date and time in the local time zone for display purposes.
|
||||
/// </summary>
|
||||
public DateTime LocalStartDateTime => ConvertToLocalTime();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the end date and time in the local time zone for display purposes.
|
||||
/// </summary>
|
||||
public DateTime LocalEndDateTime => ConvertToLocalTime();
|
||||
|
||||
public ITimePeriod Period => CalendarItem.Period;
|
||||
|
||||
public bool IsRecurringEvent => !string.IsNullOrEmpty(CalendarItem.RecurrenceRules) || !string.IsNullOrEmpty(CalendarItem.RecurringEventId);
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isSelected;
|
||||
|
||||
public ObservableCollection<CalendarEventAttendee> Attendees { get; } = new ObservableCollection<CalendarEventAttendee>();
|
||||
|
||||
public CalendarItemType ItemType => CalendarItem.ItemType;
|
||||
|
||||
public CalendarItemViewModel(CalendarItem calendarItem)
|
||||
{
|
||||
public CalendarItem CalendarItem { get; }
|
||||
CalendarItem = calendarItem;
|
||||
|
||||
public string Title => CalendarItem.Title;
|
||||
Debug.WriteLine($"{Title} : {ItemType}");
|
||||
}
|
||||
|
||||
public Guid Id => CalendarItem.Id;
|
||||
/// <summary>
|
||||
/// Converts a DateTime to local time based on the provided timezone.
|
||||
/// If timezone is empty or null, assumes the DateTime is in UTC.
|
||||
/// </summary>
|
||||
/// <param name="dateTime">The DateTime to convert</param>
|
||||
/// <param name="timeZone">The timezone string. If empty/null, assumes UTC.</param>
|
||||
/// <returns>DateTime converted to local time</returns>
|
||||
private DateTime ConvertToLocalTime()
|
||||
{
|
||||
// All day events ignore time zones and are treated as local time.
|
||||
if (ItemType == CalendarItemType.AllDay || ItemType == CalendarItemType.MultiDayAllDay || ItemType == CalendarItemType.RecurringAllDay)
|
||||
return CalendarItem.StartDateTime;
|
||||
|
||||
public IAccountCalendar AssignedCalendar => CalendarItem.AssignedCalendar;
|
||||
|
||||
public DateTime StartDate { get => CalendarItem.StartDate; set => CalendarItem.StartDate = value; }
|
||||
|
||||
public DateTime EndDate => CalendarItem.EndDate;
|
||||
|
||||
public double DurationInSeconds { get => CalendarItem.DurationInSeconds; set => CalendarItem.DurationInSeconds = value; }
|
||||
|
||||
public ITimePeriod Period => CalendarItem.Period;
|
||||
|
||||
public bool IsAllDayEvent => CalendarItem.IsAllDayEvent;
|
||||
public bool IsMultiDayEvent => CalendarItem.IsMultiDayEvent;
|
||||
public bool IsRecurringEvent => CalendarItem.IsRecurringEvent;
|
||||
public bool IsRecurringChild => CalendarItem.IsRecurringChild;
|
||||
public bool IsRecurringParent => CalendarItem.IsRecurringParent;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isSelected;
|
||||
|
||||
public ObservableCollection<CalendarEventAttendee> Attendees { get; } = new ObservableCollection<CalendarEventAttendee>();
|
||||
|
||||
public CalendarItemViewModel(CalendarItem calendarItem)
|
||||
if (string.IsNullOrEmpty(CalendarItem.TimeZone))
|
||||
{
|
||||
CalendarItem = calendarItem;
|
||||
// If no timezone specified, assume it's UTC and convert to local time
|
||||
return DateTime.SpecifyKind(CalendarItem.StartDateTime, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
|
||||
public override string ToString() => CalendarItem.Title;
|
||||
try
|
||||
{
|
||||
// Parse the timezone and convert to local time
|
||||
var sourceTimeZone = TimeZoneInfo.FindSystemTimeZoneById(CalendarItem.TimeZone);
|
||||
return TimeZoneInfo.ConvertTimeToUtc(CalendarItem.StartDateTime, sourceTimeZone).ToLocalTime();
|
||||
}
|
||||
catch (TimeZoneNotFoundException)
|
||||
{
|
||||
// If timezone is not found, fallback to treating as UTC
|
||||
return DateTime.SpecifyKind(CalendarItem.StartDateTime, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
catch (InvalidTimeZoneException)
|
||||
{
|
||||
// If timezone is invalid, fallback to treating as UTC
|
||||
return DateTime.SpecifyKind(CalendarItem.StartDateTime, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => CalendarItem.Title;
|
||||
}
|
||||
|
||||
@@ -6,141 +6,140 @@ using System.Linq;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
namespace Wino.Calendar.ViewModels.Data
|
||||
namespace Wino.Calendar.ViewModels.Data;
|
||||
|
||||
public partial class GroupedAccountCalendarViewModel : ObservableObject
|
||||
{
|
||||
public partial class GroupedAccountCalendarViewModel : ObservableObject
|
||||
public event EventHandler CollectiveSelectionStateChanged;
|
||||
public event EventHandler<AccountCalendarViewModel> CalendarSelectionStateChanged;
|
||||
|
||||
public MailAccount Account { get; }
|
||||
public ObservableCollection<AccountCalendarViewModel> AccountCalendars { get; }
|
||||
|
||||
public GroupedAccountCalendarViewModel(MailAccount account, IEnumerable<AccountCalendarViewModel> calendarViewModels)
|
||||
{
|
||||
public event EventHandler CollectiveSelectionStateChanged;
|
||||
public event EventHandler<AccountCalendarViewModel> CalendarSelectionStateChanged;
|
||||
Account = account;
|
||||
AccountCalendars = new ObservableCollection<AccountCalendarViewModel>(calendarViewModels);
|
||||
|
||||
public MailAccount Account { get; }
|
||||
public ObservableCollection<AccountCalendarViewModel> AccountCalendars { get; }
|
||||
ManageIsCheckedState();
|
||||
|
||||
public GroupedAccountCalendarViewModel(MailAccount account, IEnumerable<AccountCalendarViewModel> calendarViewModels)
|
||||
foreach (var calendarViewModel in calendarViewModels)
|
||||
{
|
||||
Account = account;
|
||||
AccountCalendars = new ObservableCollection<AccountCalendarViewModel>(calendarViewModels);
|
||||
|
||||
ManageIsCheckedState();
|
||||
|
||||
foreach (var calendarViewModel in calendarViewModels)
|
||||
{
|
||||
calendarViewModel.PropertyChanged += CalendarPropertyChanged;
|
||||
}
|
||||
|
||||
AccountCalendars.CollectionChanged += CalendarListUpdated;
|
||||
calendarViewModel.PropertyChanged += CalendarPropertyChanged;
|
||||
}
|
||||
|
||||
private void CalendarListUpdated(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
AccountCalendars.CollectionChanged += CalendarListUpdated;
|
||||
}
|
||||
|
||||
private void CalendarListUpdated(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
foreach (AccountCalendarViewModel calendar in e.NewItems)
|
||||
{
|
||||
foreach (AccountCalendarViewModel calendar in e.NewItems)
|
||||
{
|
||||
calendar.PropertyChanged += CalendarPropertyChanged;
|
||||
}
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
foreach (AccountCalendarViewModel calendar in e.OldItems)
|
||||
{
|
||||
calendar.PropertyChanged -= CalendarPropertyChanged;
|
||||
}
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Reset)
|
||||
{
|
||||
foreach (AccountCalendarViewModel calendar in e.OldItems)
|
||||
{
|
||||
calendar.PropertyChanged -= CalendarPropertyChanged;
|
||||
}
|
||||
calendar.PropertyChanged += CalendarPropertyChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void CalendarPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
if (sender is AccountCalendarViewModel viewModel)
|
||||
foreach (AccountCalendarViewModel calendar in e.OldItems)
|
||||
{
|
||||
if (e.PropertyName == nameof(AccountCalendarViewModel.IsChecked))
|
||||
{
|
||||
ManageIsCheckedState();
|
||||
UpdateCalendarCheckedState(viewModel, viewModel.IsChecked, true);
|
||||
}
|
||||
calendar.PropertyChanged -= CalendarPropertyChanged;
|
||||
}
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isExpanded = true;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool? isCheckedState = true;
|
||||
|
||||
private bool _isExternalPropChangeBlocked = false;
|
||||
|
||||
private void ManageIsCheckedState()
|
||||
else if (e.Action == NotifyCollectionChangedAction.Reset)
|
||||
{
|
||||
if (_isExternalPropChangeBlocked) return;
|
||||
|
||||
_isExternalPropChangeBlocked = true;
|
||||
|
||||
if (AccountCalendars.All(c => c.IsChecked))
|
||||
foreach (AccountCalendarViewModel calendar in e.OldItems)
|
||||
{
|
||||
IsCheckedState = true;
|
||||
calendar.PropertyChanged -= CalendarPropertyChanged;
|
||||
}
|
||||
else if (AccountCalendars.All(c => !c.IsChecked))
|
||||
{
|
||||
IsCheckedState = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsCheckedState = null;
|
||||
}
|
||||
|
||||
_isExternalPropChangeBlocked = false;
|
||||
}
|
||||
|
||||
partial void OnIsCheckedStateChanged(bool? newValue)
|
||||
{
|
||||
if (_isExternalPropChangeBlocked) return;
|
||||
|
||||
// Update is triggered by user on the three-state checkbox.
|
||||
// We should not report all changes one by one.
|
||||
|
||||
_isExternalPropChangeBlocked = true;
|
||||
|
||||
if (newValue == null)
|
||||
{
|
||||
// Only primary calendars must be checked.
|
||||
|
||||
foreach (var calendar in AccountCalendars)
|
||||
{
|
||||
UpdateCalendarCheckedState(calendar, calendar.IsPrimary);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var calendar in AccountCalendars)
|
||||
{
|
||||
UpdateCalendarCheckedState(calendar, newValue.GetValueOrDefault());
|
||||
}
|
||||
}
|
||||
|
||||
_isExternalPropChangeBlocked = false;
|
||||
|
||||
CollectiveSelectionStateChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void UpdateCalendarCheckedState(AccountCalendarViewModel accountCalendarViewModel, bool newValue, bool ignoreValueCheck = false)
|
||||
{
|
||||
var currentValue = accountCalendarViewModel.IsChecked;
|
||||
|
||||
if (currentValue == newValue && !ignoreValueCheck) return;
|
||||
|
||||
accountCalendarViewModel.IsChecked = newValue;
|
||||
|
||||
// No need to report.
|
||||
if (_isExternalPropChangeBlocked == true) return;
|
||||
|
||||
CalendarSelectionStateChanged?.Invoke(this, accountCalendarViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
private void CalendarPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (sender is AccountCalendarViewModel viewModel)
|
||||
{
|
||||
if (e.PropertyName == nameof(AccountCalendarViewModel.IsChecked))
|
||||
{
|
||||
ManageIsCheckedState();
|
||||
UpdateCalendarCheckedState(viewModel, viewModel.IsChecked, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isExpanded = true;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool? isCheckedState = true;
|
||||
|
||||
private bool _isExternalPropChangeBlocked = false;
|
||||
|
||||
private void ManageIsCheckedState()
|
||||
{
|
||||
if (_isExternalPropChangeBlocked) return;
|
||||
|
||||
_isExternalPropChangeBlocked = true;
|
||||
|
||||
if (AccountCalendars.All(c => c.IsChecked))
|
||||
{
|
||||
IsCheckedState = true;
|
||||
}
|
||||
else if (AccountCalendars.All(c => !c.IsChecked))
|
||||
{
|
||||
IsCheckedState = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsCheckedState = null;
|
||||
}
|
||||
|
||||
_isExternalPropChangeBlocked = false;
|
||||
}
|
||||
|
||||
partial void OnIsCheckedStateChanged(bool? newValue)
|
||||
{
|
||||
if (_isExternalPropChangeBlocked) return;
|
||||
|
||||
// Update is triggered by user on the three-state checkbox.
|
||||
// We should not report all changes one by one.
|
||||
|
||||
_isExternalPropChangeBlocked = true;
|
||||
|
||||
if (newValue == null)
|
||||
{
|
||||
// Only primary calendars must be checked.
|
||||
|
||||
foreach (var calendar in AccountCalendars)
|
||||
{
|
||||
UpdateCalendarCheckedState(calendar, calendar.IsPrimary);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var calendar in AccountCalendars)
|
||||
{
|
||||
UpdateCalendarCheckedState(calendar, newValue.GetValueOrDefault());
|
||||
}
|
||||
}
|
||||
|
||||
_isExternalPropChangeBlocked = false;
|
||||
|
||||
CollectiveSelectionStateChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void UpdateCalendarCheckedState(AccountCalendarViewModel accountCalendarViewModel, bool newValue, bool ignoreValueCheck = false)
|
||||
{
|
||||
var currentValue = accountCalendarViewModel.IsChecked;
|
||||
|
||||
if (currentValue == newValue && !ignoreValueCheck) return;
|
||||
|
||||
accountCalendarViewModel.IsChecked = newValue;
|
||||
|
||||
// No need to report.
|
||||
if (_isExternalPropChangeBlocked == true) return;
|
||||
|
||||
CalendarSelectionStateChanged?.Invoke(this, accountCalendarViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,105 +12,104 @@ using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.ViewModels;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Calendar.ViewModels
|
||||
namespace Wino.Calendar.ViewModels;
|
||||
|
||||
public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
{
|
||||
public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
private readonly ICalendarService _calendarService;
|
||||
private readonly INativeAppService _nativeAppService;
|
||||
private readonly IPreferencesService _preferencesService;
|
||||
|
||||
public CalendarSettings CurrentSettings { get; }
|
||||
|
||||
#region Details
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(CanViewSeries))]
|
||||
private CalendarItemViewModel _currentEvent;
|
||||
|
||||
[ObservableProperty]
|
||||
private CalendarItemViewModel _seriesParent;
|
||||
|
||||
public bool CanViewSeries => false; //CurrentEvent?.IsRecurringChild ?? false; // TODO: Implement this properly
|
||||
|
||||
#endregion
|
||||
|
||||
public EventDetailsPageViewModel(ICalendarService calendarService, INativeAppService nativeAppService, IPreferencesService preferencesService)
|
||||
{
|
||||
private readonly ICalendarService _calendarService;
|
||||
private readonly INativeAppService _nativeAppService;
|
||||
private readonly IPreferencesService _preferencesService;
|
||||
_calendarService = calendarService;
|
||||
_nativeAppService = nativeAppService;
|
||||
_preferencesService = preferencesService;
|
||||
|
||||
public CalendarSettings CurrentSettings { get; }
|
||||
CurrentSettings = _preferencesService.GetCurrentCalendarSettings();
|
||||
}
|
||||
|
||||
#region Details
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(CanViewSeries))]
|
||||
private CalendarItemViewModel _currentEvent;
|
||||
Messenger.Send(new DetailsPageStateChangedMessage(true));
|
||||
|
||||
[ObservableProperty]
|
||||
private CalendarItemViewModel _seriesParent;
|
||||
if (parameters == null || parameters is not CalendarItemTarget args)
|
||||
return;
|
||||
|
||||
public bool CanViewSeries => CurrentEvent?.IsRecurringChild ?? false;
|
||||
await LoadCalendarItemTargetAsync(args);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public EventDetailsPageViewModel(ICalendarService calendarService, INativeAppService nativeAppService, IPreferencesService preferencesService)
|
||||
private async Task LoadCalendarItemTargetAsync(CalendarItemTarget target)
|
||||
{
|
||||
try
|
||||
{
|
||||
_calendarService = calendarService;
|
||||
_nativeAppService = nativeAppService;
|
||||
_preferencesService = preferencesService;
|
||||
var currentEventItem = await _calendarService.GetCalendarItemTargetAsync(target);
|
||||
|
||||
CurrentSettings = _preferencesService.GetCurrentCalendarSettings();
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
Messenger.Send(new DetailsPageStateChangedMessage(true));
|
||||
|
||||
if (parameters == null || parameters is not CalendarItemTarget args)
|
||||
if (currentEventItem == null)
|
||||
return;
|
||||
|
||||
await LoadCalendarItemTargetAsync(args);
|
||||
}
|
||||
CurrentEvent = new CalendarItemViewModel(currentEventItem);
|
||||
|
||||
private async Task LoadCalendarItemTargetAsync(CalendarItemTarget target)
|
||||
{
|
||||
try
|
||||
var attendees = await _calendarService.GetAttendeesAsync(currentEventItem.Id);
|
||||
|
||||
foreach (var item in attendees)
|
||||
{
|
||||
var currentEventItem = await _calendarService.GetCalendarItemTargetAsync(target);
|
||||
|
||||
if (currentEventItem == null)
|
||||
return;
|
||||
|
||||
CurrentEvent = new CalendarItemViewModel(currentEventItem);
|
||||
|
||||
var attendees = await _calendarService.GetAttendeesAsync(currentEventItem.EventTrackingId);
|
||||
|
||||
foreach (var item in attendees)
|
||||
{
|
||||
CurrentEvent.Attendees.Add(item);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
CurrentEvent.Attendees.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNavigatedFrom(NavigationMode mode, object parameters)
|
||||
catch (Exception ex)
|
||||
{
|
||||
base.OnNavigatedFrom(mode, parameters);
|
||||
|
||||
Messenger.Send(new DetailsPageStateChangedMessage(false));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SaveAsync()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task DeleteAsync()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private Task JoinOnline()
|
||||
{
|
||||
if (CurrentEvent == null || string.IsNullOrEmpty(CurrentEvent.CalendarItem.HtmlLink)) return Task.CompletedTask;
|
||||
|
||||
return _nativeAppService.LaunchUriAsync(new Uri(CurrentEvent.CalendarItem.HtmlLink));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task Respond(CalendarItemStatus status)
|
||||
{
|
||||
if (CurrentEvent == null) return;
|
||||
Debug.WriteLine(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNavigatedFrom(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedFrom(mode, parameters);
|
||||
|
||||
Messenger.Send(new DetailsPageStateChangedMessage(false));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SaveAsync()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task DeleteAsync()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private Task JoinOnline()
|
||||
{
|
||||
if (CurrentEvent == null || string.IsNullOrEmpty(CurrentEvent.CalendarItem.HtmlLink)) return Task.CompletedTask;
|
||||
|
||||
return _nativeAppService.LaunchUriAsync(new Uri(CurrentEvent.CalendarItem.HtmlLink));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task Respond(CalendarItemStatus status)
|
||||
{
|
||||
if (CurrentEvent == null) return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,26 +6,25 @@ using System.Linq;
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
namespace Wino.Calendar.ViewModels.Interfaces
|
||||
namespace Wino.Calendar.ViewModels.Interfaces;
|
||||
|
||||
public interface IAccountCalendarStateService : INotifyPropertyChanged
|
||||
{
|
||||
public interface IAccountCalendarStateService : INotifyPropertyChanged
|
||||
{
|
||||
ReadOnlyObservableCollection<GroupedAccountCalendarViewModel> GroupedAccountCalendars { get; }
|
||||
ReadOnlyObservableCollection<GroupedAccountCalendarViewModel> GroupedAccountCalendars { get; }
|
||||
|
||||
event EventHandler<GroupedAccountCalendarViewModel> CollectiveAccountGroupSelectionStateChanged;
|
||||
event EventHandler<AccountCalendarViewModel> AccountCalendarSelectionStateChanged;
|
||||
event EventHandler<GroupedAccountCalendarViewModel> CollectiveAccountGroupSelectionStateChanged;
|
||||
event EventHandler<AccountCalendarViewModel> AccountCalendarSelectionStateChanged;
|
||||
|
||||
public void AddGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar);
|
||||
public void RemoveGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar);
|
||||
public void ClearGroupedAccountCalendar();
|
||||
public void AddGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar);
|
||||
public void RemoveGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar);
|
||||
public void ClearGroupedAccountCalendar();
|
||||
|
||||
public void AddAccountCalendar(AccountCalendarViewModel accountCalendar);
|
||||
public void RemoveAccountCalendar(AccountCalendarViewModel accountCalendar);
|
||||
public void AddAccountCalendar(AccountCalendarViewModel accountCalendar);
|
||||
public void RemoveAccountCalendar(AccountCalendarViewModel accountCalendar);
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of currently selected calendars.
|
||||
/// </summary>
|
||||
IEnumerable<AccountCalendarViewModel> ActiveCalendars { get; }
|
||||
IEnumerable<IGrouping<MailAccount, AccountCalendarViewModel>> GroupedAccountCalendarsEnumerable { get; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Enumeration of currently selected calendars.
|
||||
/// </summary>
|
||||
IEnumerable<AccountCalendarViewModel> ActiveCalendars { get; }
|
||||
IEnumerable<IGrouping<MailAccount, AccountCalendarViewModel>> GroupedAccountCalendarsEnumerable { get; }
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
|
||||
namespace Wino.Calendar.ViewModels.Messages
|
||||
{
|
||||
public class CalendarItemDoubleTappedMessage
|
||||
{
|
||||
public CalendarItemDoubleTappedMessage(CalendarItemViewModel calendarItemViewModel)
|
||||
{
|
||||
CalendarItemViewModel = calendarItemViewModel;
|
||||
}
|
||||
namespace Wino.Calendar.ViewModels.Messages;
|
||||
|
||||
public CalendarItemViewModel CalendarItemViewModel { get; }
|
||||
public class CalendarItemDoubleTappedMessage
|
||||
{
|
||||
public CalendarItemDoubleTappedMessage(CalendarItemViewModel calendarItemViewModel)
|
||||
{
|
||||
CalendarItemViewModel = calendarItemViewModel;
|
||||
}
|
||||
|
||||
public CalendarItemViewModel CalendarItemViewModel { get; }
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
|
||||
namespace Wino.Calendar.ViewModels.Messages
|
||||
{
|
||||
public class CalendarItemRightTappedMessage
|
||||
{
|
||||
public CalendarItemRightTappedMessage(CalendarItemViewModel calendarItemViewModel)
|
||||
{
|
||||
CalendarItemViewModel = calendarItemViewModel;
|
||||
}
|
||||
namespace Wino.Calendar.ViewModels.Messages;
|
||||
|
||||
public CalendarItemViewModel CalendarItemViewModel { get; }
|
||||
public class CalendarItemRightTappedMessage
|
||||
{
|
||||
public CalendarItemRightTappedMessage(CalendarItemViewModel calendarItemViewModel)
|
||||
{
|
||||
CalendarItemViewModel = calendarItemViewModel;
|
||||
}
|
||||
|
||||
public CalendarItemViewModel CalendarItemViewModel { get; }
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Calendar.ViewModels.Messages
|
||||
{
|
||||
public class CalendarItemTappedMessage
|
||||
{
|
||||
public CalendarItemTappedMessage(CalendarItemViewModel calendarItemViewModel, CalendarDayModel clickedPeriod)
|
||||
{
|
||||
CalendarItemViewModel = calendarItemViewModel;
|
||||
ClickedPeriod = clickedPeriod;
|
||||
}
|
||||
namespace Wino.Calendar.ViewModels.Messages;
|
||||
|
||||
public CalendarItemViewModel CalendarItemViewModel { get; }
|
||||
public CalendarDayModel ClickedPeriod { get; }
|
||||
public class CalendarItemTappedMessage
|
||||
{
|
||||
public CalendarItemTappedMessage(CalendarItemViewModel calendarItemViewModel, CalendarDayModel clickedPeriod)
|
||||
{
|
||||
CalendarItemViewModel = calendarItemViewModel;
|
||||
ClickedPeriod = clickedPeriod;
|
||||
}
|
||||
|
||||
public CalendarItemViewModel CalendarItemViewModel { get; }
|
||||
public CalendarDayModel ClickedPeriod { get; }
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>12</LangVersion>
|
||||
<Platforms>AnyCPU;x64;x86</Platforms>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Platforms>x86;x64;arm64</Platforms>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
|
||||
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TimePeriodLibrary.NET" Version="2.1.5" />
|
||||
<PackageReference Include="TimePeriodLibrary.NET" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,308 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.12.35424.110
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wino.Core.Domain", "Wino.Core.Domain\Wino.Core.Domain.csproj", "{814400B6-5A05-4596-B451-3A116A147DC1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wino.Core.UWP", "Wino.Core.UWP\Wino.Core.UWP.csproj", "{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wino.Core.ViewModels", "Wino.Core.ViewModels\Wino.Core.ViewModels.csproj", "{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wino.Messaging", "Wino.Messages\Wino.Messaging.csproj", "{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wino.Server", "Wino.Server\Wino.Server.csproj", "{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wino.Core", "Wino.Core\Wino.Core.csproj", "{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wino.Calendar", "Wino.Calendar\Wino.Calendar.csproj", "{600F4979-DB7E-409D-B7DA-B60BE4C55C35}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wino.SourceGenerators", "Wino.SourceGenerators\Wino.SourceGenerators.csproj", "{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}"
|
||||
EndProject
|
||||
Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "Wino.Calendar.Packaging", "Wino.Calendar.Packaging\Wino.Calendar.Packaging.wapproj", "{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wino.Calendar.ViewModels", "Wino.Calendar.ViewModels\Wino.Calendar.ViewModels.csproj", "{CF850F8C-5042-4376-9CBA-C8F2BB554083}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wino.Services", "Wino.Services\Wino.Services.csproj", "{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wino.Authentication", "Wino.Authentication\Wino.Authentication.csproj", "{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM = Release|ARM
|
||||
Release|ARM64 = Release|ARM64
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Debug|x64.Build.0 = Debug|x64
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Debug|x86.Build.0 = Debug|x86
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Release|x64.ActiveCfg = Release|x64
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Release|x64.Build.0 = Release|x64
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Release|x86.ActiveCfg = Release|x86
|
||||
{814400B6-5A05-4596-B451-3A116A147DC1}.Release|x86.Build.0 = Release|x86
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|x64.Build.0 = Debug|x64
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|x86.Build.0 = Debug|x86
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|x64.ActiveCfg = Release|x64
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|x64.Build.0 = Release|x64
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|x86.ActiveCfg = Release|x86
|
||||
{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|x86.Build.0 = Release|x86
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Debug|x64.Build.0 = Debug|x64
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Debug|x86.Build.0 = Debug|x86
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Release|x64.ActiveCfg = Release|x64
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Release|x64.Build.0 = Release|x64
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Release|x86.ActiveCfg = Release|x86
|
||||
{510CD96C-B3FF-4EC9-A67B-845C842E6BEC}.Release|x86.Build.0 = Release|x86
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Debug|x64.Build.0 = Debug|x64
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Debug|x86.Build.0 = Debug|x86
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Release|x64.ActiveCfg = Release|x64
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Release|x64.Build.0 = Release|x64
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Release|x86.ActiveCfg = Release|x86
|
||||
{AB588CFD-4B0C-4A1F-B711-1999E3D092D0}.Release|x86.Build.0 = Release|x86
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Debug|ARM.ActiveCfg = Debug|x64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Debug|ARM.Build.0 = Debug|x64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Debug|x64.Build.0 = Debug|x64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Debug|x86.Build.0 = Debug|x86
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Release|Any CPU.Build.0 = Release|x64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Release|ARM.ActiveCfg = Release|x64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Release|ARM.Build.0 = Release|x64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Release|x64.ActiveCfg = Release|x64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Release|x64.Build.0 = Release|x64
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Release|x86.ActiveCfg = Release|x86
|
||||
{92DA33FC-9252-40C5-BF71-67ACB0B56F2B}.Release|x86.Build.0 = Release|x86
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Debug|x64.Build.0 = Debug|x64
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Debug|x86.Build.0 = Debug|x86
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Release|x64.ActiveCfg = Release|x64
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Release|x64.Build.0 = Release|x64
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Release|x86.ActiveCfg = Release|x86
|
||||
{87FFCBF4-DC17-4F09-90D6-102CF4C72BAF}.Release|x86.Build.0 = Release|x86
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|Any CPU.Deploy.0 = Debug|x64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM64.Deploy.0 = Debug|ARM64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x64.Build.0 = Debug|x64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x86.Build.0 = Debug|x86
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|Any CPU.Build.0 = Release|x64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|Any CPU.Deploy.0 = Release|x64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM.Build.0 = Release|ARM
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM.Deploy.0 = Release|ARM
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM64.Deploy.0 = Release|ARM64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x64.ActiveCfg = Release|x64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x64.Build.0 = Release|x64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x64.Deploy.0 = Release|x64
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x86.ActiveCfg = Release|x86
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x86.Build.0 = Release|x86
|
||||
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x86.Deploy.0 = Release|x86
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{8A7EB697-D722-4E0F-B20E-9FC88373ADB5}.Release|x86.Build.0 = Release|Any CPU
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|ARM64.Deploy.0 = Debug|ARM64
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|x64.Build.0 = Debug|x64
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|x86.Build.0 = Debug|x86
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|ARM.Build.0 = Release|ARM
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|ARM.Deploy.0 = Release|ARM
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|ARM64.Deploy.0 = Release|ARM64
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|x64.ActiveCfg = Release|x64
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|x64.Build.0 = Release|x64
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|x64.Deploy.0 = Release|x64
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|x86.ActiveCfg = Release|x86
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|x86.Build.0 = Release|x86
|
||||
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|x86.Deploy.0 = Release|x86
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Debug|x64.Build.0 = Debug|x64
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Debug|x86.Build.0 = Debug|x86
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Release|x64.ActiveCfg = Release|x64
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Release|x64.Build.0 = Release|x64
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Release|x86.ActiveCfg = Release|x86
|
||||
{CF850F8C-5042-4376-9CBA-C8F2BB554083}.Release|x86.Build.0 = Release|x86
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Release|x64.Build.0 = Release|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{BBA49030-7277-48CF-B2FE-3D01CB6B6C81}.Release|x86.Build.0 = Release|Any CPU
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Debug|x64.Build.0 = Debug|x64
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Debug|x86.Build.0 = Debug|x86
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Release|x64.ActiveCfg = Release|x64
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Release|x64.Build.0 = Release|x64
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Release|x86.ActiveCfg = Release|x86
|
||||
{16A979C2-F308-464F-9B2A-0AF8ED5EDB43}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -6,19 +6,18 @@ using Windows.UI.Xaml.Media.Animation;
|
||||
using Wino.Activation;
|
||||
using Wino.Calendar.Views;
|
||||
|
||||
namespace Wino.Calendar.Activation
|
||||
namespace Wino.Calendar.Activation;
|
||||
|
||||
public class DefaultActivationHandler : ActivationHandler<IActivatedEventArgs>
|
||||
{
|
||||
public class DefaultActivationHandler : ActivationHandler<IActivatedEventArgs>
|
||||
protected override Task HandleInternalAsync(IActivatedEventArgs args)
|
||||
{
|
||||
protected override Task HandleInternalAsync(IActivatedEventArgs args)
|
||||
{
|
||||
(Window.Current.Content as Frame).Navigate(typeof(AppShell), null, new DrillInNavigationTransitionInfo());
|
||||
(Window.Current.Content as Frame).Navigate(typeof(AppShell), null, new DrillInNavigationTransitionInfo());
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Only navigate if Frame content doesn't exist.
|
||||
protected override bool CanHandleInternal(IActivatedEventArgs args)
|
||||
=> (Window.Current?.Content as Frame)?.Content == null;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Only navigate if Frame content doesn't exist.
|
||||
protected override bool CanHandleInternal(IActivatedEventArgs args)
|
||||
=> (Window.Current?.Content as Frame)?.Content == null;
|
||||
}
|
||||
|
||||
@@ -24,141 +24,136 @@ using Wino.Messaging.Client.Connection;
|
||||
using Wino.Messaging.Server;
|
||||
using Wino.Services;
|
||||
|
||||
namespace Wino.Calendar
|
||||
namespace Wino.Calendar;
|
||||
|
||||
public sealed partial class App : WinoApplication, IRecipient<NewCalendarSynchronizationRequested>
|
||||
{
|
||||
public sealed partial class App : WinoApplication, IRecipient<NewCalendarSynchronizationRequested>
|
||||
private BackgroundTaskDeferral connectionBackgroundTaskDeferral;
|
||||
|
||||
public App()
|
||||
{
|
||||
public override string AppCenterKey => "dfdad6ab-95f9-44cc-9112-45ec6730c49e";
|
||||
InitializeComponent();
|
||||
WeakReferenceMessenger.Default.Register<NewCalendarSynchronizationRequested>(this);
|
||||
}
|
||||
|
||||
private BackgroundTaskDeferral connectionBackgroundTaskDeferral;
|
||||
private BackgroundTaskDeferral toastActionBackgroundTaskDeferral;
|
||||
public override IServiceProvider ConfigureServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
public App()
|
||||
services.RegisterSharedServices();
|
||||
services.RegisterCalendarViewModelServices();
|
||||
services.RegisterCoreUWPServices();
|
||||
services.RegisterCoreViewModels();
|
||||
|
||||
RegisterUWPServices(services);
|
||||
RegisterViewModels(services);
|
||||
RegisterActivationHandlers(services);
|
||||
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
#region Dependency Injection
|
||||
|
||||
private void RegisterActivationHandlers(IServiceCollection services)
|
||||
{
|
||||
//services.AddTransient<ProtocolActivationHandler>();
|
||||
//services.AddTransient<ToastNotificationActivationHandler>();
|
||||
//services.AddTransient<FileActivationHandler>();
|
||||
}
|
||||
|
||||
private void RegisterUWPServices(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<INavigationService, NavigationService>();
|
||||
services.AddSingleton<ICalendarDialogService, DialogService>();
|
||||
services.AddTransient<ISettingsBuilderService, SettingsBuilderService>();
|
||||
services.AddTransient<IProviderService, ProviderService>();
|
||||
services.AddSingleton<IAuthenticatorConfig, CalendarAuthenticatorConfig>();
|
||||
services.AddSingleton<IAccountCalendarStateService, AccountCalendarStateService>();
|
||||
}
|
||||
|
||||
private void RegisterViewModels(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton(typeof(AppShellViewModel));
|
||||
services.AddSingleton(typeof(CalendarPageViewModel));
|
||||
services.AddTransient(typeof(CalendarSettingsPageViewModel));
|
||||
services.AddTransient(typeof(AccountManagementViewModel));
|
||||
services.AddTransient(typeof(PersonalizationPageViewModel));
|
||||
services.AddTransient(typeof(AccountDetailsPageViewModel));
|
||||
services.AddTransient(typeof(EventDetailsPageViewModel));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override void OnApplicationCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
// TODO: Check server running.
|
||||
}
|
||||
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
LogActivation($"OnLaunched -> {args.GetType().Name}, Kind -> {args.Kind}, PreviousExecutionState -> {args.PreviousExecutionState}, IsPrelaunch -> {args.PrelaunchActivated}");
|
||||
|
||||
if (!args.PrelaunchActivated)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
WeakReferenceMessenger.Default.Register(this);
|
||||
await ActivateWinoAsync(args);
|
||||
}
|
||||
}
|
||||
|
||||
public override IServiceProvider ConfigureServices()
|
||||
protected override IEnumerable<ActivationHandler> GetActivationHandlers()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override ActivationHandler<IActivatedEventArgs> GetDefaultActivationHandler()
|
||||
=> new DefaultActivationHandler();
|
||||
|
||||
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
|
||||
{
|
||||
base.OnBackgroundActivated(args);
|
||||
|
||||
if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails appServiceTriggerDetails)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
LogActivation("OnBackgroundActivated -> AppServiceTriggerDetails received.");
|
||||
|
||||
services.RegisterSharedServices();
|
||||
services.RegisterCalendarViewModelServices();
|
||||
services.RegisterCoreUWPServices();
|
||||
services.RegisterCoreViewModels();
|
||||
|
||||
RegisterUWPServices(services);
|
||||
RegisterViewModels(services);
|
||||
RegisterActivationHandlers(services);
|
||||
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
#region Dependency Injection
|
||||
|
||||
private void RegisterActivationHandlers(IServiceCollection services)
|
||||
{
|
||||
//services.AddTransient<ProtocolActivationHandler>();
|
||||
//services.AddTransient<ToastNotificationActivationHandler>();
|
||||
//services.AddTransient<FileActivationHandler>();
|
||||
}
|
||||
|
||||
private void RegisterUWPServices(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<INavigationService, NavigationService>();
|
||||
services.AddSingleton<ICalendarDialogService, DialogService>();
|
||||
services.AddTransient<ISettingsBuilderService, SettingsBuilderService>();
|
||||
services.AddTransient<IProviderService, ProviderService>();
|
||||
services.AddSingleton<IAuthenticatorConfig, CalendarAuthenticatorConfig>();
|
||||
services.AddSingleton<IAccountCalendarStateService, AccountCalendarStateService>();
|
||||
}
|
||||
|
||||
private void RegisterViewModels(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton(typeof(AppShellViewModel));
|
||||
services.AddSingleton(typeof(CalendarPageViewModel));
|
||||
services.AddTransient(typeof(CalendarSettingsPageViewModel));
|
||||
services.AddTransient(typeof(AccountManagementViewModel));
|
||||
services.AddTransient(typeof(PersonalizationPageViewModel));
|
||||
services.AddTransient(typeof(AccountDetailsPageViewModel));
|
||||
services.AddTransient(typeof(EventDetailsPageViewModel));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override void OnApplicationCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
// TODO: Check server running.
|
||||
}
|
||||
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
LogActivation($"OnLaunched -> {args.GetType().Name}, Kind -> {args.Kind}, PreviousExecutionState -> {args.PreviousExecutionState}, IsPrelaunch -> {args.PrelaunchActivated}");
|
||||
|
||||
if (!args.PrelaunchActivated)
|
||||
// Only accept connections from callers in the same package
|
||||
if (appServiceTriggerDetails.CallerPackageFamilyName == Package.Current.Id.FamilyName)
|
||||
{
|
||||
await ActivateWinoAsync(args);
|
||||
}
|
||||
}
|
||||
// Connection established from the fulltrust process
|
||||
|
||||
protected override IEnumerable<ActivationHandler> GetActivationHandlers()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
connectionBackgroundTaskDeferral = args.TaskInstance.GetDeferral();
|
||||
args.TaskInstance.Canceled += OnConnectionBackgroundTaskCanceled;
|
||||
|
||||
protected override ActivationHandler<IActivatedEventArgs> GetDefaultActivationHandler()
|
||||
=> new DefaultActivationHandler();
|
||||
AppServiceConnectionManager.Connection = appServiceTriggerDetails.AppServiceConnection;
|
||||
|
||||
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
|
||||
{
|
||||
base.OnBackgroundActivated(args);
|
||||
|
||||
if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails appServiceTriggerDetails)
|
||||
{
|
||||
LogActivation("OnBackgroundActivated -> AppServiceTriggerDetails received.");
|
||||
|
||||
// Only accept connections from callers in the same package
|
||||
if (appServiceTriggerDetails.CallerPackageFamilyName == Package.Current.Id.FamilyName)
|
||||
{
|
||||
// Connection established from the fulltrust process
|
||||
|
||||
connectionBackgroundTaskDeferral = args.TaskInstance.GetDeferral();
|
||||
args.TaskInstance.Canceled += OnConnectionBackgroundTaskCanceled;
|
||||
|
||||
AppServiceConnectionManager.Connection = appServiceTriggerDetails.AppServiceConnection;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new WinoServerConnectionEstablished());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnConnectionBackgroundTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
|
||||
{
|
||||
sender.Canceled -= OnConnectionBackgroundTaskCanceled;
|
||||
|
||||
Log.Information($"Server connection background task was canceled. Reason: {reason}");
|
||||
|
||||
connectionBackgroundTaskDeferral?.Complete();
|
||||
connectionBackgroundTaskDeferral = null;
|
||||
|
||||
AppServiceConnectionManager.Connection = null;
|
||||
}
|
||||
|
||||
public async void Receive(NewCalendarSynchronizationRequested message)
|
||||
{
|
||||
try
|
||||
{
|
||||
var synchronizationResultResponse = await AppServiceConnectionManager.GetResponseAsync<CalendarSynchronizationResult, NewCalendarSynchronizationRequested>(message);
|
||||
synchronizationResultResponse.ThrowIfFailed();
|
||||
}
|
||||
catch (WinoServerException serverException)
|
||||
{
|
||||
var dialogService = Services.GetService<ICalendarDialogService>();
|
||||
|
||||
dialogService.InfoBarMessage(Translator.Info_SyncFailedTitle, serverException.Message, InfoBarMessageType.Error);
|
||||
WeakReferenceMessenger.Default.Send(new WinoServerConnectionEstablished());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnConnectionBackgroundTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
|
||||
{
|
||||
sender.Canceled -= OnConnectionBackgroundTaskCanceled;
|
||||
|
||||
Log.Information($"Server connection background task was canceled. Reason: {reason}");
|
||||
|
||||
connectionBackgroundTaskDeferral?.Complete();
|
||||
connectionBackgroundTaskDeferral = null;
|
||||
|
||||
AppServiceConnectionManager.Connection = null;
|
||||
}
|
||||
|
||||
public async void Receive(NewCalendarSynchronizationRequested message)
|
||||
{
|
||||
try
|
||||
{
|
||||
var synchronizationResultResponse = await AppServiceConnectionManager.GetResponseAsync<CalendarSynchronizationResult, NewCalendarSynchronizationRequested>(message);
|
||||
synchronizationResultResponse.ThrowIfFailed();
|
||||
}
|
||||
catch (WinoServerException serverException)
|
||||
{
|
||||
var dialogService = Services.GetService<ICalendarDialogService>();
|
||||
|
||||
dialogService.InfoBarMessage(Translator.Info_SyncFailedTitle, serverException.Message, InfoBarMessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,40 @@
|
||||
using System;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Wino.Calendar.Args
|
||||
namespace Wino.Calendar.Args;
|
||||
|
||||
/// <summary>
|
||||
/// When a new timeline cell is selected.
|
||||
/// </summary>
|
||||
public class TimelineCellSelectedArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// When a new timeline cell is selected.
|
||||
/// </summary>
|
||||
public class TimelineCellSelectedArgs : EventArgs
|
||||
public TimelineCellSelectedArgs(DateTime clickedDate, Point canvasPoint, Point positionerPoint, Size cellSize)
|
||||
{
|
||||
public TimelineCellSelectedArgs(DateTime clickedDate, Point canvasPoint, Point positionerPoint, Size cellSize)
|
||||
{
|
||||
ClickedDate = clickedDate;
|
||||
CanvasPoint = canvasPoint;
|
||||
PositionerPoint = positionerPoint;
|
||||
CellSize = cellSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clicked date and time information for the cell.
|
||||
/// </summary>
|
||||
public DateTime ClickedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Position relative to the cell drawing part of the canvas.
|
||||
/// Used to detect clicked cell from the position.
|
||||
/// </summary>
|
||||
public Point CanvasPoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Position relative to the main root positioner element of the drawing canvas.
|
||||
/// Used to show the create event dialog teaching tip in correct position.
|
||||
/// </summary>
|
||||
public Point PositionerPoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the cell.
|
||||
/// </summary>
|
||||
public Size CellSize { get; }
|
||||
ClickedDate = clickedDate;
|
||||
CanvasPoint = canvasPoint;
|
||||
PositionerPoint = positionerPoint;
|
||||
CellSize = cellSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clicked date and time information for the cell.
|
||||
/// </summary>
|
||||
public DateTime ClickedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Position relative to the cell drawing part of the canvas.
|
||||
/// Used to detect clicked cell from the position.
|
||||
/// </summary>
|
||||
public Point CanvasPoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Position relative to the main root positioner element of the drawing canvas.
|
||||
/// Used to show the create event dialog teaching tip in correct position.
|
||||
/// </summary>
|
||||
public Point PositionerPoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the cell.
|
||||
/// </summary>
|
||||
public Size CellSize { get; }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Calendar.Args
|
||||
{
|
||||
/// <summary>
|
||||
/// When selected timeline cell is unselected.
|
||||
/// </summary>
|
||||
public class TimelineCellUnselectedArgs : EventArgs { }
|
||||
}
|
||||
namespace Wino.Calendar.Args;
|
||||
|
||||
/// <summary>
|
||||
/// When selected timeline cell is unselected.
|
||||
/// </summary>
|
||||
public class TimelineCellUnselectedArgs : EventArgs { }
|
||||
|
||||
@@ -2,30 +2,29 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
namespace Wino.Calendar.Controls;
|
||||
|
||||
public partial class CalendarItemCommandBarFlyout : CommandBarFlyout
|
||||
{
|
||||
public class CalendarItemCommandBarFlyout : CommandBarFlyout
|
||||
public static readonly DependencyProperty ItemProperty = DependencyProperty.Register(nameof(Item), typeof(CalendarItemViewModel), typeof(CalendarItemCommandBarFlyout), new PropertyMetadata(null, new PropertyChangedCallback(OnItemChanged)));
|
||||
|
||||
public CalendarItemViewModel Item
|
||||
{
|
||||
public static readonly DependencyProperty ItemProperty = DependencyProperty.Register(nameof(Item), typeof(CalendarItemViewModel), typeof(CalendarItemCommandBarFlyout), new PropertyMetadata(null, new PropertyChangedCallback(OnItemChanged)));
|
||||
get { return (CalendarItemViewModel)GetValue(ItemProperty); }
|
||||
set { SetValue(ItemProperty, value); }
|
||||
}
|
||||
|
||||
public CalendarItemViewModel Item
|
||||
|
||||
private static void OnItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is CalendarItemCommandBarFlyout flyout)
|
||||
{
|
||||
get { return (CalendarItemViewModel)GetValue(ItemProperty); }
|
||||
set { SetValue(ItemProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
private static void OnItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is CalendarItemCommandBarFlyout flyout)
|
||||
{
|
||||
flyout.UpdateMenuItems();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMenuItems()
|
||||
{
|
||||
|
||||
flyout.UpdateMenuItems();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMenuItems()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<controls:WinoFontIcon
|
||||
<!--<controls:WinoFontIcon
|
||||
FontSize="12"
|
||||
Foreground="{x:Bind helpers:XamlHelpers.GetReadableTextColor(CalendarItem.AssignedCalendar.BackgroundColorHex), Mode=OneWay}"
|
||||
Icon="CalendarEventRepeat"
|
||||
@@ -79,7 +79,7 @@
|
||||
FontSize="12"
|
||||
Foreground="{x:Bind helpers:XamlHelpers.GetReadableTextColor(CalendarItem.AssignedCalendar.BackgroundColorHex), Mode=OneWay}"
|
||||
Icon="CalendarEventMuiltiDay"
|
||||
Visibility="{x:Bind CalendarItem.IsMultiDayEvent, Mode=OneWay}" />
|
||||
Visibility="{x:Bind CalendarItem.IsMultiDayEvent, Mode=OneWay}" />-->
|
||||
</StackPanel>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
|
||||
@@ -7,192 +7,194 @@ using Windows.UI.Xaml.Input;
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
using Wino.Calendar.ViewModels.Messages;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
namespace Wino.Calendar.Controls;
|
||||
|
||||
public sealed partial class CalendarItemControl : UserControl
|
||||
{
|
||||
public sealed partial class CalendarItemControl : UserControl
|
||||
// Single tap has a delay to report double taps properly.
|
||||
private bool isSingleTap = false;
|
||||
|
||||
public static readonly DependencyProperty CalendarItemProperty = DependencyProperty.Register(nameof(CalendarItem), typeof(CalendarItemViewModel), typeof(CalendarItemControl), new PropertyMetadata(null, new PropertyChangedCallback(OnCalendarItemChanged)));
|
||||
public static readonly DependencyProperty IsDraggingProperty = DependencyProperty.Register(nameof(IsDragging), typeof(bool), typeof(CalendarItemControl), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty IsCustomEventAreaProperty = DependencyProperty.Register(nameof(IsCustomEventArea), typeof(bool), typeof(CalendarItemControl), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty CalendarItemTitleProperty = DependencyProperty.Register(nameof(CalendarItemTitle), typeof(string), typeof(CalendarItemControl), new PropertyMetadata(string.Empty));
|
||||
public static readonly DependencyProperty DisplayingDateProperty = DependencyProperty.Register(nameof(DisplayingDate), typeof(CalendarDayModel), typeof(CalendarItemControl), new PropertyMetadata(null, new PropertyChangedCallback(OnDisplayDateChanged)));
|
||||
|
||||
/// <summary>
|
||||
/// Whether the control is displaying as regular event or all-multi day area in the day control.
|
||||
/// </summary>
|
||||
public bool IsCustomEventArea
|
||||
{
|
||||
// Single tap has a delay to report double taps properly.
|
||||
private bool isSingleTap = false;
|
||||
get { return (bool)GetValue(IsCustomEventAreaProperty); }
|
||||
set { SetValue(IsCustomEventAreaProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty CalendarItemProperty = DependencyProperty.Register(nameof(CalendarItem), typeof(CalendarItemViewModel), typeof(CalendarItemControl), new PropertyMetadata(null, new PropertyChangedCallback(OnCalendarItemChanged)));
|
||||
public static readonly DependencyProperty IsDraggingProperty = DependencyProperty.Register(nameof(IsDragging), typeof(bool), typeof(CalendarItemControl), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty IsCustomEventAreaProperty = DependencyProperty.Register(nameof(IsCustomEventArea), typeof(bool), typeof(CalendarItemControl), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty CalendarItemTitleProperty = DependencyProperty.Register(nameof(CalendarItemTitle), typeof(string), typeof(CalendarItemControl), new PropertyMetadata(string.Empty));
|
||||
public static readonly DependencyProperty DisplayingDateProperty = DependencyProperty.Register(nameof(DisplayingDate), typeof(CalendarDayModel), typeof(CalendarItemControl), new PropertyMetadata(null, new PropertyChangedCallback(OnDisplayDateChanged)));
|
||||
/// <summary>
|
||||
/// Day that the calendar item is rendered at.
|
||||
/// It's needed for title manipulation and some other adjustments later on.
|
||||
/// </summary>
|
||||
public CalendarDayModel DisplayingDate
|
||||
{
|
||||
get { return (CalendarDayModel)GetValue(DisplayingDateProperty); }
|
||||
set { SetValue(DisplayingDateProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the control is displaying as regular event or all-multi day area in the day control.
|
||||
/// </summary>
|
||||
public bool IsCustomEventArea
|
||||
public string CalendarItemTitle
|
||||
{
|
||||
get { return (string)GetValue(CalendarItemTitleProperty); }
|
||||
set { SetValue(CalendarItemTitleProperty, value); }
|
||||
}
|
||||
|
||||
public CalendarItemViewModel CalendarItem
|
||||
{
|
||||
get { return (CalendarItemViewModel)GetValue(CalendarItemProperty); }
|
||||
set { SetValue(CalendarItemProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsDragging
|
||||
{
|
||||
get { return (bool)GetValue(IsDraggingProperty); }
|
||||
set { SetValue(IsDraggingProperty, value); }
|
||||
}
|
||||
|
||||
public CalendarItemControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private static void OnDisplayDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is CalendarItemControl control)
|
||||
{
|
||||
get { return (bool)GetValue(IsCustomEventAreaProperty); }
|
||||
set { SetValue(IsCustomEventAreaProperty, value); }
|
||||
control.UpdateControlVisuals();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Day that the calendar item is rendered at.
|
||||
/// It's needed for title manipulation and some other adjustments later on.
|
||||
/// </summary>
|
||||
public CalendarDayModel DisplayingDate
|
||||
private static void OnCalendarItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is CalendarItemControl control)
|
||||
{
|
||||
get { return (CalendarDayModel)GetValue(DisplayingDateProperty); }
|
||||
set { SetValue(DisplayingDateProperty, value); }
|
||||
control.UpdateControlVisuals();
|
||||
}
|
||||
}
|
||||
|
||||
public string CalendarItemTitle
|
||||
{
|
||||
get { return (string)GetValue(CalendarItemTitleProperty); }
|
||||
set { SetValue(CalendarItemTitleProperty, value); }
|
||||
}
|
||||
private void UpdateControlVisuals()
|
||||
{
|
||||
// Depending on the calendar item's duration and attributes, we might need to change the display title.
|
||||
// 1. Multi-Day events should display the start date and end date.
|
||||
// 2. Multi-Day events that occupy the whole day just shows 'all day'.
|
||||
// 3. Other events should display the title.
|
||||
|
||||
public CalendarItemViewModel CalendarItem
|
||||
{
|
||||
get { return (CalendarItemViewModel)GetValue(CalendarItemProperty); }
|
||||
set { SetValue(CalendarItemProperty, value); }
|
||||
}
|
||||
if (CalendarItem == null) return;
|
||||
if (DisplayingDate == null) return;
|
||||
|
||||
public bool IsDragging
|
||||
{
|
||||
get { return (bool)GetValue(IsDraggingProperty); }
|
||||
set { SetValue(IsDraggingProperty, value); }
|
||||
}
|
||||
bool isMultiDayEvent = CalendarItem.CalendarItem.ItemType == CalendarItemType.MultiDay || CalendarItem.CalendarItem.ItemType == CalendarItemType.MultiDayAllDay;
|
||||
|
||||
public CalendarItemControl()
|
||||
if (isMultiDayEvent)
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
// Multi day events are divided into 3 categories:
|
||||
// 1. All day events
|
||||
// 2. Events that started after the period.
|
||||
// 3. Events that started before the period and finishes within the period.
|
||||
|
||||
private static void OnDisplayDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is CalendarItemControl control)
|
||||
var periodRelation = CalendarItem.Period.GetRelation(DisplayingDate.Period);
|
||||
|
||||
if (periodRelation == Itenso.TimePeriod.PeriodRelation.StartInside ||
|
||||
periodRelation == PeriodRelation.EnclosingStartTouching)
|
||||
{
|
||||
control.UpdateControlVisuals();
|
||||
// hour -> title
|
||||
CalendarItemTitle = $"{DisplayingDate.CalendarRenderOptions.CalendarSettings.GetTimeString(CalendarItem.StartDateTime.TimeOfDay)} -> {CalendarItem.Title}";
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnCalendarItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is CalendarItemControl control)
|
||||
else if (
|
||||
periodRelation == PeriodRelation.EndInside ||
|
||||
periodRelation == PeriodRelation.EnclosingEndTouching)
|
||||
{
|
||||
control.UpdateControlVisuals();
|
||||
// title <- hour
|
||||
CalendarItemTitle = $"{CalendarItem.Title} <- {DisplayingDate.CalendarRenderOptions.CalendarSettings.GetTimeString(CalendarItem.EndDateTime.TimeOfDay)}";
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateControlVisuals()
|
||||
{
|
||||
// Depending on the calendar item's duration and attributes, we might need to change the display title.
|
||||
// 1. Multi-Day events should display the start date and end date.
|
||||
// 2. Multi-Day events that occupy the whole day just shows 'all day'.
|
||||
// 3. Other events should display the title.
|
||||
|
||||
if (CalendarItem == null) return;
|
||||
if (DisplayingDate == null) return;
|
||||
|
||||
if (CalendarItem.IsMultiDayEvent)
|
||||
else if (periodRelation == PeriodRelation.Enclosing)
|
||||
{
|
||||
// Multi day events are divided into 3 categories:
|
||||
// 1. All day events
|
||||
// 2. Events that started after the period.
|
||||
// 3. Events that started before the period and finishes within the period.
|
||||
// This event goes all day and it's multi-day.
|
||||
// Item must be hidden in the calendar but displayed on the custom area at the top.
|
||||
|
||||
var periodRelation = CalendarItem.Period.GetRelation(DisplayingDate.Period);
|
||||
|
||||
if (periodRelation == Itenso.TimePeriod.PeriodRelation.StartInside ||
|
||||
periodRelation == PeriodRelation.EnclosingStartTouching)
|
||||
{
|
||||
// hour -> title
|
||||
CalendarItemTitle = $"{DisplayingDate.CalendarRenderOptions.CalendarSettings.GetTimeString(CalendarItem.StartDate.TimeOfDay)} -> {CalendarItem.Title}";
|
||||
}
|
||||
else if (
|
||||
periodRelation == PeriodRelation.EndInside ||
|
||||
periodRelation == PeriodRelation.EnclosingEndTouching)
|
||||
{
|
||||
// title <- hour
|
||||
CalendarItemTitle = $"{CalendarItem.Title} <- {DisplayingDate.CalendarRenderOptions.CalendarSettings.GetTimeString(CalendarItem.EndDate.TimeOfDay)}";
|
||||
}
|
||||
else if (periodRelation == PeriodRelation.Enclosing)
|
||||
{
|
||||
// This event goes all day and it's multi-day.
|
||||
// Item must be hidden in the calendar but displayed on the custom area at the top.
|
||||
|
||||
CalendarItemTitle = $"{Translator.CalendarItemAllDay} {CalendarItem.Title}";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not expected, but there it is.
|
||||
CalendarItemTitle = CalendarItem.Title;
|
||||
}
|
||||
|
||||
// Debug.WriteLine($"{CalendarItem.Title} Period relation with {DisplayingDate.Period.ToString()}: {periodRelation}");
|
||||
CalendarItemTitle = $"{Translator.CalendarItemAllDay} {CalendarItem.Title}";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not expected, but there it is.
|
||||
CalendarItemTitle = CalendarItem.Title;
|
||||
}
|
||||
|
||||
UpdateVisualStates();
|
||||
// Debug.WriteLine($"{CalendarItem.Title} Period relation with {DisplayingDate.Period.ToString()}: {periodRelation}");
|
||||
}
|
||||
else
|
||||
{
|
||||
CalendarItemTitle = CalendarItem.Title;
|
||||
}
|
||||
|
||||
private void UpdateVisualStates()
|
||||
{
|
||||
if (CalendarItem == null) return;
|
||||
UpdateVisualStates();
|
||||
}
|
||||
|
||||
if (CalendarItem.IsAllDayEvent)
|
||||
private void UpdateVisualStates()
|
||||
{
|
||||
if (CalendarItem == null) return;
|
||||
|
||||
if (CalendarItem.CalendarItem.ItemType == CalendarItemType.AllDay)
|
||||
{
|
||||
VisualStateManager.GoToState(this, "AllDayEvent", true);
|
||||
}
|
||||
else if (CalendarItem.CalendarItem.ItemType == CalendarItemType.MultiDayAllDay || CalendarItem.CalendarItem.ItemType == CalendarItemType.MultiDay)
|
||||
{
|
||||
if (IsCustomEventArea)
|
||||
{
|
||||
VisualStateManager.GoToState(this, "AllDayEvent", true);
|
||||
}
|
||||
else if (CalendarItem.IsMultiDayEvent)
|
||||
{
|
||||
if (IsCustomEventArea)
|
||||
{
|
||||
VisualStateManager.GoToState(this, "CustomAreaMultiDayEvent", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hide it.
|
||||
VisualStateManager.GoToState(this, "MultiDayEvent", true);
|
||||
}
|
||||
VisualStateManager.GoToState(this, "CustomAreaMultiDayEvent", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStateManager.GoToState(this, "RegularEvent", true);
|
||||
// Hide it.
|
||||
VisualStateManager.GoToState(this, "MultiDayEvent", true);
|
||||
}
|
||||
}
|
||||
|
||||
private void ControlDragStarting(UIElement sender, DragStartingEventArgs args) => IsDragging = true;
|
||||
|
||||
private void ControlDropped(UIElement sender, DropCompletedEventArgs args) => IsDragging = false;
|
||||
|
||||
private async void ControlTapped(object sender, TappedRoutedEventArgs e)
|
||||
else
|
||||
{
|
||||
if (CalendarItem == null) return;
|
||||
|
||||
isSingleTap = true;
|
||||
|
||||
await Task.Delay(100);
|
||||
|
||||
if (isSingleTap)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemTappedMessage(CalendarItem, DisplayingDate));
|
||||
}
|
||||
}
|
||||
|
||||
private void ControlDoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
|
||||
{
|
||||
if (CalendarItem == null) return;
|
||||
|
||||
isSingleTap = false;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemDoubleTappedMessage(CalendarItem));
|
||||
}
|
||||
|
||||
private void ControlRightTapped(object sender, RightTappedRoutedEventArgs e)
|
||||
{
|
||||
if (CalendarItem == null) return;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemRightTappedMessage(CalendarItem));
|
||||
VisualStateManager.GoToState(this, "RegularEvent", true);
|
||||
}
|
||||
}
|
||||
|
||||
private void ControlDragStarting(UIElement sender, DragStartingEventArgs args) => IsDragging = true;
|
||||
|
||||
private void ControlDropped(UIElement sender, DropCompletedEventArgs args) => IsDragging = false;
|
||||
|
||||
private async void ControlTapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
if (CalendarItem == null) return;
|
||||
|
||||
isSingleTap = true;
|
||||
|
||||
await Task.Delay(100);
|
||||
|
||||
if (isSingleTap)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemTappedMessage(CalendarItem, DisplayingDate));
|
||||
}
|
||||
}
|
||||
|
||||
private void ControlDoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
|
||||
{
|
||||
if (CalendarItem == null) return;
|
||||
|
||||
isSingleTap = false;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemDoubleTappedMessage(CalendarItem));
|
||||
}
|
||||
|
||||
private void ControlRightTapped(object sender, RightTappedRoutedEventArgs e)
|
||||
{
|
||||
if (CalendarItem == null) return;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemRightTappedMessage(CalendarItem));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,42 @@
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
namespace Wino.Calendar.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// FlipView that hides the navigation buttons and exposes methods to navigate to the next and previous items with animations.
|
||||
/// </summary>
|
||||
public partial class CustomCalendarFlipView : FlipView
|
||||
{
|
||||
/// <summary>
|
||||
/// FlipView that hides the navigation buttons and exposes methods to navigate to the next and previous items with animations.
|
||||
/// </summary>
|
||||
public class CustomCalendarFlipView : FlipView
|
||||
private const string PART_PreviousButton = "PreviousButtonHorizontal";
|
||||
private const string PART_NextButton = "NextButtonHorizontal";
|
||||
|
||||
private Button PreviousButton;
|
||||
private Button NextButton;
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
private const string PART_PreviousButton = "PreviousButtonHorizontal";
|
||||
private const string PART_NextButton = "NextButtonHorizontal";
|
||||
base.OnApplyTemplate();
|
||||
|
||||
private Button PreviousButton;
|
||||
private Button NextButton;
|
||||
PreviousButton = GetTemplateChild(PART_PreviousButton) as Button;
|
||||
NextButton = GetTemplateChild(PART_NextButton) as Button;
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
// Hide navigation buttons
|
||||
PreviousButton.Opacity = NextButton.Opacity = 0;
|
||||
PreviousButton.IsHitTestVisible = NextButton.IsHitTestVisible = false;
|
||||
|
||||
PreviousButton = GetTemplateChild(PART_PreviousButton) as Button;
|
||||
NextButton = GetTemplateChild(PART_NextButton) as Button;
|
||||
var t = FindName("ScrollingHost");
|
||||
}
|
||||
|
||||
// Hide navigation buttons
|
||||
PreviousButton.Opacity = NextButton.Opacity = 0;
|
||||
PreviousButton.IsHitTestVisible = NextButton.IsHitTestVisible = false;
|
||||
public void GoPreviousFlip()
|
||||
{
|
||||
var backPeer = new ButtonAutomationPeer(PreviousButton);
|
||||
backPeer.Invoke();
|
||||
}
|
||||
|
||||
var t = FindName("ScrollingHost");
|
||||
}
|
||||
|
||||
public void GoPreviousFlip()
|
||||
{
|
||||
var backPeer = new ButtonAutomationPeer(PreviousButton);
|
||||
backPeer.Invoke();
|
||||
}
|
||||
|
||||
public void GoNextFlip()
|
||||
{
|
||||
var nextPeer = new ButtonAutomationPeer(NextButton);
|
||||
nextPeer.Invoke();
|
||||
}
|
||||
public void GoNextFlip()
|
||||
{
|
||||
var nextPeer = new ButtonAutomationPeer(NextButton);
|
||||
nextPeer.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,76 +3,75 @@ using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
namespace Wino.Calendar.Controls;
|
||||
|
||||
public partial class DayColumnControl : Control
|
||||
{
|
||||
public class DayColumnControl : Control
|
||||
private const string PART_HeaderDateDayText = nameof(PART_HeaderDateDayText);
|
||||
private const string PART_IsTodayBorder = nameof(PART_IsTodayBorder);
|
||||
private const string PART_ColumnHeaderText = nameof(PART_ColumnHeaderText);
|
||||
|
||||
private const string PART_AllDayItemsControl = nameof(PART_AllDayItemsControl);
|
||||
|
||||
private const string TodayState = nameof(TodayState);
|
||||
private const string NotTodayState = nameof(NotTodayState);
|
||||
|
||||
private TextBlock HeaderDateDayText;
|
||||
private TextBlock ColumnHeaderText;
|
||||
private Border IsTodayBorder;
|
||||
private ItemsControl AllDayItemsControl;
|
||||
|
||||
public CalendarDayModel DayModel
|
||||
{
|
||||
private const string PART_HeaderDateDayText = nameof(PART_HeaderDateDayText);
|
||||
private const string PART_IsTodayBorder = nameof(PART_IsTodayBorder);
|
||||
private const string PART_ColumnHeaderText = nameof(PART_ColumnHeaderText);
|
||||
get { return (CalendarDayModel)GetValue(DayModelProperty); }
|
||||
set { SetValue(DayModelProperty, value); }
|
||||
}
|
||||
|
||||
private const string PART_AllDayItemsControl = nameof(PART_AllDayItemsControl);
|
||||
public static readonly DependencyProperty DayModelProperty = DependencyProperty.Register(nameof(DayModel), typeof(CalendarDayModel), typeof(DayColumnControl), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
|
||||
private const string TodayState = nameof(TodayState);
|
||||
private const string NotTodayState = nameof(NotTodayState);
|
||||
public DayColumnControl()
|
||||
{
|
||||
DefaultStyleKey = typeof(DayColumnControl);
|
||||
}
|
||||
|
||||
private TextBlock HeaderDateDayText;
|
||||
private TextBlock ColumnHeaderText;
|
||||
private Border IsTodayBorder;
|
||||
private ItemsControl AllDayItemsControl;
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
public CalendarDayModel DayModel
|
||||
HeaderDateDayText = GetTemplateChild(PART_HeaderDateDayText) as TextBlock;
|
||||
ColumnHeaderText = GetTemplateChild(PART_ColumnHeaderText) as TextBlock;
|
||||
IsTodayBorder = GetTemplateChild(PART_IsTodayBorder) as Border;
|
||||
AllDayItemsControl = GetTemplateChild(PART_AllDayItemsControl) as ItemsControl;
|
||||
|
||||
UpdateValues();
|
||||
}
|
||||
|
||||
private static void OnRenderingPropertiesChanged(DependencyObject control, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (control is DayColumnControl columnControl)
|
||||
{
|
||||
get { return (CalendarDayModel)GetValue(DayModelProperty); }
|
||||
set { SetValue(DayModelProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DayModelProperty = DependencyProperty.Register(nameof(DayModel), typeof(CalendarDayModel), typeof(DayColumnControl), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
|
||||
public DayColumnControl()
|
||||
{
|
||||
DefaultStyleKey = typeof(DayColumnControl);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
HeaderDateDayText = GetTemplateChild(PART_HeaderDateDayText) as TextBlock;
|
||||
ColumnHeaderText = GetTemplateChild(PART_ColumnHeaderText) as TextBlock;
|
||||
IsTodayBorder = GetTemplateChild(PART_IsTodayBorder) as Border;
|
||||
AllDayItemsControl = GetTemplateChild(PART_AllDayItemsControl) as ItemsControl;
|
||||
|
||||
UpdateValues();
|
||||
}
|
||||
|
||||
private static void OnRenderingPropertiesChanged(DependencyObject control, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (control is DayColumnControl columnControl)
|
||||
{
|
||||
columnControl.UpdateValues();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateValues()
|
||||
{
|
||||
if (HeaderDateDayText == null || IsTodayBorder == null || DayModel == null) return;
|
||||
|
||||
HeaderDateDayText.Text = DayModel.RepresentingDate.Day.ToString();
|
||||
|
||||
// Monthly template does not use it.
|
||||
if (ColumnHeaderText != null)
|
||||
{
|
||||
ColumnHeaderText.Text = DayModel.RepresentingDate.ToString("dddd", DayModel.CalendarRenderOptions.CalendarSettings.CultureInfo);
|
||||
}
|
||||
|
||||
AllDayItemsControl.ItemsSource = DayModel.EventsCollection.AllDayEvents;
|
||||
|
||||
bool isToday = DayModel.RepresentingDate.Date == DateTime.Now.Date;
|
||||
|
||||
VisualStateManager.GoToState(this, isToday ? TodayState : NotTodayState, false);
|
||||
|
||||
UpdateLayout();
|
||||
columnControl.UpdateValues();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateValues()
|
||||
{
|
||||
if (HeaderDateDayText == null || IsTodayBorder == null || DayModel == null) return;
|
||||
|
||||
HeaderDateDayText.Text = DayModel.RepresentingDate.Day.ToString();
|
||||
|
||||
// Monthly template does not use it.
|
||||
if (ColumnHeaderText != null)
|
||||
{
|
||||
ColumnHeaderText.Text = DayModel.RepresentingDate.ToString("dddd", DayModel.CalendarRenderOptions.CalendarSettings.CultureInfo);
|
||||
}
|
||||
|
||||
AllDayItemsControl.ItemsSource = DayModel.EventsCollection.AllDayEvents;
|
||||
|
||||
bool isToday = DayModel.RepresentingDate.Date == DateTime.Now.Date;
|
||||
|
||||
VisualStateManager.GoToState(this, isToday ? TodayState : NotTodayState, false);
|
||||
|
||||
UpdateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,55 +3,54 @@ using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
namespace Wino.Calendar.Controls;
|
||||
|
||||
public partial class DayHeaderControl : Control
|
||||
{
|
||||
public class DayHeaderControl : Control
|
||||
private const string PART_DayHeaderTextBlock = nameof(PART_DayHeaderTextBlock);
|
||||
private TextBlock HeaderTextblock;
|
||||
|
||||
public DayHeaderDisplayType DisplayType
|
||||
{
|
||||
private const string PART_DayHeaderTextBlock = nameof(PART_DayHeaderTextBlock);
|
||||
private TextBlock HeaderTextblock;
|
||||
get { return (DayHeaderDisplayType)GetValue(DisplayTypeProperty); }
|
||||
set { SetValue(DisplayTypeProperty, value); }
|
||||
}
|
||||
|
||||
public DayHeaderDisplayType DisplayType
|
||||
public DateTime Date
|
||||
{
|
||||
get { return (DateTime)GetValue(DateProperty); }
|
||||
set { SetValue(DateProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DateProperty = DependencyProperty.Register(nameof(Date), typeof(DateTime), typeof(DayHeaderControl), new PropertyMetadata(default(DateTime), new PropertyChangedCallback(OnHeaderPropertyChanged)));
|
||||
public static readonly DependencyProperty DisplayTypeProperty = DependencyProperty.Register(nameof(DisplayType), typeof(DayHeaderDisplayType), typeof(DayHeaderControl), new PropertyMetadata(DayHeaderDisplayType.TwentyFourHour, new PropertyChangedCallback(OnHeaderPropertyChanged)));
|
||||
|
||||
public DayHeaderControl()
|
||||
{
|
||||
DefaultStyleKey = typeof(DayHeaderControl);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
HeaderTextblock = GetTemplateChild(PART_DayHeaderTextBlock) as TextBlock;
|
||||
UpdateHeaderText();
|
||||
}
|
||||
|
||||
private static void OnHeaderPropertyChanged(DependencyObject control, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (control is DayHeaderControl headerControl)
|
||||
{
|
||||
get { return (DayHeaderDisplayType)GetValue(DisplayTypeProperty); }
|
||||
set { SetValue(DisplayTypeProperty, value); }
|
||||
headerControl.UpdateHeaderText();
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime Date
|
||||
private void UpdateHeaderText()
|
||||
{
|
||||
if (HeaderTextblock != null)
|
||||
{
|
||||
get { return (DateTime)GetValue(DateProperty); }
|
||||
set { SetValue(DateProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DateProperty = DependencyProperty.Register(nameof(Date), typeof(DateTime), typeof(DayHeaderControl), new PropertyMetadata(default(DateTime), new PropertyChangedCallback(OnHeaderPropertyChanged)));
|
||||
public static readonly DependencyProperty DisplayTypeProperty = DependencyProperty.Register(nameof(DisplayType), typeof(DayHeaderDisplayType), typeof(DayHeaderControl), new PropertyMetadata(DayHeaderDisplayType.TwentyFourHour, new PropertyChangedCallback(OnHeaderPropertyChanged)));
|
||||
|
||||
public DayHeaderControl()
|
||||
{
|
||||
DefaultStyleKey = typeof(DayHeaderControl);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
HeaderTextblock = GetTemplateChild(PART_DayHeaderTextBlock) as TextBlock;
|
||||
UpdateHeaderText();
|
||||
}
|
||||
|
||||
private static void OnHeaderPropertyChanged(DependencyObject control, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (control is DayHeaderControl headerControl)
|
||||
{
|
||||
headerControl.UpdateHeaderText();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateHeaderText()
|
||||
{
|
||||
if (HeaderTextblock != null)
|
||||
{
|
||||
HeaderTextblock.Text = DisplayType == DayHeaderDisplayType.TwelveHour ? Date.ToString("h tt") : Date.ToString("HH:mm");
|
||||
}
|
||||
HeaderTextblock.Text = DisplayType == DayHeaderDisplayType.TwelveHour ? Date.ToString("h tt") : Date.ToString("HH:mm");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,291 +10,290 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
using Wino.Helpers;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
namespace Wino.Calendar.Controls;
|
||||
|
||||
public partial class WinoCalendarControl : Control
|
||||
{
|
||||
public class WinoCalendarControl : Control
|
||||
private const string PART_WinoFlipView = nameof(PART_WinoFlipView);
|
||||
private const string PART_IdleGrid = nameof(PART_IdleGrid);
|
||||
|
||||
public event EventHandler<TimelineCellSelectedArgs> TimelineCellSelected;
|
||||
public event EventHandler<TimelineCellUnselectedArgs> TimelineCellUnselected;
|
||||
|
||||
public event EventHandler ScrollPositionChanging;
|
||||
|
||||
#region Dependency Properties
|
||||
|
||||
public static readonly DependencyProperty DayRangesProperty = DependencyProperty.Register(nameof(DayRanges), typeof(ObservableCollection<DayRangeRenderModel>), typeof(WinoCalendarControl), new PropertyMetadata(null));
|
||||
public static readonly DependencyProperty SelectedFlipViewIndexProperty = DependencyProperty.Register(nameof(SelectedFlipViewIndex), typeof(int), typeof(WinoCalendarControl), new PropertyMetadata(-1));
|
||||
public static readonly DependencyProperty SelectedFlipViewDayRangeProperty = DependencyProperty.Register(nameof(SelectedFlipViewDayRange), typeof(DayRangeRenderModel), typeof(WinoCalendarControl), new PropertyMetadata(null));
|
||||
public static readonly DependencyProperty ActiveCanvasProperty = DependencyProperty.Register(nameof(ActiveCanvas), typeof(WinoDayTimelineCanvas), typeof(WinoCalendarControl), new PropertyMetadata(null, new PropertyChangedCallback(OnActiveCanvasChanged)));
|
||||
public static readonly DependencyProperty IsFlipIdleProperty = DependencyProperty.Register(nameof(IsFlipIdle), typeof(bool), typeof(WinoCalendarControl), new PropertyMetadata(true, new PropertyChangedCallback(OnIdleStateChanged)));
|
||||
public static readonly DependencyProperty ActiveScrollViewerProperty = DependencyProperty.Register(nameof(ActiveScrollViewer), typeof(ScrollViewer), typeof(WinoCalendarControl), new PropertyMetadata(null, new PropertyChangedCallback(OnActiveVerticalScrollViewerChanged)));
|
||||
|
||||
public static readonly DependencyProperty VerticalItemsPanelTemplateProperty = DependencyProperty.Register(nameof(VerticalItemsPanelTemplate), typeof(ItemsPanelTemplate), typeof(WinoCalendarControl), new PropertyMetadata(null, new PropertyChangedCallback(OnCalendarOrientationPropertiesUpdated)));
|
||||
public static readonly DependencyProperty HorizontalItemsPanelTemplateProperty = DependencyProperty.Register(nameof(HorizontalItemsPanelTemplate), typeof(ItemsPanelTemplate), typeof(WinoCalendarControl), new PropertyMetadata(null, new PropertyChangedCallback(OnCalendarOrientationPropertiesUpdated)));
|
||||
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(nameof(Orientation), typeof(CalendarOrientation), typeof(WinoCalendarControl), new PropertyMetadata(CalendarOrientation.Horizontal, new PropertyChangedCallback(OnCalendarOrientationPropertiesUpdated)));
|
||||
public static readonly DependencyProperty DisplayTypeProperty = DependencyProperty.Register(nameof(DisplayType), typeof(CalendarDisplayType), typeof(WinoCalendarControl), new PropertyMetadata(CalendarDisplayType.Day));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the day-week-month-year display type.
|
||||
/// Orientation is not determined by this property, but Orientation property.
|
||||
/// This property is used to determine the template to use for the calendar.
|
||||
/// </summary>
|
||||
public CalendarDisplayType DisplayType
|
||||
{
|
||||
private const string PART_WinoFlipView = nameof(PART_WinoFlipView);
|
||||
private const string PART_IdleGrid = nameof(PART_IdleGrid);
|
||||
get { return (CalendarDisplayType)GetValue(DisplayTypeProperty); }
|
||||
set { SetValue(DisplayTypeProperty, value); }
|
||||
}
|
||||
|
||||
public event EventHandler<TimelineCellSelectedArgs> TimelineCellSelected;
|
||||
public event EventHandler<TimelineCellUnselectedArgs> TimelineCellUnselected;
|
||||
public CalendarOrientation Orientation
|
||||
{
|
||||
get { return (CalendarOrientation)GetValue(OrientationProperty); }
|
||||
set { SetValue(OrientationProperty, value); }
|
||||
}
|
||||
|
||||
public event EventHandler ScrollPositionChanging;
|
||||
public ItemsPanelTemplate VerticalItemsPanelTemplate
|
||||
{
|
||||
get { return (ItemsPanelTemplate)GetValue(VerticalItemsPanelTemplateProperty); }
|
||||
set { SetValue(VerticalItemsPanelTemplateProperty, value); }
|
||||
}
|
||||
|
||||
#region Dependency Properties
|
||||
public ItemsPanelTemplate HorizontalItemsPanelTemplate
|
||||
{
|
||||
get { return (ItemsPanelTemplate)GetValue(HorizontalItemsPanelTemplateProperty); }
|
||||
set { SetValue(HorizontalItemsPanelTemplateProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DayRangesProperty = DependencyProperty.Register(nameof(DayRanges), typeof(ObservableCollection<DayRangeRenderModel>), typeof(WinoCalendarControl), new PropertyMetadata(null));
|
||||
public static readonly DependencyProperty SelectedFlipViewIndexProperty = DependencyProperty.Register(nameof(SelectedFlipViewIndex), typeof(int), typeof(WinoCalendarControl), new PropertyMetadata(-1));
|
||||
public static readonly DependencyProperty SelectedFlipViewDayRangeProperty = DependencyProperty.Register(nameof(SelectedFlipViewDayRange), typeof(DayRangeRenderModel), typeof(WinoCalendarControl), new PropertyMetadata(null));
|
||||
public static readonly DependencyProperty ActiveCanvasProperty = DependencyProperty.Register(nameof(ActiveCanvas), typeof(WinoDayTimelineCanvas), typeof(WinoCalendarControl), new PropertyMetadata(null, new PropertyChangedCallback(OnActiveCanvasChanged)));
|
||||
public static readonly DependencyProperty IsFlipIdleProperty = DependencyProperty.Register(nameof(IsFlipIdle), typeof(bool), typeof(WinoCalendarControl), new PropertyMetadata(true, new PropertyChangedCallback(OnIdleStateChanged)));
|
||||
public static readonly DependencyProperty ActiveScrollViewerProperty = DependencyProperty.Register(nameof(ActiveScrollViewer), typeof(ScrollViewer), typeof(WinoCalendarControl), new PropertyMetadata(null, new PropertyChangedCallback(OnActiveVerticalScrollViewerChanged)));
|
||||
public DayRangeRenderModel SelectedFlipViewDayRange
|
||||
{
|
||||
get { return (DayRangeRenderModel)GetValue(SelectedFlipViewDayRangeProperty); }
|
||||
set { SetValue(SelectedFlipViewDayRangeProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty VerticalItemsPanelTemplateProperty = DependencyProperty.Register(nameof(VerticalItemsPanelTemplate), typeof(ItemsPanelTemplate), typeof(WinoCalendarControl), new PropertyMetadata(null, new PropertyChangedCallback(OnCalendarOrientationPropertiesUpdated)));
|
||||
public static readonly DependencyProperty HorizontalItemsPanelTemplateProperty = DependencyProperty.Register(nameof(HorizontalItemsPanelTemplate), typeof(ItemsPanelTemplate), typeof(WinoCalendarControl), new PropertyMetadata(null, new PropertyChangedCallback(OnCalendarOrientationPropertiesUpdated)));
|
||||
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(nameof(Orientation), typeof(CalendarOrientation), typeof(WinoCalendarControl), new PropertyMetadata(CalendarOrientation.Horizontal, new PropertyChangedCallback(OnCalendarOrientationPropertiesUpdated)));
|
||||
public static readonly DependencyProperty DisplayTypeProperty = DependencyProperty.Register(nameof(DisplayType), typeof(CalendarDisplayType), typeof(WinoCalendarControl), new PropertyMetadata(CalendarDisplayType.Day));
|
||||
public ScrollViewer ActiveScrollViewer
|
||||
{
|
||||
get { return (ScrollViewer)GetValue(ActiveScrollViewerProperty); }
|
||||
set { SetValue(ActiveScrollViewerProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the day-week-month-year display type.
|
||||
/// Orientation is not determined by this property, but Orientation property.
|
||||
/// This property is used to determine the template to use for the calendar.
|
||||
/// </summary>
|
||||
public CalendarDisplayType DisplayType
|
||||
public WinoDayTimelineCanvas ActiveCanvas
|
||||
{
|
||||
get { return (WinoDayTimelineCanvas)GetValue(ActiveCanvasProperty); }
|
||||
set { SetValue(ActiveCanvasProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsFlipIdle
|
||||
{
|
||||
get { return (bool)GetValue(IsFlipIdleProperty); }
|
||||
set { SetValue(IsFlipIdleProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of day ranges to render.
|
||||
/// Each day range usually represents a week, but it may support other ranges.
|
||||
/// </summary>
|
||||
public ObservableCollection<DayRangeRenderModel> DayRanges
|
||||
{
|
||||
get { return (ObservableCollection<DayRangeRenderModel>)GetValue(DayRangesProperty); }
|
||||
set { SetValue(DayRangesProperty, value); }
|
||||
}
|
||||
|
||||
public int SelectedFlipViewIndex
|
||||
{
|
||||
get { return (int)GetValue(SelectedFlipViewIndexProperty); }
|
||||
set { SetValue(SelectedFlipViewIndexProperty, value); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private WinoCalendarFlipView InternalFlipView;
|
||||
private Grid IdleGrid;
|
||||
|
||||
public WinoCalendarControl()
|
||||
{
|
||||
DefaultStyleKey = typeof(WinoCalendarControl);
|
||||
SizeChanged += CalendarSizeChanged;
|
||||
}
|
||||
|
||||
private static void OnCalendarOrientationPropertiesUpdated(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (calendar is WinoCalendarControl control)
|
||||
{
|
||||
get { return (CalendarDisplayType)GetValue(DisplayTypeProperty); }
|
||||
set { SetValue(DisplayTypeProperty, value); }
|
||||
}
|
||||
|
||||
public CalendarOrientation Orientation
|
||||
{
|
||||
get { return (CalendarOrientation)GetValue(OrientationProperty); }
|
||||
set { SetValue(OrientationProperty, value); }
|
||||
}
|
||||
|
||||
public ItemsPanelTemplate VerticalItemsPanelTemplate
|
||||
{
|
||||
get { return (ItemsPanelTemplate)GetValue(VerticalItemsPanelTemplateProperty); }
|
||||
set { SetValue(VerticalItemsPanelTemplateProperty, value); }
|
||||
}
|
||||
|
||||
public ItemsPanelTemplate HorizontalItemsPanelTemplate
|
||||
{
|
||||
get { return (ItemsPanelTemplate)GetValue(HorizontalItemsPanelTemplateProperty); }
|
||||
set { SetValue(HorizontalItemsPanelTemplateProperty, value); }
|
||||
}
|
||||
|
||||
public DayRangeRenderModel SelectedFlipViewDayRange
|
||||
{
|
||||
get { return (DayRangeRenderModel)GetValue(SelectedFlipViewDayRangeProperty); }
|
||||
set { SetValue(SelectedFlipViewDayRangeProperty, value); }
|
||||
}
|
||||
|
||||
public ScrollViewer ActiveScrollViewer
|
||||
{
|
||||
get { return (ScrollViewer)GetValue(ActiveScrollViewerProperty); }
|
||||
set { SetValue(ActiveScrollViewerProperty, value); }
|
||||
}
|
||||
|
||||
public WinoDayTimelineCanvas ActiveCanvas
|
||||
{
|
||||
get { return (WinoDayTimelineCanvas)GetValue(ActiveCanvasProperty); }
|
||||
set { SetValue(ActiveCanvasProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsFlipIdle
|
||||
{
|
||||
get { return (bool)GetValue(IsFlipIdleProperty); }
|
||||
set { SetValue(IsFlipIdleProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of day ranges to render.
|
||||
/// Each day range usually represents a week, but it may support other ranges.
|
||||
/// </summary>
|
||||
public ObservableCollection<DayRangeRenderModel> DayRanges
|
||||
{
|
||||
get { return (ObservableCollection<DayRangeRenderModel>)GetValue(DayRangesProperty); }
|
||||
set { SetValue(DayRangesProperty, value); }
|
||||
}
|
||||
|
||||
public int SelectedFlipViewIndex
|
||||
{
|
||||
get { return (int)GetValue(SelectedFlipViewIndexProperty); }
|
||||
set { SetValue(SelectedFlipViewIndexProperty, value); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private WinoCalendarFlipView InternalFlipView;
|
||||
private Grid IdleGrid;
|
||||
|
||||
public WinoCalendarControl()
|
||||
{
|
||||
DefaultStyleKey = typeof(WinoCalendarControl);
|
||||
SizeChanged += CalendarSizeChanged;
|
||||
}
|
||||
|
||||
private static void OnCalendarOrientationPropertiesUpdated(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (calendar is WinoCalendarControl control)
|
||||
{
|
||||
control.ManageCalendarOrientation();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnIdleStateChanged(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (calendar is WinoCalendarControl calendarControl)
|
||||
{
|
||||
calendarControl.UpdateIdleState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void OnActiveVerticalScrollViewerChanged(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (calendar is WinoCalendarControl calendarControl)
|
||||
{
|
||||
if (e.OldValue is ScrollViewer oldScrollViewer)
|
||||
{
|
||||
calendarControl.DeregisterScrollChanges(oldScrollViewer);
|
||||
}
|
||||
|
||||
if (e.NewValue is ScrollViewer newScrollViewer)
|
||||
{
|
||||
calendarControl.RegisterScrollChanges(newScrollViewer);
|
||||
}
|
||||
|
||||
calendarControl.ManageHighlightedDateRange();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void OnActiveCanvasChanged(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (calendar is WinoCalendarControl calendarControl)
|
||||
{
|
||||
if (e.OldValue is WinoDayTimelineCanvas oldCanvas)
|
||||
{
|
||||
// Dismiss any selection on the old canvas.
|
||||
calendarControl.DeregisterCanvas(oldCanvas);
|
||||
}
|
||||
|
||||
if (e.NewValue is WinoDayTimelineCanvas newCanvas)
|
||||
{
|
||||
calendarControl.RegisterCanvas(newCanvas);
|
||||
}
|
||||
|
||||
calendarControl.ManageHighlightedDateRange();
|
||||
}
|
||||
}
|
||||
|
||||
private void ManageCalendarOrientation()
|
||||
{
|
||||
if (InternalFlipView == null || HorizontalItemsPanelTemplate == null || VerticalItemsPanelTemplate == null) return;
|
||||
|
||||
InternalFlipView.ItemsPanel = Orientation == CalendarOrientation.Horizontal ? HorizontalItemsPanelTemplate : VerticalItemsPanelTemplate;
|
||||
}
|
||||
|
||||
private void ManageHighlightedDateRange()
|
||||
=> SelectedFlipViewDayRange = InternalFlipView.SelectedItem as DayRangeRenderModel;
|
||||
|
||||
private void DeregisterCanvas(WinoDayTimelineCanvas canvas)
|
||||
{
|
||||
if (canvas == null) return;
|
||||
|
||||
canvas.SelectedDateTime = null;
|
||||
canvas.TimelineCellSelected -= ActiveTimelineCellSelected;
|
||||
canvas.TimelineCellUnselected -= ActiveTimelineCellUnselected;
|
||||
}
|
||||
|
||||
private void RegisterCanvas(WinoDayTimelineCanvas canvas)
|
||||
{
|
||||
if (canvas == null) return;
|
||||
|
||||
canvas.SelectedDateTime = null;
|
||||
canvas.TimelineCellSelected += ActiveTimelineCellSelected;
|
||||
canvas.TimelineCellUnselected += ActiveTimelineCellUnselected;
|
||||
}
|
||||
|
||||
private void RegisterScrollChanges(ScrollViewer scrollViewer)
|
||||
{
|
||||
if (scrollViewer == null) return;
|
||||
|
||||
scrollViewer.ViewChanging += ScrollViewChanging;
|
||||
}
|
||||
|
||||
private void DeregisterScrollChanges(ScrollViewer scrollViewer)
|
||||
{
|
||||
if (scrollViewer == null) return;
|
||||
|
||||
scrollViewer.ViewChanging -= ScrollViewChanging;
|
||||
}
|
||||
|
||||
private void ScrollViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
|
||||
=> ScrollPositionChanging?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
private void CalendarSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (ActiveCanvas == null) return;
|
||||
|
||||
ActiveCanvas.SelectedDateTime = null;
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
InternalFlipView = GetTemplateChild(PART_WinoFlipView) as WinoCalendarFlipView;
|
||||
IdleGrid = GetTemplateChild(PART_IdleGrid) as Grid;
|
||||
|
||||
UpdateIdleState();
|
||||
ManageCalendarOrientation();
|
||||
}
|
||||
|
||||
private void UpdateIdleState()
|
||||
{
|
||||
InternalFlipView.Opacity = IsFlipIdle ? 0 : 1;
|
||||
IdleGrid.Visibility = IsFlipIdle ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void ActiveTimelineCellUnselected(object sender, TimelineCellUnselectedArgs e)
|
||||
=> TimelineCellUnselected?.Invoke(this, e);
|
||||
|
||||
private void ActiveTimelineCellSelected(object sender, TimelineCellSelectedArgs e)
|
||||
=> TimelineCellSelected?.Invoke(this, e);
|
||||
|
||||
public void NavigateToDay(DateTime dateTime) => InternalFlipView.NavigateToDay(dateTime);
|
||||
|
||||
public async void NavigateToHour(TimeSpan timeSpan)
|
||||
{
|
||||
if (ActiveScrollViewer == null) return;
|
||||
|
||||
// Total height of the FlipViewItem is the same as vertical ScrollViewer to position day headers.
|
||||
|
||||
await Task.Yield();
|
||||
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
|
||||
{
|
||||
double hourHeght = 60;
|
||||
double totalHeight = ActiveScrollViewer.ScrollableHeight;
|
||||
double scrollPosition = timeSpan.TotalHours * hourHeght;
|
||||
|
||||
ActiveScrollViewer.ChangeView(null, scrollPosition, null, disableAnimation: false);
|
||||
});
|
||||
}
|
||||
public void ResetTimelineSelection()
|
||||
{
|
||||
if (ActiveCanvas == null) return;
|
||||
|
||||
ActiveCanvas.SelectedDateTime = null;
|
||||
}
|
||||
|
||||
public void GoNextRange()
|
||||
{
|
||||
if (InternalFlipView == null) return;
|
||||
|
||||
InternalFlipView.GoNextFlip();
|
||||
}
|
||||
|
||||
public void GoPreviousRange()
|
||||
{
|
||||
if (InternalFlipView == null) return;
|
||||
|
||||
InternalFlipView.GoPreviousFlip();
|
||||
}
|
||||
|
||||
public void UnselectActiveTimelineCell()
|
||||
{
|
||||
if (ActiveCanvas == null) return;
|
||||
|
||||
ActiveCanvas.SelectedDateTime = null;
|
||||
}
|
||||
|
||||
public CalendarItemControl GetCalendarItemControl(CalendarItemViewModel calendarItemViewModel)
|
||||
{
|
||||
return this.FindDescendants<CalendarItemControl>().FirstOrDefault(a => a.CalendarItem == calendarItemViewModel);
|
||||
control.ManageCalendarOrientation();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnIdleStateChanged(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (calendar is WinoCalendarControl calendarControl)
|
||||
{
|
||||
calendarControl.UpdateIdleState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void OnActiveVerticalScrollViewerChanged(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (calendar is WinoCalendarControl calendarControl)
|
||||
{
|
||||
if (e.OldValue is ScrollViewer oldScrollViewer)
|
||||
{
|
||||
calendarControl.DeregisterScrollChanges(oldScrollViewer);
|
||||
}
|
||||
|
||||
if (e.NewValue is ScrollViewer newScrollViewer)
|
||||
{
|
||||
calendarControl.RegisterScrollChanges(newScrollViewer);
|
||||
}
|
||||
|
||||
calendarControl.ManageHighlightedDateRange();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void OnActiveCanvasChanged(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (calendar is WinoCalendarControl calendarControl)
|
||||
{
|
||||
if (e.OldValue is WinoDayTimelineCanvas oldCanvas)
|
||||
{
|
||||
// Dismiss any selection on the old canvas.
|
||||
calendarControl.DeregisterCanvas(oldCanvas);
|
||||
}
|
||||
|
||||
if (e.NewValue is WinoDayTimelineCanvas newCanvas)
|
||||
{
|
||||
calendarControl.RegisterCanvas(newCanvas);
|
||||
}
|
||||
|
||||
calendarControl.ManageHighlightedDateRange();
|
||||
}
|
||||
}
|
||||
|
||||
private void ManageCalendarOrientation()
|
||||
{
|
||||
if (InternalFlipView == null || HorizontalItemsPanelTemplate == null || VerticalItemsPanelTemplate == null) return;
|
||||
|
||||
InternalFlipView.ItemsPanel = Orientation == CalendarOrientation.Horizontal ? HorizontalItemsPanelTemplate : VerticalItemsPanelTemplate;
|
||||
}
|
||||
|
||||
private void ManageHighlightedDateRange()
|
||||
=> SelectedFlipViewDayRange = InternalFlipView.SelectedItem as DayRangeRenderModel;
|
||||
|
||||
private void DeregisterCanvas(WinoDayTimelineCanvas canvas)
|
||||
{
|
||||
if (canvas == null) return;
|
||||
|
||||
canvas.SelectedDateTime = null;
|
||||
canvas.TimelineCellSelected -= ActiveTimelineCellSelected;
|
||||
canvas.TimelineCellUnselected -= ActiveTimelineCellUnselected;
|
||||
}
|
||||
|
||||
private void RegisterCanvas(WinoDayTimelineCanvas canvas)
|
||||
{
|
||||
if (canvas == null) return;
|
||||
|
||||
canvas.SelectedDateTime = null;
|
||||
canvas.TimelineCellSelected += ActiveTimelineCellSelected;
|
||||
canvas.TimelineCellUnselected += ActiveTimelineCellUnselected;
|
||||
}
|
||||
|
||||
private void RegisterScrollChanges(ScrollViewer scrollViewer)
|
||||
{
|
||||
if (scrollViewer == null) return;
|
||||
|
||||
scrollViewer.ViewChanging += ScrollViewChanging;
|
||||
}
|
||||
|
||||
private void DeregisterScrollChanges(ScrollViewer scrollViewer)
|
||||
{
|
||||
if (scrollViewer == null) return;
|
||||
|
||||
scrollViewer.ViewChanging -= ScrollViewChanging;
|
||||
}
|
||||
|
||||
private void ScrollViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
|
||||
=> ScrollPositionChanging?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
private void CalendarSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (ActiveCanvas == null) return;
|
||||
|
||||
ActiveCanvas.SelectedDateTime = null;
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
InternalFlipView = GetTemplateChild(PART_WinoFlipView) as WinoCalendarFlipView;
|
||||
IdleGrid = GetTemplateChild(PART_IdleGrid) as Grid;
|
||||
|
||||
UpdateIdleState();
|
||||
ManageCalendarOrientation();
|
||||
}
|
||||
|
||||
private void UpdateIdleState()
|
||||
{
|
||||
InternalFlipView.Opacity = IsFlipIdle ? 0 : 1;
|
||||
IdleGrid.Visibility = IsFlipIdle ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void ActiveTimelineCellUnselected(object sender, TimelineCellUnselectedArgs e)
|
||||
=> TimelineCellUnselected?.Invoke(this, e);
|
||||
|
||||
private void ActiveTimelineCellSelected(object sender, TimelineCellSelectedArgs e)
|
||||
=> TimelineCellSelected?.Invoke(this, e);
|
||||
|
||||
public void NavigateToDay(DateTime dateTime) => InternalFlipView.NavigateToDay(dateTime);
|
||||
|
||||
public async void NavigateToHour(TimeSpan timeSpan)
|
||||
{
|
||||
if (ActiveScrollViewer == null) return;
|
||||
|
||||
// Total height of the FlipViewItem is the same as vertical ScrollViewer to position day headers.
|
||||
|
||||
await Task.Yield();
|
||||
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
|
||||
{
|
||||
double hourHeght = 60;
|
||||
double totalHeight = ActiveScrollViewer.ScrollableHeight;
|
||||
double scrollPosition = timeSpan.TotalHours * hourHeght;
|
||||
|
||||
ActiveScrollViewer.ChangeView(null, scrollPosition, null, disableAnimation: false);
|
||||
});
|
||||
}
|
||||
public void ResetTimelineSelection()
|
||||
{
|
||||
if (ActiveCanvas == null) return;
|
||||
|
||||
ActiveCanvas.SelectedDateTime = null;
|
||||
}
|
||||
|
||||
public void GoNextRange()
|
||||
{
|
||||
if (InternalFlipView == null) return;
|
||||
|
||||
InternalFlipView.GoNextFlip();
|
||||
}
|
||||
|
||||
public void GoPreviousRange()
|
||||
{
|
||||
if (InternalFlipView == null) return;
|
||||
|
||||
InternalFlipView.GoPreviousFlip();
|
||||
}
|
||||
|
||||
public void UnselectActiveTimelineCell()
|
||||
{
|
||||
if (ActiveCanvas == null) return;
|
||||
|
||||
ActiveCanvas.SelectedDateTime = null;
|
||||
}
|
||||
|
||||
public CalendarItemControl GetCalendarItemControl(CalendarItemViewModel calendarItemViewModel)
|
||||
{
|
||||
return this.FindDescendants<CalendarItemControl>().FirstOrDefault(a => a.CalendarItem == calendarItemViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,179 +8,178 @@ using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Collections;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
namespace Wino.Calendar.Controls;
|
||||
|
||||
public partial class WinoCalendarFlipView : CustomCalendarFlipView
|
||||
{
|
||||
public class WinoCalendarFlipView : CustomCalendarFlipView
|
||||
public static readonly DependencyProperty IsIdleProperty = DependencyProperty.Register(nameof(IsIdle), typeof(bool), typeof(WinoCalendarFlipView), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty ActiveCanvasProperty = DependencyProperty.Register(nameof(ActiveCanvas), typeof(WinoDayTimelineCanvas), typeof(WinoCalendarFlipView), new PropertyMetadata(null));
|
||||
public static readonly DependencyProperty ActiveVerticalScrollViewerProperty = DependencyProperty.Register(nameof(ActiveVerticalScrollViewer), typeof(ScrollViewer), typeof(WinoCalendarFlipView), new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the active canvas that is currently displayed in the flip view.
|
||||
/// Each day-range of flip view item has a canvas that displays the day timeline.
|
||||
/// </summary>
|
||||
public WinoDayTimelineCanvas ActiveCanvas
|
||||
{
|
||||
public static readonly DependencyProperty IsIdleProperty = DependencyProperty.Register(nameof(IsIdle), typeof(bool), typeof(WinoCalendarFlipView), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty ActiveCanvasProperty = DependencyProperty.Register(nameof(ActiveCanvas), typeof(WinoDayTimelineCanvas), typeof(WinoCalendarFlipView), new PropertyMetadata(null));
|
||||
public static readonly DependencyProperty ActiveVerticalScrollViewerProperty = DependencyProperty.Register(nameof(ActiveVerticalScrollViewer), typeof(ScrollViewer), typeof(WinoCalendarFlipView), new PropertyMetadata(null));
|
||||
get { return (WinoDayTimelineCanvas)GetValue(ActiveCanvasProperty); }
|
||||
set { SetValue(ActiveCanvasProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the active canvas that is currently displayed in the flip view.
|
||||
/// Each day-range of flip view item has a canvas that displays the day timeline.
|
||||
/// </summary>
|
||||
public WinoDayTimelineCanvas ActiveCanvas
|
||||
/// <summary>
|
||||
/// Gets or sets the scroll viewer that is currently active in the flip view.
|
||||
/// It's the vertical scroll that scrolls the timeline only, not the header part that belongs
|
||||
/// to parent FlipView control.
|
||||
/// </summary>
|
||||
public ScrollViewer ActiveVerticalScrollViewer
|
||||
{
|
||||
get { return (ScrollViewer)GetValue(ActiveVerticalScrollViewerProperty); }
|
||||
set { SetValue(ActiveVerticalScrollViewerProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsIdle
|
||||
{
|
||||
get { return (bool)GetValue(IsIdleProperty); }
|
||||
set { SetValue(IsIdleProperty, value); }
|
||||
}
|
||||
|
||||
public WinoCalendarFlipView()
|
||||
{
|
||||
RegisterPropertyChangedCallback(SelectedIndexProperty, new DependencyPropertyChangedCallback(OnSelectedIndexUpdated));
|
||||
RegisterPropertyChangedCallback(ItemsSourceProperty, new DependencyPropertyChangedCallback(OnItemsSourceChanged));
|
||||
}
|
||||
|
||||
private static void OnItemsSourceChanged(DependencyObject d, DependencyProperty e)
|
||||
{
|
||||
if (d is WinoCalendarFlipView flipView)
|
||||
{
|
||||
get { return (WinoDayTimelineCanvas)GetValue(ActiveCanvasProperty); }
|
||||
set { SetValue(ActiveCanvasProperty, value); }
|
||||
flipView.RegisterItemsSourceChange();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnSelectedIndexUpdated(DependencyObject d, DependencyProperty e)
|
||||
{
|
||||
if (d is WinoCalendarFlipView flipView)
|
||||
{
|
||||
flipView.UpdateActiveCanvas();
|
||||
flipView.UpdateActiveScrollViewer();
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterItemsSourceChange()
|
||||
{
|
||||
if (GetItemsSource() is INotifyCollectionChanged notifyCollectionChanged)
|
||||
{
|
||||
notifyCollectionChanged.CollectionChanged += ItemsSourceUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
private void ItemsSourceUpdated(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
IsIdle = e.Action == NotifyCollectionChangedAction.Reset || e.Action == NotifyCollectionChangedAction.Replace;
|
||||
}
|
||||
|
||||
private async Task<FlipViewItem> GetCurrentFlipViewItem()
|
||||
{
|
||||
// TODO: Refactor this mechanism by listening to PrepareContainerForItemOverride and Loaded events together.
|
||||
while (ContainerFromIndex(SelectedIndex) == null)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scroll viewer that is currently active in the flip view.
|
||||
/// It's the vertical scroll that scrolls the timeline only, not the header part that belongs
|
||||
/// to parent FlipView control.
|
||||
/// </summary>
|
||||
public ScrollViewer ActiveVerticalScrollViewer
|
||||
{
|
||||
get { return (ScrollViewer)GetValue(ActiveVerticalScrollViewerProperty); }
|
||||
set { SetValue(ActiveVerticalScrollViewerProperty, value); }
|
||||
}
|
||||
return ContainerFromIndex(SelectedIndex) as FlipViewItem;
|
||||
|
||||
public bool IsIdle
|
||||
{
|
||||
get { return (bool)GetValue(IsIdleProperty); }
|
||||
set { SetValue(IsIdleProperty, value); }
|
||||
}
|
||||
|
||||
public WinoCalendarFlipView()
|
||||
{
|
||||
RegisterPropertyChangedCallback(SelectedIndexProperty, new DependencyPropertyChangedCallback(OnSelectedIndexUpdated));
|
||||
RegisterPropertyChangedCallback(ItemsSourceProperty, new DependencyPropertyChangedCallback(OnItemsSourceChanged));
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnItemsSourceChanged(DependencyObject d, DependencyProperty e)
|
||||
private void UpdateActiveScrollViewer()
|
||||
{
|
||||
if (SelectedIndex < 0)
|
||||
ActiveVerticalScrollViewer = null;
|
||||
else
|
||||
{
|
||||
if (d is WinoCalendarFlipView flipView)
|
||||
GetCurrentFlipViewItem().ContinueWith(task =>
|
||||
{
|
||||
flipView.RegisterItemsSourceChange();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnSelectedIndexUpdated(DependencyObject d, DependencyProperty e)
|
||||
{
|
||||
if (d is WinoCalendarFlipView flipView)
|
||||
{
|
||||
flipView.UpdateActiveCanvas();
|
||||
flipView.UpdateActiveScrollViewer();
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterItemsSourceChange()
|
||||
{
|
||||
if (GetItemsSource() is INotifyCollectionChanged notifyCollectionChanged)
|
||||
{
|
||||
notifyCollectionChanged.CollectionChanged += ItemsSourceUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
private void ItemsSourceUpdated(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
IsIdle = e.Action == NotifyCollectionChangedAction.Reset || e.Action == NotifyCollectionChangedAction.Replace;
|
||||
}
|
||||
|
||||
private async Task<FlipViewItem> GetCurrentFlipViewItem()
|
||||
{
|
||||
// TODO: Refactor this mechanism by listening to PrepareContainerForItemOverride and Loaded events together.
|
||||
while (ContainerFromIndex(SelectedIndex) == null)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
return ContainerFromIndex(SelectedIndex) as FlipViewItem;
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void UpdateActiveScrollViewer()
|
||||
{
|
||||
if (SelectedIndex < 0)
|
||||
ActiveVerticalScrollViewer = null;
|
||||
else
|
||||
{
|
||||
GetCurrentFlipViewItem().ContinueWith(task =>
|
||||
if (task.IsCompletedSuccessfully)
|
||||
{
|
||||
if (task.IsCompletedSuccessfully)
|
||||
var flipViewItem = task.Result;
|
||||
|
||||
_ = Dispatcher.TryRunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
var flipViewItem = task.Result;
|
||||
|
||||
_ = Dispatcher.TryRunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
ActiveVerticalScrollViewer = flipViewItem.FindDescendant<ScrollViewer>();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateActiveCanvas()
|
||||
{
|
||||
if (SelectedIndex < 0)
|
||||
ActiveCanvas = null;
|
||||
else
|
||||
{
|
||||
GetCurrentFlipViewItem().ContinueWith(task =>
|
||||
{
|
||||
if (task.IsCompletedSuccessfully)
|
||||
{
|
||||
var flipViewItem = task.Result;
|
||||
|
||||
_ = Dispatcher.TryRunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
ActiveCanvas = flipViewItem.FindDescendant<WinoDayTimelineCanvas>();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to the specified date in the calendar.
|
||||
/// </summary>
|
||||
/// <param name="dateTime">Date to navigate.</param>
|
||||
public async void NavigateToDay(DateTime dateTime)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
|
||||
{
|
||||
// Find the day range that contains the date.
|
||||
var dayRange = GetItemsSource()?.FirstOrDefault(a => a.CalendarDays.Any(b => b.RepresentingDate.Date == dateTime.Date));
|
||||
|
||||
if (dayRange != null)
|
||||
{
|
||||
var navigationItemIndex = GetItemsSource().IndexOf(dayRange);
|
||||
|
||||
if (Math.Abs(navigationItemIndex - SelectedIndex) > 4)
|
||||
{
|
||||
// Difference between dates are high.
|
||||
// No need to animate this much, just go without animating.
|
||||
|
||||
SelectedIndex = navigationItemIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Until we reach the day in the flip, simulate next-prev button clicks.
|
||||
// This will make sure the FlipView animations are triggered.
|
||||
// Setting SelectedIndex directly doesn't trigger the animations.
|
||||
|
||||
while (SelectedIndex != navigationItemIndex)
|
||||
{
|
||||
if (SelectedIndex > navigationItemIndex)
|
||||
{
|
||||
GoPreviousFlip();
|
||||
}
|
||||
else
|
||||
{
|
||||
GoNextFlip();
|
||||
}
|
||||
}
|
||||
}
|
||||
ActiveVerticalScrollViewer = flipViewItem.FindDescendant<ScrollViewer>();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ObservableRangeCollection<DayRangeRenderModel> GetItemsSource()
|
||||
=> ItemsSource as ObservableRangeCollection<DayRangeRenderModel>;
|
||||
}
|
||||
|
||||
public void UpdateActiveCanvas()
|
||||
{
|
||||
if (SelectedIndex < 0)
|
||||
ActiveCanvas = null;
|
||||
else
|
||||
{
|
||||
GetCurrentFlipViewItem().ContinueWith(task =>
|
||||
{
|
||||
if (task.IsCompletedSuccessfully)
|
||||
{
|
||||
var flipViewItem = task.Result;
|
||||
|
||||
_ = Dispatcher.TryRunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
ActiveCanvas = flipViewItem.FindDescendant<WinoDayTimelineCanvas>();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to the specified date in the calendar.
|
||||
/// </summary>
|
||||
/// <param name="dateTime">Date to navigate.</param>
|
||||
public async void NavigateToDay(DateTime dateTime)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
|
||||
{
|
||||
// Find the day range that contains the date.
|
||||
var dayRange = GetItemsSource()?.FirstOrDefault(a => a.CalendarDays.Any(b => b.RepresentingDate.Date == dateTime.Date));
|
||||
|
||||
if (dayRange != null)
|
||||
{
|
||||
var navigationItemIndex = GetItemsSource().IndexOf(dayRange);
|
||||
|
||||
if (Math.Abs(navigationItemIndex - SelectedIndex) > 4)
|
||||
{
|
||||
// Difference between dates are high.
|
||||
// No need to animate this much, just go without animating.
|
||||
|
||||
SelectedIndex = navigationItemIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Until we reach the day in the flip, simulate next-prev button clicks.
|
||||
// This will make sure the FlipView animations are triggered.
|
||||
// Setting SelectedIndex directly doesn't trigger the animations.
|
||||
|
||||
while (SelectedIndex != navigationItemIndex)
|
||||
{
|
||||
if (SelectedIndex > navigationItemIndex)
|
||||
{
|
||||
GoPreviousFlip();
|
||||
}
|
||||
else
|
||||
{
|
||||
GoNextFlip();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ObservableRangeCollection<DayRangeRenderModel> GetItemsSource()
|
||||
=> ItemsSource as ObservableRangeCollection<DayRangeRenderModel>;
|
||||
}
|
||||
|
||||
@@ -9,286 +9,290 @@ using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Calendar.Models;
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
namespace Wino.Calendar.Controls;
|
||||
|
||||
public partial class WinoCalendarPanel : Panel
|
||||
{
|
||||
public class WinoCalendarPanel : Panel
|
||||
private const double LastItemRightExtraMargin = 12d;
|
||||
|
||||
// Store each ICalendarItem measurements by their Id.
|
||||
private readonly Dictionary<ICalendarItem, CalendarItemMeasurement> _measurements = new Dictionary<ICalendarItem, CalendarItemMeasurement>();
|
||||
|
||||
public static readonly DependencyProperty EventItemMarginProperty = DependencyProperty.Register(nameof(EventItemMargin), typeof(Thickness), typeof(WinoCalendarPanel), new PropertyMetadata(new Thickness(0, 0, 0, 0)));
|
||||
public static readonly DependencyProperty HourHeightProperty = DependencyProperty.Register(nameof(HourHeight), typeof(double), typeof(WinoCalendarPanel), new PropertyMetadata(0d));
|
||||
public static readonly DependencyProperty PeriodProperty = DependencyProperty.Register(nameof(Period), typeof(ITimePeriod), typeof(WinoCalendarPanel), new PropertyMetadata(null));
|
||||
|
||||
public ITimePeriod Period
|
||||
{
|
||||
private const double LastItemRightExtraMargin = 12d;
|
||||
get { return (ITimePeriod)GetValue(PeriodProperty); }
|
||||
set { SetValue(PeriodProperty, value); }
|
||||
}
|
||||
|
||||
// Store each ICalendarItem measurements by their Id.
|
||||
private readonly Dictionary<ICalendarItem, CalendarItemMeasurement> _measurements = new Dictionary<ICalendarItem, CalendarItemMeasurement>();
|
||||
public double HourHeight
|
||||
{
|
||||
get { return (double)GetValue(HourHeightProperty); }
|
||||
set { SetValue(HourHeightProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty EventItemMarginProperty = DependencyProperty.Register(nameof(EventItemMargin), typeof(Thickness), typeof(WinoCalendarPanel), new PropertyMetadata(new Thickness(0, 0, 0, 0)));
|
||||
public static readonly DependencyProperty HourHeightProperty = DependencyProperty.Register(nameof(HourHeight), typeof(double), typeof(WinoCalendarPanel), new PropertyMetadata(0d));
|
||||
public static readonly DependencyProperty PeriodProperty = DependencyProperty.Register(nameof(Period), typeof(ITimePeriod), typeof(WinoCalendarPanel), new PropertyMetadata(null));
|
||||
public Thickness EventItemMargin
|
||||
{
|
||||
get { return (Thickness)GetValue(EventItemMarginProperty); }
|
||||
set { SetValue(EventItemMarginProperty, value); }
|
||||
}
|
||||
|
||||
public ITimePeriod Period
|
||||
private void ResetMeasurements() => _measurements.Clear();
|
||||
|
||||
private double GetChildTopMargin(ICalendarItemViewModel calendarItemViewModel, double availableHeight)
|
||||
{
|
||||
var childStart = calendarItemViewModel.LocalStartDateTime;
|
||||
|
||||
if (childStart <= Period.Start)
|
||||
{
|
||||
get { return (ITimePeriod)GetValue(PeriodProperty); }
|
||||
set { SetValue(PeriodProperty, value); }
|
||||
// Event started before or exactly at the periods tart. This might be a multi-day event.
|
||||
// We can simply consider event must not have a top margin.
|
||||
|
||||
return 0d;
|
||||
}
|
||||
|
||||
public double HourHeight
|
||||
double minutesFromStart = (childStart - Period.Start).TotalMinutes;
|
||||
return (minutesFromStart / 1440) * availableHeight;
|
||||
}
|
||||
|
||||
private double GetChildWidth(CalendarItemMeasurement calendarItemMeasurement, double availableWidth)
|
||||
{
|
||||
return (calendarItemMeasurement.Right - calendarItemMeasurement.Left) * availableWidth;
|
||||
}
|
||||
|
||||
private double GetChildLeftMargin(CalendarItemMeasurement calendarItemMeasurement, double availableWidth)
|
||||
=> availableWidth * calendarItemMeasurement.Left;
|
||||
|
||||
private double GetChildHeight(ICalendarItemViewModel child)
|
||||
{
|
||||
// All day events are not measured.
|
||||
if (child.ItemType == CalendarItemType.AllDay) return 0;
|
||||
|
||||
double childDurationInMinutes = 0d;
|
||||
double availableHeight = HourHeight * 24;
|
||||
|
||||
var periodRelation = child.Period.GetRelation(Period);
|
||||
|
||||
// Debug.WriteLine($"Render relation of {child.Title} ({child.Period.Start} - {child.Period.End}) is {periodRelation} with {Period.Start.Day}");
|
||||
|
||||
if (child.ItemType != CalendarItemType.MultiDay || child.ItemType != CalendarItemType.MultiDayAllDay)
|
||||
{
|
||||
get { return (double)GetValue(HourHeightProperty); }
|
||||
set { SetValue(HourHeightProperty, value); }
|
||||
childDurationInMinutes = child.Period.Duration.TotalMinutes;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Multi-day event.
|
||||
// Check how many of the event falls into the current period.
|
||||
childDurationInMinutes = (child.Period.End - Period.Start).TotalMinutes;
|
||||
}
|
||||
|
||||
public Thickness EventItemMargin
|
||||
return (childDurationInMinutes / 1440) * availableHeight;
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
ResetMeasurements();
|
||||
return base.MeasureOverride(availableSize);
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (Period == null || HourHeight == 0d) return finalSize;
|
||||
|
||||
// Measure/arrange each child height and width.
|
||||
// This is a vertical calendar. Therefore the height of each child is the duration of the event.
|
||||
// Children weights for left and right will be saved if they don't exist.
|
||||
// This is important because we don't want to measure the weights again.
|
||||
// They don't change until new event is added or removed.
|
||||
// Width of the each child may depend on the rectangle packing algorithm.
|
||||
// Children are first categorized into columns. Then each column is shifted to the left until
|
||||
// no overlap occurs. The width of each child is calculated based on the number of columns it spans.
|
||||
|
||||
double availableHeight = finalSize.Height;
|
||||
double availableWidth = finalSize.Width;
|
||||
|
||||
var calendarControls = Children.Cast<ContentPresenter>();
|
||||
|
||||
if (!calendarControls.Any()) return base.ArrangeOverride(finalSize);
|
||||
|
||||
var events = calendarControls.Select(a => a.Content as CalendarItemViewModel);
|
||||
|
||||
LayoutEvents(events);
|
||||
|
||||
foreach (var control in calendarControls)
|
||||
{
|
||||
get { return (Thickness)GetValue(EventItemMarginProperty); }
|
||||
set { SetValue(EventItemMarginProperty, value); }
|
||||
}
|
||||
// We can't arrange this child.
|
||||
if (!(control.Content is ICalendarItemViewModel child)) continue;
|
||||
|
||||
private void ResetMeasurements() => _measurements.Clear();
|
||||
bool isHorizontallyLastItem = false;
|
||||
|
||||
private double GetChildTopMargin(ICalendarItem calendarItemViewModel, double availableHeight)
|
||||
{
|
||||
var childStart = calendarItemViewModel.StartDate;
|
||||
double childWidth = 0,
|
||||
childHeight = Math.Max(0, GetChildHeight(child)),
|
||||
childTop = Math.Max(0, GetChildTopMargin(child, availableHeight)),
|
||||
childLeft = 0;
|
||||
|
||||
if (childStart <= Period.Start)
|
||||
{
|
||||
// Event started before or exactly at the periods tart. This might be a multi-day event.
|
||||
// We can simply consider event must not have a top margin.
|
||||
// No need to measure anything here.
|
||||
if (childHeight == 0) continue;
|
||||
|
||||
return 0d;
|
||||
}
|
||||
|
||||
double minutesFromStart = (childStart - Period.Start).TotalMinutes;
|
||||
return (minutesFromStart / 1440) * availableHeight;
|
||||
}
|
||||
|
||||
private double GetChildWidth(CalendarItemMeasurement calendarItemMeasurement, double availableWidth)
|
||||
{
|
||||
return (calendarItemMeasurement.Right - calendarItemMeasurement.Left) * availableWidth;
|
||||
}
|
||||
|
||||
private double GetChildLeftMargin(CalendarItemMeasurement calendarItemMeasurement, double availableWidth)
|
||||
=> availableWidth * calendarItemMeasurement.Left;
|
||||
|
||||
private double GetChildHeight(ICalendarItem child)
|
||||
{
|
||||
// All day events are not measured.
|
||||
if (child.IsAllDayEvent) return 0;
|
||||
|
||||
double childDurationInMinutes = 0d;
|
||||
double availableHeight = HourHeight * 24;
|
||||
|
||||
var periodRelation = child.Period.GetRelation(Period);
|
||||
|
||||
// Debug.WriteLine($"Render relation of {child.Title} ({child.Period.Start} - {child.Period.End}) is {periodRelation} with {Period.Start.Day}");
|
||||
|
||||
if (!child.IsMultiDayEvent)
|
||||
{
|
||||
childDurationInMinutes = child.Period.Duration.TotalMinutes;
|
||||
}
|
||||
else
|
||||
if (!_measurements.ContainsKey(child))
|
||||
{
|
||||
// Multi-day event.
|
||||
// Check how many of the event falls into the current period.
|
||||
childDurationInMinutes = (child.Period.End - Period.Start).TotalMinutes;
|
||||
}
|
||||
|
||||
return (childDurationInMinutes / 1440) * availableHeight;
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
ResetMeasurements();
|
||||
return base.MeasureOverride(availableSize);
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (Period == null || HourHeight == 0d) return finalSize;
|
||||
|
||||
// Measure/arrange each child height and width.
|
||||
// This is a vertical calendar. Therefore the height of each child is the duration of the event.
|
||||
// Children weights for left and right will be saved if they don't exist.
|
||||
// This is important because we don't want to measure the weights again.
|
||||
// They don't change until new event is added or removed.
|
||||
// Width of the each child may depend on the rectangle packing algorithm.
|
||||
// Children are first categorized into columns. Then each column is shifted to the left until
|
||||
// no overlap occurs. The width of each child is calculated based on the number of columns it spans.
|
||||
|
||||
double availableHeight = finalSize.Height;
|
||||
double availableWidth = finalSize.Width;
|
||||
|
||||
var calendarControls = Children.Cast<ContentPresenter>();
|
||||
|
||||
if (!calendarControls.Any()) return base.ArrangeOverride(finalSize);
|
||||
|
||||
var events = calendarControls.Select(a => a.Content as CalendarItemViewModel);
|
||||
|
||||
LayoutEvents(events);
|
||||
|
||||
foreach (var control in calendarControls)
|
||||
{
|
||||
// We can't arrange this child.
|
||||
if (!(control.Content is ICalendarItem child)) continue;
|
||||
|
||||
bool isHorizontallyLastItem = false;
|
||||
|
||||
double childWidth = 0,
|
||||
childHeight = Math.Max(0, GetChildHeight(child)),
|
||||
childTop = Math.Max(0, GetChildTopMargin(child, availableHeight)),
|
||||
childLeft = 0;
|
||||
|
||||
// No need to measure anything here.
|
||||
if (childHeight == 0) continue;
|
||||
|
||||
if (!_measurements.ContainsKey(child))
|
||||
{
|
||||
// Multi-day event.
|
||||
|
||||
childLeft = 0;
|
||||
childWidth = availableWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
var childMeasurement = _measurements[child];
|
||||
|
||||
childWidth = Math.Max(0, GetChildWidth(childMeasurement, finalSize.Width));
|
||||
childLeft = Math.Max(0, GetChildLeftMargin(childMeasurement, availableWidth));
|
||||
|
||||
isHorizontallyLastItem = childMeasurement.Right == 1;
|
||||
}
|
||||
|
||||
// Add additional right margin to items that falls on the right edge of the panel.
|
||||
double extraRightMargin = 0;
|
||||
|
||||
// Multi-day events don't have any margin and their hit test is disabled.
|
||||
if (!child.IsMultiDayEvent)
|
||||
{
|
||||
// Max of 5% of the width or 20px max.
|
||||
extraRightMargin = isHorizontallyLastItem ? Math.Max(LastItemRightExtraMargin, finalSize.Width * 5 / 100) : 0;
|
||||
}
|
||||
|
||||
if (childWidth < 0) childWidth = 1;
|
||||
|
||||
// Regular events must have 2px margin
|
||||
if (!child.IsMultiDayEvent && !child.IsAllDayEvent)
|
||||
{
|
||||
childLeft += 2;
|
||||
childTop += 2;
|
||||
childHeight -= 2;
|
||||
childWidth -= 2;
|
||||
}
|
||||
|
||||
var arrangementRect = new Rect(childLeft + EventItemMargin.Left, childTop + EventItemMargin.Top, Math.Max(childWidth - extraRightMargin, 1), childHeight);
|
||||
|
||||
// Make sure measured size will fit in the arranged box.
|
||||
var measureSize = arrangementRect.ToSize();
|
||||
control.Measure(measureSize);
|
||||
control.Arrange(arrangementRect);
|
||||
|
||||
//Debug.WriteLine($"{child.Title}, Measured: {measureSize}, Arranged: {arrangementRect}");
|
||||
}
|
||||
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
#region ColumSpanning and Packing Algorithm
|
||||
|
||||
private void AddOrUpdateMeasurement(ICalendarItem calendarItem, CalendarItemMeasurement measurement)
|
||||
{
|
||||
if (_measurements.ContainsKey(calendarItem))
|
||||
{
|
||||
_measurements[calendarItem] = measurement;
|
||||
childLeft = 0;
|
||||
childWidth = availableWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
_measurements.Add(calendarItem, measurement);
|
||||
var childMeasurement = _measurements[child];
|
||||
|
||||
childWidth = Math.Max(0, GetChildWidth(childMeasurement, finalSize.Width));
|
||||
childLeft = Math.Max(0, GetChildLeftMargin(childMeasurement, availableWidth));
|
||||
|
||||
isHorizontallyLastItem = childMeasurement.Right == 1;
|
||||
}
|
||||
|
||||
// Add additional right margin to items that falls on the right edge of the panel.
|
||||
double extraRightMargin = 0;
|
||||
|
||||
// Multi-day events don't have any margin and their hit test is disabled.
|
||||
if (child.ItemType != CalendarItemType.MultiDay || child.ItemType != CalendarItemType.MultiDayAllDay)
|
||||
{
|
||||
// Max of 5% of the width or 20px max.
|
||||
extraRightMargin = isHorizontallyLastItem ? Math.Max(LastItemRightExtraMargin, finalSize.Width * 5 / 100) : 0;
|
||||
}
|
||||
|
||||
if (childWidth < 0) childWidth = 1;
|
||||
|
||||
bool isAllOrMultiDayEvent = child.ItemType == CalendarItemType.AllDay || child.ItemType == CalendarItemType.MultiDay || child.ItemType == CalendarItemType.MultiDayAllDay;
|
||||
|
||||
// Regular events must have 2px margin
|
||||
if (!isAllOrMultiDayEvent)
|
||||
{
|
||||
childLeft += 2;
|
||||
childTop += 2;
|
||||
childHeight -= 2;
|
||||
childWidth -= 2;
|
||||
}
|
||||
|
||||
var arrangementRect = new Rect(childLeft + EventItemMargin.Left, childTop + EventItemMargin.Top, Math.Max(childWidth - extraRightMargin, 1), childHeight);
|
||||
|
||||
// Make sure measured size will fit in the arranged box.
|
||||
var measureSize = arrangementRect.ToSize();
|
||||
control.Measure(measureSize);
|
||||
control.Arrange(arrangementRect);
|
||||
|
||||
//Debug.WriteLine($"{child.Title}, Measured: {measureSize}, Arranged: {arrangementRect}");
|
||||
}
|
||||
|
||||
// Pick the left and right positions of each event, such that there are no overlap.
|
||||
private void LayoutEvents(IEnumerable<ICalendarItem> events)
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
#region ColumSpanning and Packing Algorithm
|
||||
|
||||
private void AddOrUpdateMeasurement(ICalendarItem calendarItem, CalendarItemMeasurement measurement)
|
||||
{
|
||||
if (_measurements.ContainsKey(calendarItem))
|
||||
{
|
||||
var columns = new List<List<ICalendarItem>>();
|
||||
DateTime? lastEventEnding = null;
|
||||
_measurements[calendarItem] = measurement;
|
||||
}
|
||||
else
|
||||
{
|
||||
_measurements.Add(calendarItem, measurement);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var ev in events.OrderBy(ev => ev.StartDate).ThenBy(ev => ev.EndDate))
|
||||
{
|
||||
// Multi-day events are not measured.
|
||||
if (ev.IsMultiDayEvent) continue;
|
||||
// Pick the left and right positions of each event, such that there are no overlap.
|
||||
private void LayoutEvents(IEnumerable<ICalendarItem> events)
|
||||
{
|
||||
var columns = new List<List<ICalendarItem>>();
|
||||
DateTime? lastEventEnding = null;
|
||||
|
||||
if (ev.Period.Start >= lastEventEnding)
|
||||
{
|
||||
PackEvents(columns);
|
||||
columns.Clear();
|
||||
lastEventEnding = null;
|
||||
}
|
||||
foreach (var ev in events.OrderBy(ev => ev.StartDateTime).ThenBy(ev => ev.EndDateTime))
|
||||
{
|
||||
// Multi-day events are not measured.
|
||||
bool isMultiDayEvent = ev.ItemType == CalendarItemType.MultiDay || ev.ItemType == CalendarItemType.MultiDayAllDay;
|
||||
|
||||
bool placed = false;
|
||||
if (isMultiDayEvent) continue;
|
||||
|
||||
foreach (var col in columns)
|
||||
{
|
||||
if (!col.Last().Period.OverlapsWith(ev.Period))
|
||||
{
|
||||
col.Add(ev);
|
||||
placed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!placed)
|
||||
{
|
||||
columns.Add(new List<ICalendarItem> { ev });
|
||||
}
|
||||
if (lastEventEnding == null || ev.Period.End > lastEventEnding.Value)
|
||||
{
|
||||
lastEventEnding = ev.Period.End;
|
||||
}
|
||||
}
|
||||
if (columns.Count > 0)
|
||||
if (ev.Period.Start >= lastEventEnding)
|
||||
{
|
||||
PackEvents(columns);
|
||||
columns.Clear();
|
||||
lastEventEnding = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the left and right positions for each event in the connected group.
|
||||
private void PackEvents(List<List<ICalendarItem>> columns)
|
||||
{
|
||||
float numColumns = columns.Count;
|
||||
int iColumn = 0;
|
||||
bool placed = false;
|
||||
|
||||
foreach (var col in columns)
|
||||
{
|
||||
foreach (var ev in col)
|
||||
if (!col.Last().Period.OverlapsWith(ev.Period))
|
||||
{
|
||||
int colSpan = ExpandEvent(ev, iColumn, columns);
|
||||
|
||||
var leftWeight = iColumn / numColumns;
|
||||
var rightWeight = (iColumn + colSpan) / numColumns;
|
||||
|
||||
AddOrUpdateMeasurement(ev, new CalendarItemMeasurement(leftWeight, rightWeight));
|
||||
col.Add(ev);
|
||||
placed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
iColumn++;
|
||||
}
|
||||
}
|
||||
|
||||
// Checks how many columns the event can expand into, without colliding with other events.
|
||||
private int ExpandEvent(ICalendarItem ev, int iColumn, List<List<ICalendarItem>> columns)
|
||||
{
|
||||
int colSpan = 1;
|
||||
|
||||
foreach (var col in columns.Skip(iColumn + 1))
|
||||
if (!placed)
|
||||
{
|
||||
foreach (var ev1 in col)
|
||||
{
|
||||
if (ev1.Period.OverlapsWith(ev.Period)) return colSpan;
|
||||
}
|
||||
columns.Add(new List<ICalendarItem> { ev });
|
||||
}
|
||||
if (lastEventEnding == null || ev.Period.End > lastEventEnding.Value)
|
||||
{
|
||||
lastEventEnding = ev.Period.End;
|
||||
}
|
||||
}
|
||||
if (columns.Count > 0)
|
||||
{
|
||||
PackEvents(columns);
|
||||
}
|
||||
}
|
||||
|
||||
colSpan++;
|
||||
// Set the left and right positions for each event in the connected group.
|
||||
private void PackEvents(List<List<ICalendarItem>> columns)
|
||||
{
|
||||
float numColumns = columns.Count;
|
||||
int iColumn = 0;
|
||||
|
||||
foreach (var col in columns)
|
||||
{
|
||||
foreach (var ev in col)
|
||||
{
|
||||
int colSpan = ExpandEvent(ev, iColumn, columns);
|
||||
|
||||
var leftWeight = iColumn / numColumns;
|
||||
var rightWeight = (iColumn + colSpan) / numColumns;
|
||||
|
||||
AddOrUpdateMeasurement(ev, new CalendarItemMeasurement(leftWeight, rightWeight));
|
||||
}
|
||||
|
||||
return colSpan;
|
||||
iColumn++;
|
||||
}
|
||||
}
|
||||
|
||||
// Checks how many columns the event can expand into, without colliding with other events.
|
||||
private int ExpandEvent(ICalendarItem ev, int iColumn, List<List<ICalendarItem>> columns)
|
||||
{
|
||||
int colSpan = 1;
|
||||
|
||||
foreach (var col in columns.Skip(iColumn + 1))
|
||||
{
|
||||
foreach (var ev1 in col)
|
||||
{
|
||||
if (ev1.Period.OverlapsWith(ev.Period)) return colSpan;
|
||||
}
|
||||
|
||||
colSpan++;
|
||||
}
|
||||
|
||||
#endregion
|
||||
return colSpan;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -4,89 +4,88 @@ using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
namespace Wino.Calendar.Controls;
|
||||
|
||||
public partial class WinoCalendarTypeSelectorControl : Control
|
||||
{
|
||||
public class WinoCalendarTypeSelectorControl : Control
|
||||
private const string PART_TodayButton = nameof(PART_TodayButton);
|
||||
private const string PART_DayToggle = nameof(PART_DayToggle);
|
||||
private const string PART_WeekToggle = nameof(PART_WeekToggle);
|
||||
private const string PART_MonthToggle = nameof(PART_MonthToggle);
|
||||
private const string PART_YearToggle = nameof(PART_YearToggle);
|
||||
|
||||
public static readonly DependencyProperty SelectedTypeProperty = DependencyProperty.Register(nameof(SelectedType), typeof(CalendarDisplayType), typeof(WinoCalendarTypeSelectorControl), new PropertyMetadata(CalendarDisplayType.Week));
|
||||
public static readonly DependencyProperty DisplayDayCountProperty = DependencyProperty.Register(nameof(DisplayDayCount), typeof(int), typeof(WinoCalendarTypeSelectorControl), new PropertyMetadata(0));
|
||||
public static readonly DependencyProperty TodayClickedCommandProperty = DependencyProperty.Register(nameof(TodayClickedCommand), typeof(ICommand), typeof(WinoCalendarTypeSelectorControl), new PropertyMetadata(null));
|
||||
|
||||
public ICommand TodayClickedCommand
|
||||
{
|
||||
private const string PART_TodayButton = nameof(PART_TodayButton);
|
||||
private const string PART_DayToggle = nameof(PART_DayToggle);
|
||||
private const string PART_WeekToggle = nameof(PART_WeekToggle);
|
||||
private const string PART_MonthToggle = nameof(PART_MonthToggle);
|
||||
private const string PART_YearToggle = nameof(PART_YearToggle);
|
||||
get { return (ICommand)GetValue(TodayClickedCommandProperty); }
|
||||
set { SetValue(TodayClickedCommandProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SelectedTypeProperty = DependencyProperty.Register(nameof(SelectedType), typeof(CalendarDisplayType), typeof(WinoCalendarTypeSelectorControl), new PropertyMetadata(CalendarDisplayType.Week));
|
||||
public static readonly DependencyProperty DisplayDayCountProperty = DependencyProperty.Register(nameof(DisplayDayCount), typeof(int), typeof(WinoCalendarTypeSelectorControl), new PropertyMetadata(0));
|
||||
public static readonly DependencyProperty TodayClickedCommandProperty = DependencyProperty.Register(nameof(TodayClickedCommand), typeof(ICommand), typeof(WinoCalendarTypeSelectorControl), new PropertyMetadata(null));
|
||||
public CalendarDisplayType SelectedType
|
||||
{
|
||||
get { return (CalendarDisplayType)GetValue(SelectedTypeProperty); }
|
||||
set { SetValue(SelectedTypeProperty, value); }
|
||||
}
|
||||
|
||||
public ICommand TodayClickedCommand
|
||||
{
|
||||
get { return (ICommand)GetValue(TodayClickedCommandProperty); }
|
||||
set { SetValue(TodayClickedCommandProperty, value); }
|
||||
}
|
||||
public int DisplayDayCount
|
||||
{
|
||||
get { return (int)GetValue(DisplayDayCountProperty); }
|
||||
set { SetValue(DisplayDayCountProperty, value); }
|
||||
}
|
||||
|
||||
public CalendarDisplayType SelectedType
|
||||
{
|
||||
get { return (CalendarDisplayType)GetValue(SelectedTypeProperty); }
|
||||
set { SetValue(SelectedTypeProperty, value); }
|
||||
}
|
||||
private AppBarButton _todayButton;
|
||||
private AppBarToggleButton _dayToggle;
|
||||
private AppBarToggleButton _weekToggle;
|
||||
private AppBarToggleButton _monthToggle;
|
||||
private AppBarToggleButton _yearToggle;
|
||||
|
||||
public int DisplayDayCount
|
||||
{
|
||||
get { return (int)GetValue(DisplayDayCountProperty); }
|
||||
set { SetValue(DisplayDayCountProperty, value); }
|
||||
}
|
||||
public WinoCalendarTypeSelectorControl()
|
||||
{
|
||||
DefaultStyleKey = typeof(WinoCalendarTypeSelectorControl);
|
||||
}
|
||||
|
||||
private AppBarButton _todayButton;
|
||||
private AppBarToggleButton _dayToggle;
|
||||
private AppBarToggleButton _weekToggle;
|
||||
private AppBarToggleButton _monthToggle;
|
||||
private AppBarToggleButton _yearToggle;
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
public WinoCalendarTypeSelectorControl()
|
||||
{
|
||||
DefaultStyleKey = typeof(WinoCalendarTypeSelectorControl);
|
||||
}
|
||||
_todayButton = GetTemplateChild(PART_TodayButton) as AppBarButton;
|
||||
_dayToggle = GetTemplateChild(PART_DayToggle) as AppBarToggleButton;
|
||||
_weekToggle = GetTemplateChild(PART_WeekToggle) as AppBarToggleButton;
|
||||
_monthToggle = GetTemplateChild(PART_MonthToggle) as AppBarToggleButton;
|
||||
_yearToggle = GetTemplateChild(PART_YearToggle) as AppBarToggleButton;
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
Guard.IsNotNull(_todayButton, nameof(_todayButton));
|
||||
Guard.IsNotNull(_dayToggle, nameof(_dayToggle));
|
||||
Guard.IsNotNull(_weekToggle, nameof(_weekToggle));
|
||||
Guard.IsNotNull(_monthToggle, nameof(_monthToggle));
|
||||
Guard.IsNotNull(_yearToggle, nameof(_yearToggle));
|
||||
|
||||
_todayButton = GetTemplateChild(PART_TodayButton) as AppBarButton;
|
||||
_dayToggle = GetTemplateChild(PART_DayToggle) as AppBarToggleButton;
|
||||
_weekToggle = GetTemplateChild(PART_WeekToggle) as AppBarToggleButton;
|
||||
_monthToggle = GetTemplateChild(PART_MonthToggle) as AppBarToggleButton;
|
||||
_yearToggle = GetTemplateChild(PART_YearToggle) as AppBarToggleButton;
|
||||
_todayButton.Click += TodayClicked;
|
||||
|
||||
Guard.IsNotNull(_todayButton, nameof(_todayButton));
|
||||
Guard.IsNotNull(_dayToggle, nameof(_dayToggle));
|
||||
Guard.IsNotNull(_weekToggle, nameof(_weekToggle));
|
||||
Guard.IsNotNull(_monthToggle, nameof(_monthToggle));
|
||||
Guard.IsNotNull(_yearToggle, nameof(_yearToggle));
|
||||
_dayToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Day); };
|
||||
_weekToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Week); };
|
||||
_monthToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Month); };
|
||||
_yearToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Year); };
|
||||
|
||||
_todayButton.Click += TodayClicked;
|
||||
UpdateToggleButtonStates();
|
||||
}
|
||||
|
||||
_dayToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Day); };
|
||||
_weekToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Week); };
|
||||
_monthToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Month); };
|
||||
_yearToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Year); };
|
||||
private void TodayClicked(object sender, RoutedEventArgs e) => TodayClickedCommand?.Execute(null);
|
||||
|
||||
UpdateToggleButtonStates();
|
||||
}
|
||||
private void SetSelectedType(CalendarDisplayType type)
|
||||
{
|
||||
SelectedType = type;
|
||||
UpdateToggleButtonStates();
|
||||
}
|
||||
|
||||
private void TodayClicked(object sender, RoutedEventArgs e) => TodayClickedCommand?.Execute(null);
|
||||
|
||||
private void SetSelectedType(CalendarDisplayType type)
|
||||
{
|
||||
SelectedType = type;
|
||||
UpdateToggleButtonStates();
|
||||
}
|
||||
|
||||
private void UpdateToggleButtonStates()
|
||||
{
|
||||
_dayToggle.IsChecked = SelectedType == CalendarDisplayType.Day;
|
||||
_weekToggle.IsChecked = SelectedType == CalendarDisplayType.Week;
|
||||
_monthToggle.IsChecked = SelectedType == CalendarDisplayType.Month;
|
||||
_yearToggle.IsChecked = SelectedType == CalendarDisplayType.Year;
|
||||
}
|
||||
private void UpdateToggleButtonStates()
|
||||
{
|
||||
_dayToggle.IsChecked = SelectedType == CalendarDisplayType.Day;
|
||||
_weekToggle.IsChecked = SelectedType == CalendarDisplayType.Week;
|
||||
_monthToggle.IsChecked = SelectedType == CalendarDisplayType.Month;
|
||||
_yearToggle.IsChecked = SelectedType == CalendarDisplayType.Year;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,140 +8,139 @@ using Windows.UI.Xaml.Media;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
using Wino.Helpers;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
namespace Wino.Calendar.Controls;
|
||||
|
||||
public partial class WinoCalendarView : Control
|
||||
{
|
||||
public class WinoCalendarView : Control
|
||||
private const string PART_DayViewItemBorder = nameof(PART_DayViewItemBorder);
|
||||
private const string PART_CalendarView = nameof(PART_CalendarView);
|
||||
|
||||
public static readonly DependencyProperty HighlightedDateRangeProperty = DependencyProperty.Register(nameof(HighlightedDateRange), typeof(DateRange), typeof(WinoCalendarView), new PropertyMetadata(null, new PropertyChangedCallback(OnHighlightedDateRangeChanged)));
|
||||
public static readonly DependencyProperty VisibleDateBackgroundProperty = DependencyProperty.Register(nameof(VisibleDateBackground), typeof(Brush), typeof(WinoCalendarView), new PropertyMetadata(null, new PropertyChangedCallback(OnPropertiesChanged)));
|
||||
public static readonly DependencyProperty DateClickedCommandProperty = DependencyProperty.Register(nameof(DateClickedCommand), typeof(ICommand), typeof(WinoCalendarView), new PropertyMetadata(null));
|
||||
public static readonly DependencyProperty TodayBackgroundColorProperty = DependencyProperty.Register(nameof(TodayBackgroundColor), typeof(Color), typeof(WinoCalendarView), new PropertyMetadata(null));
|
||||
|
||||
public Color TodayBackgroundColor
|
||||
{
|
||||
private const string PART_DayViewItemBorder = nameof(PART_DayViewItemBorder);
|
||||
private const string PART_CalendarView = nameof(PART_CalendarView);
|
||||
get { return (Color)GetValue(TodayBackgroundColorProperty); }
|
||||
set { SetValue(TodayBackgroundColorProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty HighlightedDateRangeProperty = DependencyProperty.Register(nameof(HighlightedDateRange), typeof(DateRange), typeof(WinoCalendarView), new PropertyMetadata(null, new PropertyChangedCallback(OnHighlightedDateRangeChanged)));
|
||||
public static readonly DependencyProperty VisibleDateBackgroundProperty = DependencyProperty.Register(nameof(VisibleDateBackground), typeof(Brush), typeof(WinoCalendarView), new PropertyMetadata(null, new PropertyChangedCallback(OnPropertiesChanged)));
|
||||
public static readonly DependencyProperty DateClickedCommandProperty = DependencyProperty.Register(nameof(DateClickedCommand), typeof(ICommand), typeof(WinoCalendarView), new PropertyMetadata(null));
|
||||
public static readonly DependencyProperty TodayBackgroundColorProperty = DependencyProperty.Register(nameof(TodayBackgroundColor), typeof(Color), typeof(WinoCalendarView), new PropertyMetadata(null));
|
||||
/// <summary>
|
||||
/// Gets or sets the command to execute when a date is picked.
|
||||
/// Unused.
|
||||
/// </summary>
|
||||
public ICommand DateClickedCommand
|
||||
{
|
||||
get { return (ICommand)GetValue(DateClickedCommandProperty); }
|
||||
set { SetValue(DateClickedCommandProperty, value); }
|
||||
}
|
||||
|
||||
public Color TodayBackgroundColor
|
||||
/// <summary>
|
||||
/// Gets or sets the highlighted range of dates.
|
||||
/// </summary>
|
||||
public DateRange HighlightedDateRange
|
||||
{
|
||||
get { return (DateRange)GetValue(HighlightedDateRangeProperty); }
|
||||
set { SetValue(HighlightedDateRangeProperty, value); }
|
||||
}
|
||||
|
||||
public Brush VisibleDateBackground
|
||||
{
|
||||
get { return (Brush)GetValue(VisibleDateBackgroundProperty); }
|
||||
set { SetValue(VisibleDateBackgroundProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
private CalendarView CalendarView;
|
||||
|
||||
public WinoCalendarView()
|
||||
{
|
||||
DefaultStyleKey = typeof(WinoCalendarView);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
CalendarView = GetTemplateChild(PART_CalendarView) as CalendarView;
|
||||
|
||||
Guard.IsNotNull(CalendarView, nameof(CalendarView));
|
||||
|
||||
CalendarView.SelectedDatesChanged -= InternalCalendarViewSelectionChanged;
|
||||
CalendarView.SelectedDatesChanged += InternalCalendarViewSelectionChanged;
|
||||
|
||||
// TODO: Should come from settings.
|
||||
CalendarView.FirstDayOfWeek = Windows.Globalization.DayOfWeek.Monday;
|
||||
|
||||
// Everytime display mode changes, update the visible date range backgrounds.
|
||||
// If users go back from year -> month -> day, we need to update the visible date range backgrounds.
|
||||
|
||||
CalendarView.RegisterPropertyChangedCallback(CalendarView.DisplayModeProperty, (s, e) => UpdateVisibleDateRangeBackgrounds());
|
||||
}
|
||||
|
||||
private void InternalCalendarViewSelectionChanged(CalendarView sender, CalendarViewSelectedDatesChangedEventArgs args)
|
||||
{
|
||||
if (args.AddedDates?.Count > 0)
|
||||
{
|
||||
get { return (Color)GetValue(TodayBackgroundColorProperty); }
|
||||
set { SetValue(TodayBackgroundColorProperty, value); }
|
||||
var clickedDate = args.AddedDates[0].Date;
|
||||
SetInnerDisplayDate(clickedDate);
|
||||
|
||||
var clickArgs = new CalendarViewDayClickedEventArgs(clickedDate);
|
||||
DateClickedCommand?.Execute(clickArgs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the command to execute when a date is picked.
|
||||
/// Unused.
|
||||
/// </summary>
|
||||
public ICommand DateClickedCommand
|
||||
// Reset selection, we don't show selected dates but react to them.
|
||||
CalendarView.SelectedDates.Clear();
|
||||
}
|
||||
|
||||
private static void OnPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is WinoCalendarView control)
|
||||
{
|
||||
get { return (ICommand)GetValue(DateClickedCommandProperty); }
|
||||
set { SetValue(DateClickedCommandProperty, value); }
|
||||
control.UpdateVisibleDateRangeBackgrounds();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the highlighted range of dates.
|
||||
/// </summary>
|
||||
public DateRange HighlightedDateRange
|
||||
private void SetInnerDisplayDate(DateTime dateTime) => CalendarView?.SetDisplayDate(dateTime);
|
||||
|
||||
// Changing selected dates will trigger the selection changed event.
|
||||
// It will behave like user clicked the date.
|
||||
public void GoToDay(DateTime dateTime) => CalendarView.SelectedDates.Add(dateTime);
|
||||
|
||||
private static void OnHighlightedDateRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is WinoCalendarView control)
|
||||
{
|
||||
get { return (DateRange)GetValue(HighlightedDateRangeProperty); }
|
||||
set { SetValue(HighlightedDateRangeProperty, value); }
|
||||
control.SetInnerDisplayDate(control.HighlightedDateRange.StartDate);
|
||||
control.UpdateVisibleDateRangeBackgrounds();
|
||||
}
|
||||
}
|
||||
|
||||
public Brush VisibleDateBackground
|
||||
public void UpdateVisibleDateRangeBackgrounds()
|
||||
{
|
||||
if (HighlightedDateRange == null || VisibleDateBackground == null || TodayBackgroundColor == null || CalendarView == null) return;
|
||||
|
||||
var markDateCalendarDayItems = WinoVisualTreeHelper.FindDescendants<CalendarViewDayItem>(CalendarView);
|
||||
|
||||
foreach (var calendarDayItem in markDateCalendarDayItems)
|
||||
{
|
||||
get { return (Brush)GetValue(VisibleDateBackgroundProperty); }
|
||||
set { SetValue(VisibleDateBackgroundProperty, value); }
|
||||
}
|
||||
var border = WinoVisualTreeHelper.GetChildObject<Border>(calendarDayItem, PART_DayViewItemBorder);
|
||||
|
||||
if (border == null) return;
|
||||
|
||||
|
||||
private CalendarView CalendarView;
|
||||
|
||||
public WinoCalendarView()
|
||||
{
|
||||
DefaultStyleKey = typeof(WinoCalendarView);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
CalendarView = GetTemplateChild(PART_CalendarView) as CalendarView;
|
||||
|
||||
Guard.IsNotNull(CalendarView, nameof(CalendarView));
|
||||
|
||||
CalendarView.SelectedDatesChanged -= InternalCalendarViewSelectionChanged;
|
||||
CalendarView.SelectedDatesChanged += InternalCalendarViewSelectionChanged;
|
||||
|
||||
// TODO: Should come from settings.
|
||||
CalendarView.FirstDayOfWeek = Windows.Globalization.DayOfWeek.Monday;
|
||||
|
||||
// Everytime display mode changes, update the visible date range backgrounds.
|
||||
// If users go back from year -> month -> day, we need to update the visible date range backgrounds.
|
||||
|
||||
CalendarView.RegisterPropertyChangedCallback(CalendarView.DisplayModeProperty, (s, e) => UpdateVisibleDateRangeBackgrounds());
|
||||
}
|
||||
|
||||
private void InternalCalendarViewSelectionChanged(CalendarView sender, CalendarViewSelectedDatesChangedEventArgs args)
|
||||
{
|
||||
if (args.AddedDates?.Count > 0)
|
||||
if (calendarDayItem.Date.Date == DateTime.Today.Date)
|
||||
{
|
||||
var clickedDate = args.AddedDates[0].Date;
|
||||
SetInnerDisplayDate(clickedDate);
|
||||
|
||||
var clickArgs = new CalendarViewDayClickedEventArgs(clickedDate);
|
||||
DateClickedCommand?.Execute(clickArgs);
|
||||
border.Background = new SolidColorBrush(TodayBackgroundColor);
|
||||
}
|
||||
|
||||
// Reset selection, we don't show selected dates but react to them.
|
||||
CalendarView.SelectedDates.Clear();
|
||||
}
|
||||
|
||||
private static void OnPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is WinoCalendarView control)
|
||||
else if (calendarDayItem.Date.Date >= HighlightedDateRange.StartDate.Date && calendarDayItem.Date.Date < HighlightedDateRange.EndDate.Date)
|
||||
{
|
||||
control.UpdateVisibleDateRangeBackgrounds();
|
||||
border.Background = VisibleDateBackground;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetInnerDisplayDate(DateTime dateTime) => CalendarView?.SetDisplayDate(dateTime);
|
||||
|
||||
// Changing selected dates will trigger the selection changed event.
|
||||
// It will behave like user clicked the date.
|
||||
public void GoToDay(DateTime dateTime) => CalendarView.SelectedDates.Add(dateTime);
|
||||
|
||||
private static void OnHighlightedDateRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is WinoCalendarView control)
|
||||
else
|
||||
{
|
||||
control.SetInnerDisplayDate(control.HighlightedDateRange.StartDate);
|
||||
control.UpdateVisibleDateRangeBackgrounds();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateVisibleDateRangeBackgrounds()
|
||||
{
|
||||
if (HighlightedDateRange == null || VisibleDateBackground == null || TodayBackgroundColor == null || CalendarView == null) return;
|
||||
|
||||
var markDateCalendarDayItems = WinoVisualTreeHelper.FindDescendants<CalendarViewDayItem>(CalendarView);
|
||||
|
||||
foreach (var calendarDayItem in markDateCalendarDayItems)
|
||||
{
|
||||
var border = WinoVisualTreeHelper.GetChildObject<Border>(calendarDayItem, PART_DayViewItemBorder);
|
||||
|
||||
if (border == null) return;
|
||||
|
||||
if (calendarDayItem.Date.Date == DateTime.Today.Date)
|
||||
{
|
||||
border.Background = new SolidColorBrush(TodayBackgroundColor);
|
||||
}
|
||||
else if (calendarDayItem.Date.Date >= HighlightedDateRange.StartDate.Date && calendarDayItem.Date.Date < HighlightedDateRange.EndDate.Date)
|
||||
{
|
||||
border.Background = VisibleDateBackground;
|
||||
}
|
||||
else
|
||||
{
|
||||
border.Background = null;
|
||||
}
|
||||
border.Background = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,269 +10,268 @@ using Windows.UI.Xaml.Media;
|
||||
using Wino.Calendar.Args;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
namespace Wino.Calendar.Controls;
|
||||
|
||||
public partial class WinoDayTimelineCanvas : Control, IDisposable
|
||||
{
|
||||
public class WinoDayTimelineCanvas : Control, IDisposable
|
||||
public event EventHandler<TimelineCellSelectedArgs> TimelineCellSelected;
|
||||
public event EventHandler<TimelineCellUnselectedArgs> TimelineCellUnselected;
|
||||
|
||||
private const string PART_InternalCanvas = nameof(PART_InternalCanvas);
|
||||
private CanvasControl Canvas;
|
||||
|
||||
public static readonly DependencyProperty RenderOptionsProperty = DependencyProperty.Register(nameof(RenderOptions), typeof(CalendarRenderOptions), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty SeperatorColorProperty = DependencyProperty.Register(nameof(SeperatorColor), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty HalfHourSeperatorColorProperty = DependencyProperty.Register(nameof(HalfHourSeperatorColor), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty SelectedCellBackgroundBrushProperty = DependencyProperty.Register(nameof(SelectedCellBackgroundBrush), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty WorkingHourCellBackgroundColorProperty = DependencyProperty.Register(nameof(WorkingHourCellBackgroundColor), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty SelectedDateTimeProperty = DependencyProperty.Register(nameof(SelectedDateTime), typeof(DateTime?), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedDateTimeChanged)));
|
||||
public static readonly DependencyProperty PositionerUIElementProperty = DependencyProperty.Register(nameof(PositionerUIElement), typeof(UIElement), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null));
|
||||
|
||||
public UIElement PositionerUIElement
|
||||
{
|
||||
public event EventHandler<TimelineCellSelectedArgs> TimelineCellSelected;
|
||||
public event EventHandler<TimelineCellUnselectedArgs> TimelineCellUnselected;
|
||||
get { return (UIElement)GetValue(PositionerUIElementProperty); }
|
||||
set { SetValue(PositionerUIElementProperty, value); }
|
||||
}
|
||||
|
||||
private const string PART_InternalCanvas = nameof(PART_InternalCanvas);
|
||||
private CanvasControl Canvas;
|
||||
public CalendarRenderOptions RenderOptions
|
||||
{
|
||||
get { return (CalendarRenderOptions)GetValue(RenderOptionsProperty); }
|
||||
set { SetValue(RenderOptionsProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty RenderOptionsProperty = DependencyProperty.Register(nameof(RenderOptions), typeof(CalendarRenderOptions), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty SeperatorColorProperty = DependencyProperty.Register(nameof(SeperatorColor), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty HalfHourSeperatorColorProperty = DependencyProperty.Register(nameof(HalfHourSeperatorColor), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty SelectedCellBackgroundBrushProperty = DependencyProperty.Register(nameof(SelectedCellBackgroundBrush), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty WorkingHourCellBackgroundColorProperty = DependencyProperty.Register(nameof(WorkingHourCellBackgroundColor), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty SelectedDateTimeProperty = DependencyProperty.Register(nameof(SelectedDateTime), typeof(DateTime?), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedDateTimeChanged)));
|
||||
public static readonly DependencyProperty PositionerUIElementProperty = DependencyProperty.Register(nameof(PositionerUIElement), typeof(UIElement), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null));
|
||||
public SolidColorBrush HalfHourSeperatorColor
|
||||
{
|
||||
get { return (SolidColorBrush)GetValue(HalfHourSeperatorColorProperty); }
|
||||
set { SetValue(HalfHourSeperatorColorProperty, value); }
|
||||
}
|
||||
|
||||
public UIElement PositionerUIElement
|
||||
public SolidColorBrush SeperatorColor
|
||||
{
|
||||
get { return (SolidColorBrush)GetValue(SeperatorColorProperty); }
|
||||
set { SetValue(SeperatorColorProperty, value); }
|
||||
}
|
||||
|
||||
public SolidColorBrush WorkingHourCellBackgroundColor
|
||||
{
|
||||
get { return (SolidColorBrush)GetValue(WorkingHourCellBackgroundColorProperty); }
|
||||
set { SetValue(WorkingHourCellBackgroundColorProperty, value); }
|
||||
}
|
||||
|
||||
public SolidColorBrush SelectedCellBackgroundBrush
|
||||
{
|
||||
get { return (SolidColorBrush)GetValue(SelectedCellBackgroundBrushProperty); }
|
||||
set { SetValue(SelectedCellBackgroundBrushProperty, value); }
|
||||
}
|
||||
|
||||
public DateTime? SelectedDateTime
|
||||
{
|
||||
get { return (DateTime?)GetValue(SelectedDateTimeProperty); }
|
||||
set { SetValue(SelectedDateTimeProperty, value); }
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
Canvas = GetTemplateChild(PART_InternalCanvas) as CanvasControl;
|
||||
|
||||
// TODO: These will leak. Dispose them properly when needed.
|
||||
Canvas.Draw += OnCanvasDraw;
|
||||
Canvas.PointerPressed += OnCanvasPointerPressed;
|
||||
|
||||
ForceDraw();
|
||||
}
|
||||
|
||||
private static void OnSelectedDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is WinoDayTimelineCanvas control)
|
||||
{
|
||||
get { return (UIElement)GetValue(PositionerUIElementProperty); }
|
||||
set { SetValue(PositionerUIElementProperty, value); }
|
||||
}
|
||||
|
||||
public CalendarRenderOptions RenderOptions
|
||||
{
|
||||
get { return (CalendarRenderOptions)GetValue(RenderOptionsProperty); }
|
||||
set { SetValue(RenderOptionsProperty, value); }
|
||||
}
|
||||
|
||||
public SolidColorBrush HalfHourSeperatorColor
|
||||
{
|
||||
get { return (SolidColorBrush)GetValue(HalfHourSeperatorColorProperty); }
|
||||
set { SetValue(HalfHourSeperatorColorProperty, value); }
|
||||
}
|
||||
|
||||
public SolidColorBrush SeperatorColor
|
||||
{
|
||||
get { return (SolidColorBrush)GetValue(SeperatorColorProperty); }
|
||||
set { SetValue(SeperatorColorProperty, value); }
|
||||
}
|
||||
|
||||
public SolidColorBrush WorkingHourCellBackgroundColor
|
||||
{
|
||||
get { return (SolidColorBrush)GetValue(WorkingHourCellBackgroundColorProperty); }
|
||||
set { SetValue(WorkingHourCellBackgroundColorProperty, value); }
|
||||
}
|
||||
|
||||
public SolidColorBrush SelectedCellBackgroundBrush
|
||||
{
|
||||
get { return (SolidColorBrush)GetValue(SelectedCellBackgroundBrushProperty); }
|
||||
set { SetValue(SelectedCellBackgroundBrushProperty, value); }
|
||||
}
|
||||
|
||||
public DateTime? SelectedDateTime
|
||||
{
|
||||
get { return (DateTime?)GetValue(SelectedDateTimeProperty); }
|
||||
set { SetValue(SelectedDateTimeProperty, value); }
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
Canvas = GetTemplateChild(PART_InternalCanvas) as CanvasControl;
|
||||
|
||||
// TODO: These will leak. Dispose them properly when needed.
|
||||
Canvas.Draw += OnCanvasDraw;
|
||||
Canvas.PointerPressed += OnCanvasPointerPressed;
|
||||
|
||||
ForceDraw();
|
||||
}
|
||||
|
||||
private static void OnSelectedDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is WinoDayTimelineCanvas control)
|
||||
if (e.OldValue != null && e.NewValue == null)
|
||||
{
|
||||
if (e.OldValue != null && e.NewValue == null)
|
||||
{
|
||||
control.RaiseCellUnselected();
|
||||
}
|
||||
|
||||
control.ForceDraw();
|
||||
}
|
||||
}
|
||||
|
||||
private void RaiseCellUnselected()
|
||||
{
|
||||
TimelineCellUnselected?.Invoke(this, new TimelineCellUnselectedArgs());
|
||||
}
|
||||
|
||||
private void OnCanvasPointerPressed(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
if (RenderOptions == null) return;
|
||||
|
||||
var hourHeight = RenderOptions.CalendarSettings.HourHeight;
|
||||
|
||||
// When users click to cell we need to find the day, hour and minutes (first 30 minutes or second 30 minutes) that it represents on the timeline.
|
||||
|
||||
PointerPoint positionerRootPoint = e.GetCurrentPoint(PositionerUIElement);
|
||||
PointerPoint canvasPointerPoint = e.GetCurrentPoint(Canvas);
|
||||
|
||||
Point touchPoint = canvasPointerPoint.Position;
|
||||
|
||||
var singleDayWidth = (Canvas.ActualWidth / RenderOptions.TotalDayCount);
|
||||
|
||||
int day = (int)(touchPoint.X / singleDayWidth);
|
||||
int hour = (int)(touchPoint.Y / hourHeight);
|
||||
|
||||
bool isSecondHalf = touchPoint.Y % hourHeight > (hourHeight / 2);
|
||||
|
||||
var diffX = positionerRootPoint.Position.X - touchPoint.X;
|
||||
var diffY = positionerRootPoint.Position.Y - touchPoint.Y;
|
||||
|
||||
var cellStartRelativePositionX = diffX + (day * singleDayWidth);
|
||||
var cellEndRelativePositionX = cellStartRelativePositionX + singleDayWidth;
|
||||
|
||||
var cellStartRelativePositionY = diffY + (hour * hourHeight) + (isSecondHalf ? hourHeight / 2 : 0);
|
||||
var cellEndRelativePositionY = cellStartRelativePositionY + (isSecondHalf ? (hourHeight / 2) : hourHeight);
|
||||
|
||||
var cellSize = new Size(cellEndRelativePositionX - cellStartRelativePositionX, hourHeight / 2);
|
||||
var positionerPoint = new Point(cellStartRelativePositionX, cellStartRelativePositionY);
|
||||
|
||||
var clickedDateTime = RenderOptions.DateRange.StartDate.AddDays(day).AddHours(hour).AddMinutes(isSecondHalf ? 30 : 0);
|
||||
|
||||
// If there is already a selected date, in order to mimic the popup behavior, we need to dismiss the previous selection first.
|
||||
// Next click will be a new selection.
|
||||
|
||||
// Raise the events directly here instead of DP to not lose pointer position.
|
||||
if (clickedDateTime == SelectedDateTime || SelectedDateTime != null)
|
||||
{
|
||||
SelectedDateTime = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedDateTime = clickedDateTime;
|
||||
TimelineCellSelected?.Invoke(this, new TimelineCellSelectedArgs(clickedDateTime, touchPoint, positionerPoint, cellSize));
|
||||
control.RaiseCellUnselected();
|
||||
}
|
||||
|
||||
Debug.WriteLine($"Clicked: {clickedDateTime}");
|
||||
}
|
||||
|
||||
public WinoDayTimelineCanvas()
|
||||
{
|
||||
DefaultStyleKey = typeof(WinoDayTimelineCanvas);
|
||||
}
|
||||
|
||||
private static void OnRenderingPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is WinoDayTimelineCanvas control)
|
||||
{
|
||||
control.ForceDraw();
|
||||
}
|
||||
}
|
||||
|
||||
private void ForceDraw() => Canvas?.Invalidate();
|
||||
|
||||
private bool CanDrawTimeline()
|
||||
{
|
||||
return RenderOptions != null
|
||||
&& Canvas != null
|
||||
&& Canvas.ReadyToDraw
|
||||
&& WorkingHourCellBackgroundColor != null
|
||||
&& SeperatorColor != null
|
||||
&& HalfHourSeperatorColor != null
|
||||
&& SelectedCellBackgroundBrush != null;
|
||||
}
|
||||
|
||||
private void OnCanvasDraw(CanvasControl sender, CanvasDrawEventArgs args)
|
||||
{
|
||||
if (!CanDrawTimeline()) return;
|
||||
|
||||
int hours = 24;
|
||||
|
||||
double canvasWidth = Canvas.ActualWidth;
|
||||
double canvasHeight = Canvas.ActualHeight;
|
||||
|
||||
if (canvasWidth == 0 || canvasHeight == 0) return;
|
||||
|
||||
// Calculate the width of each rectangle (1 day column)
|
||||
// Equal distribution of the whole width.
|
||||
double rectWidth = canvasWidth / RenderOptions.TotalDayCount;
|
||||
|
||||
// Calculate the height of each rectangle (1 hour row)
|
||||
double rectHeight = RenderOptions.CalendarSettings.HourHeight;
|
||||
|
||||
// Define stroke and fill colors
|
||||
var strokeColor = SeperatorColor.Color;
|
||||
float strokeThickness = 0.5f;
|
||||
|
||||
for (int day = 0; day < RenderOptions.TotalDayCount; day++)
|
||||
{
|
||||
var currentDay = RenderOptions.DateRange.StartDate.AddDays(day);
|
||||
|
||||
bool isWorkingDay = RenderOptions.CalendarSettings.WorkingDays.Contains(currentDay.DayOfWeek);
|
||||
|
||||
// Loop through each hour (rows)
|
||||
for (int hour = 0; hour < hours; hour++)
|
||||
{
|
||||
var renderTime = TimeSpan.FromHours(hour);
|
||||
|
||||
var representingDateTime = currentDay.AddHours(hour);
|
||||
|
||||
// Calculate the position and size of the rectangle
|
||||
double x = day * rectWidth;
|
||||
double y = hour * rectHeight;
|
||||
|
||||
var rectangle = new Rect(x, y, rectWidth, rectHeight);
|
||||
|
||||
// Draw the rectangle border.
|
||||
// This is the main rectangle.
|
||||
args.DrawingSession.DrawRectangle(rectangle, strokeColor, strokeThickness);
|
||||
|
||||
// Fill another rectangle with the working hour background color
|
||||
// This rectangle must be placed with -1 margin to prevent invisible borders of the main rectangle.
|
||||
if (isWorkingDay && renderTime >= RenderOptions.CalendarSettings.WorkingHourStart && renderTime <= RenderOptions.CalendarSettings.WorkingHourEnd)
|
||||
{
|
||||
var backgroundRectangle = new Rect(x + 1, y + 1, rectWidth - 1, rectHeight - 1);
|
||||
|
||||
args.DrawingSession.DrawRectangle(backgroundRectangle, strokeColor, strokeThickness);
|
||||
args.DrawingSession.FillRectangle(backgroundRectangle, WorkingHourCellBackgroundColor.Color);
|
||||
}
|
||||
|
||||
// Draw a line in the center of the rectangle for representing half hours.
|
||||
double lineY = y + rectHeight / 2;
|
||||
|
||||
args.DrawingSession.DrawLine((float)x, (float)lineY, (float)(x + rectWidth), (float)lineY, HalfHourSeperatorColor.Color, strokeThickness, new CanvasStrokeStyle()
|
||||
{
|
||||
DashStyle = CanvasDashStyle.Dot
|
||||
});
|
||||
}
|
||||
|
||||
// Draw selected item background color for the date if possible.
|
||||
if (SelectedDateTime != null)
|
||||
{
|
||||
var selectedDateTime = SelectedDateTime.Value;
|
||||
if (selectedDateTime.Date == currentDay.Date)
|
||||
{
|
||||
var selectionRectHeight = rectHeight / 2;
|
||||
var selectedY = selectedDateTime.Hour * rectHeight + (selectedDateTime.Minute / 60) * rectHeight;
|
||||
|
||||
// Second half of the hour is selected.
|
||||
if (selectedDateTime.TimeOfDay.Minutes == 30)
|
||||
{
|
||||
selectedY += rectHeight / 2;
|
||||
}
|
||||
|
||||
var selectedRectangle = new Rect(day * rectWidth, selectedY, rectWidth, selectionRectHeight);
|
||||
args.DrawingSession.FillRectangle(selectedRectangle, SelectedCellBackgroundBrush.Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Canvas == null) return;
|
||||
|
||||
Canvas.Draw -= OnCanvasDraw;
|
||||
Canvas.PointerPressed -= OnCanvasPointerPressed;
|
||||
Canvas.RemoveFromVisualTree();
|
||||
|
||||
Canvas = null;
|
||||
control.ForceDraw();
|
||||
}
|
||||
}
|
||||
|
||||
private void RaiseCellUnselected()
|
||||
{
|
||||
TimelineCellUnselected?.Invoke(this, new TimelineCellUnselectedArgs());
|
||||
}
|
||||
|
||||
private void OnCanvasPointerPressed(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
if (RenderOptions == null) return;
|
||||
|
||||
var hourHeight = RenderOptions.CalendarSettings.HourHeight;
|
||||
|
||||
// When users click to cell we need to find the day, hour and minutes (first 30 minutes or second 30 minutes) that it represents on the timeline.
|
||||
|
||||
PointerPoint positionerRootPoint = e.GetCurrentPoint(PositionerUIElement);
|
||||
PointerPoint canvasPointerPoint = e.GetCurrentPoint(Canvas);
|
||||
|
||||
Point touchPoint = canvasPointerPoint.Position;
|
||||
|
||||
var singleDayWidth = (Canvas.ActualWidth / RenderOptions.TotalDayCount);
|
||||
|
||||
int day = (int)(touchPoint.X / singleDayWidth);
|
||||
int hour = (int)(touchPoint.Y / hourHeight);
|
||||
|
||||
bool isSecondHalf = touchPoint.Y % hourHeight > (hourHeight / 2);
|
||||
|
||||
var diffX = positionerRootPoint.Position.X - touchPoint.X;
|
||||
var diffY = positionerRootPoint.Position.Y - touchPoint.Y;
|
||||
|
||||
var cellStartRelativePositionX = diffX + (day * singleDayWidth);
|
||||
var cellEndRelativePositionX = cellStartRelativePositionX + singleDayWidth;
|
||||
|
||||
var cellStartRelativePositionY = diffY + (hour * hourHeight) + (isSecondHalf ? hourHeight / 2 : 0);
|
||||
var cellEndRelativePositionY = cellStartRelativePositionY + (isSecondHalf ? (hourHeight / 2) : hourHeight);
|
||||
|
||||
var cellSize = new Size(cellEndRelativePositionX - cellStartRelativePositionX, hourHeight / 2);
|
||||
var positionerPoint = new Point(cellStartRelativePositionX, cellStartRelativePositionY);
|
||||
|
||||
var clickedDateTime = RenderOptions.DateRange.StartDate.AddDays(day).AddHours(hour).AddMinutes(isSecondHalf ? 30 : 0);
|
||||
|
||||
// If there is already a selected date, in order to mimic the popup behavior, we need to dismiss the previous selection first.
|
||||
// Next click will be a new selection.
|
||||
|
||||
// Raise the events directly here instead of DP to not lose pointer position.
|
||||
if (clickedDateTime == SelectedDateTime || SelectedDateTime != null)
|
||||
{
|
||||
SelectedDateTime = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedDateTime = clickedDateTime;
|
||||
TimelineCellSelected?.Invoke(this, new TimelineCellSelectedArgs(clickedDateTime, touchPoint, positionerPoint, cellSize));
|
||||
}
|
||||
|
||||
Debug.WriteLine($"Clicked: {clickedDateTime}");
|
||||
}
|
||||
|
||||
public WinoDayTimelineCanvas()
|
||||
{
|
||||
DefaultStyleKey = typeof(WinoDayTimelineCanvas);
|
||||
}
|
||||
|
||||
private static void OnRenderingPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is WinoDayTimelineCanvas control)
|
||||
{
|
||||
control.ForceDraw();
|
||||
}
|
||||
}
|
||||
|
||||
private void ForceDraw() => Canvas?.Invalidate();
|
||||
|
||||
private bool CanDrawTimeline()
|
||||
{
|
||||
return RenderOptions != null
|
||||
&& Canvas != null
|
||||
&& Canvas.ReadyToDraw
|
||||
&& WorkingHourCellBackgroundColor != null
|
||||
&& SeperatorColor != null
|
||||
&& HalfHourSeperatorColor != null
|
||||
&& SelectedCellBackgroundBrush != null;
|
||||
}
|
||||
|
||||
private void OnCanvasDraw(CanvasControl sender, CanvasDrawEventArgs args)
|
||||
{
|
||||
if (!CanDrawTimeline()) return;
|
||||
|
||||
int hours = 24;
|
||||
|
||||
double canvasWidth = Canvas.ActualWidth;
|
||||
double canvasHeight = Canvas.ActualHeight;
|
||||
|
||||
if (canvasWidth == 0 || canvasHeight == 0) return;
|
||||
|
||||
// Calculate the width of each rectangle (1 day column)
|
||||
// Equal distribution of the whole width.
|
||||
double rectWidth = canvasWidth / RenderOptions.TotalDayCount;
|
||||
|
||||
// Calculate the height of each rectangle (1 hour row)
|
||||
double rectHeight = RenderOptions.CalendarSettings.HourHeight;
|
||||
|
||||
// Define stroke and fill colors
|
||||
var strokeColor = SeperatorColor.Color;
|
||||
float strokeThickness = 0.5f;
|
||||
|
||||
for (int day = 0; day < RenderOptions.TotalDayCount; day++)
|
||||
{
|
||||
var currentDay = RenderOptions.DateRange.StartDate.AddDays(day);
|
||||
|
||||
bool isWorkingDay = RenderOptions.CalendarSettings.WorkingDays.Contains(currentDay.DayOfWeek);
|
||||
|
||||
// Loop through each hour (rows)
|
||||
for (int hour = 0; hour < hours; hour++)
|
||||
{
|
||||
var renderTime = TimeSpan.FromHours(hour);
|
||||
|
||||
var representingDateTime = currentDay.AddHours(hour);
|
||||
|
||||
// Calculate the position and size of the rectangle
|
||||
double x = day * rectWidth;
|
||||
double y = hour * rectHeight;
|
||||
|
||||
var rectangle = new Rect(x, y, rectWidth, rectHeight);
|
||||
|
||||
// Draw the rectangle border.
|
||||
// This is the main rectangle.
|
||||
args.DrawingSession.DrawRectangle(rectangle, strokeColor, strokeThickness);
|
||||
|
||||
// Fill another rectangle with the working hour background color
|
||||
// This rectangle must be placed with -1 margin to prevent invisible borders of the main rectangle.
|
||||
if (isWorkingDay && renderTime >= RenderOptions.CalendarSettings.WorkingHourStart && renderTime <= RenderOptions.CalendarSettings.WorkingHourEnd)
|
||||
{
|
||||
var backgroundRectangle = new Rect(x + 1, y + 1, rectWidth - 1, rectHeight - 1);
|
||||
|
||||
args.DrawingSession.DrawRectangle(backgroundRectangle, strokeColor, strokeThickness);
|
||||
args.DrawingSession.FillRectangle(backgroundRectangle, WorkingHourCellBackgroundColor.Color);
|
||||
}
|
||||
|
||||
// Draw a line in the center of the rectangle for representing half hours.
|
||||
double lineY = y + rectHeight / 2;
|
||||
|
||||
args.DrawingSession.DrawLine((float)x, (float)lineY, (float)(x + rectWidth), (float)lineY, HalfHourSeperatorColor.Color, strokeThickness, new CanvasStrokeStyle()
|
||||
{
|
||||
DashStyle = CanvasDashStyle.Dot
|
||||
});
|
||||
}
|
||||
|
||||
// Draw selected item background color for the date if possible.
|
||||
if (SelectedDateTime != null)
|
||||
{
|
||||
var selectedDateTime = SelectedDateTime.Value;
|
||||
if (selectedDateTime.Date == currentDay.Date)
|
||||
{
|
||||
var selectionRectHeight = rectHeight / 2;
|
||||
var selectedY = selectedDateTime.Hour * rectHeight + (selectedDateTime.Minute / 60) * rectHeight;
|
||||
|
||||
// Second half of the hour is selected.
|
||||
if (selectedDateTime.TimeOfDay.Minutes == 30)
|
||||
{
|
||||
selectedY += rectHeight / 2;
|
||||
}
|
||||
|
||||
var selectedRectangle = new Rect(day * rectWidth, selectedY, rectWidth, selectionRectHeight);
|
||||
args.DrawingSession.FillRectangle(selectedRectangle, SelectedCellBackgroundBrush.Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Canvas == null) return;
|
||||
|
||||
Canvas.Draw -= OnCanvasDraw;
|
||||
Canvas.PointerPressed -= OnCanvasPointerPressed;
|
||||
Canvas.RemoveFromVisualTree();
|
||||
|
||||
Canvas = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,98 +10,62 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
using Wino.Helpers;
|
||||
|
||||
namespace Wino.Calendar.Helpers
|
||||
namespace Wino.Calendar.Helpers;
|
||||
|
||||
public static class CalendarXamlHelpers
|
||||
{
|
||||
public static class CalendarXamlHelpers
|
||||
public static CalendarItemViewModel GetFirstAllDayEvent(CalendarEventCollection collection)
|
||||
=> (CalendarItemViewModel)collection.AllDayEvents.FirstOrDefault();
|
||||
|
||||
/// <summary>
|
||||
/// Returns full date + duration info in Event Details page details title.
|
||||
/// </summary>
|
||||
public static string GetEventDetailsDateString(CalendarItemViewModel calendarItemViewModel, CalendarSettings settings)
|
||||
{
|
||||
public static CalendarItemViewModel GetFirstAllDayEvent(CalendarEventCollection collection)
|
||||
=> (CalendarItemViewModel)collection.AllDayEvents.FirstOrDefault();
|
||||
// TODO: This is not correct.
|
||||
if (calendarItemViewModel == null || settings == null) return string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Returns full date + duration info in Event Details page details title.
|
||||
/// </summary>
|
||||
public static string GetEventDetailsDateString(CalendarItemViewModel calendarItemViewModel, CalendarSettings settings)
|
||||
var start = calendarItemViewModel.Period.Start;
|
||||
var end = calendarItemViewModel.Period.End;
|
||||
|
||||
string timeFormat = settings.DayHeaderDisplayType == DayHeaderDisplayType.TwelveHour ? "h:mm tt" : "HH:mm";
|
||||
string dateFormat = settings.DayHeaderDisplayType == DayHeaderDisplayType.TwelveHour ? "dddd, dd MMMM h:mm tt" : "dddd, dd MMMM HH:mm";
|
||||
|
||||
if (calendarItemViewModel.CalendarItem.ItemType == CalendarItemType.MultiDay || calendarItemViewModel.CalendarItem.ItemType == CalendarItemType.MultiDayAllDay)
|
||||
{
|
||||
if (calendarItemViewModel == null || settings == null) return string.Empty;
|
||||
|
||||
var start = calendarItemViewModel.Period.Start;
|
||||
var end = calendarItemViewModel.Period.End;
|
||||
|
||||
string timeFormat = settings.DayHeaderDisplayType == DayHeaderDisplayType.TwelveHour ? "h:mm tt" : "HH:mm";
|
||||
string dateFormat = settings.DayHeaderDisplayType == DayHeaderDisplayType.TwelveHour ? "dddd, dd MMMM h:mm tt" : "dddd, dd MMMM HH:mm";
|
||||
|
||||
if (calendarItemViewModel.IsMultiDayEvent)
|
||||
{
|
||||
return $"{start.ToString($"dd MMMM ddd {timeFormat}", settings.CultureInfo)} - {end.ToString($"dd MMMM ddd {timeFormat}", settings.CultureInfo)}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"{start.ToString(dateFormat, settings.CultureInfo)} - {end.ToString(timeFormat, settings.CultureInfo)}";
|
||||
}
|
||||
return $"{start.ToString($"dd MMMM ddd {timeFormat}", settings.CultureInfo)} - {end.ToString($"dd MMMM ddd {timeFormat}", settings.CultureInfo)}";
|
||||
}
|
||||
|
||||
public static string GetRecurrenceString(CalendarItemViewModel calendarItemViewModel)
|
||||
else
|
||||
{
|
||||
if (calendarItemViewModel == null || !calendarItemViewModel.IsRecurringChild) return string.Empty;
|
||||
|
||||
// Parse recurrence rules
|
||||
var calendarEvent = new CalendarEvent
|
||||
{
|
||||
Start = new CalDateTime(calendarItemViewModel.StartDate),
|
||||
End = new CalDateTime(calendarItemViewModel.EndDate),
|
||||
};
|
||||
|
||||
var recurrenceLines = Regex.Split(calendarItemViewModel.CalendarItem.Recurrence, Constants.CalendarEventRecurrenceRuleSeperator);
|
||||
|
||||
foreach (var line in recurrenceLines)
|
||||
{
|
||||
calendarEvent.RecurrenceRules.Add(new RecurrencePattern(line));
|
||||
}
|
||||
|
||||
if (calendarEvent.RecurrenceRules == null || !calendarEvent.RecurrenceRules.Any())
|
||||
{
|
||||
return "No recurrence pattern.";
|
||||
}
|
||||
|
||||
var recurrenceRule = calendarEvent.RecurrenceRules.First();
|
||||
var daysOfWeek = string.Join(", ", recurrenceRule.ByDay.Select(day => day.DayOfWeek.ToString()));
|
||||
string timeZone = calendarEvent.DtStart.TzId ?? "UTC";
|
||||
|
||||
return $"Every {daysOfWeek}, effective {calendarEvent.DtStart.Value.ToShortDateString()} " +
|
||||
$"from {calendarEvent.DtStart.Value.ToShortTimeString()} to {calendarEvent.DtEnd.Value.ToShortTimeString()} " +
|
||||
$"{timeZone}.";
|
||||
}
|
||||
|
||||
public static string GetDetailsPopupDurationString(CalendarItemViewModel calendarItemViewModel, CalendarSettings settings)
|
||||
{
|
||||
if (calendarItemViewModel == null || settings == null) return string.Empty;
|
||||
|
||||
// Single event in a day.
|
||||
if (!calendarItemViewModel.IsAllDayEvent && !calendarItemViewModel.IsMultiDayEvent)
|
||||
{
|
||||
return $"{calendarItemViewModel.Period.Start.ToString("d", settings.CultureInfo)} {settings.GetTimeString(calendarItemViewModel.Period.Duration)}";
|
||||
}
|
||||
else if (calendarItemViewModel.IsMultiDayEvent)
|
||||
{
|
||||
return $"{calendarItemViewModel.Period.Start.ToString("d", settings.CultureInfo)} - {calendarItemViewModel.Period.End.ToString("d", settings.CultureInfo)}";
|
||||
}
|
||||
else
|
||||
{
|
||||
// All day event.
|
||||
return $"{calendarItemViewModel.Period.Start.ToString("d", settings.CultureInfo)} ({Translator.CalendarItemAllDay})";
|
||||
}
|
||||
}
|
||||
|
||||
public static PopupPlacementMode GetDesiredPlacementModeForEventsDetailsPopup(
|
||||
CalendarItemViewModel calendarItemViewModel,
|
||||
CalendarDisplayType calendarDisplayType)
|
||||
{
|
||||
if (calendarItemViewModel == null) return PopupPlacementMode.Auto;
|
||||
|
||||
// All and/or multi day events always go to the top of the screen.
|
||||
if (calendarItemViewModel.IsAllDayEvent || calendarItemViewModel.IsMultiDayEvent) return PopupPlacementMode.Bottom;
|
||||
|
||||
return XamlHelpers.GetPlaccementModeForCalendarType(calendarDisplayType);
|
||||
return $"{start.ToString(dateFormat, settings.CultureInfo)} - {end.ToString(timeFormat, settings.CultureInfo)}";
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetRecurrenceString(CalendarItemViewModel calendarItemViewModel)
|
||||
{
|
||||
// TODO
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static string GetDetailsPopupDurationString(CalendarItemViewModel calendarItemViewModel, CalendarSettings settings)
|
||||
{
|
||||
// TODO
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static PopupPlacementMode GetDesiredPlacementModeForEventsDetailsPopup(
|
||||
CalendarItemViewModel calendarItemViewModel,
|
||||
CalendarDisplayType calendarDisplayType)
|
||||
{
|
||||
if (calendarItemViewModel == null) return PopupPlacementMode.Auto;
|
||||
|
||||
bool isAllDayOrMultiDay = calendarItemViewModel.CalendarItem.ItemType == CalendarItemType.MultiDay ||
|
||||
calendarItemViewModel.CalendarItem.ItemType == CalendarItemType.AllDay ||
|
||||
calendarItemViewModel.CalendarItem.ItemType == CalendarItemType.MultiDayAllDay;
|
||||
|
||||
// All and/or multi day events always go to the top of the screen.
|
||||
if (isAllDayOrMultiDay) return PopupPlacementMode.Bottom;
|
||||
|
||||
return XamlHelpers.GetPlaccementModeForCalendarType(calendarDisplayType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,15 @@ using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
|
||||
|
||||
namespace Wino.Calendar
|
||||
namespace Wino.Calendar;
|
||||
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MainPage : Page
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MainPage : Page
|
||||
public MainPage()
|
||||
{
|
||||
public MainPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
namespace Wino.Calendar.Models
|
||||
namespace Wino.Calendar.Models;
|
||||
|
||||
public struct CalendarItemMeasurement
|
||||
{
|
||||
public struct CalendarItemMeasurement
|
||||
// Where to start?
|
||||
public double Left { get; set; }
|
||||
|
||||
// Extend until where?
|
||||
public double Right { get; set; }
|
||||
|
||||
public CalendarItemMeasurement(double left, double right)
|
||||
{
|
||||
// Where to start?
|
||||
public double Left { get; set; }
|
||||
|
||||
// Extend until where?
|
||||
public double Right { get; set; }
|
||||
|
||||
public CalendarItemMeasurement(double left, double right)
|
||||
{
|
||||
Left = left;
|
||||
Right = right;
|
||||
}
|
||||
Left = left;
|
||||
Right = right;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
IgnorableNamespaces="uap mp">
|
||||
|
||||
<Identity
|
||||
Name="58272BurakKSE.WinoCalendar"
|
||||
Publisher="CN=51FBDAF3-E212-4149-89A2-A2636B3BC911"
|
||||
Version="1.0.15.0" />
|
||||
|
||||
<!-- Publisher Cache Folders -->
|
||||
<Extensions>
|
||||
<Extension Category="windows.publisherCacheFolders">
|
||||
@@ -16,13 +21,8 @@
|
||||
</PublisherCacheFolders>
|
||||
</Extension>
|
||||
</Extensions>
|
||||
|
||||
<Identity
|
||||
Name="58272BurakKSE.WinoCalendar"
|
||||
Publisher="CN=51FBDAF3-E212-4149-89A2-A2636B3BC911"
|
||||
Version="1.0.15.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="f047b7dd-96ec-4d54-a862-9321e271e449" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
<mp:PhoneIdentity PhoneProductId="f047b7dd-96ec-4d54-a862-9321e271e449" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Wino Calendar</DisplayName>
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Wino.Calendar")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Wino.Calendar")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2023")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: ComVisible(false)]
|
||||
@@ -1,44 +0,0 @@
|
||||
<!--
|
||||
This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
|
||||
developers. However, you can modify these parameters to modify the behavior of the .NET Native
|
||||
optimizer.
|
||||
|
||||
Runtime Directives are documented at https://go.microsoft.com/fwlink/?LinkID=391919
|
||||
|
||||
To fully enable reflection for App1.MyClass and all of its public/private members
|
||||
<Type Name="App1.MyClass" Dynamic="Required All"/>
|
||||
|
||||
To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
|
||||
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
|
||||
|
||||
Using the Namespace directive to apply reflection policy to all the types in a particular namespace
|
||||
<Namespace Name="DataClasses.ViewModels" Serialize="All" />
|
||||
-->
|
||||
|
||||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||
<Application>
|
||||
<!--
|
||||
An Assembly element with Name="*Application*" applies to all assemblies in
|
||||
the application package. The asterisks are not wildcards.
|
||||
-->
|
||||
<Assembly Name="*Application*" Dynamic="Required All" />
|
||||
|
||||
<!-- Reduce memory footprint when building with Microsoft.Graph -->
|
||||
|
||||
<Assembly Name="Microsoft.Graph" Serialize="Excluded" />
|
||||
|
||||
<Assembly Name="Microsoft.Kiota.Abstractions" Dynamic="Public" />
|
||||
<Assembly Name="Microsoft.Kiota.Authentication.Azure" Dynamic="Public" />
|
||||
<Assembly Name="Microsoft.Kiota.Http.HttpClientLibrary" Dynamic="Public" />
|
||||
<Assembly Name="Microsoft.Kiota.Serialization.Form" Dynamic="Public" />
|
||||
<Assembly Name="Microsoft.Kiota.Serialization.Json" Dynamic="Public" />
|
||||
<Assembly Name="Microsoft.Kiota.Serialization.Multipart" Dynamic="Public" />
|
||||
|
||||
<!-- Add your application specific runtime directives here. -->
|
||||
<Type Name="Windows.Foundation.TypedEventHandler{Microsoft.UI.Xaml.Controls.NavigationView,Microsoft.UI.Xaml.Controls.NavigationViewItemInvokedEventArgs}" MarshalObject="Public" />
|
||||
|
||||
<Type Name="Microsoft.UI.Xaml.Controls.NavigationView">
|
||||
<Event Name="ItemInvoked" Dynamic="Required"/>
|
||||
</Type>
|
||||
</Application>
|
||||
</Directives>
|
||||
7
Wino.Calendar/Properties/launchSettings.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Wino.Calendar": {
|
||||
"commandName": "MsixPackage"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,21 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
|
||||
namespace Wino.Calendar.Selectors
|
||||
namespace Wino.Calendar.Selectors;
|
||||
|
||||
public partial class CustomAreaCalendarItemSelector : DataTemplateSelector
|
||||
{
|
||||
public class CustomAreaCalendarItemSelector : DataTemplateSelector
|
||||
public DataTemplate AllDayTemplate { get; set; }
|
||||
public DataTemplate MultiDayTemplate { get; set; }
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
{
|
||||
public DataTemplate AllDayTemplate { get; set; }
|
||||
public DataTemplate MultiDayTemplate { get; set; }
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
if (item is CalendarItemViewModel calendarItemViewModel)
|
||||
{
|
||||
if (item is CalendarItemViewModel calendarItemViewModel)
|
||||
{
|
||||
return calendarItemViewModel.IsMultiDayEvent ? MultiDayTemplate : AllDayTemplate;
|
||||
}
|
||||
|
||||
return base.SelectTemplateCore(item, container);
|
||||
return calendarItemViewModel.CalendarItem.ItemType == Core.Domain.Enums.CalendarItemType.MultiDay ||
|
||||
calendarItemViewModel.CalendarItem.ItemType == Core.Domain.Enums.CalendarItemType.MultiDayAllDay ? MultiDayTemplate : AllDayTemplate;
|
||||
}
|
||||
|
||||
return base.SelectTemplateCore(item, container);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,33 +2,32 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Calendar.Selectors
|
||||
namespace Wino.Calendar.Selectors;
|
||||
|
||||
public partial class WinoCalendarItemTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public class WinoCalendarItemTemplateSelector : DataTemplateSelector
|
||||
public CalendarDisplayType DisplayType { get; set; }
|
||||
|
||||
public DataTemplate DayWeekWorkWeekTemplate { get; set; }
|
||||
public DataTemplate MonthlyTemplate { get; set; }
|
||||
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
{
|
||||
public CalendarDisplayType DisplayType { get; set; }
|
||||
|
||||
public DataTemplate DayWeekWorkWeekTemplate { get; set; }
|
||||
public DataTemplate MonthlyTemplate { get; set; }
|
||||
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
switch (DisplayType)
|
||||
{
|
||||
switch (DisplayType)
|
||||
{
|
||||
case CalendarDisplayType.Day:
|
||||
case CalendarDisplayType.Week:
|
||||
case CalendarDisplayType.WorkWeek:
|
||||
return DayWeekWorkWeekTemplate;
|
||||
case CalendarDisplayType.Month:
|
||||
return MonthlyTemplate;
|
||||
case CalendarDisplayType.Year:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return base.SelectTemplateCore(item, container);
|
||||
case CalendarDisplayType.Day:
|
||||
case CalendarDisplayType.Week:
|
||||
case CalendarDisplayType.WorkWeek:
|
||||
return DayWeekWorkWeekTemplate;
|
||||
case CalendarDisplayType.Month:
|
||||
return MonthlyTemplate;
|
||||
case CalendarDisplayType.Year:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return base.SelectTemplateCore(item, container);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,108 +7,107 @@ using Wino.Calendar.ViewModels.Data;
|
||||
using Wino.Calendar.ViewModels.Interfaces;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
namespace Wino.Calendar.Services
|
||||
namespace Wino.Calendar.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulated state manager for collectively managing the state of account calendars.
|
||||
/// Callers must react to the events to update their state only from this service.
|
||||
/// </summary>
|
||||
public partial class AccountCalendarStateService : ObservableObject, IAccountCalendarStateService
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulated state manager for collectively managing the state of account calendars.
|
||||
/// Callers must react to the events to update their state only from this service.
|
||||
/// </summary>
|
||||
public partial class AccountCalendarStateService : ObservableObject, IAccountCalendarStateService
|
||||
public event EventHandler<GroupedAccountCalendarViewModel> CollectiveAccountGroupSelectionStateChanged;
|
||||
public event EventHandler<AccountCalendarViewModel> AccountCalendarSelectionStateChanged;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ReadOnlyObservableCollection<GroupedAccountCalendarViewModel> GroupedAccountCalendars { get; set; }
|
||||
|
||||
private ObservableCollection<GroupedAccountCalendarViewModel> _internalGroupedAccountCalendars = new ObservableCollection<GroupedAccountCalendarViewModel>();
|
||||
|
||||
public IEnumerable<AccountCalendarViewModel> ActiveCalendars
|
||||
{
|
||||
public event EventHandler<GroupedAccountCalendarViewModel> CollectiveAccountGroupSelectionStateChanged;
|
||||
public event EventHandler<AccountCalendarViewModel> AccountCalendarSelectionStateChanged;
|
||||
|
||||
[ObservableProperty]
|
||||
private ReadOnlyObservableCollection<GroupedAccountCalendarViewModel> groupedAccountCalendars;
|
||||
|
||||
private ObservableCollection<GroupedAccountCalendarViewModel> _internalGroupedAccountCalendars = new ObservableCollection<GroupedAccountCalendarViewModel>();
|
||||
|
||||
public IEnumerable<AccountCalendarViewModel> ActiveCalendars
|
||||
get
|
||||
{
|
||||
get
|
||||
{
|
||||
return GroupedAccountCalendars
|
||||
.SelectMany(a => a.AccountCalendars)
|
||||
.Where(b => b.IsChecked);
|
||||
}
|
||||
return GroupedAccountCalendars
|
||||
.SelectMany(a => a.AccountCalendars)
|
||||
.Where(b => b.IsChecked);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<IGrouping<MailAccount, AccountCalendarViewModel>> GroupedAccountCalendarsEnumerable
|
||||
public IEnumerable<IGrouping<MailAccount, AccountCalendarViewModel>> GroupedAccountCalendarsEnumerable
|
||||
{
|
||||
get
|
||||
{
|
||||
get
|
||||
{
|
||||
return GroupedAccountCalendars
|
||||
.Select(a => a.AccountCalendars)
|
||||
.SelectMany(b => b)
|
||||
.GroupBy(c => c.Account);
|
||||
}
|
||||
return GroupedAccountCalendars
|
||||
.Select(a => a.AccountCalendars)
|
||||
.SelectMany(b => b)
|
||||
.GroupBy(c => c.Account);
|
||||
}
|
||||
}
|
||||
|
||||
public AccountCalendarStateService()
|
||||
public AccountCalendarStateService()
|
||||
{
|
||||
GroupedAccountCalendars = new ReadOnlyObservableCollection<GroupedAccountCalendarViewModel>(_internalGroupedAccountCalendars);
|
||||
}
|
||||
|
||||
private void SingleGroupCalendarCollectiveStateChanged(object sender, EventArgs e)
|
||||
=> CollectiveAccountGroupSelectionStateChanged?.Invoke(this, sender as GroupedAccountCalendarViewModel);
|
||||
|
||||
private void SingleCalendarSelectionStateChanged(object sender, AccountCalendarViewModel e)
|
||||
=> AccountCalendarSelectionStateChanged?.Invoke(this, e);
|
||||
|
||||
public void AddGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar)
|
||||
{
|
||||
groupedAccountCalendar.CalendarSelectionStateChanged += SingleCalendarSelectionStateChanged;
|
||||
groupedAccountCalendar.CollectiveSelectionStateChanged += SingleGroupCalendarCollectiveStateChanged;
|
||||
|
||||
_internalGroupedAccountCalendars.Add(groupedAccountCalendar);
|
||||
}
|
||||
|
||||
public void RemoveGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar)
|
||||
{
|
||||
groupedAccountCalendar.CalendarSelectionStateChanged -= SingleCalendarSelectionStateChanged;
|
||||
groupedAccountCalendar.CollectiveSelectionStateChanged -= SingleGroupCalendarCollectiveStateChanged;
|
||||
|
||||
_internalGroupedAccountCalendars.Remove(groupedAccountCalendar);
|
||||
}
|
||||
|
||||
public void ClearGroupedAccountCalendar()
|
||||
{
|
||||
foreach (var groupedAccountCalendar in _internalGroupedAccountCalendars)
|
||||
{
|
||||
GroupedAccountCalendars = new ReadOnlyObservableCollection<GroupedAccountCalendarViewModel>(_internalGroupedAccountCalendars);
|
||||
RemoveGroupedAccountCalendar(groupedAccountCalendar);
|
||||
}
|
||||
}
|
||||
|
||||
private void SingleGroupCalendarCollectiveStateChanged(object sender, EventArgs e)
|
||||
=> CollectiveAccountGroupSelectionStateChanged?.Invoke(this, sender as GroupedAccountCalendarViewModel);
|
||||
public void AddAccountCalendar(AccountCalendarViewModel accountCalendar)
|
||||
{
|
||||
// Find the group that this calendar belongs to.
|
||||
var group = _internalGroupedAccountCalendars.FirstOrDefault(g => g.Account.Id == accountCalendar.Account.Id);
|
||||
|
||||
private void SingleCalendarSelectionStateChanged(object sender, AccountCalendarViewModel e)
|
||||
=> AccountCalendarSelectionStateChanged?.Invoke(this, e);
|
||||
|
||||
public void AddGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar)
|
||||
if (group == null)
|
||||
{
|
||||
groupedAccountCalendar.CalendarSelectionStateChanged += SingleCalendarSelectionStateChanged;
|
||||
groupedAccountCalendar.CollectiveSelectionStateChanged += SingleGroupCalendarCollectiveStateChanged;
|
||||
|
||||
_internalGroupedAccountCalendars.Add(groupedAccountCalendar);
|
||||
// If the group doesn't exist, create it.
|
||||
group = new GroupedAccountCalendarViewModel(accountCalendar.Account, new[] { accountCalendar });
|
||||
AddGroupedAccountCalendar(group);
|
||||
}
|
||||
|
||||
public void RemoveGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar)
|
||||
else
|
||||
{
|
||||
groupedAccountCalendar.CalendarSelectionStateChanged -= SingleCalendarSelectionStateChanged;
|
||||
groupedAccountCalendar.CollectiveSelectionStateChanged -= SingleGroupCalendarCollectiveStateChanged;
|
||||
|
||||
_internalGroupedAccountCalendars.Remove(groupedAccountCalendar);
|
||||
group.AccountCalendars.Add(accountCalendar);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearGroupedAccountCalendar()
|
||||
public void RemoveAccountCalendar(AccountCalendarViewModel accountCalendar)
|
||||
{
|
||||
var group = _internalGroupedAccountCalendars.FirstOrDefault(g => g.Account.Id == accountCalendar.Account.Id);
|
||||
|
||||
// We don't expect but just in case.
|
||||
if (group == null) return;
|
||||
|
||||
group.AccountCalendars.Remove(accountCalendar);
|
||||
|
||||
if (group.AccountCalendars.Count == 0)
|
||||
{
|
||||
foreach (var groupedAccountCalendar in _internalGroupedAccountCalendars)
|
||||
{
|
||||
RemoveGroupedAccountCalendar(groupedAccountCalendar);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddAccountCalendar(AccountCalendarViewModel accountCalendar)
|
||||
{
|
||||
// Find the group that this calendar belongs to.
|
||||
var group = _internalGroupedAccountCalendars.FirstOrDefault(g => g.Account.Id == accountCalendar.Account.Id);
|
||||
|
||||
if (group == null)
|
||||
{
|
||||
// If the group doesn't exist, create it.
|
||||
group = new GroupedAccountCalendarViewModel(accountCalendar.Account, new[] { accountCalendar });
|
||||
AddGroupedAccountCalendar(group);
|
||||
}
|
||||
else
|
||||
{
|
||||
group.AccountCalendars.Add(accountCalendar);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAccountCalendar(AccountCalendarViewModel accountCalendar)
|
||||
{
|
||||
var group = _internalGroupedAccountCalendars.FirstOrDefault(g => g.Account.Id == accountCalendar.Account.Id);
|
||||
|
||||
// We don't expect but just in case.
|
||||
if (group == null) return;
|
||||
|
||||
group.AccountCalendars.Remove(accountCalendar);
|
||||
|
||||
if (group.AccountCalendars.Count == 0)
|
||||
{
|
||||
RemoveGroupedAccountCalendar(group);
|
||||
}
|
||||
RemoveGroupedAccountCalendar(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.UWP.Services;
|
||||
|
||||
namespace Wino.Calendar.Services
|
||||
namespace Wino.Calendar.Services;
|
||||
|
||||
public class DialogService : DialogServiceBase, ICalendarDialogService
|
||||
{
|
||||
public class DialogService : DialogServiceBase, ICalendarDialogService
|
||||
public DialogService(IThemeService themeService,
|
||||
IConfigurationService configurationService,
|
||||
IApplicationResourceManager<ResourceDictionary> applicationResourceManager) : base(themeService, configurationService, applicationResourceManager)
|
||||
{
|
||||
public DialogService(IThemeService themeService,
|
||||
IConfigurationService configurationService,
|
||||
IApplicationResourceManager<ResourceDictionary> applicationResourceManager) : base(themeService, configurationService, applicationResourceManager)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,54 +10,53 @@ using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.UWP.Services;
|
||||
using Wino.Views;
|
||||
|
||||
namespace Wino.Calendar.Services
|
||||
namespace Wino.Calendar.Services;
|
||||
|
||||
public class NavigationService : NavigationServiceBase, INavigationService
|
||||
{
|
||||
public class NavigationService : NavigationServiceBase, INavigationService
|
||||
public Type GetPageType(WinoPage winoPage)
|
||||
{
|
||||
public Type GetPageType(WinoPage winoPage)
|
||||
return winoPage switch
|
||||
{
|
||||
return winoPage switch
|
||||
{
|
||||
WinoPage.CalendarPage => typeof(CalendarPage),
|
||||
WinoPage.SettingsPage => typeof(SettingsPage),
|
||||
WinoPage.CalendarSettingsPage => typeof(CalendarSettingsPage),
|
||||
WinoPage.AccountManagementPage => typeof(AccountManagementPage),
|
||||
WinoPage.ManageAccountsPage => typeof(ManageAccountsPage),
|
||||
WinoPage.PersonalizationPage => typeof(PersonalizationPage),
|
||||
WinoPage.AccountDetailsPage => typeof(AccountDetailsPage),
|
||||
WinoPage.EventDetailsPage => typeof(EventDetailsPage),
|
||||
_ => throw new Exception("Page is not implemented yet."),
|
||||
};
|
||||
}
|
||||
WinoPage.CalendarPage => typeof(CalendarPage),
|
||||
WinoPage.SettingsPage => typeof(SettingsPage),
|
||||
WinoPage.CalendarSettingsPage => typeof(CalendarSettingsPage),
|
||||
WinoPage.AccountManagementPage => typeof(AccountManagementPage),
|
||||
WinoPage.ManageAccountsPage => typeof(ManageAccountsPage),
|
||||
WinoPage.PersonalizationPage => typeof(PersonalizationPage),
|
||||
WinoPage.AccountDetailsPage => typeof(AccountDetailsPage),
|
||||
WinoPage.EventDetailsPage => typeof(EventDetailsPage),
|
||||
_ => throw new Exception("Page is not implemented yet."),
|
||||
};
|
||||
}
|
||||
|
||||
public void GoBack()
|
||||
public void GoBack()
|
||||
{
|
||||
if (Window.Current.Content is Frame appFrame && appFrame.Content is AppShell shellPage)
|
||||
{
|
||||
if (Window.Current.Content is Frame appFrame && appFrame.Content is AppShell shellPage)
|
||||
{
|
||||
var shellFrame = shellPage.GetShellFrame();
|
||||
var shellFrame = shellPage.GetShellFrame();
|
||||
|
||||
if (shellFrame.CanGoBack)
|
||||
{
|
||||
shellFrame.GoBack();
|
||||
}
|
||||
if (shellFrame.CanGoBack)
|
||||
{
|
||||
shellFrame.GoBack();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Navigate(WinoPage page, object parameter = null, NavigationReferenceFrame frame = NavigationReferenceFrame.ShellFrame, NavigationTransitionType transition = NavigationTransitionType.None)
|
||||
{
|
||||
// All navigations are performed on shell frame for calendar.
|
||||
|
||||
if (Window.Current.Content is Frame appFrame && appFrame.Content is AppShell shellPage)
|
||||
{
|
||||
var shellFrame = shellPage.GetShellFrame();
|
||||
|
||||
var pageType = GetPageType(page);
|
||||
|
||||
shellFrame.Navigate(pageType, parameter);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Navigate(WinoPage page, object parameter = null, NavigationReferenceFrame frame = NavigationReferenceFrame.ShellFrame, NavigationTransitionType transition = NavigationTransitionType.None)
|
||||
{
|
||||
// All navigations are performed on shell frame for calendar.
|
||||
|
||||
if (Window.Current.Content is Frame appFrame && appFrame.Content is AppShell shellPage)
|
||||
{
|
||||
var shellFrame = shellPage.GetShellFrame();
|
||||
|
||||
var pageType = GetPageType(page);
|
||||
|
||||
shellFrame.Navigate(pageType, parameter);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,33 +4,32 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Accounts;
|
||||
|
||||
namespace Wino.Calendar.Services
|
||||
namespace Wino.Calendar.Services;
|
||||
|
||||
public class ProviderService : IProviderService
|
||||
{
|
||||
public class ProviderService : IProviderService
|
||||
public IProviderDetail GetProviderDetail(MailProviderType type)
|
||||
{
|
||||
public IProviderDetail GetProviderDetail(MailProviderType type)
|
||||
{
|
||||
var details = GetAvailableProviders();
|
||||
var details = GetAvailableProviders();
|
||||
|
||||
return details.FirstOrDefault(a => a.Type == type);
|
||||
return details.FirstOrDefault(a => a.Type == type);
|
||||
}
|
||||
|
||||
public List<IProviderDetail> GetAvailableProviders()
|
||||
{
|
||||
var providerList = new List<IProviderDetail>();
|
||||
|
||||
var providers = new MailProviderType[]
|
||||
{
|
||||
MailProviderType.Outlook,
|
||||
MailProviderType.Gmail
|
||||
};
|
||||
|
||||
foreach (var type in providers)
|
||||
{
|
||||
providerList.Add(new ProviderDetail(type, SpecialImapProvider.None));
|
||||
}
|
||||
|
||||
public List<IProviderDetail> GetAvailableProviders()
|
||||
{
|
||||
var providerList = new List<IProviderDetail>();
|
||||
|
||||
var providers = new MailProviderType[]
|
||||
{
|
||||
MailProviderType.Outlook,
|
||||
MailProviderType.Gmail
|
||||
};
|
||||
|
||||
foreach (var type in providers)
|
||||
{
|
||||
providerList.Add(new ProviderDetail(type, SpecialImapProvider.None));
|
||||
}
|
||||
|
||||
return providerList;
|
||||
}
|
||||
return providerList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Wino.Calendar.Styles
|
||||
namespace Wino.Calendar.Styles;
|
||||
|
||||
public sealed partial class WinoCalendarResources : ResourceDictionary
|
||||
{
|
||||
public sealed partial class WinoCalendarResources : ResourceDictionary
|
||||
public WinoCalendarResources()
|
||||
{
|
||||
public WinoCalendarResources()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Wino.Calendar.ViewModels;
|
||||
using Wino.Core.UWP;
|
||||
|
||||
namespace Wino.Calendar.Views.Abstract
|
||||
{
|
||||
public abstract class AccountDetailsPageAbstract : BasePage<AccountDetailsPageViewModel> { }
|
||||
}
|
||||
namespace Wino.Calendar.Views.Abstract;
|
||||
|
||||
public abstract class AccountDetailsPageAbstract : BasePage<AccountDetailsPageViewModel> { }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Wino.Calendar.ViewModels;
|
||||
using Wino.Core.UWP;
|
||||
|
||||
namespace Wino.Calendar.Views.Abstract
|
||||
{
|
||||
public class AccountManagementPageAbstract : BasePage<AccountManagementViewModel> { }
|
||||
}
|
||||
namespace Wino.Calendar.Views.Abstract;
|
||||
|
||||
public partial class AccountManagementPageAbstract : BasePage<AccountManagementViewModel> { }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Wino.Calendar.ViewModels;
|
||||
using Wino.Core.UWP;
|
||||
|
||||
namespace Wino.Calendar.Views.Abstract
|
||||
{
|
||||
public abstract class AppShellAbstract : BasePage<AppShellViewModel> { }
|
||||
}
|
||||
namespace Wino.Calendar.Views.Abstract;
|
||||
|
||||
public abstract class AppShellAbstract : BasePage<AppShellViewModel> { }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Wino.Calendar.ViewModels;
|
||||
using Wino.Core.UWP;
|
||||
|
||||
namespace Wino.Calendar.Views.Abstract
|
||||
{
|
||||
public abstract class CalendarPageAbstract : BasePage<CalendarPageViewModel> { }
|
||||
}
|
||||
namespace Wino.Calendar.Views.Abstract;
|
||||
|
||||
public abstract class CalendarPageAbstract : BasePage<CalendarPageViewModel> { }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Wino.Calendar.ViewModels;
|
||||
using Wino.Core.UWP;
|
||||
|
||||
namespace Wino.Calendar.Views.Abstract
|
||||
{
|
||||
public abstract class CalendarSettingsPageAbstract : BasePage<CalendarSettingsPageViewModel> { }
|
||||
}
|
||||
namespace Wino.Calendar.Views.Abstract;
|
||||
|
||||
public abstract class CalendarSettingsPageAbstract : BasePage<CalendarSettingsPageViewModel> { }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Wino.Calendar.ViewModels;
|
||||
using Wino.Core.UWP;
|
||||
|
||||
namespace Wino.Calendar.Views.Abstract
|
||||
{
|
||||
public abstract class EventDetailsPageAbstract : BasePage<EventDetailsPageViewModel> { }
|
||||
}
|
||||
namespace Wino.Calendar.Views.Abstract;
|
||||
|
||||
public abstract class EventDetailsPageAbstract : BasePage<EventDetailsPageViewModel> { }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Wino.Core.UWP;
|
||||
using Wino.Core.ViewModels;
|
||||
|
||||
namespace Wino.Calendar.Views.Abstract
|
||||
{
|
||||
public class PersonalizationPageAbstract : BasePage<PersonalizationPageViewModel> { }
|
||||
}
|
||||
namespace Wino.Calendar.Views.Abstract;
|
||||
|
||||
public partial class PersonalizationPageAbstract : BasePage<PersonalizationPageViewModel> { }
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Wino.Calendar.Views.Abstract;
|
||||
|
||||
namespace Wino.Calendar.Views.Account
|
||||
namespace Wino.Calendar.Views.Account;
|
||||
|
||||
public sealed partial class AccountManagementPage : AccountManagementPageAbstract
|
||||
{
|
||||
public sealed partial class AccountManagementPage : AccountManagementPageAbstract
|
||||
public AccountManagementPage()
|
||||
{
|
||||
public AccountManagementPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
BorderBrush="Transparent"
|
||||
IsTabStop="False"
|
||||
PlaceholderText="Search" />
|
||||
|
||||
<StackPanel
|
||||
|
||||
@@ -5,50 +5,49 @@ using Wino.Calendar.Views.Abstract;
|
||||
using Wino.Core.UWP;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Calendar.Views
|
||||
namespace Wino.Calendar.Views;
|
||||
|
||||
public sealed partial class AppShell : AppShellAbstract,
|
||||
IRecipient<CalendarDisplayTypeChangedMessage>
|
||||
{
|
||||
public sealed partial class AppShell : AppShellAbstract,
|
||||
IRecipient<CalendarDisplayTypeChangedMessage>
|
||||
private const string STATE_HorizontalCalendar = "HorizontalCalendar";
|
||||
private const string STATE_VerticalCalendar = "VerticalCalendar";
|
||||
|
||||
public Frame GetShellFrame() => ShellFrame;
|
||||
|
||||
public AppShell()
|
||||
{
|
||||
private const string STATE_HorizontalCalendar = "HorizontalCalendar";
|
||||
private const string STATE_VerticalCalendar = "VerticalCalendar";
|
||||
InitializeComponent();
|
||||
|
||||
public Frame GetShellFrame() => ShellFrame;
|
||||
|
||||
public AppShell()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Window.Current.SetTitleBar(DragArea);
|
||||
ManageCalendarDisplayType();
|
||||
}
|
||||
|
||||
private void ManageCalendarDisplayType()
|
||||
{
|
||||
// Go to different states based on the display type.
|
||||
if (ViewModel.IsVerticalCalendar)
|
||||
{
|
||||
VisualStateManager.GoToState(this, STATE_VerticalCalendar, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStateManager.GoToState(this, STATE_HorizontalCalendar, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void PreviousDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoPreviousDateRequestedMessage());
|
||||
|
||||
private void NextDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoNextDateRequestedMessage());
|
||||
|
||||
public void Receive(CalendarDisplayTypeChangedMessage message)
|
||||
{
|
||||
ManageCalendarDisplayType();
|
||||
}
|
||||
|
||||
private void ShellFrameContentNavigated(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
|
||||
=> RealAppBar.ShellFrameContent = (e.Content as BasePage).ShellContent;
|
||||
|
||||
private void AppBarBackButtonClicked(Core.UWP.Controls.WinoAppTitleBar sender, RoutedEventArgs args)
|
||||
=> ViewModel.NavigationService.GoBack();
|
||||
Window.Current.SetTitleBar(DragArea);
|
||||
ManageCalendarDisplayType();
|
||||
}
|
||||
|
||||
private void ManageCalendarDisplayType()
|
||||
{
|
||||
// Go to different states based on the display type.
|
||||
if (ViewModel.IsVerticalCalendar)
|
||||
{
|
||||
VisualStateManager.GoToState(this, STATE_VerticalCalendar, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStateManager.GoToState(this, STATE_HorizontalCalendar, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void PreviousDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoPreviousDateRequestedMessage());
|
||||
|
||||
private void NextDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoNextDateRequestedMessage());
|
||||
|
||||
public void Receive(CalendarDisplayTypeChangedMessage message)
|
||||
{
|
||||
ManageCalendarDisplayType();
|
||||
}
|
||||
|
||||
private void ShellFrameContentNavigated(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
|
||||
=> RealAppBar.ShellFrameContent = (e.Content as BasePage).ShellContent;
|
||||
|
||||
private void AppBarBackButtonClicked(Core.UWP.Controls.WinoAppTitleBar sender, RoutedEventArgs args)
|
||||
=> ViewModel.NavigationService.GoBack();
|
||||
}
|
||||
|
||||
@@ -9,153 +9,152 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Calendar.Views
|
||||
namespace Wino.Calendar.Views;
|
||||
|
||||
public sealed partial class CalendarPage : CalendarPageAbstract,
|
||||
IRecipient<ScrollToDateMessage>,
|
||||
IRecipient<ScrollToHourMessage>,
|
||||
IRecipient<GoNextDateRequestedMessage>,
|
||||
IRecipient<GoPreviousDateRequestedMessage>
|
||||
{
|
||||
public sealed partial class CalendarPage : CalendarPageAbstract,
|
||||
IRecipient<ScrollToDateMessage>,
|
||||
IRecipient<ScrollToHourMessage>,
|
||||
IRecipient<GoNextDateRequestedMessage>,
|
||||
IRecipient<GoPreviousDateRequestedMessage>
|
||||
private const int PopupDialogOffset = 12;
|
||||
|
||||
public CalendarPage()
|
||||
{
|
||||
private const int PopupDialogOffset = 12;
|
||||
InitializeComponent();
|
||||
NavigationCacheMode = NavigationCacheMode.Enabled;
|
||||
|
||||
public CalendarPage()
|
||||
ViewModel.DetailsShowCalendarItemChanged += CalendarItemDetailContextChanged;
|
||||
}
|
||||
|
||||
private void CalendarItemDetailContextChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (ViewModel.DisplayDetailsCalendarItemViewModel != null)
|
||||
{
|
||||
InitializeComponent();
|
||||
NavigationCacheMode = NavigationCacheMode.Enabled;
|
||||
var control = CalendarControl.GetCalendarItemControl(ViewModel.DisplayDetailsCalendarItemViewModel);
|
||||
|
||||
ViewModel.DetailsShowCalendarItemChanged += CalendarItemDetailContextChanged;
|
||||
}
|
||||
|
||||
private void CalendarItemDetailContextChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (ViewModel.DisplayDetailsCalendarItemViewModel != null)
|
||||
if (control != null)
|
||||
{
|
||||
var control = CalendarControl.GetCalendarItemControl(ViewModel.DisplayDetailsCalendarItemViewModel);
|
||||
|
||||
if (control != null)
|
||||
{
|
||||
EventDetailsPopup.PlacementTarget = control;
|
||||
}
|
||||
EventDetailsPopup.PlacementTarget = control;
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(ScrollToHourMessage message) => CalendarControl.NavigateToHour(message.TimeSpan);
|
||||
public void Receive(ScrollToDateMessage message) => CalendarControl.NavigateToDay(message.Date);
|
||||
public void Receive(GoNextDateRequestedMessage message) => CalendarControl.GoNextRange();
|
||||
public void Receive(GoPreviousDateRequestedMessage message) => CalendarControl.GoPreviousRange();
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if (e.NavigationMode == NavigationMode.Back) return;
|
||||
|
||||
if (e.Parameter is CalendarPageNavigationArgs args)
|
||||
{
|
||||
if (args.RequestDefaultNavigation)
|
||||
{
|
||||
// Go today.
|
||||
WeakReferenceMessenger.Default.Send(new LoadCalendarMessage(DateTime.Now.Date, CalendarInitInitiative.App));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Go specified date.
|
||||
WeakReferenceMessenger.Default.Send(new LoadCalendarMessage(args.NavigationDate, CalendarInitInitiative.User));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CellSelected(object sender, TimelineCellSelectedArgs e)
|
||||
{
|
||||
// Dismiss event details if exists and cancel the selection.
|
||||
// This is to prevent the event details from being displayed when the user clicks somewhere else.
|
||||
|
||||
if (EventDetailsPopup.IsOpen)
|
||||
{
|
||||
CalendarControl.UnselectActiveTimelineCell();
|
||||
ViewModel.DisplayDetailsCalendarItemViewModel = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ViewModel.SelectedQuickEventDate = e.ClickedDate;
|
||||
|
||||
TeachingTipPositionerGrid.Width = e.CellSize.Width;
|
||||
TeachingTipPositionerGrid.Height = e.CellSize.Height;
|
||||
|
||||
Canvas.SetLeft(TeachingTipPositionerGrid, e.PositionerPoint.X);
|
||||
Canvas.SetTop(TeachingTipPositionerGrid, e.PositionerPoint.Y);
|
||||
|
||||
// Adjust the start and end time in the flyout.
|
||||
var startTime = ViewModel.SelectedQuickEventDate.Value.TimeOfDay;
|
||||
var endTime = startTime.Add(TimeSpan.FromMinutes(30));
|
||||
|
||||
ViewModel.SelectQuickEventTimeRange(startTime, endTime);
|
||||
|
||||
QuickEventPopupDialog.IsOpen = true;
|
||||
}
|
||||
|
||||
private void CellUnselected(object sender, TimelineCellUnselectedArgs e)
|
||||
{
|
||||
QuickEventPopupDialog.IsOpen = false;
|
||||
}
|
||||
|
||||
private void QuickEventAccountSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
QuickEventAccountSelectorFlyout.Hide();
|
||||
}
|
||||
|
||||
private void QuickEventPopupClosed(object sender, object e)
|
||||
{
|
||||
// Reset the timeline selection when the tip is closed.
|
||||
CalendarControl.ResetTimelineSelection();
|
||||
}
|
||||
|
||||
private void PopupPlacementChanged(object sender, object e)
|
||||
{
|
||||
if (sender is Popup senderPopup)
|
||||
{
|
||||
// When the quick event Popup is positioned for different calendar types,
|
||||
// we must adjust the offset to make sure the tip is not hidden and has nice
|
||||
// spacing from the cell.
|
||||
|
||||
switch (senderPopup.ActualPlacement)
|
||||
{
|
||||
case PopupPlacementMode.Top:
|
||||
senderPopup.VerticalOffset = PopupDialogOffset * -1;
|
||||
break;
|
||||
case PopupPlacementMode.Bottom:
|
||||
senderPopup.VerticalOffset = PopupDialogOffset;
|
||||
break;
|
||||
case PopupPlacementMode.Left:
|
||||
senderPopup.HorizontalOffset = PopupDialogOffset * -1;
|
||||
break;
|
||||
case PopupPlacementMode.Right:
|
||||
senderPopup.HorizontalOffset = PopupDialogOffset;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void StartTimeDurationSubmitted(ComboBox sender, ComboBoxTextSubmittedEventArgs args)
|
||||
=> ViewModel.SelectedStartTimeString = args.Text;
|
||||
|
||||
private void EndTimeDurationSubmitted(ComboBox sender, ComboBoxTextSubmittedEventArgs args)
|
||||
=> ViewModel.SelectedEndTimeString = args.Text;
|
||||
|
||||
private void EventDetailsPopupClosed(object sender, object e)
|
||||
{
|
||||
ViewModel.DisplayDetailsCalendarItemViewModel = null;
|
||||
}
|
||||
|
||||
private void CalendarScrolling(object sender, EventArgs e)
|
||||
{
|
||||
// In case of scrolling, we must dismiss the event details dialog.
|
||||
ViewModel.DisplayDetailsCalendarItemViewModel = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(ScrollToHourMessage message) => CalendarControl.NavigateToHour(message.TimeSpan);
|
||||
public void Receive(ScrollToDateMessage message) => CalendarControl.NavigateToDay(message.Date);
|
||||
public void Receive(GoNextDateRequestedMessage message) => CalendarControl.GoNextRange();
|
||||
public void Receive(GoPreviousDateRequestedMessage message) => CalendarControl.GoPreviousRange();
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if (e.NavigationMode == NavigationMode.Back) return;
|
||||
|
||||
if (e.Parameter is CalendarPageNavigationArgs args)
|
||||
{
|
||||
if (args.RequestDefaultNavigation)
|
||||
{
|
||||
// Go today.
|
||||
WeakReferenceMessenger.Default.Send(new LoadCalendarMessage(DateTime.Now.Date, CalendarInitInitiative.App));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Go specified date.
|
||||
WeakReferenceMessenger.Default.Send(new LoadCalendarMessage(args.NavigationDate, CalendarInitInitiative.User));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CellSelected(object sender, TimelineCellSelectedArgs e)
|
||||
{
|
||||
// Dismiss event details if exists and cancel the selection.
|
||||
// This is to prevent the event details from being displayed when the user clicks somewhere else.
|
||||
|
||||
if (EventDetailsPopup.IsOpen)
|
||||
{
|
||||
CalendarControl.UnselectActiveTimelineCell();
|
||||
ViewModel.DisplayDetailsCalendarItemViewModel = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ViewModel.SelectedQuickEventDate = e.ClickedDate;
|
||||
|
||||
TeachingTipPositionerGrid.Width = e.CellSize.Width;
|
||||
TeachingTipPositionerGrid.Height = e.CellSize.Height;
|
||||
|
||||
Canvas.SetLeft(TeachingTipPositionerGrid, e.PositionerPoint.X);
|
||||
Canvas.SetTop(TeachingTipPositionerGrid, e.PositionerPoint.Y);
|
||||
|
||||
// Adjust the start and end time in the flyout.
|
||||
var startTime = ViewModel.SelectedQuickEventDate.Value.TimeOfDay;
|
||||
var endTime = startTime.Add(TimeSpan.FromMinutes(30));
|
||||
|
||||
ViewModel.SelectQuickEventTimeRange(startTime, endTime);
|
||||
|
||||
QuickEventPopupDialog.IsOpen = true;
|
||||
}
|
||||
|
||||
private void CellUnselected(object sender, TimelineCellUnselectedArgs e)
|
||||
{
|
||||
QuickEventPopupDialog.IsOpen = false;
|
||||
}
|
||||
|
||||
private void QuickEventAccountSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
QuickEventAccountSelectorFlyout.Hide();
|
||||
}
|
||||
|
||||
private void QuickEventPopupClosed(object sender, object e)
|
||||
{
|
||||
// Reset the timeline selection when the tip is closed.
|
||||
CalendarControl.ResetTimelineSelection();
|
||||
}
|
||||
|
||||
private void PopupPlacementChanged(object sender, object e)
|
||||
{
|
||||
if (sender is Popup senderPopup)
|
||||
{
|
||||
// When the quick event Popup is positioned for different calendar types,
|
||||
// we must adjust the offset to make sure the tip is not hidden and has nice
|
||||
// spacing from the cell.
|
||||
|
||||
switch (senderPopup.ActualPlacement)
|
||||
{
|
||||
case PopupPlacementMode.Top:
|
||||
senderPopup.VerticalOffset = PopupDialogOffset * -1;
|
||||
break;
|
||||
case PopupPlacementMode.Bottom:
|
||||
senderPopup.VerticalOffset = PopupDialogOffset;
|
||||
break;
|
||||
case PopupPlacementMode.Left:
|
||||
senderPopup.HorizontalOffset = PopupDialogOffset * -1;
|
||||
break;
|
||||
case PopupPlacementMode.Right:
|
||||
senderPopup.HorizontalOffset = PopupDialogOffset;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void StartTimeDurationSubmitted(ComboBox sender, ComboBoxTextSubmittedEventArgs args)
|
||||
=> ViewModel.SelectedStartTimeString = args.Text;
|
||||
|
||||
private void EndTimeDurationSubmitted(ComboBox sender, ComboBoxTextSubmittedEventArgs args)
|
||||
=> ViewModel.SelectedEndTimeString = args.Text;
|
||||
|
||||
private void EventDetailsPopupClosed(object sender, object e)
|
||||
{
|
||||
ViewModel.DisplayDetailsCalendarItemViewModel = null;
|
||||
}
|
||||
|
||||
private void CalendarScrolling(object sender, EventArgs e)
|
||||
{
|
||||
// In case of scrolling, we must dismiss the event details dialog.
|
||||
ViewModel.DisplayDetailsCalendarItemViewModel = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@
|
||||
<PersonPicture
|
||||
Width="40"
|
||||
Height="40"
|
||||
DisplayName="{x:Bind Name}" />
|
||||
DisplayName="{x:Bind DisplayName}" />
|
||||
|
||||
<!-- TODO: Organizer -->
|
||||
<Grid Grid.Column="1">
|
||||
@@ -265,7 +265,7 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock FontWeight="SemiBold" Text="{x:Bind Name}" />
|
||||
<TextBlock FontWeight="SemiBold" Text="{x:Bind DisplayName}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
FontSize="13"
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
|
||||
using Wino.Calendar.Views.Abstract;
|
||||
|
||||
namespace Wino.Calendar.Views
|
||||
namespace Wino.Calendar.Views;
|
||||
|
||||
public sealed partial class EventDetailsPage : EventDetailsPageAbstract
|
||||
{
|
||||
public sealed partial class EventDetailsPage : EventDetailsPageAbstract
|
||||
public EventDetailsPage()
|
||||
{
|
||||
public EventDetailsPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<controls:SettingsCard.HeaderIcon>
|
||||
<PathIcon Data="F1 M 10 0.625 C 10 0.45573 10.061849 0.309246 10.185547 0.185547 C 10.309244 0.06185 10.455729 0 10.625 0 L 15.625 0 C 15.79427 0 15.940754 0.06185 16.064453 0.185547 C 16.18815 0.309246 16.25 0.45573 16.25 0.625 C 16.25 0.794271 16.18815 0.940756 16.064453 1.064453 C 15.940754 1.188152 15.79427 1.25 15.625 1.25 L 13.75 1.25 L 13.75 18.75 L 15.625 18.75 C 15.79427 18.75 15.940754 18.81185 16.064453 18.935547 C 16.18815 19.059244 16.25 19.205729 16.25 19.375 C 16.25 19.544271 16.18815 19.690756 16.064453 19.814453 C 15.940754 19.93815 15.79427 20 15.625 20 L 10.625 20 C 10.455729 20 10.309244 19.93815 10.185547 19.814453 C 10.061849 19.690756 10 19.544271 10 19.375 C 10 19.205729 10.061849 19.059244 10.185547 18.935547 C 10.309244 18.81185 10.455729 18.75 10.625 18.75 L 12.5 18.75 L 12.5 1.25 L 10.625 1.25 C 10.455729 1.25 10.309244 1.188152 10.185547 1.064453 C 10.061849 0.940756 10 0.794271 10 0.625 Z M 0 6.25 C 0 5.735678 0.097656 5.250651 0.292969 4.794922 C 0.488281 4.339193 0.756836 3.94043 1.098633 3.598633 C 1.44043 3.256836 1.837565 2.988281 2.290039 2.792969 C 2.742513 2.597656 3.229167 2.5 3.75 2.5 L 11.25 2.5 L 11.25 3.75 L 3.75 3.75 C 3.404948 3.75 3.081055 3.815105 2.77832 3.945312 C 2.475586 4.075521 2.210286 4.254558 1.982422 4.482422 C 1.754557 4.710287 1.575521 4.975587 1.445312 5.27832 C 1.315104 5.581056 1.25 5.904949 1.25 6.25 L 1.25 13.75 C 1.25 14.095053 1.315104 14.418945 1.445312 14.72168 C 1.575521 15.024414 1.754557 15.289714 1.982422 15.517578 C 2.210286 15.745443 2.475586 15.924479 2.77832 16.054688 C 3.081055 16.184896 3.404948 16.25 3.75 16.25 L 11.25 16.25 L 11.25 17.5 L 3.75 17.5 C 3.229167 17.5 2.742513 17.402344 2.290039 17.207031 C 1.837565 17.011719 1.44043 16.743164 1.098633 16.401367 C 0.756836 16.05957 0.488281 15.662436 0.292969 15.209961 C 0.097656 14.757487 0 14.270834 0 13.75 Z M 15 3.75 L 15 2.5 L 16.25 2.5 C 16.764322 2.5 17.249348 2.597656 17.705078 2.792969 C 18.160807 2.988281 18.55957 3.256836 18.901367 3.598633 C 19.243164 3.94043 19.511719 4.339193 19.707031 4.794922 C 19.902344 5.250651 20 5.735678 20 6.25 L 20 13.75 C 20 14.270834 19.902344 14.757487 19.707031 15.209961 C 19.511719 15.662436 19.243164 16.05957 18.901367 16.401367 C 18.55957 16.743164 18.160807 17.011719 17.705078 17.207031 C 17.249348 17.402344 16.764322 17.5 16.25 17.5 L 15 17.5 L 15 16.25 L 16.25 16.25 C 16.595051 16.25 16.918945 16.184896 17.22168 16.054688 C 17.524414 15.924479 17.789713 15.745443 18.017578 15.517578 C 18.245441 15.289714 18.424479 15.024414 18.554688 14.72168 C 18.684895 14.418945 18.75 14.095053 18.75 13.75 L 18.75 6.25 C 18.75 5.904949 18.684895 5.581056 18.554688 5.27832 C 18.424479 4.975587 18.245441 4.710287 18.017578 4.482422 C 17.789713 4.254558 17.524414 4.075521 17.22168 3.945312 C 16.918945 3.815105 16.595051 3.75 16.25 3.75 Z M 7.441406 5.361328 C 7.389323 5.250651 7.312825 5.162761 7.211914 5.097656 C 7.111002 5.032553 6.998697 5.000001 6.875 5 C 6.751302 5.000001 6.638997 5.032553 6.538086 5.097656 C 6.437174 5.162761 6.360677 5.250651 6.308594 5.361328 L 2.871094 12.861328 C 2.799479 13.017578 2.792969 13.177084 2.851562 13.339844 C 2.910156 13.502604 3.017578 13.619792 3.173828 13.691406 C 3.330078 13.763021 3.489583 13.769531 3.652344 13.710938 C 3.815104 13.652344 3.932292 13.544922 4.003906 13.388672 L 4.84375 11.5625 L 8.896484 11.5625 L 8.90625 11.5625 L 9.746094 13.388672 C 9.817708 13.544922 9.934896 13.652344 10.097656 13.710938 C 10.260416 13.769531 10.419922 13.763021 10.576172 13.691406 C 10.732422 13.619792 10.839844 13.502604 10.898438 13.339844 C 10.957031 13.177084 10.950521 13.017578 10.878906 12.861328 Z M 5.410156 10.3125 L 6.875 7.128906 L 8.339844 10.3125 Z " />
|
||||
</controls:SettingsCard.HeaderIcon>
|
||||
<Button Command="{x:Bind ViewModel.RenameAccountCommand}" Content="{x:Bind domain:Translator.FolderOperation_Rename}" />
|
||||
<Button Command="{x:Bind ViewModel.EditAccountDetailsCommand}" Content="{x:Bind domain:Translator.FolderOperation_Rename}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<!-- TODO -->
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Wino.Calendar.Views.Abstract;
|
||||
|
||||
namespace Wino.Calendar.Views.Settings
|
||||
namespace Wino.Calendar.Views.Settings;
|
||||
|
||||
public sealed partial class AccountDetailsPage : AccountDetailsPageAbstract
|
||||
{
|
||||
public sealed partial class AccountDetailsPage : AccountDetailsPageAbstract
|
||||
public AccountDetailsPage()
|
||||
{
|
||||
public AccountDetailsPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using Wino.Calendar.Views.Abstract;
|
||||
|
||||
|
||||
namespace Wino.Calendar.Views.Settings
|
||||
namespace Wino.Calendar.Views.Settings;
|
||||
|
||||
public sealed partial class CalendarSettingsPage : CalendarSettingsPageAbstract
|
||||
{
|
||||
public sealed partial class CalendarSettingsPage : CalendarSettingsPageAbstract
|
||||
public CalendarSettingsPage()
|
||||
{
|
||||
public CalendarSettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Wino.Calendar.Views.Abstract;
|
||||
|
||||
namespace Wino.Calendar.Views.Settings
|
||||
namespace Wino.Calendar.Views.Settings;
|
||||
|
||||
public sealed partial class PersonalizationPage : PersonalizationPageAbstract
|
||||
{
|
||||
public sealed partial class PersonalizationPage : PersonalizationPageAbstract
|
||||
public PersonalizationPage()
|
||||
{
|
||||
public PersonalizationPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,184 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
<!-- UWP WAM Authentication on Xbox needs this. -->
|
||||
<UseDotNetNativeSharedAssemblyFrameworkPackage>false</UseDotNetNativeSharedAssemblyFrameworkPackage>
|
||||
<PackageCertificateThumbprint>
|
||||
</PackageCertificateThumbprint>
|
||||
<PackageCertificateKeyFile>Wino.Mail_TemporaryKey.pfx</PackageCertificateKeyFile>
|
||||
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
||||
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
|
||||
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
|
||||
<AppxSymbolPackageEnabled>False</AppxSymbolPackageEnabled>
|
||||
<GenerateTestArtifacts>True</GenerateTestArtifacts>
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
<AppxBundlePlatforms>x64</AppxBundlePlatforms>
|
||||
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProjectGuid>{600F4979-DB7E-409D-B7DA-B60BE4C55C35}</ProjectGuid>
|
||||
<OutputType>AppContainerExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Wino.Calendar</RootNamespace>
|
||||
<AssemblyName>Wino.Calendar</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.22621.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WindowsXamlEnableOverview>true</WindowsXamlEnableOverview>
|
||||
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
|
||||
<GenerateTemporaryStoreCertificate>True</GenerateTemporaryStoreCertificate>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\ARM64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>ARM64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM64'">
|
||||
<OutputPath>bin\ARM64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<!-- .NET Native Shit -->
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
<UseDotNetNativeSharedAssemblyFrameworkPackage>false</UseDotNetNativeSharedAssemblyFrameworkPackage>
|
||||
<Use64BitCompiler>true</Use64BitCompiler>
|
||||
<OutOfProcPDB>true</OutOfProcPDB>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Activation\DefaultActivationHandler.cs" />
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Args\TimelineCellSelectedArgs.cs" />
|
||||
<Compile Include="Args\TimelineCellUnselectedArgs.cs" />
|
||||
<Compile Include="Controls\CalendarItemCommandBarFlyout.cs" />
|
||||
<Compile Include="Controls\CalendarItemControl.xaml.cs">
|
||||
<DependentUpon>CalendarItemControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Controls\CustomCalendarFlipView.cs" />
|
||||
<Compile Include="Controls\DayColumnControl.cs" />
|
||||
<Compile Include="Controls\DayHeaderControl.cs" />
|
||||
<Compile Include="Controls\WinoCalendarControl.cs" />
|
||||
<Compile Include="Controls\WinoCalendarFlipView.cs" />
|
||||
<Compile Include="Controls\WinoCalendarPanel.cs" />
|
||||
<Compile Include="Controls\WinoCalendarTypeSelectorControl.cs" />
|
||||
<Compile Include="Controls\WinoCalendarView.cs" />
|
||||
<Compile Include="Controls\WinoDayTimelineCanvas.cs" />
|
||||
<Compile Include="Helpers\CalendarXamlHelpers.cs" />
|
||||
<Compile Include="MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Models\CalendarItemMeasurement.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Selectors\CustomAreaCalendarItemSelector.cs" />
|
||||
<Compile Include="Selectors\WinoCalendarItemTemplateSelector.cs" />
|
||||
<Compile Include="Services\AccountCalendarStateService.cs" />
|
||||
<Compile Include="Services\CalendarAuthenticatorConfig.cs" />
|
||||
<Compile Include="Services\DialogService.cs" />
|
||||
<Compile Include="Services\NavigationService.cs" />
|
||||
<Compile Include="Services\ProviderService.cs" />
|
||||
<Compile Include="Services\SettingsBuilderService.cs" />
|
||||
<Compile Include="Styles\WinoCalendarResources.xaml.cs" />
|
||||
<Compile Include="Views\Abstract\AccountDetailsPageAbstract.cs" />
|
||||
<Compile Include="Views\Abstract\AccountManagementPageAbstract.cs" />
|
||||
<Compile Include="Views\Abstract\AppShellAbstract.cs" />
|
||||
<Compile Include="Views\Abstract\CalendarPageAbstract.cs" />
|
||||
<Compile Include="Views\Abstract\CalendarSettingsPageAbstract.cs" />
|
||||
<Compile Include="Views\Abstract\EventDetailsPageAbstract.cs" />
|
||||
<Compile Include="Views\Abstract\PersonalizationPageAbstract.cs" />
|
||||
<Compile Include="Views\Account\AccountManagementPage.xaml.cs">
|
||||
<DependentUpon>AccountManagementPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\AppShell.xaml.cs">
|
||||
<DependentUpon>AppShell.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\CalendarPage.xaml.cs">
|
||||
<DependentUpon>CalendarPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\EventDetailsPage.xaml.cs">
|
||||
<DependentUpon>EventDetailsPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Settings\AccountDetailsPage.xaml.cs">
|
||||
<DependentUpon>AccountDetailsPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Settings\CalendarSettingsPage.xaml.cs">
|
||||
<DependentUpon>CalendarSettingsPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Settings\PersonalizationPage.xaml.cs">
|
||||
<DependentUpon>PersonalizationPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net9.0-windows10.0.26100.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.18362.0</TargetPlatformMinVersion>
|
||||
<UseUwp>true</UseUwp>
|
||||
<Platforms>x86;x64;arm64</Platforms>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<!--<PublishAot>true</PublishAot>-->
|
||||
<PublishProfile>win-$(Platform).pubxml</PublishProfile>
|
||||
<DisableRuntimeMarshalling>true</DisableRuntimeMarshalling>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<GenerateAppInstallerFile>True</GenerateAppInstallerFile>
|
||||
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
|
||||
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="BundleArtifacts\**" />
|
||||
<EmbeddedResource Remove="BundleArtifacts\**" />
|
||||
<None Remove="BundleArtifacts\**" />
|
||||
<Page Remove="BundleArtifacts\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PRIResource Remove="BundleArtifacts\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Package.StoreAssociation.xml" />
|
||||
<Content Include="Assets\LargeTile.scale-100.png" />
|
||||
@@ -226,7 +73,6 @@
|
||||
<Content Include="Assets\Wide310x150Logo.scale-125.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-150.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-400.png" />
|
||||
<Content Include="Properties\Default.rd.xml" />
|
||||
<Content Include="Assets\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||
<Content Include="Assets\Square150x150Logo.scale-200.png" />
|
||||
@@ -235,120 +81,18 @@
|
||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Page Include="Controls\CalendarItemControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="MainPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Styles\CalendarThemeResources.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Styles\DayHeaderControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Styles\WinoCalendarResources.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Styles\WinoCalendarTypeSelectorControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\WinoCalendarView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\WinoDayTimelineCanvas.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\Account\AccountManagementPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\AppShell.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\CalendarPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\EventDetailsPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Settings\AccountDetailsPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Settings\CalendarSettingsPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Settings\PersonalizationPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<PackageReference Include="CommunityToolkit.Uwp.Controls.Primitives" />
|
||||
<PackageReference Include="Microsoft.Identity.Client" />
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" />
|
||||
<PackageReference Include="Win2D.uwp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Uwp.Controls.Primitives">
|
||||
<Version>8.1.240916</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Identity.Client">
|
||||
<Version>4.66.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>6.2.14</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Win2D.uwp">
|
||||
<Version>1.28.1</Version>
|
||||
</PackageReference>
|
||||
<ProjectReference Include="..\Wino.Authentication\Wino.Authentication.csproj" />
|
||||
<ProjectReference Include="..\Wino.Calendar.ViewModels\Wino.Calendar.ViewModels.csproj" />
|
||||
<ProjectReference Include="..\Wino.Core.Domain\Wino.Core.Domain.csproj" />
|
||||
<ProjectReference Include="..\Wino.Core.UWP\Wino.Core.UWP.csproj" />
|
||||
<ProjectReference Include="..\Wino.Core.ViewModels\Wino.Core.ViewModels.csproj" />
|
||||
<ProjectReference Include="..\Wino.Messages\Wino.Messaging.csproj" />
|
||||
<ProjectReference Include="..\Wino.Services\Wino.Services.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Wino.Calendar.ViewModels\Wino.Calendar.ViewModels.csproj">
|
||||
<Project>{039affa8-c1cc-4e3b-8a31-6814d7557f74}</Project>
|
||||
<Name>Wino.Calendar.ViewModels</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Wino.Core.Domain\Wino.Core.Domain.csproj">
|
||||
<Project>{cf3312e5-5da0-4867-9945-49ea7598af1f}</Project>
|
||||
<Name>Wino.Core.Domain</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Wino.Core.UWP\Wino.Core.UWP.csproj">
|
||||
<Project>{395f19ba-1e42-495c-9db5-1a6f537fccb8}</Project>
|
||||
<Name>Wino.Core.UWP</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Wino.Core.ViewModels\Wino.Core.ViewModels.csproj">
|
||||
<Project>{53723ae8-7e7e-4d54-adab-0a6033255cc8}</Project>
|
||||
<Name>Wino.Core.ViewModels</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Wino.Messages\Wino.Messaging.csproj">
|
||||
<Project>{0c307d7e-256f-448c-8265-5622a812fbcc}</Project>
|
||||
<Name>Wino.Messaging</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Wino.Services\Wino.Services.csproj">
|
||||
<Project>{bba49030-7277-48cf-b2fe-3d01cb6b6c81}</Project>
|
||||
<Name>Wino.Services</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -80,11 +80,11 @@ public class CalendarEventCollection
|
||||
// Multi-day events go to both.
|
||||
// Anything else goes to regular.
|
||||
|
||||
if (calendarItem.IsAllDayEvent)
|
||||
if (calendarItem.ItemType == Enums.CalendarItemType.AllDay || calendarItem.ItemType == Enums.CalendarItemType.MultiDayAllDay || calendarItem.ItemType == Enums.CalendarItemType.RecurringAllDay)
|
||||
{
|
||||
return [_internalAllDayEvents];
|
||||
}
|
||||
else if (calendarItem.IsMultiDayEvent)
|
||||
else if (calendarItem.ItemType == Enums.CalendarItemType.MultiDay || calendarItem.ItemType == Enums.CalendarItemType.MultiDayAllDay)
|
||||
{
|
||||
return [_internalRegularEvents, _internalAllDayEvents];
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ public static class Constants
|
||||
public const string WinoLocalDraftHeader = "X-Wino-Draft-Id";
|
||||
public const string LocalDraftStartPrefix = "localDraft_";
|
||||
|
||||
public const string CalendarEventRecurrenceRuleSeperator = "___";
|
||||
|
||||
public const string ToastMailUniqueIdKey = nameof(ToastMailUniqueIdKey);
|
||||
public const string ToastActionKey = nameof(ToastActionKey);
|
||||
|
||||
|
||||
@@ -7,12 +7,40 @@ namespace Wino.Core.Domain.Entities.Calendar;
|
||||
public class AccountCalendar : IAccountCalendar
|
||||
{
|
||||
[PrimaryKey]
|
||||
public Guid Id { get; set; }
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
[NotNull]
|
||||
public string RemoteCalendarId { get; set; } = string.Empty;
|
||||
|
||||
[NotNull]
|
||||
public Guid AccountId { get; set; }
|
||||
public string RemoteCalendarId { get; set; }
|
||||
public string SynchronizationDeltaToken { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool IsPrimary { get; set; }
|
||||
|
||||
[NotNull]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string? Location { get; set; }
|
||||
|
||||
public string? TimeZone { get; set; }
|
||||
|
||||
public string? AccessRole { get; set; }
|
||||
|
||||
public bool IsPrimary { get; set; } = false;
|
||||
|
||||
public string? BackgroundColor { get; set; }
|
||||
|
||||
public string? ForegroundColor { get; set; }
|
||||
|
||||
public DateTime CreatedDate { get; set; }
|
||||
|
||||
public DateTime LastModified { get; set; }
|
||||
|
||||
public DateTime? LastSyncTime { get; set; }
|
||||
|
||||
public string? SynchronizationDeltaToken { get; set; }
|
||||
|
||||
public bool IsDeleted { get; set; } = false;
|
||||
public bool IsExtended { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
@@ -20,5 +48,4 @@ public class AccountCalendar : IAccountCalendar
|
||||
/// </summary>
|
||||
public string TextColorHex { get; set; }
|
||||
public string BackgroundColorHex { get; set; }
|
||||
public string TimeZone { get; set; }
|
||||
}
|
||||
|
||||
@@ -8,12 +8,29 @@ namespace Wino.Core.Domain.Entities.Calendar;
|
||||
public class CalendarEventAttendee
|
||||
{
|
||||
[PrimaryKey]
|
||||
public Guid Id { get; set; }
|
||||
public Guid CalendarItemId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
public AttendeeStatus AttendenceStatus { get; set; }
|
||||
public bool IsOrganizer { get; set; }
|
||||
public bool IsOptionalAttendee { get; set; }
|
||||
public string Comment { get; set; }
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
[NotNull]
|
||||
public Guid EventId { get; set; }
|
||||
|
||||
[NotNull]
|
||||
public string Email { get; set; } = string.Empty;
|
||||
|
||||
public string? DisplayName { get; set; }
|
||||
|
||||
public AttendeeResponseStatus ResponseStatus { get; set; } = AttendeeResponseStatus.NeedsAction;
|
||||
|
||||
public bool IsOptional { get; set; } = false;
|
||||
|
||||
public bool IsOrganizer { get; set; } = false;
|
||||
|
||||
public bool IsSelf { get; set; } = false;
|
||||
|
||||
public string? Comment { get; set; }
|
||||
|
||||
public int? AdditionalGuests { get; set; }
|
||||
|
||||
public DateTime CreatedDate { get; set; }
|
||||
|
||||
public DateTime LastModified { get; set; }
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Diagnostics;
|
||||
using Itenso.TimePeriod;
|
||||
using SQLite;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Helpers;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.Domain.Entities.Calendar;
|
||||
@@ -11,169 +12,81 @@ namespace Wino.Core.Domain.Entities.Calendar;
|
||||
public class CalendarItem : ICalendarItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
public Guid Id { get; set; }
|
||||
public string RemoteEventId { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Location { get; set; }
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
public DateTime StartDate { get; set; }
|
||||
[NotNull]
|
||||
public string RemoteEventId { get; set; } = string.Empty;
|
||||
|
||||
public DateTime EndDate
|
||||
{
|
||||
get
|
||||
{
|
||||
return StartDate.AddSeconds(DurationInSeconds);
|
||||
}
|
||||
}
|
||||
[NotNull]
|
||||
public Guid CalendarId { get; set; }
|
||||
|
||||
public TimeSpan StartDateOffset { get; set; }
|
||||
public TimeSpan EndDateOffset { get; set; }
|
||||
[Ignore]
|
||||
public IAccountCalendar AssignedCalendar { get; set; }
|
||||
|
||||
[NotNull]
|
||||
public string Title { get; set; } = string.Empty;
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string? Location { get; set; }
|
||||
public string? HtmlLink { get; set; }
|
||||
|
||||
public DateTime StartDateTime { get; set; }
|
||||
|
||||
public DateTime EndDateTime { get; set; }
|
||||
|
||||
private ITimePeriod _period;
|
||||
public ITimePeriod Period
|
||||
{
|
||||
get
|
||||
{
|
||||
_period ??= new TimeRange(StartDate, EndDate);
|
||||
_period ??= new TimeRange(StartDateTime, EndDateTime);
|
||||
|
||||
return _period;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAllDay { get; set; }
|
||||
|
||||
public string? TimeZone { get; set; }
|
||||
|
||||
public string? RecurrenceRules { get; set; }
|
||||
|
||||
public string? Status { get; set; }
|
||||
|
||||
public string? OrganizerDisplayName { get; set; }
|
||||
|
||||
public string? OrganizerEmail { get; set; }
|
||||
|
||||
public DateTime CreatedDate { get; set; }
|
||||
|
||||
public DateTime LastModified { get; set; }
|
||||
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
public string? RecurringEventId { get; set; }
|
||||
|
||||
public string? OriginalStartTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Events that starts at midnight and ends at midnight are considered all-day events.
|
||||
/// The type of calendar item (Timed, AllDay, MultiDay, etc.)
|
||||
/// </summary>
|
||||
public bool IsAllDayEvent
|
||||
public CalendarItemType ItemType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Automatically determines and sets the ItemType based on event properties
|
||||
/// </summary>
|
||||
public void DetermineItemType()
|
||||
{
|
||||
get
|
||||
{
|
||||
return
|
||||
StartDate.TimeOfDay == TimeSpan.Zero &&
|
||||
EndDate.TimeOfDay == TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
var hasRecurrence = !string.IsNullOrEmpty(RecurrenceRules);
|
||||
var isCancelled = Status?.ToLowerInvariant() == "cancelled" || IsDeleted;
|
||||
|
||||
/// <summary>
|
||||
/// Events that are either an exceptional instance of a recurring event or occurrences.
|
||||
/// IsOccurrence is used to display occurrence instances of parent recurring events.
|
||||
/// IsOccurrence == false && IsRecurringChild == true => exceptional single instance.
|
||||
/// </summary>
|
||||
public bool IsRecurringChild
|
||||
{
|
||||
get
|
||||
{
|
||||
return RecurringCalendarItemId != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Events that are either an exceptional instance of a recurring event or occurrences.
|
||||
/// </summary>
|
||||
public bool IsRecurringEvent => IsRecurringChild || IsRecurringParent;
|
||||
|
||||
/// <summary>
|
||||
/// Events that are the master event definition of recurrence events.
|
||||
/// </summary>
|
||||
public bool IsRecurringParent
|
||||
{
|
||||
get
|
||||
{
|
||||
return !string.IsNullOrEmpty(Recurrence) && RecurringCalendarItemId == null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Events that are not all-day events and last more than one day are considered multi-day events.
|
||||
/// </summary>
|
||||
public bool IsMultiDayEvent
|
||||
{
|
||||
get
|
||||
{
|
||||
return Period.Duration.TotalDays >= 1 && !IsAllDayEvent;
|
||||
}
|
||||
}
|
||||
|
||||
public double DurationInSeconds { get; set; }
|
||||
public string Recurrence { get; set; }
|
||||
|
||||
public string OrganizerDisplayName { get; set; }
|
||||
public string OrganizerEmail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The id of the parent calendar item of the recurring event.
|
||||
/// Exceptional instances are stored as a separate calendar item.
|
||||
/// This makes the calendar item a child of the recurring event.
|
||||
/// </summary>
|
||||
public Guid? RecurringCalendarItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates read-only events. Default is false.
|
||||
/// </summary>
|
||||
public bool IsLocked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Hidden events must not be displayed to the user.
|
||||
/// This usually happens when a child instance of recurring parent is cancelled after creation.
|
||||
/// </summary>
|
||||
public bool IsHidden { get; set; }
|
||||
|
||||
// TODO
|
||||
public string CustomEventColorHex { get; set; }
|
||||
public string HtmlLink { get; set; }
|
||||
public CalendarItemStatus Status { get; set; }
|
||||
public CalendarItemVisibility Visibility { get; set; }
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
public DateTimeOffset UpdatedAt { get; set; }
|
||||
public Guid CalendarId { get; set; }
|
||||
|
||||
[Ignore]
|
||||
public IAccountCalendar AssignedCalendar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this item does not really exist in the database or not.
|
||||
/// These are used to display occurrence instances of parent recurring events.
|
||||
/// </summary>
|
||||
[Ignore]
|
||||
public bool IsOccurrence { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Id to load information related to this event.
|
||||
/// Occurrences tracked by the parent recurring event if they are not exceptional instances.
|
||||
/// Recurring children here are exceptional instances. They have their own info in the database including Id.
|
||||
/// </summary>
|
||||
public Guid EventTrackingId => IsOccurrence ? RecurringCalendarItemId.Value : Id;
|
||||
|
||||
public CalendarItem CreateRecurrence(DateTime startDate, double durationInSeconds)
|
||||
{
|
||||
// Create a copy with the new start date and duration
|
||||
|
||||
return new CalendarItem
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Title = Title,
|
||||
Description = Description,
|
||||
Location = Location,
|
||||
StartDate = startDate,
|
||||
DurationInSeconds = durationInSeconds,
|
||||
Recurrence = Recurrence,
|
||||
OrganizerDisplayName = OrganizerDisplayName,
|
||||
OrganizerEmail = OrganizerEmail,
|
||||
RecurringCalendarItemId = Id,
|
||||
AssignedCalendar = AssignedCalendar,
|
||||
CalendarId = CalendarId,
|
||||
CreatedAt = CreatedAt,
|
||||
UpdatedAt = UpdatedAt,
|
||||
Visibility = Visibility,
|
||||
Status = Status,
|
||||
CustomEventColorHex = CustomEventColorHex,
|
||||
HtmlLink = HtmlLink,
|
||||
StartDateOffset = StartDateOffset,
|
||||
EndDateOffset = EndDateOffset,
|
||||
RemoteEventId = RemoteEventId,
|
||||
IsHidden = IsHidden,
|
||||
IsLocked = IsLocked,
|
||||
IsOccurrence = true
|
||||
};
|
||||
ItemType = CalendarItemTypeHelper.DetermineItemType(
|
||||
StartDateTime,
|
||||
EndDateTime,
|
||||
IsAllDay,
|
||||
hasRecurrence,
|
||||
isCancelled,
|
||||
Status);
|
||||
}
|
||||
}
|
||||
|
||||
14
Wino.Core.Domain/Entities/Shared/Thumbnail.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using SQLite;
|
||||
|
||||
namespace Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
public class Thumbnail
|
||||
{
|
||||
[PrimaryKey]
|
||||
public string Domain { get; set; }
|
||||
|
||||
public string Gravatar { get; set; }
|
||||
public string Favicon { get; set; }
|
||||
public DateTime LastUpdated { get; set; }
|
||||
}
|
||||
32
Wino.Core.Domain/Enums/AttendeeResponseStatus.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wino.Core.Domain.Enums;
|
||||
/// <summary>
|
||||
/// Represents the response status of an attendee to a calendar event
|
||||
/// </summary>
|
||||
public enum AttendeeResponseStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The attendee has not responded to the invitation
|
||||
/// </summary>
|
||||
NeedsAction = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The attendee has accepted the invitation
|
||||
/// </summary>
|
||||
Accepted = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The attendee has declined the invitation
|
||||
/// </summary>
|
||||
Declined = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The attendee has tentatively accepted the invitation
|
||||
/// </summary>
|
||||
Tentative = 3
|
||||
}
|
||||
49
Wino.Core.Domain/Enums/CalendarItemType.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wino.Core.Domain.Enums;
|
||||
public enum CalendarItemType
|
||||
{
|
||||
/// <summary>
|
||||
/// A standard timed event with specific start and end times on the same day
|
||||
/// </summary>
|
||||
Timed = 0,
|
||||
|
||||
/// <summary>
|
||||
/// An all-day event that spans exactly one day
|
||||
/// </summary>
|
||||
AllDay = 1,
|
||||
|
||||
/// <summary>
|
||||
/// A multi-day event that spans more than one day but has specific times
|
||||
/// </summary>
|
||||
MultiDay = 2,
|
||||
|
||||
/// <summary>
|
||||
/// A multi-day all-day event (e.g., vacation, conference spanning multiple days)
|
||||
/// </summary>
|
||||
MultiDayAllDay = 3,
|
||||
|
||||
/// <summary>
|
||||
/// A recurring event with a defined pattern (daily, weekly, monthly, yearly)
|
||||
/// </summary>
|
||||
Recurring = 4,
|
||||
|
||||
/// <summary>
|
||||
/// A recurring all-day event (e.g., annual holiday, weekly all-day event)
|
||||
/// </summary>
|
||||
RecurringAllDay = 5,
|
||||
|
||||
/// <summary>
|
||||
/// A single instance of a recurring event that has been modified
|
||||
/// </summary>
|
||||
RecurringException = 6,
|
||||
|
||||
/// <summary>
|
||||
/// An event that extends beyond midnight but is not multi-day (e.g., 11 PM to 2 AM)
|
||||
/// </summary>
|
||||
CrossMidnight = 7,
|
||||
}
|
||||
@@ -25,7 +25,7 @@ public enum WinoPage
|
||||
AppPreferencesPage,
|
||||
SettingOptionsPage,
|
||||
AliasManagementPage,
|
||||
|
||||
EditAccountDetailsPage,
|
||||
// Calendar
|
||||
CalendarPage,
|
||||
CalendarSettingsPage,
|
||||
|
||||
@@ -20,7 +20,7 @@ public class ImapClientPoolException : Exception
|
||||
ProtocolLog = protocolLog;
|
||||
}
|
||||
|
||||
public ImapClientPoolException(Exception innerException, string protocolLog) : base(Translator.Exception_ImapClientPoolFailed, innerException)
|
||||
public ImapClientPoolException(Exception innerException, string protocolLog) : base(innerException.Message, innerException)
|
||||
{
|
||||
ProtocolLog = protocolLog;
|
||||
}
|
||||
|
||||
143
Wino.Core.Domain/Helpers/CalendarItemTypeHelper.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Helpers;
|
||||
/// <summary>
|
||||
/// Helper class for CalendarItemType operations
|
||||
/// </summary>
|
||||
public static class CalendarItemTypeHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines the calendar item type based on event properties
|
||||
/// </summary>
|
||||
/// <param name="startDateTime">Event start date/time</param>
|
||||
/// <param name="endDateTime">Event end date/time</param>
|
||||
/// <param name="isAllDay">Whether the event is marked as all-day</param>
|
||||
/// <param name="isRecurring">Whether the event has recurrence rules</param>
|
||||
/// <param name="isCancelled">Whether the event is cancelled</param>
|
||||
/// <param name="status">Event status</param>
|
||||
/// <returns>The appropriate CalendarItemType</returns>
|
||||
public static CalendarItemType DetermineItemType(
|
||||
DateTime startDateTime,
|
||||
DateTime endDateTime,
|
||||
bool isAllDay,
|
||||
bool isRecurring = false,
|
||||
bool isCancelled = false,
|
||||
string? status = null)
|
||||
{
|
||||
// Handle recurring events
|
||||
if (isRecurring)
|
||||
{
|
||||
return isAllDay ? CalendarItemType.RecurringAllDay : CalendarItemType.Recurring;
|
||||
}
|
||||
|
||||
// Handle all-day events
|
||||
if (isAllDay)
|
||||
{
|
||||
var daySpan = (endDateTime.Date - startDateTime.Date).Days;
|
||||
return daySpan > 1 ? CalendarItemType.MultiDayAllDay : CalendarItemType.AllDay;
|
||||
}
|
||||
|
||||
// Handle timed events
|
||||
var duration = endDateTime - startDateTime;
|
||||
|
||||
|
||||
|
||||
// Multi-day timed events
|
||||
if (duration.TotalDays >= 1)
|
||||
{
|
||||
return CalendarItemType.MultiDay;
|
||||
}
|
||||
|
||||
// Cross midnight events (same calendar day but extends past midnight)
|
||||
if (startDateTime.Date != endDateTime.Date && duration.TotalHours <= 24)
|
||||
{
|
||||
return CalendarItemType.CrossMidnight;
|
||||
}
|
||||
|
||||
// Standard timed events
|
||||
return CalendarItemType.Timed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a human-readable description of the calendar item type
|
||||
/// </summary>
|
||||
/// <param name="itemType">The calendar item type</param>
|
||||
/// <returns>Description string</returns>
|
||||
public static string GetDescription(CalendarItemType itemType)
|
||||
{
|
||||
return itemType switch
|
||||
{
|
||||
CalendarItemType.Timed => "Timed Event",
|
||||
CalendarItemType.AllDay => "All-Day Event",
|
||||
CalendarItemType.MultiDay => "Multi-Day Event",
|
||||
CalendarItemType.MultiDayAllDay => "Multi-Day All-Day Event",
|
||||
CalendarItemType.Recurring => "Recurring Event",
|
||||
CalendarItemType.RecurringAllDay => "Recurring All-Day Event",
|
||||
CalendarItemType.RecurringException => "Modified Recurring Event",
|
||||
CalendarItemType.CrossMidnight => "Cross-Midnight Event",
|
||||
_ => "Unknown Event Type"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the event type represents an all-day event
|
||||
/// </summary>
|
||||
/// <param name="itemType">The calendar item type</param>
|
||||
/// <returns>True if it's an all-day event type</returns>
|
||||
public static bool IsAllDayType(CalendarItemType itemType)
|
||||
{
|
||||
return itemType == CalendarItemType.AllDay ||
|
||||
itemType == CalendarItemType.MultiDayAllDay ||
|
||||
itemType == CalendarItemType.RecurringAllDay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the event type represents a recurring event
|
||||
/// </summary>
|
||||
/// <param name="itemType">The calendar item type</param>
|
||||
/// <returns>True if it's a recurring event type</returns>
|
||||
public static bool IsRecurringType(CalendarItemType itemType)
|
||||
{
|
||||
return itemType == CalendarItemType.Recurring ||
|
||||
itemType == CalendarItemType.RecurringAllDay ||
|
||||
itemType == CalendarItemType.RecurringException;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the event type represents a multi-day event
|
||||
/// </summary>
|
||||
/// <param name="itemType">The calendar item type</param>
|
||||
/// <returns>True if it's a multi-day event type</returns>
|
||||
public static bool IsMultiDayType(CalendarItemType itemType)
|
||||
{
|
||||
return itemType == CalendarItemType.MultiDay ||
|
||||
itemType == CalendarItemType.MultiDayAllDay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the priority level for sorting events (lower number = higher priority)
|
||||
/// </summary>
|
||||
/// <param name="itemType">The calendar item type</param>
|
||||
/// <returns>Priority number for sorting</returns>
|
||||
public static int GetSortPriority(CalendarItemType itemType)
|
||||
{
|
||||
return itemType switch
|
||||
{
|
||||
|
||||
CalendarItemType.AllDay => 2,
|
||||
CalendarItemType.MultiDayAllDay => 3,
|
||||
CalendarItemType.Timed => 4,
|
||||
CalendarItemType.CrossMidnight => 5,
|
||||
CalendarItemType.MultiDay => 6,
|
||||
CalendarItemType.Recurring => 7,
|
||||
CalendarItemType.RecurringAllDay => 8,
|
||||
CalendarItemType.RecurringException => 9,
|
||||
_ => 99
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -62,15 +62,6 @@ public interface IAccountService
|
||||
/// <param name="accountId">Account id to remove from</param>
|
||||
Task ClearAccountAttentionAsync(Guid accountId);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the account synchronization identifier.
|
||||
/// For example: Gmail uses this identifier to keep track of the last synchronization.
|
||||
/// Update is ignored for Gmail if the new identifier is older than the current one.
|
||||
/// </summary>
|
||||
/// <param name="newIdentifier">Identifier to update</param>
|
||||
/// <returns>Current account synchronization modifier.</returns>
|
||||
Task<string> UpdateSynchronizationIdentifierAsync(Guid accountId, string newIdentifier);
|
||||
|
||||
/// <summary>
|
||||
/// Renames the merged inbox with the given id.
|
||||
/// </summary>
|
||||
@@ -164,4 +155,19 @@ public interface IAccountService
|
||||
/// <param name="accountId">Account id.</param>
|
||||
/// <param name="AccountCacheResetReason">Reason for the cache reset.</param>
|
||||
Task DeleteAccountMailCacheAsync(Guid accountId, AccountCacheResetReason accountCacheResetReason);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the synchronization identifier for a specific account asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="accountId">Identifies the account for which the synchronization identifier is being updated.</param>
|
||||
/// <param name="syncIdentifier">Represents the new synchronization identifier to be set for the specified account.</param>
|
||||
Task<string> UpdateSyncIdentifierRawAsync(Guid accountId, string syncIdentifier);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the notifications are enabled for the given account id.
|
||||
/// </summary>
|
||||
/// <param name="accountId">Account id.</param>
|
||||
/// <returns>Whether the notifications should be created after sync or not.</returns>
|
||||
Task<bool> IsNotificationsEnabled(Guid accountId);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Itenso.TimePeriod;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Interfaces;
|
||||
|
||||
@@ -8,15 +9,8 @@ public interface ICalendarItem
|
||||
string Title { get; }
|
||||
Guid Id { get; }
|
||||
IAccountCalendar AssignedCalendar { get; }
|
||||
DateTime StartDate { get; set; }
|
||||
DateTime EndDate { get; }
|
||||
double DurationInSeconds { get; set; }
|
||||
DateTime StartDateTime { get; set; }
|
||||
DateTime EndDateTime { get; }
|
||||
ITimePeriod Period { get; }
|
||||
|
||||
bool IsAllDayEvent { get; }
|
||||
bool IsMultiDayEvent { get; }
|
||||
|
||||
bool IsRecurringChild { get; }
|
||||
bool IsRecurringParent { get; }
|
||||
bool IsRecurringEvent { get; }
|
||||
CalendarItemType ItemType { get; }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
namespace Wino.Core.Domain.Interfaces;
|
||||
using System;
|
||||
|
||||
namespace Wino.Core.Domain.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily to enforce CalendarItemViewModel. Used in CalendarEventCollection.
|
||||
/// </summary>
|
||||
public interface ICalendarItemViewModel
|
||||
public interface ICalendarItemViewModel : ICalendarItem
|
||||
{
|
||||
bool IsSelected { get; set; }
|
||||
DateTime LocalStartDateTime { get; }
|
||||
DateTime LocalEndDateTime { get; }
|
||||
}
|
||||
|
||||
55
Wino.Core.Domain/Interfaces/ICalendarServiceEx.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Interfaces;
|
||||
public interface ICalendarServiceEx
|
||||
{
|
||||
Task<int> ClearAllCalendarEventAttendeesAsync();
|
||||
Task<int> ClearAllCalendarsAsync();
|
||||
Task<int> ClearAllDataAsync();
|
||||
Task<int> ClearAllEventsAsync();
|
||||
Task<int> DeleteCalendarAsync(string remoteCalendarId);
|
||||
Task<int> DeleteCalendarEventAttendeesForEventAsync(Guid eventId);
|
||||
Task<int> DeleteEventAsync(string remoteEventId);
|
||||
Task<List<CalendarEventAttendee>> GetAllCalendarEventAttendeesAsync();
|
||||
Task<List<AccountCalendar>> GetAllCalendarsAsync();
|
||||
Task<List<CalendarItem>> GetAllDayEventsAsync();
|
||||
Task<List<CalendarItem>> GetAllEventsAsync();
|
||||
Task<List<CalendarItem>> GetAllEventsIncludingDeletedAsync();
|
||||
Task<List<CalendarItem>> GetAllRecurringEventsByTypeAsync();
|
||||
Task<AccountCalendar> GetCalendarByRemoteIdAsync(string remoteCalendarId);
|
||||
Task<Dictionary<AttendeeResponseStatus, int>> GetCalendarEventAttendeeResponseCountsAsync(Guid eventId);
|
||||
Task<List<CalendarEventAttendee>> GetCalendarEventAttendeesForEventAsync(Guid eventId);
|
||||
Task<List<CalendarEventAttendee>> GetCalendarEventAttendeesForEventByRemoteIdAsync(string remoteEventId);
|
||||
Task<string> GetCalendarSyncTokenAsync(string calendarId);
|
||||
Task<CalendarItem> GetEventByRemoteIdAsync(string remoteEventId);
|
||||
Task<List<CalendarItem>> GetEventsByItemTypeAsync(CalendarItemType itemType);
|
||||
Task<List<CalendarItem>> GetEventsByItemTypesAsync(params CalendarItemType[] itemTypes);
|
||||
Task<List<CalendarItem>> GetEventsByremoteCalendarIdAsync(string remoteCalendarId);
|
||||
Task<List<CalendarItem>> GetEventsForCalendarAsync(Guid calendarId);
|
||||
Task<List<CalendarItem>> GetEventsInDateRangeAsync(DateTime startDate, DateTime endDate);
|
||||
Task<List<CalendarItem>> GetEventsSinceLastSyncAsync(DateTime? lastSyncTime);
|
||||
Task<Dictionary<CalendarItemType, int>> GetEventStatsByItemTypeAsync();
|
||||
Task<List<CalendarItem>> GetExpandedEventsInDateRangeAsync(DateTime startDate, DateTime endDate);
|
||||
Task<List<CalendarItem>> GetExpandedEventsInDateRangeWithExceptionsAsync(DateTime startDate, DateTime endDate, AccountCalendar calendar);
|
||||
Task<DateTime?> GetLastSyncTimeAsync(string calendarId);
|
||||
Task<List<CalendarItem>> GetMultiDayEventsAsync();
|
||||
Task<List<CalendarItem>> GetRecurringEventsAsync();
|
||||
Task<int> HardDeleteEventAsync(string remoteEventId);
|
||||
Task<int> InsertCalendarAsync(AccountCalendar calendar);
|
||||
Task<int> InsertCalendarEventAttendeeAsync(CalendarEventAttendee calendareventattendee);
|
||||
Task<int> InsertEventAsync(CalendarItem calendarItem);
|
||||
Task<int> MarkEventAsDeletedAsync(string remoteEventId, string remoteCalendarId);
|
||||
Task<int> SyncAttendeesForEventAsync(Guid eventId, List<CalendarEventAttendee> attendees);
|
||||
Task<int> SyncCalendarEventAttendeesForEventAsync(Guid eventId, List<CalendarEventAttendee> calendareventattendees);
|
||||
Task<int> UpdateAllEventItemTypesAsync();
|
||||
Task<int> UpdateCalendarAsync(AccountCalendar calendar);
|
||||
Task<int> UpdateCalendarEventAttendeeAsync(CalendarEventAttendee calendareventattendee);
|
||||
Task<int> UpdateCalendarSyncTokenAsync(string calendarId, string syncToken);
|
||||
Task<int> UpdateEventAsync(CalendarItem calendarItem);
|
||||
Task<int> UpsertCalendarAsync(AccountCalendar calendar);
|
||||
Task<int> UpsertEventAsync(CalendarItem calendarItem);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Accounts;
|
||||
using Wino.Core.Domain.Models.Common;
|
||||
@@ -17,7 +16,6 @@ public interface IDialogServiceBase
|
||||
void InfoBarMessage(string title, string message, InfoBarMessageType messageType);
|
||||
void InfoBarMessage(string title, string message, InfoBarMessageType messageType, string actionButtonText, Action action);
|
||||
void ShowNotSupportedMessage();
|
||||
Task<MailAccount> ShowEditAccountDialogAsync(MailAccount account);
|
||||
Task<string> ShowTextInputDialogAsync(string currentInput, string dialogTitle, string dialogDescription, string primaryButtonText);
|
||||
Task<bool> ShowWinoCustomMessageDialogAsync(string title,
|
||||
string description,
|
||||
|
||||
@@ -13,4 +13,5 @@ public interface IImapSynchronizer
|
||||
Task<List<NewMailItemPackage>> CreateNewMailPackagesAsync(ImapMessageCreationPackage message, MailItemFolder assignedFolder, CancellationToken cancellationToken = default);
|
||||
Task StartIdleClientAsync();
|
||||
Task StopIdleClientAsync();
|
||||
Task PreWarmClientPoolAsync();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
using Wino.Core.Domain.Models.Reader;
|
||||
|
||||
namespace Wino.Core.Domain.Interfaces;
|
||||
|
||||
public interface IPreferencesService
|
||||
public interface IPreferencesService: INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// When any of the preferences are changed.
|
||||
@@ -188,6 +189,21 @@ public interface IPreferencesService
|
||||
/// </summary>
|
||||
bool IsMailListActionBarEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setting: Whether the mail rendering page will show the action labels
|
||||
/// </summary>
|
||||
bool IsShowActionLabelsEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setting: Enable/disable Gravatar for sender avatars.
|
||||
/// </summary>
|
||||
bool IsGravatarEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setting: Enable/disable Favicon for sender avatars.
|
||||
/// </summary>
|
||||
bool IsFaviconEnabled { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Calendar
|
||||
|
||||
@@ -14,7 +14,7 @@ public interface IThemeService : IInitializeAsync
|
||||
Task<List<AppThemeBase>> GetAvailableThemesAsync();
|
||||
Task<CustomThemeMetadata> CreateNewCustomThemeAsync(string themeName, string accentColor, byte[] wallpaperData);
|
||||
Task<List<CustomThemeMetadata>> GetCurrentCustomThemesAsync();
|
||||
|
||||
List<string> GetAvailableAccountColors();
|
||||
Task ApplyCustomThemeAsync(bool isInitializing);
|
||||
|
||||
// Settings
|
||||
|
||||
20
Wino.Core.Domain/Interfaces/IThumbnailService.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wino.Core.Domain.Interfaces;
|
||||
|
||||
public interface IThumbnailService
|
||||
{
|
||||
/// <summary>
|
||||
/// Clears the thumbnail cache.
|
||||
/// </summary>
|
||||
Task ClearCache();
|
||||
|
||||
/// <summary>
|
||||
/// Gets thumbnail
|
||||
/// </summary>
|
||||
/// <param name="email">Address for thumbnail</param>
|
||||
/// <param name="awaitLoad">Force to wait for thumbnail loading.
|
||||
/// Should be used in non-UI threads or where delay is acceptable
|
||||
/// </param>
|
||||
ValueTask<string> GetThumbnailAsync(string email, bool awaitLoad = false);
|
||||
}
|
||||
@@ -56,6 +56,12 @@ public partial class AccountMenuItem : MenuItemBase<MailAccount, MenuItemBase<IM
|
||||
set => SetProperty(Parameter.Base64ProfilePictureData, value, Parameter, (u, n) => u.Base64ProfilePictureData = n);
|
||||
}
|
||||
|
||||
public string AccountColorHex
|
||||
{
|
||||
get => Parameter.AccountColorHex;
|
||||
set => SetProperty(Parameter.AccountColorHex, value, Parameter, (u, n) => u.AccountColorHex = n);
|
||||
}
|
||||
|
||||
public IEnumerable<MailAccount> HoldingAccounts => new List<MailAccount> { Parameter };
|
||||
|
||||
public AccountMenuItem(MailAccount account, IMenuItem parent = null) : base(account, account.Id, parent)
|
||||
@@ -66,9 +72,11 @@ public partial class AccountMenuItem : MenuItemBase<MailAccount, MenuItemBase<IM
|
||||
public void UpdateAccount(MailAccount account)
|
||||
{
|
||||
Parameter = account;
|
||||
AccountName = account.Name;
|
||||
AttentionReason = account.AttentionReason;
|
||||
Base64ProfilePicture = account.Base64ProfilePictureData;
|
||||
|
||||
OnPropertyChanged(nameof(AccountName));
|
||||
OnPropertyChanged(nameof(Base64ProfilePicture));
|
||||
OnPropertyChanged(nameof(AccountColorHex));
|
||||
OnPropertyChanged(nameof(IsAttentionRequired));
|
||||
|
||||
if (SubMenuItems == null) return;
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
namespace Wino.Core.Domain.Models.Accounts;
|
||||
|
||||
public record AccountCreationDialogResult(MailProviderType ProviderType, string AccountName, SpecialImapProviderDetails SpecialImapProviderDetails);
|
||||
public record AccountCreationDialogResult(MailProviderType ProviderType, string AccountName, SpecialImapProviderDetails SpecialImapProviderDetails, string AccountColorHex);
|
||||
|
||||
@@ -10,4 +10,4 @@ namespace Wino.Core.Domain.Models;
|
||||
[JsonSerializable(typeof(CustomThemeMetadata))]
|
||||
[JsonSerializable(typeof(WebViewMessage))]
|
||||
[JsonSerializable(typeof(List<ImageInfo>))]
|
||||
public partial class DomainModelsJsonContext: JsonSerializerContext;
|
||||
public partial class DomainModelsJsonContext : JsonSerializerContext;
|
||||
|
||||
695
Wino.Core.Domain/Translations/bg_BG/resources.json
Normal file
@@ -0,0 +1,695 @@
|
||||
{
|
||||
"AccountAlias_Column_Alias": "Псевдоним",
|
||||
"AccountAlias_Column_IsPrimaryAlias": "Основен",
|
||||
"AccountAlias_Column_Verified": "Потвърден",
|
||||
"AccountAlias_Disclaimer_FirstLine": "Wino може да импортира псевдоними само за вашите акаунти в Gmail.",
|
||||
"AccountAlias_Disclaimer_SecondLine": "Ако искате да използвате псевдоними за акаунта си в Outlook или IMAP, добавете ги сами.",
|
||||
"AccountCacheReset_Title": "Нулиране на кеша на акаунта",
|
||||
"AccountCacheReset_Message": "Този акаунт изисква пълна ресинхронизация, за да продължи да работи. Моля, изчакайте, докато Wino ресинхронизира съобщенията ви...",
|
||||
"AccountContactNameYou": "Вие",
|
||||
"AccountCreationDialog_Completed": "всичко е готово",
|
||||
"AccountCreationDialog_FetchingEvents": "Извличане на събитията от календара.",
|
||||
"AccountCreationDialog_FetchingProfileInformation": "Извличане на данните за профила.",
|
||||
"AccountCreationDialog_GoogleAuthHelpClipboardText_Row0": "Ако браузърът ви не се е стартирал автоматично, за да завърши удостоверяването:",
|
||||
"AccountCreationDialog_GoogleAuthHelpClipboardText_Row1": "1) Щракнете върху бутона по-долу, за да копирате адреса за удостоверяване",
|
||||
"AccountCreationDialog_GoogleAuthHelpClipboardText_Row2": "2) Стартирайте уеб браузъра си (Edge, Chrome, Firefox и др.)",
|
||||
"AccountCreationDialog_GoogleAuthHelpClipboardText_Row3": "3) Поставете копирания адрес и отидете на уебсайта, за да завършите удостоверяването ръчно.",
|
||||
"AccountCreationDialog_Initializing": "инициализиране",
|
||||
"AccountCreationDialog_PreparingFolders": "В момента получаваме информация за папките.",
|
||||
"AccountCreationDialog_SigninIn": "Информацията за акаунта се запазва.",
|
||||
"AccountEditDialog_Message": "Име на акаунта",
|
||||
"AccountEditDialog_Title": "Редактиране на акаунта",
|
||||
"AccountPickerDialog_Title": "Изберете акаунт",
|
||||
"AccountSettingsDialog_AccountName": "Име на подателя",
|
||||
"AccountSettingsDialog_AccountNamePlaceholder": "напр. Иван Иванов",
|
||||
"AccountDetailsPage_Title": "Информация за акаунта",
|
||||
"AccountDetailsPage_Description": "Променете името на акаунта в Wino и задайте желаното име на изпращача.",
|
||||
"AccountDetailsPage_ColorPicker_Title": "Цвят на акаунта",
|
||||
"AccountDetailsPage_ColorPicker_Description": "Задайте цвят на акаунта, за да оцветите символа му в списъка.",
|
||||
"AddHyperlink": "Добавяне",
|
||||
"AppCloseBackgroundSynchronizationWarningTitle": "Синхронизация на заден план",
|
||||
"AppCloseStartupLaunchDisabledWarningMessageFirstLine": "Приложението не е настроено да се стартира при стартиране на Windows.",
|
||||
"AppCloseStartupLaunchDisabledWarningMessageSecondLine": "Това ще доведе до пропускане на известия при рестартиране на компютъра.",
|
||||
"AppCloseStartupLaunchDisabledWarningMessageThirdLine": "Искате ли да отидете на страницата с настройки на приложението, за да я активирате?",
|
||||
"AppCloseTerminateBehaviorWarningMessageFirstLine": "Прекратявате Wino Mail и поведението му при затваряне на приложението е настроено на „Прекратяване“.",
|
||||
"AppCloseTerminateBehaviorWarningMessageSecondLine": "Това ще спре всички фонови синхронизации и известия.",
|
||||
"AppCloseTerminateBehaviorWarningMessageThirdLine": "Искате ли да отидете в Предпочитания за приложението, за да настроите Wino Mail да работи минимизиран или във фонов режим?",
|
||||
"AutoDiscoveryProgressMessage": "Търсене на настройки на пощата...",
|
||||
"BasicIMAPSetupDialog_AdvancedConfiguration": "Разширено конфигуриране",
|
||||
"BasicIMAPSetupDialog_CredentialLocalMessage": "Вашите идентификационни данни се съхраняват само локално на вашия компютър.",
|
||||
"BasicIMAPSetupDialog_Description": "Някои акаунти изискват допълнителни стъпки за влизане",
|
||||
"BasicIMAPSetupDialog_DisplayName": "Показвано име",
|
||||
"BasicIMAPSetupDialog_DisplayNamePlaceholder": "напр. Иван Иванов",
|
||||
"BasicIMAPSetupDialog_LearnMore": "Научете повече",
|
||||
"BasicIMAPSetupDialog_MailAddress": "Адрес на имейл",
|
||||
"BasicIMAPSetupDialog_MailAddressPlaceholder": "ivanivanov@mail.com",
|
||||
"BasicIMAPSetupDialog_Password": "Парола",
|
||||
"BasicIMAPSetupDialog_Title": "IMAP акаунт",
|
||||
"Busy": "Зает",
|
||||
"Buttons_AddAccount": "Добавяне на акаунт",
|
||||
"Buttons_AddNewAlias": "Добавяне на нов псевдоним",
|
||||
"Buttons_Allow": "Позволяване",
|
||||
"Buttons_ApplyTheme": "Прилагане на темата",
|
||||
"Buttons_Browse": "Преглед",
|
||||
"Buttons_Cancel": "Отказ",
|
||||
"Buttons_Close": "Затваряне",
|
||||
"Buttons_Copy": "Копиране",
|
||||
"Buttons_Create": "Създаване",
|
||||
"Buttons_CreateAccount": "Създаване на акаунт",
|
||||
"Buttons_Delete": "Изтриване",
|
||||
"Buttons_Deny": "Отказване",
|
||||
"Buttons_Discard": "Отхвърляне",
|
||||
"Buttons_Edit": "Редактиране",
|
||||
"Buttons_EnableImageRendering": "Активиране",
|
||||
"Buttons_Multiselect": "Избор на няколко",
|
||||
"Buttons_No": "Не",
|
||||
"Buttons_Open": "Отваряне",
|
||||
"Buttons_Purchase": "Купете",
|
||||
"Buttons_RateWino": "Оценете Wino",
|
||||
"Buttons_Reset": "Нулиране",
|
||||
"Buttons_Save": "Запазване",
|
||||
"Buttons_SaveConfiguration": "Запазване на конфигурацията",
|
||||
"Buttons_Send": "Изпращане",
|
||||
"Buttons_Share": "Споделяне",
|
||||
"Buttons_SignIn": "Вход",
|
||||
"Buttons_Sync": "Синхронизиране",
|
||||
"Buttons_SyncAliases": "Синхронизиране на псевдонимите",
|
||||
"Buttons_TryAgain": "Нов опит",
|
||||
"Buttons_Yes": "Да",
|
||||
"CalendarAllDayEventSummary": "целодневни събития",
|
||||
"CalendarDisplayOptions_Color": "Цвят",
|
||||
"CalendarDisplayOptions_Expand": "Разширяване",
|
||||
"CalendarItem_DetailsPopup_JoinOnline": "Присъединяване онлайн",
|
||||
"CalendarItem_DetailsPopup_ViewEventButton": "Преглед на събитието",
|
||||
"CalendarItem_DetailsPopup_ViewSeriesButton": "Преглед на сериите",
|
||||
"CalendarItemAllDay": "цял ден",
|
||||
"CategoriesFolderNameOverride": "Категории",
|
||||
"Center": "Център",
|
||||
"ClipboardTextCopied_Message": "{0} е копирано в клипборда.",
|
||||
"ClipboardTextCopied_Title": "Копирано",
|
||||
"ClipboardTextCopyFailed_Message": "Неуспешно копиране на {0} в клипборда.",
|
||||
"ComingSoon": "Очаквайте скоро...",
|
||||
"ComposerAttachmentsDragDropAttach_Message": "Прикачване",
|
||||
"ComposerAttachmentsDropZone_Message": "Пуснете файловете си тук",
|
||||
"ComposerFrom": "От: ",
|
||||
"ComposerImagesDropZone_Message": "Пуснете изображенията си тук",
|
||||
"ComposerSubject": "Тема: ",
|
||||
"ComposerTo": "До: ",
|
||||
"ComposerToPlaceholder": "натиснете Enter, за да въведете адреси",
|
||||
"CreateAccountAliasDialog_AliasAddress": "Адрес",
|
||||
"CreateAccountAliasDialog_AliasAddressPlaceholder": "напр. support@mydomain.com",
|
||||
"CreateAccountAliasDialog_Description": "Уверете се, че вашият изходящ сървър позволява изпращането на имейли от този псевдоним.",
|
||||
"CreateAccountAliasDialog_ReplyToAddress": "Адрес за отговор",
|
||||
"CreateAccountAliasDialog_ReplyToAddressPlaceholder": "admin@mydomain.com",
|
||||
"CreateAccountAliasDialog_Title": "Създаване на псевдоним на акаунта",
|
||||
"CustomThemeBuilder_AccentColorDescription": "Задайте персонализиран цвят на акцента, ако желаете. Ако не изберете цвят, ще се използва акцентиращият цвят на Windows.",
|
||||
"CustomThemeBuilder_AccentColorTitle": "Акцентиращ цвят",
|
||||
"CustomThemeBuilder_PickColor": "Избор",
|
||||
"CustomThemeBuilder_ThemeNameDescription": "Уникално име за вашата персонализирана тема.",
|
||||
"CustomThemeBuilder_ThemeNameTitle": "Име на темата",
|
||||
"CustomThemeBuilder_Title": "Изграждане на персонализирана тема",
|
||||
"CustomThemeBuilder_WallpaperDescription": "Задаване на персонализиран тапет за Wino",
|
||||
"CustomThemeBuilder_WallpaperTitle": "Задаване на персонализиран тапет",
|
||||
"Dialog_DontAskAgain": "Не питайте отново",
|
||||
"DialogMessage_AccountLimitMessage": "Достигнали сте лимита за създаване на акаунти.\nИскате ли да закупите добавката „Неограничен акаунт“, за да продължите?",
|
||||
"DialogMessage_AccountLimitTitle": "Достигнат е лимита на акаунтите",
|
||||
"DialogMessage_AliasCreatedMessage": "Новият псевдоним е създаден успешно.",
|
||||
"DialogMessage_AliasCreatedTitle": "Създаден е нов псевдоним",
|
||||
"DialogMessage_AliasExistsMessage": "Този псевдоним вече се използва.",
|
||||
"DialogMessage_AliasExistsTitle": "Съществуващ псевдоним",
|
||||
"DialogMessage_AliasNotSelectedMessage": "Трябва да изберете псевдоним, преди да изпратите съобщение.",
|
||||
"DialogMessage_AliasNotSelectedTitle": "Липсващ псевдоним",
|
||||
"DialogMessage_CantDeleteRootAliasMessage": "Основният псевдоним не може да бъде изтрит. Това е основната ви самоличност, свързана с настройката на профила ви.",
|
||||
"DialogMessage_CantDeleteRootAliasTitle": "Не може да се изтрие псевдонимът",
|
||||
"DialogMessage_CleanupFolderMessage": "Искате ли да изтриете окончателно всички писма в тази папка?",
|
||||
"DialogMessage_CleanupFolderTitle": "Почистване на папката",
|
||||
"DialogMessage_ComposerMissingRecipientMessage": "Съобщението няма получател.",
|
||||
"DialogMessage_ComposerValidationFailedTitle": "Валидирането е неуспешно",
|
||||
"DialogMessage_CreateLinkedAccountMessage": "Дайте име на тази нова връзка. Акаунтите ще бъдат обединени под това име.",
|
||||
"DialogMessage_CreateLinkedAccountTitle": "Име на връзката с акаунта",
|
||||
"DialogMessage_DeleteAccountConfirmationMessage": "Изтриване на {0}?",
|
||||
"DialogMessage_DeleteAccountConfirmationTitle": "Всички данни, свързани с този акаунт, ще бъдат изтрити от диска за постоянно.",
|
||||
"DialogMessage_DiscardDraftConfirmationMessage": "Тази чернова ще бъде отхвърлена. Искате ли да продължите?",
|
||||
"DialogMessage_DiscardDraftConfirmationTitle": "Отхвърляне на черновата",
|
||||
"DialogMessage_EmptySubjectConfirmation": "Липсваща тема",
|
||||
"DialogMessage_EmptySubjectConfirmationMessage": "Съобщението няма тема. Искате ли да продължите?",
|
||||
"DialogMessage_EnableStartupLaunchDeniedMessage": "Можете да активирате автоматично стартиране от Настройки -> Предпочитания за приложението.",
|
||||
"DialogMessage_EnableStartupLaunchMessage": "Позволете на Wino Mail да се стартира автоматично минимизиран при стартиране на Windows, за да не пропускате никакви известия.\n\nИскате ли да разрешите автоматичното стартиране?",
|
||||
"DialogMessage_EnableStartupLaunchTitle": "Активиране на автоматичното стартиране",
|
||||
"DialogMessage_HardDeleteConfirmationMessage": "Окончателно изтриване",
|
||||
"DialogMessage_HardDeleteConfirmationTitle": "Съобщението/ята ще бъдат изтрити за постоянно. Искате ли да продължите?",
|
||||
"DialogMessage_InvalidAliasMessage": "Този псевдоним не е валиден. Уверете се, че всички адреси от псевдонима са валидни имейл адреси.",
|
||||
"DialogMessage_InvalidAliasTitle": "Невалиден псевдоним",
|
||||
"DialogMessage_NoAccountsForCreateMailMessage": "Нямате акаунти, от които да създадете съобщение.",
|
||||
"DialogMessage_NoAccountsForCreateMailTitle": "Липсва акаунт",
|
||||
"DialogMessage_PrintingFailedMessage": "Неуспешно отпечатване на този имейл. Резултат: {0}",
|
||||
"DialogMessage_PrintingFailedTitle": "Неуспешно",
|
||||
"DialogMessage_PrintingSuccessMessage": "Имейлът се изпраща към принтера.",
|
||||
"DialogMessage_PrintingSuccessTitle": "Успешно",
|
||||
"DialogMessage_RenameFolderMessage": "Въведете ново име за тази папка",
|
||||
"DialogMessage_RenameFolderTitle": "Преименуване на папката",
|
||||
"DialogMessage_RenameLinkedAccountsMessage": "Въведете ново име за свързания акаунт",
|
||||
"DialogMessage_RenameLinkedAccountsTitle": "Преименуване на свързания акаунт",
|
||||
"DialogMessage_UnlinkAccountsConfirmationMessage": "Тази операция няма да доведе до изтриване на акаунтите ви, а само до прекъсване на връзката към споделените папки. Искате ли да продължите?",
|
||||
"DialogMessage_UnlinkAccountsConfirmationTitle": "Премахване на връзката между акаунтите",
|
||||
"DialogMessage_UnsubscribeConfirmationGoToWebsiteConfirmButton": "Към уебсайта",
|
||||
"DialogMessage_UnsubscribeConfirmationGoToWebsiteMessage": "За да спрете да получавате съобщения от {0}, отидете на техния уебсайт, за да се отпишете.",
|
||||
"DialogMessage_UnsubscribeConfirmationMailtoMessage": "Искате ли да спрете да получавате съобщения от {0}? Wino ще се откаже от абонамента вместо вас, като изпрати имейл от вашия имейл акаунт до {1}.",
|
||||
"DialogMessage_UnsubscribeConfirmationOneClickMessage": "Искате ли да спрете да получавате съобщения от {0}?",
|
||||
"DialogMessage_UnsubscribeConfirmationTitle": "Отписване",
|
||||
"DiscordChannelDisclaimerMessage": "Wino няма собствен сървър в Discord, но специалният канал 'wino-mail' се хоства на сървъра „Developer Sanctuary“.\nЗа да получавате актуализации за Wino, моля, присъединете се към сървъра Developer Sanctuary и следвайте канала „wino-mail“ в „Community Projects“.\n\nЩе бъдете пренасочени към URL адреса на сървъра, тъй като Discord не поддържа покани за канали.",
|
||||
"DiscordChannelDisclaimerTitle": "Важна информация за Discord",
|
||||
"Draft": "Чернова",
|
||||
"DragMoveToFolderCaption": "Преместване в {0}",
|
||||
"EditorToolbarOption_Draw": "Рисуване",
|
||||
"EditorToolbarOption_Format": "Форматиране",
|
||||
"EditorToolbarOption_Insert": "Вмъкване",
|
||||
"EditorToolbarOption_None": "Няма",
|
||||
"EditorToolbarOption_Options": "Опции",
|
||||
"EditorTooltip_WebViewEditor": "Използване на редактор в уеб изглед",
|
||||
"ElementTheme_Dark": "Тъмен режим",
|
||||
"ElementTheme_Default": "Използване на системната настройка",
|
||||
"ElementTheme_Light": "Светъл режим",
|
||||
"Emoji": "Емотикони",
|
||||
"Error_FailedToSetupSystemFolders_Title": "Неуспешна настройка на системните папки",
|
||||
"Exception_AuthenticationCanceled": "Удостоверяването е отменено",
|
||||
"Exception_CustomThemeExists": "Тази тема вече съществува.",
|
||||
"Exception_CustomThemeMissingName": "Трябва да посочите име.",
|
||||
"Exception_CustomThemeMissingWallpaper": "Трябва да предоставите персонализирано фоново изображение.",
|
||||
"Exception_FailedToSynchronizeAliases": "Неуспешно синхронизиране на псевдонимите",
|
||||
"Exception_FailedToSynchronizeFolders": "Неуспешно синхронизиране на папките",
|
||||
"Exception_FailedToSynchronizeProfileInformation": "Неуспешно синхронизиране на информацията за профила",
|
||||
"Exception_GoogleAuthCallbackNull": "Callback uri е празенl при активиране.",
|
||||
"Exception_GoogleAuthCorruptedCode": "Повреден отговор за упълномощаване.",
|
||||
"Exception_GoogleAuthError": "Грешка при оторизацията на OAuth: {0}",
|
||||
"Exception_GoogleAuthInvalidResponse": "Получена заявка с невалидно състояние ({0})",
|
||||
"Exception_GoogleAuthorizationCodeExchangeFailed": "Обменът на код за упълномощаване е неуспешен.",
|
||||
"Exception_ImapAutoDiscoveryFailed": "Не могат да се намерят настройките на пощенската кутия.",
|
||||
"Exception_ImapClientPoolFailed": "IMAP Client Pool се провали.",
|
||||
"Exception_InboxNotAvailable": "Не може да се настроят папките на акаунта.",
|
||||
"Exception_InvalidSystemFolderConfiguration": "Конфигурацията на системната папка не е валидна. Проверете конфигурацията и опитайте отново.",
|
||||
"Exception_InvalidMultiAccountMoveTarget": "Не можете да премествате няколко елемента, които принадлежат на различни акаунти, в свързан акаунт.",
|
||||
"Exception_MailProcessing": "Тази поща все още се обработва. Моля, опитайте отново след няколко секунди.",
|
||||
"Exception_MissingAlias": "За този акаунт не съществува първичен псевдоним. Създаването на чернова е неуспешно.",
|
||||
"Exception_NullAssignedAccount": "Присвоеният акаунт е нулев",
|
||||
"Exception_NullAssignedFolder": "Присвоената папка е нулева",
|
||||
"Exception_SynchronizerFailureHTTP": "Обработката на отговора е неуспешна с грешка HTTP код {0}",
|
||||
"Exception_TokenGenerationFailed": "Неуспешно генериране на токен",
|
||||
"Exception_TokenInfoRetrivalFailed": "Не се получи информация за токена.",
|
||||
"Exception_UnknowErrorDuringAuthentication": "Възникна неизвестна грешка по време на удостоверяването",
|
||||
"Exception_UnsupportedAction": "Действието {0} не е внедрено в процесора за обработка на заявки",
|
||||
"Exception_UnsupportedProvider": "Този доставчик не се поддържа.",
|
||||
"Exception_UnsupportedSynchronizerOperation": "Тази операция не се поддържа за {0}",
|
||||
"Exception_UserCancelSystemFolderSetupDialog": "Диалогът за конфигуриране на системната папка е отменен от потребителя.",
|
||||
"Exception_WinoServerException": "Сървърът на Wino е неуспешен.",
|
||||
"Files": "Файлове",
|
||||
"FilteringOption_All": "Всички",
|
||||
"FilteringOption_Files": "Съдържа файлове",
|
||||
"FilteringOption_Flagged": "Отбелязани",
|
||||
"FilteringOption_Unread": "Непрочетени",
|
||||
"Focused": "На фокус",
|
||||
"FolderOperation_CreateSubFolder": "Създаване на подпапка",
|
||||
"FolderOperation_Delete": "Изтриване",
|
||||
"FolderOperation_DontSync": "Да не се синхронизира тази папка",
|
||||
"FolderOperation_Empty": "Изпразване на тази папка",
|
||||
"FolderOperation_MarkAllAsRead": "Маркиране на всички като прочетени",
|
||||
"FolderOperation_Move": "Преместване",
|
||||
"FolderOperation_None": "Няма",
|
||||
"FolderOperation_Pin": "Фиксиране",
|
||||
"FolderOperation_Rename": "Преименуване",
|
||||
"FolderOperation_Unpin": "Освобождаване",
|
||||
"GeneralTitle_Error": "Грешка",
|
||||
"GeneralTitle_Info": "Информация",
|
||||
"GeneralTitle_Warning": "Внимание",
|
||||
"GmailServiceDisabled_Title": "Грешка в Gmail",
|
||||
"GmailServiceDisabled_Message": "Вашият акаунт в Google Workspace изглежда е деактивиран за услугата Gmail. Моля, свържете се с вашия администратор, за да активира услугата Gmail за вашия акаунт.",
|
||||
"GmailArchiveFolderNameOverride": "Архив",
|
||||
"HoverActionOption_Archive": "Архивиране",
|
||||
"HoverActionOption_Delete": "Изтриване",
|
||||
"HoverActionOption_MoveJunk": "Преместване в Нежелани",
|
||||
"HoverActionOption_ToggleFlag": "Отбелязване / Без отбелязване",
|
||||
"HoverActionOption_ToggleRead": "Прочетено / Непрочетено",
|
||||
"ImageRenderingDisabled": "Показването на изображения е деактивирано за това съобщение.",
|
||||
"ImapAdvancedSetupDialog_AuthenticationMethod": "Метод за удостоверяване",
|
||||
"ImapAdvancedSetupDialog_ConnectionSecurity": "Сигурност на връзката",
|
||||
"IMAPAdvancedSetupDialog_ValidationAuthMethodRequired": "Изисква се метод за удостоверяване",
|
||||
"IMAPAdvancedSetupDialog_ValidationConnectionSecurityRequired": "Изисква се тип сигурност на връзката",
|
||||
"IMAPAdvancedSetupDialog_ValidationDisplayNameRequired": "Изисква се показвано име",
|
||||
"IMAPAdvancedSetupDialog_ValidationEmailInvalid": "Моля, въведете валиден имейл адрес",
|
||||
"IMAPAdvancedSetupDialog_ValidationEmailRequired": "Изисква се имейл адрес",
|
||||
"IMAPAdvancedSetupDialog_ValidationErrorTitle": "Моля, проверете следното:",
|
||||
"IMAPAdvancedSetupDialog_ValidationIncomingPortInvalid": "Входящият порт трябва да е между 1-65535",
|
||||
"IMAPAdvancedSetupDialog_ValidationIncomingPortRequired": "Изисква се порт на входящия сървър",
|
||||
"IMAPAdvancedSetupDialog_ValidationIncomingServerRequired": "Изисква се адрес на входящия сървър",
|
||||
"IMAPAdvancedSetupDialog_ValidationOutgoingPasswordRequired": "Изисква се парола на изходящия сървър",
|
||||
"IMAPAdvancedSetupDialog_ValidationOutgoingPortInvalid": "Изходящият порт трябва да е между 1-65535",
|
||||
"IMAPAdvancedSetupDialog_ValidationOutgoingPortRequired": "Изисква се порт на изходящия сървър",
|
||||
"IMAPAdvancedSetupDialog_ValidationOutgoingServerRequired": "Изисква се адрес на изходящия сървър",
|
||||
"IMAPAdvancedSetupDialog_ValidationOutgoingUsernameRequired": "Изисква се потребителско име на изходящия сървър",
|
||||
"IMAPAdvancedSetupDialog_ValidationPasswordRequired": "Изисква се парола",
|
||||
"IMAPAdvancedSetupDialog_ValidationUsernameRequired": "Изисква се потребителско име",
|
||||
"ImapAuthenticationMethod_Auto": "Автоматично",
|
||||
"ImapAuthenticationMethod_CramMD5": "CRAM-MD5",
|
||||
"ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5",
|
||||
"ImapAuthenticationMethod_EncryptedPassword": "Криптирана парола",
|
||||
"ImapAuthenticationMethod_None": "Без удостоверяване",
|
||||
"ImapAuthenticationMethod_Ntlm": "NTLM",
|
||||
"ImapAuthenticationMethod_Plain": "Нормална парола",
|
||||
"ImapConnectionSecurity_Auto": "Автоматично",
|
||||
"ImapConnectionSecurity_None": "Няма",
|
||||
"ImapConnectionSecurity_SslTls": "SSL/TLS",
|
||||
"ImapConnectionSecurity_StartTls": "STARTTLS",
|
||||
"IMAPSetupDialog_AccountType": "Тип на акаунта",
|
||||
"IMAPSetupDialog_ValidationSuccess_Title": "Успешно",
|
||||
"IMAPSetupDialog_ValidationSuccess_Message": "Успешно валидиране",
|
||||
"IMAPSetupDialog_ValidationFailed_Title": "Неуспешно валидиране на IMAP сървъра.",
|
||||
"IMAPSetupDialog_CertificateAllowanceRequired_Row0": "Този сървър изисква установяване на защитена SSL връзка, за да продължи. Моля, потвърдете данните за сертификата по-долу.",
|
||||
"IMAPSetupDialog_CertificateAllowanceRequired_Row1": "Разрешете защитената връзка, за да продължите да настройвате акаунта си.",
|
||||
"IMAPSetupDialog_CertificateDenied": "Потребителят не е разрешил защитената връзка със сертификата.",
|
||||
"IMAPSetupDialog_CertificateIssuer": "Издател",
|
||||
"IMAPSetupDialog_CertificateSubject": "Тема",
|
||||
"IMAPSetupDialog_CertificateValidFrom": "Валиден от",
|
||||
"IMAPSetupDialog_CertificateValidTo": "Валиден до",
|
||||
"IMAPSetupDialog_CertificateView": "Преглед на сертификата",
|
||||
"IMAPSetupDialog_ConnectionFailedMessage": "Връзката с IMAP е неуспешна.",
|
||||
"IMAPSetupDialog_ConnectionFailedTitle": "Връзката е неуспешна",
|
||||
"IMAPSetupDialog_DisplayName": "Показвано име",
|
||||
"IMAPSetupDialog_DisplayNamePlaceholder": "напр. Иван Иванов",
|
||||
"IMAPSetupDialog_IncomingMailServer": "Сървър за входяща поща",
|
||||
"IMAPSetupDialog_IncomingMailServerPort": "Порт",
|
||||
"IMAPSetupDialog_IMAPSettings": "Настройки на IMAP сървъра",
|
||||
"IMAPSetupDialog_SMTPSettings": "Настройки на SMTP сървъра",
|
||||
"IMAPSetupDialog_MailAddress": "Имейл адрес",
|
||||
"IMAPSetupDialog_MailAddressPlaceholder": "nyakoy@primer.com",
|
||||
"IMAPSetupDialog_OutgoingMailServer": "Сървър за изходяща поща (SMTP)",
|
||||
"IMAPSetupDialog_OutgoingMailServerPassword": "Парола на изходящия сървър",
|
||||
"IMAPSetupDialog_OutgoingMailServerPort": "Порт",
|
||||
"IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Изходящият сървър изисква удостоверяване",
|
||||
"IMAPSetupDialog_OutgoingMailServerUsername": "Потребителско име на изходящия сървър",
|
||||
"IMAPSetupDialog_Password": "Парола",
|
||||
"IMAPSetupDialog_RequireSSLForIncomingMail": "Изисква SSL за входящата поща",
|
||||
"IMAPSetupDialog_RequireSSLForOutgoingMail": "Изисква SSL за изходящата поща",
|
||||
"IMAPSetupDialog_Title": "Разширено конфигуриране на IMAP",
|
||||
"IMAPSetupDialog_Username": "Потребител",
|
||||
"IMAPSetupDialog_UsernamePlaceholder": "ivanivanov, ivanivanov@primer.com, domain/ivanivanov",
|
||||
"IMAPSetupDialog_UseSameConfig": "Използване на същото потребителско име и парола за изпращане на имейли",
|
||||
"Info_AccountCreatedMessage": "{0} е създаден",
|
||||
"Info_AccountCreatedTitle": "Създаване на акаунт",
|
||||
"Info_AccountCreationFailedTitle": "Неуспешно създаване на акаунт",
|
||||
"Info_AccountDeletedMessage": "{0} е изтрит успешно.",
|
||||
"Info_AccountDeletedTitle": "Акаунтът е изтрит",
|
||||
"Info_AccountIssueFixFailedTitle": "Неуспешно",
|
||||
"Info_AccountIssueFixSuccessMessage": "Отстранени са всички проблеми с акаунтите.",
|
||||
"Info_AccountIssueFixSuccessTitle": "Успешно",
|
||||
"Info_AttachmentOpenFailedMessage": "Не може да се отвори този прикачен файл.",
|
||||
"Info_AttachmentOpenFailedTitle": "Неуспешно",
|
||||
"Info_AttachmentSaveFailedMessage": "Не може да се запази този прикачен файл.",
|
||||
"Info_AttachmentSaveFailedTitle": "Неуспешно",
|
||||
"Info_AttachmentSaveSuccessMessage": "Прикаченият файл е запазен.",
|
||||
"Info_AttachmentSaveSuccessTitle": "Запазен прикачен файл",
|
||||
"Info_BackgroundExecutionDeniedMessage": "Изпълнението на приложението на заден план е отказано. Това може да повлияе на синхронизацията на заден план и известията в реално време.",
|
||||
"Info_BackgroundExecutionDeniedTitle": "Отказано изпълнение на задан план",
|
||||
"Info_BackgroundExecutionUnknownErrorMessage": "Възникна неизвестно изключение при регистриране на синхронизатора на заден план.",
|
||||
"Info_BackgroundExecutionUnknownErrorTitle": "Неуспешно изпълнение на заден план",
|
||||
"Info_CantDeletePrimaryAliasMessage": "Основният псевдоним не може да бъде изтрит. Моля, променете псевдонима си, преди да изтриете този",
|
||||
"Info_ComposerMissingMIMEMessage": "MIME файлът не можа да се намери. Синхронизирането може да помогне.",
|
||||
"Info_ComposerMissingMIMETitle": "Неуспешно",
|
||||
"Info_ContactExistsMessage": "Този контакт вече е в списъка с получатели.",
|
||||
"Info_ContactExistsTitle": "Контактът съществува",
|
||||
"Info_DraftFolderMissingMessage": "За този акаунт липсва папка Чернови. Моля, проверете настройките на акаунта си.",
|
||||
"Info_DraftFolderMissingTitle": "Липсва папка Чернови",
|
||||
"Info_FailedToOpenFileMessage": "Файлът може да бъде премахнат от диска.",
|
||||
"Info_FailedToOpenFileTitle": "Неуспешно стартиране на файла.",
|
||||
"Info_FileLaunchFailedTitle": "Неуспешно стартиране на файла",
|
||||
"Info_InvalidAddressMessage": "„{0}“ не е валиден имейл адрес.",
|
||||
"Info_InvalidAddressTitle": "Невалиден адрес",
|
||||
"Info_InvalidMoveTargetMessage": "Не можете да преместите избраните писма в тази папка.",
|
||||
"Info_InvalidMoveTargetTitle": "Невалидна цел за преместване",
|
||||
"Info_LogsNotFoundMessage": "Няма дневници за споделяне.",
|
||||
"Info_LogsNotFoundTitle": "Дневниците не са намерени",
|
||||
"Info_LogsSavedMessage": "{0} се запазва в избраната папка.",
|
||||
"Info_LogsSavedTitle": "Запазено",
|
||||
"Info_MailListSizeResetSuccessMessage": "Размерът на пощенския списък е нулиран.",
|
||||
"Info_MailRenderingFailedMessage": "Тази поща е повредена или не може да бъде отворена.\n{0}",
|
||||
"Info_MailRenderingFailedTitle": "Неуспешно визуализиране",
|
||||
"Info_MessageCorruptedMessage": "Това съобщение е повредено.",
|
||||
"Info_MessageCorruptedTitle": "Грешка",
|
||||
"Info_MissingFolderMessage": "{0} не съществува за този акаунт.",
|
||||
"Info_MissingFolderTitle": "Липсваща папка",
|
||||
"Info_PDFSaveFailedTitle": "Неуспешно записване на PDF файл",
|
||||
"Info_PDFSaveSuccessMessage": "PDF файлът е записан в {0}",
|
||||
"Info_PDFSaveSuccessTitle": "Успешно",
|
||||
"Info_PurchaseExistsMessage": "Изглежда, че този продукт вече е бил закупен преди.",
|
||||
"Info_PurchaseExistsTitle": "Съществуващ продукт",
|
||||
"Info_PurchaseThankYouMessage": "Благодарим ви",
|
||||
"Info_PurchaseThankYouTitle": "Покупката е успешна",
|
||||
"Info_RequestCreationFailedTitle": "Неуспешно създаване на заявки",
|
||||
"Info_ReviewNetworkErrorMessage": "Имаше мрежов проблем с прегледа ви.",
|
||||
"Info_ReviewNetworkErrorTitle": "Мрежов проблем",
|
||||
"Info_ReviewNewMessage": "Оценяваме всички отзиви. Благодарим ви за прегледа!",
|
||||
"Info_ReviewSuccessTitle": "Благодарим ви",
|
||||
"Info_ReviewUnknownErrorMessage": "Имаше неизвестен проблем с прегледа ви. ({0})",
|
||||
"Info_ReviewUnknownErrorTitle": "Неизвестна грешка",
|
||||
"Info_ReviewUpdatedMessage": "Благодарим ви за актуализирания преглед.",
|
||||
"Info_SignatureDisabledMessage": "Деактивиран подпис за този акаунт",
|
||||
"Info_SignatureDisabledTitle": "Успешно",
|
||||
"Info_SignatureSavedMessage": "Новият подпис се запазва",
|
||||
"Info_SignatureSavedTitle": "Успешно",
|
||||
"Info_SyncCanceledMessage": "Отменено",
|
||||
"Info_SyncCanceledTitle": "Синхронизиране",
|
||||
"Info_SyncFailedTitle": "Неуспешна синхронизация",
|
||||
"Info_UnsubscribeErrorMessage": "Неуспешно отписване",
|
||||
"Info_UnsubscribeLinkInvalidMessage": "Тази връзка за отписване е невалидна. Неуспешно отписване от списъка.",
|
||||
"Info_UnsubscribeLinkInvalidTitle": "Невалиден Uri за отписване",
|
||||
"Info_UnsubscribeSuccessMessage": "Успешно се отписахте от {0}.",
|
||||
"Info_UnsupportedFunctionalityDescription": "Тази функционалност все още не е реализирана.",
|
||||
"Info_UnsupportedFunctionalityTitle": "Не се поддържа",
|
||||
"InfoBarAction_Enable": "Активиране",
|
||||
"InfoBarMessage_SynchronizationDisabledFolder": "Синхронизирането на тази папка е деактивирано.",
|
||||
"InfoBarTitle_SynchronizationDisabledFolder": "Изключена папка",
|
||||
"Justify": "Двустранно",
|
||||
"Left": "Ляво",
|
||||
"Link": "Връзка",
|
||||
"LinkedAccountsCreatePolicyMessage": "Трябва да имате поне 2 акаунта, за да създадете връзка.\nВръзката ще бъде премахната при запазване.",
|
||||
"LinkedAccountsTitle": "Свързани акаунти",
|
||||
"MailItemNoSubject": "Без тема",
|
||||
"MailOperation_AlwaysMoveFocused": "Винаги да се преместват в На фокус",
|
||||
"MailOperation_AlwaysMoveOther": "Винаги да се преместват в Други",
|
||||
"MailOperation_Archive": "Архивиране",
|
||||
"MailOperation_ClearFlag": "Без отбелязване",
|
||||
"MailOperation_DarkEditor": "Тъмна",
|
||||
"MailOperation_Delete": "Изтриване",
|
||||
"MailOperation_ExportPDF": "Експортиране в PDF",
|
||||
"MailOperation_Find": "Намиране",
|
||||
"MailOperation_Forward": "Препращане",
|
||||
"MailOperation_Ignore": "Игнориране",
|
||||
"MailOperation_LightEditor": "Светла",
|
||||
"MailOperation_MarkAsJunk": "Маркиране като нежелано",
|
||||
"MailOperation_MarkAsRead": "Маркиране като прочетено",
|
||||
"MailOperation_MarkAsUnread": "Маркиране като непрочетено",
|
||||
"MailOperation_MarkNotJunk": "Не е нежелано",
|
||||
"MailOperation_Move": "Преместване",
|
||||
"MailOperation_MoveFocused": "Преместване в На фокус",
|
||||
"MailOperation_MoveJunk": "Преместване в Нежелани",
|
||||
"MailOperation_MoveOther": "Преместване в Други",
|
||||
"MailOperation_Navigate": "Отиване",
|
||||
"MailOperation_Print": "Отпечатване",
|
||||
"MailOperation_Reply": "Отговор",
|
||||
"MailOperation_ReplyAll": "Отговор на всички",
|
||||
"MailOperation_SaveAs": "Запазване като",
|
||||
"MailOperation_SetFlag": "Отбелязване",
|
||||
"MailOperation_Unarchive": "Разархивиране",
|
||||
"MailOperation_ViewMessageSource": "Преглед на изходния код",
|
||||
"MailOperation_Zoom": "Мащаб",
|
||||
"MailsSelected": "{0} избран(и) елемент(и)",
|
||||
"MarkFlagUnflag": "Отбелязано/Без отбелязване",
|
||||
"MarkReadUnread": "Маркиране като прочетено/непрочетено",
|
||||
"MenuManageAccounts": "Управление на акаунтите",
|
||||
"MenuMergedAccountItemAccountsSuffix": " акаунти",
|
||||
"MenuNewMail": "Нов имейл",
|
||||
"MenuRate": "Оценете Wino",
|
||||
"MenuSettings": "Настройки",
|
||||
"MergedAccountCommonFolderArchive": "Архив",
|
||||
"MergedAccountCommonFolderDraft": "Чернова",
|
||||
"MergedAccountCommonFolderInbox": "Входящи",
|
||||
"MergedAccountCommonFolderJunk": "Нежелани",
|
||||
"MergedAccountCommonFolderSent": "Изпратени",
|
||||
"MergedAccountCommonFolderTrash": "Изтрити",
|
||||
"MergedAccountsAvailableAccountsTitle": "Налични акаунти",
|
||||
"MessageSourceDialog_Title": "Източник на съобщението",
|
||||
"More": "Още",
|
||||
"MoreFolderNameOverride": "Още",
|
||||
"MoveMailDialog_InvalidFolderMessage": "{0} не е валидна папка за тази поща.",
|
||||
"MoveMailDialog_Title": "Изберете папка",
|
||||
"NewAccountDialog_AccountName": "Име на акаунта",
|
||||
"NewAccountDialog_AccountNameDefaultValue": "Личен",
|
||||
"NewAccountDialog_AccountNamePlaceholder": "напр. Лична поща",
|
||||
"NewAccountDialog_Title": "Добавяне на нов акаунт",
|
||||
"NoMailSelected": "Не е избрано съобщение",
|
||||
"NoMessageCrieteria": "Няма съобщения, които да отговарят на критериите ви за търсене",
|
||||
"NoMessageEmptyFolder": "Тази папка е празна",
|
||||
"Notifications_MultipleNotificationsMessage": "Имате {0} нови съобщения.",
|
||||
"Notifications_MultipleNotificationsTitle": "Нов имейл",
|
||||
"Notifications_WinoUpdatedMessage": "Вижте новата версия {0}",
|
||||
"Notifications_WinoUpdatedTitle": "Wino Mail е актуализиран.",
|
||||
"OnlineSearchFailed_Message": "Неуспешно търсене\n{0}\n\nПоказване на офлайн имейли.",
|
||||
"OnlineSearchTry_Line1": "Не можете да намерите това, което търсите?",
|
||||
"OnlineSearchTry_Line2": "Опитайте онлайн търсене.",
|
||||
"Other": "Друго",
|
||||
"PaneLengthOption_Default": "По подразбиране",
|
||||
"PaneLengthOption_ExtraLarge": "Много голям",
|
||||
"PaneLengthOption_Large": "Голям",
|
||||
"PaneLengthOption_Medium": "Среден",
|
||||
"PaneLengthOption_Micro": "Много малък",
|
||||
"PaneLengthOption_Small": "Малък",
|
||||
"Photos": "Снимки",
|
||||
"PreparingFoldersMessage": "Подготовка на папките",
|
||||
"ProtocolLogAvailable_Message": "Протоколните дневници са достъпни за диагностика.",
|
||||
"ProviderDetail_Gmail_Description": "Акаунт в Google",
|
||||
"ProviderDetail_iCloud_Description": "Акаунт в Apple iCloud",
|
||||
"ProviderDetail_iCloud_Title": "iCloud",
|
||||
"ProviderDetail_IMAP_Description": "Потребителски IMAP/SMTP сървър",
|
||||
"ProviderDetail_IMAP_Title": "IMAP сървър",
|
||||
"ProviderDetail_Yahoo_Description": "Акаунт в Yahoo",
|
||||
"ProviderDetail_Yahoo_Title": "Yahoo Mail",
|
||||
"QuickEventDialog_EventName": "Име на събитието",
|
||||
"QuickEventDialog_IsAllDay": "Цял ден",
|
||||
"QuickEventDialog_Location": "Местоположение",
|
||||
"QuickEventDialog_RemindMe": "Напомнете ми",
|
||||
"QuickEventDialogMoreDetailsButtonText": "Повече подробности",
|
||||
"Reader_SaveAllAttachmentButtonText": "Запазване на всички прикачени файлове",
|
||||
"Results": "Резултати",
|
||||
"Right": "Дясно",
|
||||
"SearchBarPlaceholder": "Търсене",
|
||||
"SearchingIn": "Търсене в",
|
||||
"SearchPivotName": "Резултати",
|
||||
"SettingConfigureSpecialFolders_Button": "Конфигуриране",
|
||||
"SettingsEditAccountDetails_IMAPConfiguration_Title": "Конфигурация на IMAP/SMTP",
|
||||
"SettingsEditAccountDetails_IMAPConfiguration_Description": "Променете настройките на входящия/изходящия сървър.",
|
||||
"SettingsAbout_Description": "Научете повече за Wino.",
|
||||
"SettingsAbout_Title": "За приложението",
|
||||
"SettingsAboutGithub_Description": "Към хранилището на GitHub за проследяване на проблеми.",
|
||||
"SettingsAboutGithub_Title": "GitHub",
|
||||
"SettingsAboutVersion": "Версия ",
|
||||
"SettingsAboutWinoDescription": "Лек пощенски клиент за семейства устройства с Windows.",
|
||||
"SettingsAccentColor_Description": "Промяна на цвета на акцента в приложението",
|
||||
"SettingsAccentColor_Title": "Цвят на акцента",
|
||||
"SettingsAccentColor_UseWindowsAccentColor": "Използване на акцентния цвят на Windows",
|
||||
"SettingsAccountManagementAppendMessage_Description": "Създаване на копие на съобщението в папка „Изпратени“ след изпращане на черновата. Включете това, ако не виждате писмата си след изпращането им в папка „Изпратени“.",
|
||||
"SettingsAccountManagementAppendMessage_Title": "Добавяне на съобщенията в папка „Изпратени“",
|
||||
"SettingsAccountName_Description": "Промяна на името на акаунта.",
|
||||
"SettingsAccountName_Title": "Име на акаунта",
|
||||
"SettingsApplicationTheme_Description": "Персонализирайте Wino с различни потребителски теми за приложението, които ви харесват.",
|
||||
"SettingsApplicationTheme_Title": "Тема на приложението",
|
||||
"SettingsAppPreferences_CloseBehavior_Description": "Какво да се случи, когато затворите приложението?",
|
||||
"SettingsAppPreferences_CloseBehavior_Title": "Поведение при затваряне на приложението",
|
||||
"SettingsAppPreferences_Description": "Общи настройки / предпочитания за Wino Mail.",
|
||||
"SettingsAppPreferences_SearchMode_Description": "Задайте дали Wino да проверява първо получените писма при търсене или да попита вашия пощенски сървър онлайн. Местното търсене винаги е по-бързо и винаги можете да направите онлайн търсене, ако вашата поща не е в резултатите.",
|
||||
"SettingsAppPreferences_SearchMode_Local": "Локално",
|
||||
"SettingsAppPreferences_SearchMode_Online": "Онлайн",
|
||||
"SettingsAppPreferences_SearchMode_Title": "Режим на търсене по подразбиране",
|
||||
"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Description": "Wino Mail ще продължи да работи на заден план. Ще бъдете уведомявани за пристигането на нови имейли.",
|
||||
"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Title": "Работа на заден план",
|
||||
"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Description": "Wino Mail ще продължи да работи в системната област. Можете да го стартирате, като щракнете върху иконата. Ще бъдете уведомявани при пристигането на нови имейли.",
|
||||
"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Title": "Минимизиране в системната област",
|
||||
"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Description": "Wino Mail няма да продължи да работи никъде. Няма да бъдете уведомявани за пристигането на нови имейли. Стартирайте отново Wino Mail, за да продължите синхронизирането на пощата.",
|
||||
"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Title": "Прекратяване",
|
||||
"SettingsAppPreferences_StartupBehavior_Description": "Позволете на Wino Mail да се стартира минимизиран при стартиране на Windows. Винаги разрешавайте да получава известия.",
|
||||
"SettingsAppPreferences_StartupBehavior_Disable": "Деактивиране",
|
||||
"SettingsAppPreferences_StartupBehavior_Disabled": "Wino Mail няма да се стартира при стартиране на Windows. Това ще доведе до пропускане на известия при рестартиране на компютъра.",
|
||||
"SettingsAppPreferences_StartupBehavior_DisabledByPolicy": "Вашият администратор или групови политики са забранили стартирането на приложения при начално стартиране. Поради това Wino Mail не може да бъде настроен да се стартира при стартиране на Windows.",
|
||||
"SettingsAppPreferences_StartupBehavior_DisabledByUser": "Моля, отидете в Мениджър на задачите -> раздел Автоматично стартиращи се приложения, за да разрешите на Wino Mail да се стартира при стартиране на Windows.",
|
||||
"SettingsAppPreferences_StartupBehavior_Enable": "Активиране",
|
||||
"SettingsAppPreferences_StartupBehavior_Enabled": "Wino Mail е настроен успешно да се стартира на заден план при стартиране на Windows.",
|
||||
"SettingsAppPreferences_StartupBehavior_FatalError": "Възникна фатална грешка при промяна на режима на стартиране на Wino Mail.",
|
||||
"SettingsAppPreferences_StartupBehavior_Title": "Стартиране в минимизиран вид при стартиране на Windows",
|
||||
"SettingsAppPreferences_Title": "Предпочитания за приложението",
|
||||
"SettingsAutoSelectNextItem_Description": "Избиране на следващия елемент, след като изтриете или преместите имейл.",
|
||||
"SettingsAutoSelectNextItem_Title": "Автоматично избиране на следващия елемент",
|
||||
"SettingsAvailableThemes_Description": "Изберете тема от собствената колекция на Wino по ваш вкус или приложете свои собствени теми.",
|
||||
"SettingsAvailableThemes_Title": "Налични теми",
|
||||
"SettingsCalendarSettings_Description": "Променете първия ден от седмицата, височината на часовата клетка и други...",
|
||||
"SettingsCalendarSettings_Title": "Настройки на календара",
|
||||
"SettingsComposer_Title": "Съставяне",
|
||||
"SettingsComposerFont_Title": "Шрифт по подразбиране за съставяне",
|
||||
"SettingsComposerFontFamily_Description": "Промяна на шрифтовото семейство и размера на шрифта по подразбиране за съставяне на имейли.",
|
||||
"SettingsConfigureSpecialFolders_Description": "Задаване на папки със специални функции. Папки като Архив, Входящи и Чернови са от съществено значение за правилното функциониране на Wino.",
|
||||
"SettingsConfigureSpecialFolders_Title": "Конфигуриране на системните папки",
|
||||
"SettingsCustomTheme_Description": "Създайте своя собствена тема с персонализиран тапет и цвят на акцента.",
|
||||
"SettingsCustomTheme_Title": "Потребителска тема",
|
||||
"SettingsDeleteAccount_Description": "Изтриване на всички имейли и идентификационни данни, свързани с този акаунт.",
|
||||
"SettingsDeleteAccount_Title": "Изтриване на този акаунт",
|
||||
"SettingsDeleteProtection_Description": "Трябва ли Wino да ви пита за потвърждение всеки път, когато се опитвате да изтриете окончателно имейл с клавишите Shift + Del?",
|
||||
"SettingsDeleteProtection_Title": "Защита от окончателно изтриване",
|
||||
"SettingsDiagnostics_Description": "За разработчици",
|
||||
"SettingsDiagnostics_DiagnosticId_Description": "Споделете този идентификационен номер с разработчиците, когато ви помолят, за да получите помощ за проблемите, с които се сблъсквате в Wino Mail.",
|
||||
"SettingsDiagnostics_DiagnosticId_Title": "Идентификатор за диагностика",
|
||||
"SettingsDiagnostics_Title": "Диагностика",
|
||||
"SettingsDiscord_Description": "Получавайте редовно актуализации за разработката, включвайте се в обсъждания на пътната карта и предоставяйте обратна връзка.",
|
||||
"SettingsDiscord_Title": "Канал в Discord",
|
||||
"SettingsEditLinkedInbox_Description": "Добавяне/премахване на акаунти, преименуване или прекъсване на връзката между акаунтите.",
|
||||
"SettingsEditLinkedInbox_Title": "Редактиране на свързаната входяща поща",
|
||||
"SettingsElementTheme_Description": "Избор на тема на Windows за Wino",
|
||||
"SettingsElementTheme_Title": "Цветови режим",
|
||||
"SettingsElementThemeSelectionDisabled": "Изборът на цветови режим е забранен, когато е избрана тема на приложението, различна от тази по подразбиране.",
|
||||
"SettingsEnableHoverActions_Title": "Активиране на действията при поставяне на курсора",
|
||||
"SettingsEnableIMAPLogs_Description": "Активирайте тази опция, за да предоставите подробна информация за проблемите със свързаността на IMAP, които сте имали по време на настройката на IMAP сървъра.",
|
||||
"SettingsEnableIMAPLogs_Title": "Активиране на дневниците на IMAP протокола",
|
||||
"SettingsEnableLogs_Description": "Може да ми трябват дневниците за сривове, за да диагностицирам проблемите, които сте открили в GitHub. Нито един от дневниците няма да разкрие публично вашите идентификационни данни или чувствителна информация.",
|
||||
"SettingsEnableLogs_Title": "Активиране на дневниците",
|
||||
"SettingsEnableSignature": "Активиране на подписа",
|
||||
"SettingsExpandOnStartup_Description": "Задайте дали Wino да разгръща папките на този акаунт при стартиране.",
|
||||
"SettingsExpandOnStartup_Title": "Разгръщане на менюто при стартиране",
|
||||
"SettingsExternalContent_Description": "Управление на настройките за външно съдържание при визуализиране на имейли.",
|
||||
"SettingsExternalContent_Title": "Външно съдържание",
|
||||
"SettingsFocusedInbox_Description": "Задайте дали входящата поща да бъде разделена на две като На фокус - Други.",
|
||||
"SettingsFocusedInbox_Title": "Входящи на фокус",
|
||||
"SettingsFolderMenuStyle_Description": "Промяна на това дали папките на акаунта трябва да са вложени в менюто на акаунта или не. Изключете тази опция, ако ви харесва старата система на менютата в Поща на Windows",
|
||||
"SettingsFolderMenuStyle_Title": "Създаване на вложени папки",
|
||||
"SettingsFolderOptions_Description": "Промяна настройките на отделните папки, като например разрешаване/забрана на синхронизацията или показване/скриване на значката за непрочетените.",
|
||||
"SettingsFolderOptions_Title": "Конфигурация на папките",
|
||||
"SettingsFolderSync_Description": "Активиране или деактивиране синхронизирането на определени папки.",
|
||||
"SettingsFolderSync_Title": "Синхронизиране на папките",
|
||||
"SettingsFontFamily_Title": "Семейство шрифтове",
|
||||
"SettingsFontPreview_Title": "Преглед",
|
||||
"SettingsFontSize_Title": "Размер на шрифта",
|
||||
"SettingsHoverActionCenter": "Централно действие",
|
||||
"SettingsHoverActionLeft": "Ляво действие",
|
||||
"SettingsHoverActionRight": "Дясно действие",
|
||||
"SettingsHoverActions_Description": "Изберете 3 действия, които да се показват при преминаване с курсора над имейлите.",
|
||||
"SettingsHoverActions_Title": "Действия при преминаване с мишката",
|
||||
"SettingsLanguage_Description": "Промяна на езика за показване на Wino.",
|
||||
"SettingsLanguage_Title": "Език на показване",
|
||||
"SettingsLanguageTime_Description": "Език на показване на Wino, предпочитан формат на часа.",
|
||||
"SettingsLanguageTime_Title": "Език и формат на часа",
|
||||
"SettingsLinkAccounts_Description": "Сливане на няколко акаунта в един. Виждайте имейлите заедно в една входяща кутия.",
|
||||
"SettingsLinkAccounts_Title": "Създаване на свързани акаунти",
|
||||
"SettingsLinkedAccountsSave_Description": "Промяна на текущото свързване с новите акаунти.",
|
||||
"SettingsLinkedAccountsSave_Title": "Запазване на промените",
|
||||
"SettingsLoadImages_Title": "Автоматично зареждане на изображенията",
|
||||
"SettingsLoadPlaintextLinks_Title": "Конвертиране на обикновените текстови връзки в такива, върху които може да се клика",
|
||||
"SettingsLoadStyles_Title": "Автоматично зареждане на стиловете",
|
||||
"SettingsMailListActionBar_Description": "Скриване/показване на лентата за действия в горната част на списъка със съобщения.",
|
||||
"SettingsMailListActionBar_Title": "Показване на действията в списъка с имейлите",
|
||||
"SettingsMailSpacing_Description": "Настройте отстоянието между имейлите.",
|
||||
"SettingsMailSpacing_Title": "Интервал между имейлите",
|
||||
"SettingsManageAccountSettings_Description": "Известия, подписи, синхронизация и други настройки за всеки акаунт.",
|
||||
"SettingsManageAccountSettings_Title": "Управление на настройките на акаунта",
|
||||
"SettingsManageAliases_Description": "Вижте псевдонимите на имейлите, назначени за този акаунт, актуализирайте ги или ги изтрийте.",
|
||||
"SettingsManageAliases_Title": "Псевдоними",
|
||||
"SettingsEditAccountDetails_Title": "Редактиране на данните на акаунта",
|
||||
"SettingsEditAccountDetails_Description": "Променете името на акаунта, името на подателя и задайте нов цвят, ако желаете.",
|
||||
"SettingsManageLink_Description": "Преместете елементите, за да добавите нова връзка или да премахнете съществуваща връзка.",
|
||||
"SettingsManageLink_Title": "Управление на връзката",
|
||||
"SettingsMarkAsRead_Description": "Променете какво трябва да се случи с избрания елемент.",
|
||||
"SettingsMarkAsRead_DontChange": "Да не се маркира автоматично елемента като прочетен",
|
||||
"SettingsMarkAsRead_SecondsToWait": "Секунди за изчакване: ",
|
||||
"SettingsMarkAsRead_Timer": "При преглед в прозореца за четене",
|
||||
"SettingsMarkAsRead_Title": "Маркиране на елемента като прочетен",
|
||||
"SettingsMarkAsRead_WhenSelected": "Когато е избран",
|
||||
"SettingsMessageList_Description": "Променете начина на организиране на съобщенията в списъка с имейли.",
|
||||
"SettingsMessageList_Title": "Списък със съобщения",
|
||||
"SettingsNoAccountSetupMessage": "Все още не сте настроили никакви акаунти.",
|
||||
"SettingsNotifications_Description": "Включване или изключване на известията за този акаунт.",
|
||||
"SettingsNotifications_Title": "Известия",
|
||||
"SettingsNotificationsAndTaskbar_Description": "Променете дали да се показват известия и значка в лентата на задачите за този акаунт.",
|
||||
"SettingsNotificationsAndTaskbar_Title": "Известия и лента на задачите",
|
||||
"SettingsOptions_Title": "Настройки",
|
||||
"SettingsPaneLengthReset_Description": "Възстановете първоначалния размер на списъка с имейли, ако имате проблеми с него.",
|
||||
"SettingsPaneLengthReset_Title": "Възстановяване размера на списъка с имейли",
|
||||
"SettingsPaypal_Description": "Покажете много повече любов ❤️ Всички дарения са добре дошли.",
|
||||
"SettingsPaypal_Title": "Дарете чрез PayPal",
|
||||
"SettingsPersonalization_Description": "Променете външния вид на Wino, както ви харесва.",
|
||||
"SettingsPersonalization_Title": "Персонализация",
|
||||
"SettingsPersonalizationMailDisplayCompactMode": "Компактен режим",
|
||||
"SettingsPersonalizationMailDisplayMediumMode": "Среден режим",
|
||||
"SettingsPersonalizationMailDisplaySpaciousMode": "Просторен режим",
|
||||
"SettingsPrefer24HourClock_Description": "Часовете на получената поща ще се показват в 24-часов формат вместо в 12-часов (AM/PM)",
|
||||
"SettingsPrefer24HourClock_Title": "Показване на часовете в 24-часов формат",
|
||||
"SettingsPrivacyPolicy_Description": "Прегледайте политиката за поверителност.",
|
||||
"SettingsPrivacyPolicy_Title": "Политика за поверителност",
|
||||
"SettingsReadComposePane_Description": "Шрифтове, външно съдържание.",
|
||||
"SettingsReadComposePane_Title": "Четене и съставяне",
|
||||
"SettingsReader_Title": "Четене",
|
||||
"SettingsReaderFont_Title": "Шрифт по подразбиране за четене",
|
||||
"SettingsReaderFontFamily_Description": "Променете шрифтовото семейство и размера на шрифта по подразбиране за четене на имейли.",
|
||||
"SettingsRenameMergeAccount_Description": "Променете показваното име на свързаните акаунти.",
|
||||
"SettingsRenameMergeAccount_Title": "Преименуване",
|
||||
"SettingsReorderAccounts_Description": "Променете реда на акаунтите в списъка с акаунти.",
|
||||
"SettingsReorderAccounts_Title": "Пренареждане на акаунтите",
|
||||
"SettingsSemanticZoom_Description": "Това ще ви позволи да кликнете върху заглавията в списъка със съобщения и да преминете към определена дата",
|
||||
"SettingsSemanticZoom_Title": "Семантично увеличение за заглавия с дати",
|
||||
"SettingsShowPreviewText_Description": "Скриване/показване на текста за визуализация.",
|
||||
"SettingsShowPreviewText_Title": "Показване визуализация на текста",
|
||||
"SettingsShowSenderPictures_Description": "Скриване/показване на миниатюрите на снимките на изпращача.",
|
||||
"SettingsShowSenderPictures_Title": "Показване на аватарите на подателите",
|
||||
"SettingsSignature_AddCustomSignature_Button": "Добавяне на подпис",
|
||||
"SettingsSignature_AddCustomSignature_Title": "Добавяне на персонализиран подпис",
|
||||
"SettingsSignature_DeleteSignature_Title": "Изтриване на подписа",
|
||||
"SettingsSignature_Description": "Управление на подписите на акаунтите",
|
||||
"SettingsSignature_EditSignature_Title": "Редактиране на подписа",
|
||||
"SettingsSignature_ForFollowingMessages_Title": "За отговори/препратки",
|
||||
"SettingsSignature_ForNewMessages_Title": "За нови съобщения",
|
||||
"SettingsSignature_NoneSignatureName": "Няма",
|
||||
"SettingsSignature_SignatureDefaults": "Параметри на подписа по подразбиране",
|
||||
"SettingsSignature_Signatures": "Подписи",
|
||||
"SettingsSignature_Title": "Подпис",
|
||||
"SettingsStartupItem_Description": "Основен елемент от акаунта за зареждане на Входящи при стартиране.",
|
||||
"SettingsStartupItem_Title": "Елемент за стартиране",
|
||||
"SettingsStore_Description": "Покажете малко любов ❤️",
|
||||
"SettingsStore_Title": "Оценете ни в магазина",
|
||||
"SettingsTaskbarBadge_Description": "Показване броя на непрочетените писма в иконата в лентата на задачите.",
|
||||
"SettingsTaskbarBadge_Title": "Значка на лентата на задачите",
|
||||
"SettingsThreads_Description": "Организиране на съобщенията в разговори.",
|
||||
"SettingsThreads_Title": "Групиране в разговори",
|
||||
"SettingsUnlinkAccounts_Description": "Премахване на връзката между акаунтите. Това няма да доведе до изтриване на акаунтите ви.",
|
||||
"SettingsUnlinkAccounts_Title": "Премахване на връзката между акаунтите",
|
||||
"SignatureDeleteDialog_Message": "Наистина ли искате да изтриете подписа „{0}“?",
|
||||
"SignatureDeleteDialog_Title": "Изтриване на подписа",
|
||||
"SignatureEditorDialog_SignatureName_Placeholder": "Име на вашия подпис",
|
||||
"SignatureEditorDialog_SignatureName_TitleEdit": "Текущо име на подписа: {0}",
|
||||
"SignatureEditorDialog_SignatureName_TitleNew": "Име на подписа",
|
||||
"SignatureEditorDialog_Title": "Редактор на подписи",
|
||||
"SortingOption_Date": "по дата",
|
||||
"SortingOption_Name": "по име",
|
||||
"StoreRatingDialog_MessageFirstLine": "Всички отзиви се оценяват и ще направят Wino много по-добър в бъдеще. Искате ли да оцените Wino в Microsoft Store?",
|
||||
"StoreRatingDialog_MessageSecondLine": "Искате ли да оцените Wino Mail в Microsoft Store?",
|
||||
"StoreRatingDialog_Title": "Wino ви харесва?",
|
||||
"SynchronizationFolderReport_Failed": "неуспешна синхронизация",
|
||||
"SynchronizationFolderReport_Success": "актуални",
|
||||
"SystemFolderConfigDialog_ArchiveFolderDescription": "Архивираните съобщения ще бъдат преместени тук.",
|
||||
"SystemFolderConfigDialog_ArchiveFolderHeader": "Папка Архив",
|
||||
"SystemFolderConfigDialog_DeletedFolderDescription": "Изтритите съобщения ще бъдат преместени тук.",
|
||||
"SystemFolderConfigDialog_DeletedFolderHeader": "Изтрита папка",
|
||||
"SystemFolderConfigDialog_DraftFolderDescription": "Новите писма/отговори ще бъдат създавани тук.",
|
||||
"SystemFolderConfigDialog_DraftFolderHeader": "Папка Чернови",
|
||||
"SystemFolderConfigDialog_JunkFolderDescription": "Всички спам/нежелани имейли ще бъдат тук.",
|
||||
"SystemFolderConfigDialog_JunkFolderHeader": "Папка за нежелана поща/спам",
|
||||
"SystemFolderConfigDialog_MessageFirstLine": "Този IMAP сървър не поддържа разширението SPECIAL-USE, поради което Wino не може да настрои правилно системните папки.",
|
||||
"SystemFolderConfigDialog_MessageSecondLine": "Моля, изберете подходящите папки за конкретни функционалности.",
|
||||
"SystemFolderConfigDialog_SentFolderDescription": "Папка, в която ще се съхраняват изпратените съобщения.",
|
||||
"SystemFolderConfigDialog_SentFolderHeader": "Папка Изпратени",
|
||||
"SystemFolderConfigDialog_Title": "Конфигуриране на системните папки",
|
||||
"SystemFolderConfigDialogValidation_DuplicateSystemFolders": "Някои от системните папки се използват повече от веднъж в конфигурацията.",
|
||||
"SystemFolderConfigDialogValidation_InboxSelected": "Не можете да присвоите папка Входящи към друга системна папка.",
|
||||
"SystemFolderConfigSetupSuccess_Message": "Системните папки са успешно конфигурирани.",
|
||||
"SystemFolderConfigSetupSuccess_Title": "Настройка на системните папки",
|
||||
"TestingImapConnectionMessage": "Тестване на връзката със сървъра...",
|
||||
"TitleBarServerDisconnectedButton_Description": "Wino е изключен от мрежата. Щракнете върху Повторно свързване, за да възстановите връзката.",
|
||||
"TitleBarServerDisconnectedButton_Title": "няма връзка",
|
||||
"TitleBarServerReconnectButton_Title": "повторно свързване",
|
||||
"TitleBarServerReconnectingButton_Title": "свързване",
|
||||
"Today": "Днес",
|
||||
"UnknownAddress": "неизвестен адрес",
|
||||
"UnknownDateHeader": "Неизвестна дата",
|
||||
"UnknownGroupAddress": "неизвестен адрес на пощенската група",
|
||||
"UnknownSender": "Неизвестен подател",
|
||||
"Unsubscribe": "Отписване",
|
||||
"ViewContactDetails": "Вижте подробностите",
|
||||
"WinoUpgradeDescription": "Wino предлага 3 безплатни акаунта за начало. Ако имате нужда от повече от 3 акаунта, моля, надстройте",
|
||||
"WinoUpgradeMessage": "Надграждане до неограничен брой акаунти",
|
||||
"WinoUpgradeRemainingAccountsMessage": "Използвани са {0} от {1} безплатни акаунта.",
|
||||
"Yesterday": "Вчера"
|
||||
}
|
||||
|
||||
|
||||