1 Commits

Author SHA1 Message Date
Burak Kaan Köse
4f09345769 Added simple validations for advanced imap setup dialog to prevent users from making mistakes. 2025-02-22 01:43:09 +01:00
302 changed files with 11690 additions and 15296 deletions

View File

@@ -1,68 +1,65 @@
<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="Sentry.Serilog" Version="5.12.0" />
<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>
<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>

View File

@@ -67,8 +67,7 @@ public class OutlookAuthenticator : BaseAuthenticator, IOutlookAuthenticator
{
await EnsureTokenCacheAttachedAsync();
var storedAccount = (await _publicClientApplication.GetAccountsAsync()).FirstOrDefault(
a => string.Equals(a.Username?.Trim(), account.Address?.Trim(), StringComparison.OrdinalIgnoreCase));
var storedAccount = (await _publicClientApplication.GetAccountsAsync()).FirstOrDefault(a => a.Username == account.Address);
if (storedAccount == null)
return await GenerateTokenInformationAsync(account);
@@ -108,7 +107,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. If you are signing with a Office365, it is not officially supported yet.");
throw new AuthenticationException("Authenticated address does not match with your account address.");
}
return new TokenInformationEx(authResult.AccessToken, authResult.Account.Username);

View File

@@ -9,12 +9,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Diagnostics" />
<PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="Google.Apis.Auth" />
<PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="Google.Apis.Auth" />
<PackageReference Include="Microsoft.Identity.Client" />
<PackageReference Include="Microsoft.Identity.Client.Broker" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" />
<PackageReference Include="Sentry.Serilog" />
<PackageReference Include="Microsoft.Identity.Client.Broker" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Wino.Core.Domain\Wino.Core.Domain.csproj" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,90 @@
<?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>

View File

@@ -0,0 +1,77 @@
<?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)' &lt; '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>

View File

@@ -1,37 +1,48 @@
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Input;
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.Client.Navigation;
using Wino.Messaging.UI;
namespace Wino.Calendar.ViewModels;
public partial class AccountDetailsPageViewModel : CalendarBaseViewModel
namespace Wino.Calendar.ViewModels
{
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)
public partial class AccountDetailsPageViewModel : CalendarBaseViewModel
{
CalendarDialogService = calendarDialogService;
_accountService = accountService;
AccountCalendarStateService = accountCalendarStateService;
}
private readonly IAccountService _accountService;
[RelayCommand]
private void EditAccountDetails()
=> Messenger.Send(new BreadcrumbNavigationRequested(Translator.SettingsEditAccountDetails_Title, WinoPage.EditAccountDetailsPage, Account));
public AccountProviderDetailViewModel Account { get; private set; }
public ICalendarDialogService CalendarDialogService { get; }
public IAccountCalendarStateService AccountCalendarStateService { get; }
public override void OnNavigatedTo(NavigationMode mode, object parameters)
{
base.OnNavigatedTo(mode, parameters);
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);
}
}
}

View File

@@ -13,134 +13,135 @@ using Wino.Core.Domain.Models.Synchronization;
using Wino.Core.ViewModels;
using Wino.Messaging.Server;
namespace Wino.Calendar.ViewModels;
public partial class AccountManagementViewModel : AccountManagementPageViewModelBase
namespace Wino.Calendar.ViewModels
{
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)
public partial class AccountManagementViewModel : AccountManagementPageViewModelBase
{
CalendarDialogService = dialogService;
_providerService = providerService;
}
private readonly IProviderService _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(() =>
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)
{
foreach (var account in accounts)
{
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;
CalendarDialogService = dialogService;
_providerService = providerService;
}
var availableProviders = _providerService.GetAvailableProviders();
public ICalendarDialogService CalendarDialogService { get; }
var accountCreationDialogResult = await DialogService.ShowAccountProviderSelectionDialogAsync(availableProviders);
if (accountCreationDialogResult == null) return;
var accountCreationCancellationTokenSource = new CancellationTokenSource();
var accountCreationDialog = CalendarDialogService.GetAccountCreationDialog(accountCreationDialogResult);
await accountCreationDialog.ShowDialogAsync(accountCreationCancellationTokenSource);
await Task.Delay(500);
// For OAuth authentications, we just generate token and assign it to the MailAccount.
var createdAccount = new MailAccount()
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
{
ProviderType = accountCreationDialogResult.ProviderType,
Name = accountCreationDialogResult.AccountName,
Id = Guid.NewGuid()
};
base.OnNavigatedTo(mode, parameters);
var tokenInformationResponse = await WinoServerConnectionManager
.GetResponseAsync<TokenInformationEx, AuthorizationRequested>(new AuthorizationRequested(accountCreationDialogResult.ProviderType,
createdAccount,
createdAccount.ProviderType == MailProviderType.Gmail), accountCreationCancellationTokenSource.Token);
await InitializeAccountsAsync();
}
if (accountCreationDialog.State == AccountCreationDialogState.Canceled)
throw new AccountSetupCanceledException();
tokenInformationResponse.ThrowIfFailed();
await AccountService.CreateAccountAsync(createdAccount, null);
// Sync profile information if supported.
if (createdAccount.IsProfileInfoSyncSupported)
public override async Task InitializeAccountsAsync()
{
// Start profile information synchronization.
// It's only available for Outlook and Gmail synchronizers.
Accounts.Clear();
var profileSyncOptions = new MailSynchronizationOptions()
var accounts = await AccountService.GetAccountsAsync().ConfigureAwait(false);
await ExecuteUIThread(() =>
{
AccountId = createdAccount.Id,
Type = MailSynchronizationType.UpdateProfile
foreach (var account in accounts)
{
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;
}
var availableProviders = _providerService.GetAvailableProviders();
var accountCreationDialogResult = await DialogService.ShowAccountProviderSelectionDialogAsync(availableProviders);
if (accountCreationDialogResult == null) return;
var accountCreationCancellationTokenSource = new CancellationTokenSource();
var accountCreationDialog = CalendarDialogService.GetAccountCreationDialog(accountCreationDialogResult);
accountCreationDialog.ShowDialog(accountCreationCancellationTokenSource);
accountCreationDialog.State = AccountCreationDialogState.SigningIn;
// For OAuth authentications, we just generate token and assign it to the MailAccount.
var createdAccount = new MailAccount()
{
ProviderType = accountCreationDialogResult.ProviderType,
Name = accountCreationDialogResult.AccountName,
Id = Guid.NewGuid()
};
var profileSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewMailSynchronizationRequested>(new NewMailSynchronizationRequested(profileSyncOptions, SynchronizationSource.Client));
var tokenInformationResponse = await WinoServerConnectionManager
.GetResponseAsync<TokenInformationEx, AuthorizationRequested>(new AuthorizationRequested(accountCreationDialogResult.ProviderType,
createdAccount,
createdAccount.ProviderType == MailProviderType.Gmail), accountCreationCancellationTokenSource.Token);
var profileSynchronizationResult = profileSynchronizationResponse.Data;
if (accountCreationDialog.State == AccountCreationDialogState.Canceled)
throw new AccountSetupCanceledException();
if (profileSynchronizationResult.CompletedState != SynchronizationCompletedState.Success)
throw new Exception(Translator.Exception_FailedToSynchronizeProfileInformation);
tokenInformationResponse.ThrowIfFailed();
createdAccount.SenderName = profileSynchronizationResult.ProfileInformation.SenderName;
createdAccount.Base64ProfilePictureData = profileSynchronizationResult.ProfileInformation.Base64ProfilePictureData;
await AccountService.CreateAccountAsync(createdAccount, null);
await AccountService.UpdateProfileInformationAsync(createdAccount.Id, profileSynchronizationResult.ProfileInformation);
// Sync profile information if supported.
if (createdAccount.IsProfileInfoSyncSupported)
{
// Start profile information synchronization.
// It's only available for Outlook and Gmail synchronizers.
var profileSyncOptions = new MailSynchronizationOptions()
{
AccountId = createdAccount.Id,
Type = MailSynchronizationType.UpdateProfile
};
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 synchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<CalendarSynchronizationResult, NewCalendarSynchronizationRequested>(new NewCalendarSynchronizationRequested(synchronizationOptions, SynchronizationSource.Client));
}
accountCreationDialog.State = AccountCreationDialogState.FetchingEvents;
// Start synchronizing events.
var synchronizationOptions = new CalendarSynchronizationOptions()
{
AccountId = createdAccount.Id,
Type = CalendarSynchronizationType.CalendarMetadata
};
var synchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<CalendarSynchronizationResult, NewCalendarSynchronizationRequested>(new NewCalendarSynchronizationRequested(synchronizationOptions, SynchronizationSource.Client));
}
}

View File

@@ -21,346 +21,347 @@ using Wino.Messaging.Client.Calendar;
using Wino.Messaging.Client.Navigation;
using Wino.Messaging.Server;
namespace Wino.Calendar.ViewModels;
public partial class AppShellViewModel : CalendarBaseViewModel,
IRecipient<VisibleDateRangeChangedMessage>,
IRecipient<CalendarEnableStatusChangedMessage>,
IRecipient<NavigateManageAccountsRequested>,
IRecipient<CalendarDisplayTypeChangedMessage>,
IRecipient<DetailsPageStateChangedMessage>
namespace Wino.Calendar.ViewModels
{
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 partial class AppShellViewModel : CalendarBaseViewModel,
IRecipient<VisibleDateRangeChangedMessage>,
IRecipient<CalendarEnableStatusChangedMessage>,
IRecipient<NavigateManageAccountsRequested>,
IRecipient<CalendarDisplayTypeChangedMessage>,
IRecipient<DetailsPageStateChangedMessage>
{
_accountService = accountService;
_calendarService = calendarService;
public IPreferencesService PreferencesService { get; }
public IStatePersistanceService StatePersistenceService { get; }
public IAccountCalendarStateService AccountCalendarStateService { get; }
public INavigationService NavigationService { get; }
public IWinoServerConnectionManager ServerConnectionManager { get; }
AccountCalendarStateService = accountCalendarStateService;
AccountCalendarStateService.AccountCalendarSelectionStateChanged += UpdateAccountCalendarRequested;
AccountCalendarStateService.CollectiveAccountGroupSelectionStateChanged += AccountCalendarStateCollectivelyChanged;
[ObservableProperty]
private bool _isEventDetailsPageActive;
NavigationService = navigationService;
ServerConnectionManager = serverConnectionManager;
PreferencesService = preferencesService;
[ObservableProperty]
private int _selectedMenuItemIndex = -1;
StatePersistenceService = statePersistanceService;
StatePersistenceService.StatePropertyChanged += PrefefencesChanged;
}
[ObservableProperty]
private bool isCalendarEnabled;
private void SelectedCalendarItemsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
throw new NotImplementedException();
}
/// <summary>
/// Gets or sets the active connection status of the Wino server.
/// </summary>
[ObservableProperty]
private WinoServerConnectionStatus activeConnectionStatus;
private void PrefefencesChanged(object sender, string e)
{
if (e == nameof(StatePersistenceService.CalendarDisplayType))
/// <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)
{
Messenger.Send(new CalendarDisplayTypeChangedMessage(StatePersistenceService.CalendarDisplayType));
_accountService = accountService;
_calendarService = calendarService;
AccountCalendarStateService = accountCalendarStateService;
AccountCalendarStateService.AccountCalendarSelectionStateChanged += UpdateAccountCalendarRequested;
AccountCalendarStateService.CollectiveAccountGroupSelectionStateChanged += AccountCalendarStateCollectivelyChanged;
// Change the calendar.
DateClicked(new CalendarViewDayClickedEventArgs(GetDisplayTypeSwitchDate()));
NavigationService = navigationService;
ServerConnectionManager = serverConnectionManager;
PreferencesService = preferencesService;
StatePersistenceService = statePersistanceService;
StatePersistenceService.StatePropertyChanged += PrefefencesChanged;
}
}
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
{
base.OnNavigatedTo(mode, parameters);
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
private void SelectedCalendarItemsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
await _accountCalendarUpdateSemaphoreSlim.WaitAsync();
throw new NotImplementedException();
}
foreach (var calendar in e.AccountCalendars)
private void PrefefencesChanged(object sender, string e)
{
if (e == nameof(StatePersistenceService.CalendarDisplayType))
{
await _calendarService.UpdateAccountCalendarAsync(calendar.AccountCalendar).ConfigureAwait(false);
Messenger.Send(new CalendarDisplayTypeChangedMessage(StatePersistenceService.CalendarDisplayType));
// Change the calendar.
DateClicked(new CalendarViewDayClickedEventArgs(GetDisplayTypeSwitchDate()));
}
}
catch (Exception ex)
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
{
Log.Error(ex, "Error while waiting for account calendar update semaphore.");
base.OnNavigatedTo(mode, parameters);
UpdateDateNavigationHeaderItems();
await InitializeAccountCalendarsAsync();
TodayClicked();
}
finally
private async void AccountCalendarStateCollectivelyChanged(object sender, GroupedAccountCalendarViewModel e)
{
_accountCalendarUpdateSemaphoreSlim.Release();
}
}
// 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.
private async void UpdateAccountCalendarRequested(object sender, AccountCalendarViewModel e)
=> await _calendarService.UpdateAccountCalendarAsync(e.AccountCalendar).ConfigureAwait(false);
private async Task InitializeAccountCalendarsAsync()
{
await Dispatcher.ExecuteOnUIThread(() => AccountCalendarStateService.ClearGroupedAccountCalendar());
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
foreach (var account in accounts)
{
var accountCalendars = await _calendarService.GetAccountCalendarsAsync(account.Id).ConfigureAwait(false);
var calendarViewModels = new List<AccountCalendarViewModel>();
foreach (var calendar in accountCalendars)
// Update all calendar states at once.
try
{
var calendarViewModel = new AccountCalendarViewModel(account, calendar);
await _accountCalendarUpdateSemaphoreSlim.WaitAsync();
calendarViewModels.Add(calendarViewModel);
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();
}
}
private async void UpdateAccountCalendarRequested(object sender, AccountCalendarViewModel e)
=> await _calendarService.UpdateAccountCalendarAsync(e.AccountCalendar).ConfigureAwait(false);
private async Task InitializeAccountCalendarsAsync()
{
await Dispatcher.ExecuteOnUIThread(() => AccountCalendarStateService.ClearGroupedAccountCalendar());
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
foreach (var account in accounts)
{
var accountCalendars = await _calendarService.GetAccountCalendarsAsync(account.Id).ConfigureAwait(false);
var calendarViewModels = new List<AccountCalendarViewModel>();
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;
}
var groupedAccountCalendarViewModel = new GroupedAccountCalendarViewModel(account, calendarViewModels);
return DateTime.Today.Date;
}
await Dispatcher.ExecuteOnUIThread(() =>
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)
{
AccountCalendarStateService.AddGroupedAccountCalendar(groupedAccountCalendarViewModel);
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;
});
}
}
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;
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -8,119 +8,120 @@ using Wino.Core.Domain.Translations;
using Wino.Core.ViewModels;
using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.ViewModels;
public partial class CalendarSettingsPageViewModel : CalendarBaseViewModel
namespace Wino.Calendar.ViewModels
{
[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)
public partial class CalendarSettingsPageViewModel : CalendarBaseViewModel
{
PreferencesService = preferencesService;
[ObservableProperty]
private double _cellHourHeight;
var currentLanguageLanguageCode = WinoTranslationDictionary.GetLanguageFileNameRelativePath(preferencesService.CurrentLanguage);
[ObservableProperty]
private int _selectedFirstDayOfWeekIndex;
var cultureInfo = new CultureInfo(currentLanguageLanguageCode);
[ObservableProperty]
private bool _is24HourHeaders;
// Populate the day names list
for (var i = 0; i < 7; i++)
[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)
{
_dayNames.Add(cultureInfo.DateTimeFormat.DayNames[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;
}
var cultureFirstDayName = cultureInfo.DateTimeFormat.GetDayName(preferencesService.FirstDayOfWeek);
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();
_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
public void SaveSettings()
{
0 => DayOfWeek.Sunday,
1 => DayOfWeek.Monday,
2 => DayOfWeek.Tuesday,
3 => DayOfWeek.Wednesday,
4 => DayOfWeek.Thursday,
5 => DayOfWeek.Friday,
6 => DayOfWeek.Saturday,
_ => throw new ArgumentOutOfRangeException()
};
if (!_isLoaded) return;
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.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.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.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.Prefer24HourTimeFormat = Is24HourHeaders;
PreferencesService.WorkingHourStart = WorkingHourStart;
PreferencesService.WorkingHourEnd = WorkingHourEnd;
PreferencesService.HourHeight = CellHourHeight;
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()
};
Messenger.Send(new CalendarSettingsUpdatedMessage());
PreferencesService.Prefer24HourTimeFormat = Is24HourHeaders;
PreferencesService.WorkingHourStart = WorkingHourStart;
PreferencesService.WorkingHourEnd = WorkingHourEnd;
PreferencesService.HourHeight = CellHourHeight;
Messenger.Send(new CalendarSettingsUpdatedMessage());
}
}
}

View File

@@ -1,12 +1,13 @@
using Microsoft.Extensions.DependencyInjection;
using Wino.Core;
namespace Wino.Calendar.ViewModels;
public static class CalendarViewModelContainerSetup
namespace Wino.Calendar.ViewModels
{
public static void RegisterCalendarViewModelServices(this IServiceCollection services)
public static class CalendarViewModelContainerSetup
{
services.RegisterCoreServices();
public static void RegisterCalendarViewModelServices(this IServiceCollection services)
{
services.RegisterCoreServices();
}
}
}

View File

@@ -4,66 +4,67 @@ using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Interfaces;
namespace Wino.Calendar.ViewModels.Data;
public partial class AccountCalendarViewModel : ObservableObject, IAccountCalendar
namespace Wino.Calendar.ViewModels.Data
{
public MailAccount Account { get; }
public AccountCalendar AccountCalendar { get; }
public AccountCalendarViewModel(MailAccount account, AccountCalendar accountCalendar)
public partial class AccountCalendarViewModel : ObservableObject, IAccountCalendar
{
Account = account;
AccountCalendar = accountCalendar;
public MailAccount Account { get; }
public AccountCalendar AccountCalendar { get; }
IsChecked = accountCalendar.IsExtended;
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; }
}
[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; }
}

View File

@@ -5,41 +5,42 @@ using Itenso.TimePeriod;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Interfaces;
namespace Wino.Calendar.ViewModels.Data;
public partial class CalendarItemViewModel : ObservableObject, ICalendarItem, ICalendarItemViewModel
namespace Wino.Calendar.ViewModels.Data
{
public CalendarItem CalendarItem { get; }
public string Title => CalendarItem.Title;
public Guid Id => CalendarItem.Id;
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)
public partial class CalendarItemViewModel : ObservableObject, ICalendarItem, ICalendarItemViewModel
{
CalendarItem = calendarItem;
}
public CalendarItem CalendarItem { get; }
public override string ToString() => CalendarItem.Title;
public string Title => CalendarItem.Title;
public Guid Id => CalendarItem.Id;
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)
{
CalendarItem = calendarItem;
}
public override string ToString() => CalendarItem.Title;
}
}

View File

@@ -6,140 +6,141 @@ using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using Wino.Core.Domain.Entities.Shared;
namespace Wino.Calendar.ViewModels.Data;
public partial class GroupedAccountCalendarViewModel : ObservableObject
namespace Wino.Calendar.ViewModels.Data
{
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 partial class GroupedAccountCalendarViewModel : ObservableObject
{
Account = account;
AccountCalendars = new ObservableCollection<AccountCalendarViewModel>(calendarViewModels);
public event EventHandler CollectiveSelectionStateChanged;
public event EventHandler<AccountCalendarViewModel> CalendarSelectionStateChanged;
ManageIsCheckedState();
public MailAccount Account { get; }
public ObservableCollection<AccountCalendarViewModel> AccountCalendars { get; }
foreach (var calendarViewModel in calendarViewModels)
public GroupedAccountCalendarViewModel(MailAccount account, IEnumerable<AccountCalendarViewModel> calendarViewModels)
{
calendarViewModel.PropertyChanged += CalendarPropertyChanged;
}
Account = account;
AccountCalendars = new ObservableCollection<AccountCalendarViewModel>(calendarViewModels);
AccountCalendars.CollectionChanged += CalendarListUpdated;
}
ManageIsCheckedState();
private void CalendarListUpdated(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (AccountCalendarViewModel calendar in e.NewItems)
foreach (var calendarViewModel in calendarViewModels)
{
calendar.PropertyChanged += CalendarPropertyChanged;
calendarViewModel.PropertyChanged += CalendarPropertyChanged;
}
AccountCalendars.CollectionChanged += CalendarListUpdated;
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
private void CalendarListUpdated(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
foreach (AccountCalendarViewModel calendar in e.OldItems)
if (e.Action == NotifyCollectionChangedAction.Add)
{
calendar.PropertyChanged -= CalendarPropertyChanged;
foreach (AccountCalendarViewModel calendar in e.NewItems)
{
calendar.PropertyChanged += CalendarPropertyChanged;
}
}
}
else if (e.Action == NotifyCollectionChangedAction.Reset)
{
foreach (AccountCalendarViewModel calendar in e.OldItems)
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
calendar.PropertyChanged -= CalendarPropertyChanged;
foreach (AccountCalendarViewModel calendar in e.OldItems)
{
calendar.PropertyChanged -= CalendarPropertyChanged;
}
}
}
}
private void CalendarPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (sender is AccountCalendarViewModel viewModel)
{
if (e.PropertyName == nameof(AccountCalendarViewModel.IsChecked))
else if (e.Action == NotifyCollectionChangedAction.Reset)
{
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());
foreach (AccountCalendarViewModel calendar in e.OldItems)
{
calendar.PropertyChanged -= CalendarPropertyChanged;
}
}
}
_isExternalPropChangeBlocked = false;
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);
}
}
}
CollectiveSelectionStateChanged?.Invoke(this, EventArgs.Empty);
}
[ObservableProperty]
private bool _isExpanded = true;
private void UpdateCalendarCheckedState(AccountCalendarViewModel accountCalendarViewModel, bool newValue, bool ignoreValueCheck = false)
{
var currentValue = accountCalendarViewModel.IsChecked;
[ObservableProperty]
private bool? isCheckedState = true;
if (currentValue == newValue && !ignoreValueCheck) return;
private bool _isExternalPropChangeBlocked = false;
accountCalendarViewModel.IsChecked = newValue;
private void ManageIsCheckedState()
{
if (_isExternalPropChangeBlocked) return;
// No need to report.
if (_isExternalPropChangeBlocked == true) return;
_isExternalPropChangeBlocked = true;
CalendarSelectionStateChanged?.Invoke(this, accountCalendarViewModel);
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);
}
}
}

View File

@@ -12,104 +12,105 @@ using Wino.Core.Domain.Models.Navigation;
using Wino.Core.ViewModels;
using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.ViewModels;
public partial class EventDetailsPageViewModel : CalendarBaseViewModel
namespace Wino.Calendar.ViewModels
{
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 => CurrentEvent?.IsRecurringChild ?? false;
#endregion
public EventDetailsPageViewModel(ICalendarService calendarService, INativeAppService nativeAppService, IPreferencesService preferencesService)
public partial class EventDetailsPageViewModel : CalendarBaseViewModel
{
_calendarService = calendarService;
_nativeAppService = nativeAppService;
_preferencesService = preferencesService;
private readonly ICalendarService _calendarService;
private readonly INativeAppService _nativeAppService;
private readonly IPreferencesService _preferencesService;
CurrentSettings = _preferencesService.GetCurrentCalendarSettings();
}
public CalendarSettings CurrentSettings { get; }
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
{
base.OnNavigatedTo(mode, parameters);
#region Details
Messenger.Send(new DetailsPageStateChangedMessage(true));
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanViewSeries))]
private CalendarItemViewModel _currentEvent;
if (parameters == null || parameters is not CalendarItemTarget args)
return;
[ObservableProperty]
private CalendarItemViewModel _seriesParent;
await LoadCalendarItemTargetAsync(args);
}
public bool CanViewSeries => CurrentEvent?.IsRecurringChild ?? false;
private async Task LoadCalendarItemTargetAsync(CalendarItemTarget target)
{
try
#endregion
public EventDetailsPageViewModel(ICalendarService calendarService, INativeAppService nativeAppService, IPreferencesService preferencesService)
{
var currentEventItem = await _calendarService.GetCalendarItemTargetAsync(target);
_calendarService = calendarService;
_nativeAppService = nativeAppService;
_preferencesService = preferencesService;
if (currentEventItem == null)
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)
return;
CurrentEvent = new CalendarItemViewModel(currentEventItem);
await LoadCalendarItemTargetAsync(args);
}
var attendees = await _calendarService.GetAttendeesAsync(currentEventItem.EventTrackingId);
foreach (var item in attendees)
private async Task LoadCalendarItemTargetAsync(CalendarItemTarget target)
{
try
{
CurrentEvent.Attendees.Add(item);
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);
}
}
catch (Exception ex)
public override void OnNavigatedFrom(NavigationMode mode, object parameters)
{
Debug.WriteLine(ex.Message);
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;
}
}
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;
}
}

View File

@@ -6,25 +6,26 @@ using System.Linq;
using Wino.Calendar.ViewModels.Data;
using Wino.Core.Domain.Entities.Shared;
namespace Wino.Calendar.ViewModels.Interfaces;
public interface IAccountCalendarStateService : INotifyPropertyChanged
namespace Wino.Calendar.ViewModels.Interfaces
{
ReadOnlyObservableCollection<GroupedAccountCalendarViewModel> GroupedAccountCalendars { get; }
public interface IAccountCalendarStateService : INotifyPropertyChanged
{
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; }
}
}

View File

@@ -1,13 +1,14 @@
using Wino.Calendar.ViewModels.Data;
namespace Wino.Calendar.ViewModels.Messages;
public class CalendarItemDoubleTappedMessage
namespace Wino.Calendar.ViewModels.Messages
{
public CalendarItemDoubleTappedMessage(CalendarItemViewModel calendarItemViewModel)
public class CalendarItemDoubleTappedMessage
{
CalendarItemViewModel = calendarItemViewModel;
}
public CalendarItemDoubleTappedMessage(CalendarItemViewModel calendarItemViewModel)
{
CalendarItemViewModel = calendarItemViewModel;
}
public CalendarItemViewModel CalendarItemViewModel { get; }
public CalendarItemViewModel CalendarItemViewModel { get; }
}
}

View File

@@ -1,13 +1,14 @@
using Wino.Calendar.ViewModels.Data;
namespace Wino.Calendar.ViewModels.Messages;
public class CalendarItemRightTappedMessage
namespace Wino.Calendar.ViewModels.Messages
{
public CalendarItemRightTappedMessage(CalendarItemViewModel calendarItemViewModel)
public class CalendarItemRightTappedMessage
{
CalendarItemViewModel = calendarItemViewModel;
}
public CalendarItemRightTappedMessage(CalendarItemViewModel calendarItemViewModel)
{
CalendarItemViewModel = calendarItemViewModel;
}
public CalendarItemViewModel CalendarItemViewModel { get; }
public CalendarItemViewModel CalendarItemViewModel { get; }
}
}

View File

@@ -1,16 +1,17 @@
using Wino.Calendar.ViewModels.Data;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Calendar.ViewModels.Messages;
public class CalendarItemTappedMessage
namespace Wino.Calendar.ViewModels.Messages
{
public CalendarItemTappedMessage(CalendarItemViewModel calendarItemViewModel, CalendarDayModel clickedPeriod)
public class CalendarItemTappedMessage
{
CalendarItemViewModel = calendarItemViewModel;
ClickedPeriod = clickedPeriod;
}
public CalendarItemTappedMessage(CalendarItemViewModel calendarItemViewModel, CalendarDayModel clickedPeriod)
{
CalendarItemViewModel = calendarItemViewModel;
ClickedPeriod = clickedPeriod;
}
public CalendarItemViewModel CalendarItemViewModel { get; }
public CalendarDayModel ClickedPeriod { get; }
public CalendarItemViewModel CalendarItemViewModel { get; }
public CalendarDayModel ClickedPeriod { get; }
}
}

View File

@@ -1,13 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Platforms>x86;x64;arm64</Platforms>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>12</LangVersion>
<Platforms>AnyCPU;x64;x86</Platforms>
<AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="TimePeriodLibrary.NET" />
<PackageReference Include="TimePeriodLibrary.NET" Version="2.1.5" />
</ItemGroup>
<ItemGroup>

308
Wino.Calendar.sln Normal file
View File

@@ -0,0 +1,308 @@
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

View File

@@ -6,18 +6,19 @@ using Windows.UI.Xaml.Media.Animation;
using Wino.Activation;
using Wino.Calendar.Views;
namespace Wino.Calendar.Activation;
public class DefaultActivationHandler : ActivationHandler<IActivatedEventArgs>
namespace Wino.Calendar.Activation
{
protected override Task HandleInternalAsync(IActivatedEventArgs args)
public class DefaultActivationHandler : ActivationHandler<IActivatedEventArgs>
{
(Window.Current.Content as Frame).Navigate(typeof(AppShell), null, new DrillInNavigationTransitionInfo());
protected override Task HandleInternalAsync(IActivatedEventArgs args)
{
(Window.Current.Content as Frame).Navigate(typeof(AppShell), null, new DrillInNavigationTransitionInfo());
return Task.CompletedTask;
return Task.CompletedTask;
}
// Only navigate if Frame content doesn't exist.
protected override bool CanHandleInternal(IActivatedEventArgs args)
=> (Window.Current?.Content as Frame)?.Content == null;
}
// Only navigate if Frame content doesn't exist.
protected override bool CanHandleInternal(IActivatedEventArgs args)
=> (Window.Current?.Content as Frame)?.Content == null;
}

View File

@@ -24,136 +24,141 @@ using Wino.Messaging.Client.Connection;
using Wino.Messaging.Server;
using Wino.Services;
namespace Wino.Calendar;
public sealed partial class App : WinoApplication, IRecipient<NewCalendarSynchronizationRequested>
namespace Wino.Calendar
{
private BackgroundTaskDeferral connectionBackgroundTaskDeferral;
public App()
public sealed partial class App : WinoApplication, IRecipient<NewCalendarSynchronizationRequested>
{
InitializeComponent();
WeakReferenceMessenger.Default.Register<NewCalendarSynchronizationRequested>(this);
}
public override string AppCenterKey => "dfdad6ab-95f9-44cc-9112-45ec6730c49e";
public override IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
private BackgroundTaskDeferral connectionBackgroundTaskDeferral;
private BackgroundTaskDeferral toastActionBackgroundTaskDeferral;
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)
public App()
{
await ActivateWinoAsync(args);
InitializeComponent();
WeakReferenceMessenger.Default.Register(this);
}
}
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)
public override IServiceProvider ConfigureServices()
{
LogActivation("OnBackgroundActivated -> AppServiceTriggerDetails received.");
var services = new ServiceCollection();
// Only accept connections from callers in the same package
if (appServiceTriggerDetails.CallerPackageFamilyName == Package.Current.Id.FamilyName)
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)
{
// Connection established from the fulltrust process
await ActivateWinoAsync(args);
}
}
connectionBackgroundTaskDeferral = args.TaskInstance.GetDeferral();
args.TaskInstance.Canceled += OnConnectionBackgroundTaskCanceled;
protected override IEnumerable<ActivationHandler> GetActivationHandlers()
{
return null;
}
AppServiceConnectionManager.Connection = appServiceTriggerDetails.AppServiceConnection;
protected override ActivationHandler<IActivatedEventArgs> GetDefaultActivationHandler()
=> new DefaultActivationHandler();
WeakReferenceMessenger.Default.Send(new WinoServerConnectionEstablished());
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);
}
}
}
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);
}
}
}

View File

@@ -1,40 +1,41 @@
using System;
using Windows.Foundation;
namespace Wino.Calendar.Args;
/// <summary>
/// When a new timeline cell is selected.
/// </summary>
public class TimelineCellSelectedArgs : EventArgs
namespace Wino.Calendar.Args
{
public TimelineCellSelectedArgs(DateTime clickedDate, Point canvasPoint, Point positionerPoint, Size cellSize)
/// <summary>
/// When a new timeline cell is selected.
/// </summary>
public class TimelineCellSelectedArgs : EventArgs
{
ClickedDate = clickedDate;
CanvasPoint = canvasPoint;
PositionerPoint = positionerPoint;
CellSize = 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; }
}
/// <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; }
}

View File

@@ -1,8 +1,9 @@
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 { }
}

View File

@@ -2,29 +2,30 @@
using Windows.UI.Xaml;
using Wino.Calendar.ViewModels.Data;
namespace Wino.Calendar.Controls;
public partial class CalendarItemCommandBarFlyout : CommandBarFlyout
namespace Wino.Calendar.Controls
{
public static readonly DependencyProperty ItemProperty = DependencyProperty.Register(nameof(Item), typeof(CalendarItemViewModel), typeof(CalendarItemCommandBarFlyout), new PropertyMetadata(null, new PropertyChangedCallback(OnItemChanged)));
public CalendarItemViewModel Item
public class CalendarItemCommandBarFlyout : CommandBarFlyout
{
get { return (CalendarItemViewModel)GetValue(ItemProperty); }
set { SetValue(ItemProperty, value); }
}
public static readonly DependencyProperty ItemProperty = DependencyProperty.Register(nameof(Item), typeof(CalendarItemViewModel), typeof(CalendarItemCommandBarFlyout), new PropertyMetadata(null, new PropertyChangedCallback(OnItemChanged)));
private static void OnItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CalendarItemCommandBarFlyout flyout)
public CalendarItemViewModel Item
{
flyout.UpdateMenuItems();
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()
{
}
}
private void UpdateMenuItems()
{
}
}

View File

@@ -9,189 +9,190 @@ using Wino.Calendar.ViewModels.Messages;
using Wino.Core.Domain;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Calendar.Controls;
public sealed partial class CalendarItemControl : UserControl
namespace Wino.Calendar.Controls
{
// 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
public sealed partial class CalendarItemControl : UserControl
{
get { return (bool)GetValue(IsCustomEventAreaProperty); }
set { SetValue(IsCustomEventAreaProperty, value); }
}
// Single tap has a delay to report double taps properly.
private bool isSingleTap = false;
/// <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); }
}
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)));
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)
/// <summary>
/// Whether the control is displaying as regular event or all-multi day area in the day control.
/// </summary>
public bool IsCustomEventArea
{
control.UpdateControlVisuals();
get { return (bool)GetValue(IsCustomEventAreaProperty); }
set { SetValue(IsCustomEventAreaProperty, value); }
}
}
private static void OnCalendarItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CalendarItemControl control)
/// <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
{
control.UpdateControlVisuals();
get { return (CalendarDayModel)GetValue(DisplayingDateProperty); }
set { SetValue(DisplayingDateProperty, 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.
if (CalendarItem == null) return;
if (DisplayingDate == null) return;
if (CalendarItem.IsMultiDayEvent)
public string CalendarItemTitle
{
// 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.
get { return (string)GetValue(CalendarItemTitleProperty); }
set { SetValue(CalendarItemTitleProperty, value); }
}
var periodRelation = CalendarItem.Period.GetRelation(DisplayingDate.Period);
public CalendarItemViewModel CalendarItem
{
get { return (CalendarItemViewModel)GetValue(CalendarItemProperty); }
set { SetValue(CalendarItemProperty, value); }
}
if (periodRelation == Itenso.TimePeriod.PeriodRelation.StartInside ||
periodRelation == PeriodRelation.EnclosingStartTouching)
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)
{
// hour -> title
CalendarItemTitle = $"{DisplayingDate.CalendarRenderOptions.CalendarSettings.GetTimeString(CalendarItem.StartDate.TimeOfDay)} -> {CalendarItem.Title}";
control.UpdateControlVisuals();
}
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}";
private static void OnCalendarItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CalendarItemControl control)
{
control.UpdateControlVisuals();
}
}
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)
{
// 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.
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}");
}
else
{
// Not expected, but there it is.
CalendarItemTitle = CalendarItem.Title;
}
// Debug.WriteLine($"{CalendarItem.Title} Period relation with {DisplayingDate.Period.ToString()}: {periodRelation}");
}
else
{
CalendarItemTitle = CalendarItem.Title;
UpdateVisualStates();
}
UpdateVisualStates();
}
private void UpdateVisualStates()
{
if (CalendarItem == null) return;
if (CalendarItem.IsAllDayEvent)
private void UpdateVisualStates()
{
VisualStateManager.GoToState(this, "AllDayEvent", true);
}
else if (CalendarItem.IsMultiDayEvent)
{
if (IsCustomEventArea)
if (CalendarItem == null) return;
if (CalendarItem.IsAllDayEvent)
{
VisualStateManager.GoToState(this, "CustomAreaMultiDayEvent", true);
VisualStateManager.GoToState(this, "AllDayEvent", true);
}
else if (CalendarItem.IsMultiDayEvent)
{
if (IsCustomEventArea)
{
VisualStateManager.GoToState(this, "CustomAreaMultiDayEvent", true);
}
else
{
// Hide it.
VisualStateManager.GoToState(this, "MultiDayEvent", true);
}
}
else
{
// Hide it.
VisualStateManager.GoToState(this, "MultiDayEvent", true);
VisualStateManager.GoToState(this, "RegularEvent", true);
}
}
else
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)
{
VisualStateManager.GoToState(this, "RegularEvent", true);
if (CalendarItem == null) return;
isSingleTap = true;
await Task.Delay(100);
if (isSingleTap)
{
WeakReferenceMessenger.Default.Send(new CalendarItemTappedMessage(CalendarItem, DisplayingDate));
}
}
}
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)
private void ControlDoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
WeakReferenceMessenger.Default.Send(new CalendarItemTappedMessage(CalendarItem, DisplayingDate));
if (CalendarItem == null) return;
isSingleTap = false;
WeakReferenceMessenger.Default.Send(new CalendarItemDoubleTappedMessage(CalendarItem));
}
}
private void ControlDoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
if (CalendarItem == null) return;
private void ControlRightTapped(object sender, RightTappedRoutedEventArgs 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));
WeakReferenceMessenger.Default.Send(new CalendarItemRightTappedMessage(CalendarItem));
}
}
}

View File

@@ -1,42 +1,43 @@
using Windows.UI.Xaml.Automation.Peers;
using Windows.UI.Xaml.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
namespace Wino.Calendar.Controls
{
private const string PART_PreviousButton = "PreviousButtonHorizontal";
private const string PART_NextButton = "NextButtonHorizontal";
private Button PreviousButton;
private Button NextButton;
protected override void OnApplyTemplate()
/// <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
{
base.OnApplyTemplate();
private const string PART_PreviousButton = "PreviousButtonHorizontal";
private const string PART_NextButton = "NextButtonHorizontal";
PreviousButton = GetTemplateChild(PART_PreviousButton) as Button;
NextButton = GetTemplateChild(PART_NextButton) as Button;
private Button PreviousButton;
private Button NextButton;
// Hide navigation buttons
PreviousButton.Opacity = NextButton.Opacity = 0;
PreviousButton.IsHitTestVisible = NextButton.IsHitTestVisible = false;
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
var t = FindName("ScrollingHost");
}
PreviousButton = GetTemplateChild(PART_PreviousButton) as Button;
NextButton = GetTemplateChild(PART_NextButton) as Button;
public void GoPreviousFlip()
{
var backPeer = new ButtonAutomationPeer(PreviousButton);
backPeer.Invoke();
}
// Hide navigation buttons
PreviousButton.Opacity = NextButton.Opacity = 0;
PreviousButton.IsHitTestVisible = NextButton.IsHitTestVisible = false;
public void GoNextFlip()
{
var nextPeer = new ButtonAutomationPeer(NextButton);
nextPeer.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();
}
}
}

View File

@@ -3,75 +3,76 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Calendar.Controls;
public partial class DayColumnControl : Control
namespace Wino.Calendar.Controls
{
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
public class DayColumnControl : Control
{
get { return (CalendarDayModel)GetValue(DayModelProperty); }
set { SetValue(DayModelProperty, value); }
}
private const string PART_HeaderDateDayText = nameof(PART_HeaderDateDayText);
private const string PART_IsTodayBorder = nameof(PART_IsTodayBorder);
private const string PART_ColumnHeaderText = nameof(PART_ColumnHeaderText);
public static readonly DependencyProperty DayModelProperty = DependencyProperty.Register(nameof(DayModel), typeof(CalendarDayModel), typeof(DayColumnControl), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
private const string PART_AllDayItemsControl = nameof(PART_AllDayItemsControl);
public DayColumnControl()
{
DefaultStyleKey = typeof(DayColumnControl);
}
private const string TodayState = nameof(TodayState);
private const string NotTodayState = nameof(NotTodayState);
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
private TextBlock HeaderDateDayText;
private TextBlock ColumnHeaderText;
private Border IsTodayBorder;
private ItemsControl AllDayItemsControl;
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)
public CalendarDayModel DayModel
{
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);
get { return (CalendarDayModel)GetValue(DayModelProperty); }
set { SetValue(DayModelProperty, value); }
}
AllDayItemsControl.ItemsSource = DayModel.EventsCollection.AllDayEvents;
public static readonly DependencyProperty DayModelProperty = DependencyProperty.Register(nameof(DayModel), typeof(CalendarDayModel), typeof(DayColumnControl), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
bool isToday = DayModel.RepresentingDate.Date == DateTime.Now.Date;
public DayColumnControl()
{
DefaultStyleKey = typeof(DayColumnControl);
}
VisualStateManager.GoToState(this, isToday ? TodayState : NotTodayState, false);
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
UpdateLayout();
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();
}
}
}

View File

@@ -3,54 +3,55 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
namespace Wino.Calendar.Controls;
public partial class DayHeaderControl : Control
namespace Wino.Calendar.Controls
{
private const string PART_DayHeaderTextBlock = nameof(PART_DayHeaderTextBlock);
private TextBlock HeaderTextblock;
public DayHeaderDisplayType DisplayType
public class DayHeaderControl : Control
{
get { return (DayHeaderDisplayType)GetValue(DisplayTypeProperty); }
set { SetValue(DisplayTypeProperty, value); }
}
private const string PART_DayHeaderTextBlock = nameof(PART_DayHeaderTextBlock);
private TextBlock HeaderTextblock;
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)
public DayHeaderDisplayType DisplayType
{
headerControl.UpdateHeaderText();
get { return (DayHeaderDisplayType)GetValue(DisplayTypeProperty); }
set { SetValue(DisplayTypeProperty, value); }
}
}
private void UpdateHeaderText()
{
if (HeaderTextblock != null)
public DateTime Date
{
HeaderTextblock.Text = DisplayType == DayHeaderDisplayType.TwelveHour ? Date.ToString("h tt") : Date.ToString("HH:mm");
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");
}
}
}
}

View File

@@ -10,290 +10,291 @@ using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Calendar;
using Wino.Helpers;
namespace Wino.Calendar.Controls;
public partial class WinoCalendarControl : Control
namespace Wino.Calendar.Controls
{
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
public class WinoCalendarControl : Control
{
get { return (CalendarDisplayType)GetValue(DisplayTypeProperty); }
set { SetValue(DisplayTypeProperty, value); }
}
private const string PART_WinoFlipView = nameof(PART_WinoFlipView);
private const string PART_IdleGrid = nameof(PART_IdleGrid);
public CalendarOrientation Orientation
{
get { return (CalendarOrientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
public event EventHandler<TimelineCellSelectedArgs> TimelineCellSelected;
public event EventHandler<TimelineCellUnselectedArgs> TimelineCellUnselected;
public ItemsPanelTemplate VerticalItemsPanelTemplate
{
get { return (ItemsPanelTemplate)GetValue(VerticalItemsPanelTemplateProperty); }
set { SetValue(VerticalItemsPanelTemplateProperty, value); }
}
public event EventHandler ScrollPositionChanging;
public ItemsPanelTemplate HorizontalItemsPanelTemplate
{
get { return (ItemsPanelTemplate)GetValue(HorizontalItemsPanelTemplateProperty); }
set { SetValue(HorizontalItemsPanelTemplateProperty, value); }
}
#region Dependency Properties
public DayRangeRenderModel SelectedFlipViewDayRange
{
get { return (DayRangeRenderModel)GetValue(SelectedFlipViewDayRangeProperty); }
set { SetValue(SelectedFlipViewDayRangeProperty, 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 ScrollViewer ActiveScrollViewer
{
get { return (ScrollViewer)GetValue(ActiveScrollViewerProperty); }
set { SetValue(ActiveScrollViewerProperty, 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 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)
/// <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
{
control.ManageCalendarOrientation();
get { return (CalendarDisplayType)GetValue(DisplayTypeProperty); }
set { SetValue(DisplayTypeProperty, value); }
}
}
private static void OnIdleStateChanged(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
{
if (calendar is WinoCalendarControl calendarControl)
public CalendarOrientation Orientation
{
calendarControl.UpdateIdleState();
get { return (CalendarOrientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
}
private static void OnActiveVerticalScrollViewerChanged(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
{
if (calendar is WinoCalendarControl calendarControl)
public ItemsPanelTemplate VerticalItemsPanelTemplate
{
if (e.OldValue is ScrollViewer oldScrollViewer)
{
calendarControl.DeregisterScrollChanges(oldScrollViewer);
}
if (e.NewValue is ScrollViewer newScrollViewer)
{
calendarControl.RegisterScrollChanges(newScrollViewer);
}
calendarControl.ManageHighlightedDateRange();
get { return (ItemsPanelTemplate)GetValue(VerticalItemsPanelTemplateProperty); }
set { SetValue(VerticalItemsPanelTemplateProperty, value); }
}
}
private static void OnActiveCanvasChanged(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
{
if (calendar is WinoCalendarControl calendarControl)
public ItemsPanelTemplate HorizontalItemsPanelTemplate
{
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();
get { return (ItemsPanelTemplate)GetValue(HorizontalItemsPanelTemplateProperty); }
set { SetValue(HorizontalItemsPanelTemplateProperty, value); }
}
}
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, () =>
public DayRangeRenderModel SelectedFlipViewDayRange
{
double hourHeght = 60;
double totalHeight = ActiveScrollViewer.ScrollableHeight;
double scrollPosition = timeSpan.TotalHours * hourHeght;
get { return (DayRangeRenderModel)GetValue(SelectedFlipViewDayRangeProperty); }
set { SetValue(SelectedFlipViewDayRangeProperty, value); }
}
ActiveScrollViewer.ChangeView(null, scrollPosition, null, disableAnimation: false);
});
}
public void ResetTimelineSelection()
{
if (ActiveCanvas == null) return;
public ScrollViewer ActiveScrollViewer
{
get { return (ScrollViewer)GetValue(ActiveScrollViewerProperty); }
set { SetValue(ActiveScrollViewerProperty, value); }
}
ActiveCanvas.SelectedDateTime = null;
}
public WinoDayTimelineCanvas ActiveCanvas
{
get { return (WinoDayTimelineCanvas)GetValue(ActiveCanvasProperty); }
set { SetValue(ActiveCanvasProperty, value); }
}
public void GoNextRange()
{
if (InternalFlipView == null) return;
public bool IsFlipIdle
{
get { return (bool)GetValue(IsFlipIdleProperty); }
set { SetValue(IsFlipIdleProperty, value); }
}
InternalFlipView.GoNextFlip();
}
/// <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 void GoPreviousRange()
{
if (InternalFlipView == null) return;
public int SelectedFlipViewIndex
{
get { return (int)GetValue(SelectedFlipViewIndexProperty); }
set { SetValue(SelectedFlipViewIndexProperty, value); }
}
InternalFlipView.GoPreviousFlip();
}
#endregion
public void UnselectActiveTimelineCell()
{
if (ActiveCanvas == null) return;
private WinoCalendarFlipView InternalFlipView;
private Grid IdleGrid;
ActiveCanvas.SelectedDateTime = null;
}
public WinoCalendarControl()
{
DefaultStyleKey = typeof(WinoCalendarControl);
SizeChanged += CalendarSizeChanged;
}
public CalendarItemControl GetCalendarItemControl(CalendarItemViewModel calendarItemViewModel)
{
return this.FindDescendants<CalendarItemControl>().FirstOrDefault(a => a.CalendarItem == calendarItemViewModel);
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);
}
}
}

View File

@@ -8,178 +8,179 @@ using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Collections;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Calendar.Controls;
public partial class WinoCalendarFlipView : CustomCalendarFlipView
namespace Wino.Calendar.Controls
{
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 class WinoCalendarFlipView : CustomCalendarFlipView
{
get { return (WinoDayTimelineCanvas)GetValue(ActiveCanvasProperty); }
set { SetValue(ActiveCanvasProperty, value); }
}
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 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)
/// <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
{
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);
get { return (WinoDayTimelineCanvas)GetValue(ActiveCanvasProperty); }
set { SetValue(ActiveCanvasProperty, value); }
}
return ContainerFromIndex(SelectedIndex) as FlipViewItem;
}
private void UpdateActiveScrollViewer()
{
if (SelectedIndex < 0)
ActiveVerticalScrollViewer = null;
else
/// <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
{
GetCurrentFlipViewItem().ContinueWith(task =>
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)
{
if (task.IsCompletedSuccessfully)
{
var flipViewItem = task.Result;
_ = Dispatcher.TryRunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
ActiveVerticalScrollViewer = flipViewItem.FindDescendant<ScrollViewer>();
});
}
});
flipView.RegisterItemsSourceChange();
}
}
}
public void UpdateActiveCanvas()
{
if (SelectedIndex < 0)
ActiveCanvas = null;
else
private static void OnSelectedIndexUpdated(DependencyObject d, DependencyProperty e)
{
GetCurrentFlipViewItem().ContinueWith(task =>
if (d is WinoCalendarFlipView flipView)
{
if (task.IsCompletedSuccessfully)
{
var flipViewItem = task.Result;
_ = Dispatcher.TryRunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
ActiveCanvas = flipViewItem.FindDescendant<WinoDayTimelineCanvas>();
});
}
});
flipView.UpdateActiveCanvas();
flipView.UpdateActiveScrollViewer();
}
}
}
/// <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, () =>
private void RegisterItemsSourceChange()
{
// 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)
if (GetItemsSource() is INotifyCollectionChanged notifyCollectionChanged)
{
var navigationItemIndex = GetItemsSource().IndexOf(dayRange);
notifyCollectionChanged.CollectionChanged += ItemsSourceUpdated;
}
}
if (Math.Abs(navigationItemIndex - SelectedIndex) > 4)
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 =>
{
// 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 (task.IsCompletedSuccessfully)
{
if (SelectedIndex > navigationItemIndex)
var flipViewItem = task.Result;
_ = Dispatcher.TryRunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
GoPreviousFlip();
}
else
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, () =>
{
GoNextFlip();
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>;
private ObservableRangeCollection<DayRangeRenderModel> GetItemsSource()
=> ItemsSource as ObservableRangeCollection<DayRangeRenderModel>;
}
}

View File

@@ -11,283 +11,284 @@ using Wino.Calendar.Models;
using Wino.Calendar.ViewModels.Data;
using Wino.Core.Domain.Interfaces;
namespace Wino.Calendar.Controls;
public partial class WinoCalendarPanel : Panel
namespace Wino.Calendar.Controls
{
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
public class WinoCalendarPanel : Panel
{
get { return (ITimePeriod)GetValue(PeriodProperty); }
set { SetValue(PeriodProperty, value); }
}
private const double LastItemRightExtraMargin = 12d;
public double HourHeight
{
get { return (double)GetValue(HourHeightProperty); }
set { SetValue(HourHeightProperty, value); }
}
// Store each ICalendarItem measurements by their Id.
private readonly Dictionary<ICalendarItem, CalendarItemMeasurement> _measurements = new Dictionary<ICalendarItem, CalendarItemMeasurement>();
public Thickness EventItemMargin
{
get { return (Thickness)GetValue(EventItemMarginProperty); }
set { SetValue(EventItemMarginProperty, 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));
private void ResetMeasurements() => _measurements.Clear();
private double GetChildTopMargin(ICalendarItem calendarItemViewModel, double availableHeight)
{
var childStart = calendarItemViewModel.StartDate;
if (childStart <= Period.Start)
public ITimePeriod Period
{
// 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;
get { return (ITimePeriod)GetValue(PeriodProperty); }
set { SetValue(PeriodProperty, value); }
}
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)
public double HourHeight
{
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;
get { return (double)GetValue(HourHeightProperty); }
set { SetValue(HourHeightProperty, value); }
}
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)
public Thickness EventItemMargin
{
// We can't arrange this child.
if (!(control.Content is ICalendarItem child)) continue;
get { return (Thickness)GetValue(EventItemMarginProperty); }
set { SetValue(EventItemMarginProperty, value); }
}
bool isHorizontallyLastItem = false;
private void ResetMeasurements() => _measurements.Clear();
double childWidth = 0,
childHeight = Math.Max(0, GetChildHeight(child)),
childTop = Math.Max(0, GetChildTopMargin(child, availableHeight)),
childLeft = 0;
private double GetChildTopMargin(ICalendarItem calendarItemViewModel, double availableHeight)
{
var childStart = calendarItemViewModel.StartDate;
// No need to measure anything here.
if (childHeight == 0) continue;
if (!_measurements.ContainsKey(child))
if (childStart <= Period.Start)
{
// Multi-day event.
// 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.
childLeft = 0;
childWidth = availableWidth;
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
{
var childMeasurement = _measurements[child];
childWidth = Math.Max(0, GetChildWidth(childMeasurement, finalSize.Width));
childLeft = Math.Max(0, GetChildLeftMargin(childMeasurement, availableWidth));
isHorizontallyLastItem = childMeasurement.Right == 1;
// Multi-day event.
// Check how many of the event falls into the current period.
childDurationInMinutes = (child.Period.End - Period.Start).TotalMinutes;
}
// Add additional right margin to items that falls on the right edge of the panel.
double extraRightMargin = 0;
return (childDurationInMinutes / 1440) * availableHeight;
}
// Multi-day events don't have any margin and their hit test is disabled.
if (!child.IsMultiDayEvent)
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)
{
// Max of 5% of the width or 20px max.
extraRightMargin = isHorizontallyLastItem ? Math.Max(LastItemRightExtraMargin, finalSize.Width * 5 / 100) : 0;
// 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}");
}
if (childWidth < 0) childWidth = 1;
// Regular events must have 2px margin
if (!child.IsMultiDayEvent && !child.IsAllDayEvent)
return finalSize;
}
#region ColumSpanning and Packing Algorithm
private void AddOrUpdateMeasurement(ICalendarItem calendarItem, CalendarItemMeasurement measurement)
{
if (_measurements.ContainsKey(calendarItem))
{
childLeft += 2;
childTop += 2;
childHeight -= 2;
childWidth -= 2;
_measurements[calendarItem] = measurement;
}
else
{
_measurements.Add(calendarItem, measurement);
}
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))
// Pick the left and right positions of each event, such that there are no overlap.
private void LayoutEvents(IEnumerable<ICalendarItem> events)
{
_measurements[calendarItem] = measurement;
}
else
{
_measurements.Add(calendarItem, measurement);
}
}
var columns = new List<List<ICalendarItem>>();
DateTime? lastEventEnding = null;
// 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;
foreach (var ev in events.OrderBy(ev => ev.StartDate).ThenBy(ev => ev.EndDate))
{
// Multi-day events are not measured.
if (ev.IsMultiDayEvent) continue;
foreach (var ev in events.OrderBy(ev => ev.StartDate).ThenBy(ev => ev.EndDate))
{
// Multi-day events are not measured.
if (ev.IsMultiDayEvent) continue;
if (ev.Period.Start >= lastEventEnding)
{
PackEvents(columns);
columns.Clear();
lastEventEnding = null;
}
if (ev.Period.Start >= lastEventEnding)
bool placed = false;
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)
{
PackEvents(columns);
columns.Clear();
lastEventEnding = null;
}
}
bool placed = false;
// 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)
{
if (!col.Last().Period.OverlapsWith(ev.Period))
foreach (var ev in col)
{
col.Add(ev);
placed = true;
break;
int colSpan = ExpandEvent(ev, iColumn, columns);
var leftWeight = iColumn / numColumns;
var rightWeight = (iColumn + colSpan) / numColumns;
AddOrUpdateMeasurement(ev, new CalendarItemMeasurement(leftWeight, rightWeight));
}
}
if (!placed)
{
columns.Add(new List<ICalendarItem> { ev });
}
if (lastEventEnding == null || ev.Period.End > lastEventEnding.Value)
{
lastEventEnding = ev.Period.End;
iColumn++;
}
}
if (columns.Count > 0)
// 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)
{
PackEvents(columns);
}
}
int colSpan = 1;
// 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)
foreach (var col in columns.Skip(iColumn + 1))
{
int colSpan = ExpandEvent(ev, iColumn, columns);
foreach (var ev1 in col)
{
if (ev1.Period.OverlapsWith(ev.Period)) return colSpan;
}
var leftWeight = iColumn / numColumns;
var rightWeight = (iColumn + colSpan) / numColumns;
AddOrUpdateMeasurement(ev, new CalendarItemMeasurement(leftWeight, rightWeight));
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++;
return colSpan;
}
return colSpan;
#endregion
}
#endregion
}

View File

@@ -4,88 +4,89 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
namespace Wino.Calendar.Controls;
public partial class WinoCalendarTypeSelectorControl : Control
namespace Wino.Calendar.Controls
{
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
public class WinoCalendarTypeSelectorControl : Control
{
get { return (ICommand)GetValue(TodayClickedCommandProperty); }
set { SetValue(TodayClickedCommandProperty, value); }
}
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 CalendarDisplayType SelectedType
{
get { return (CalendarDisplayType)GetValue(SelectedTypeProperty); }
set { SetValue(SelectedTypeProperty, 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 int DisplayDayCount
{
get { return (int)GetValue(DisplayDayCountProperty); }
set { SetValue(DisplayDayCountProperty, value); }
}
public ICommand TodayClickedCommand
{
get { return (ICommand)GetValue(TodayClickedCommandProperty); }
set { SetValue(TodayClickedCommandProperty, value); }
}
private AppBarButton _todayButton;
private AppBarToggleButton _dayToggle;
private AppBarToggleButton _weekToggle;
private AppBarToggleButton _monthToggle;
private AppBarToggleButton _yearToggle;
public CalendarDisplayType SelectedType
{
get { return (CalendarDisplayType)GetValue(SelectedTypeProperty); }
set { SetValue(SelectedTypeProperty, value); }
}
public WinoCalendarTypeSelectorControl()
{
DefaultStyleKey = typeof(WinoCalendarTypeSelectorControl);
}
public int DisplayDayCount
{
get { return (int)GetValue(DisplayDayCountProperty); }
set { SetValue(DisplayDayCountProperty, value); }
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
private AppBarButton _todayButton;
private AppBarToggleButton _dayToggle;
private AppBarToggleButton _weekToggle;
private AppBarToggleButton _monthToggle;
private AppBarToggleButton _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;
public WinoCalendarTypeSelectorControl()
{
DefaultStyleKey = typeof(WinoCalendarTypeSelectorControl);
}
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));
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_todayButton.Click += TodayClicked;
_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;
_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); };
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));
UpdateToggleButtonStates();
}
_todayButton.Click += TodayClicked;
private void TodayClicked(object sender, RoutedEventArgs e) => TodayClickedCommand?.Execute(null);
_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 SetSelectedType(CalendarDisplayType type)
{
SelectedType = type;
UpdateToggleButtonStates();
}
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 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;
}
}
}

View File

@@ -8,139 +8,140 @@ using Windows.UI.Xaml.Media;
using Wino.Core.Domain.Models.Calendar;
using Wino.Helpers;
namespace Wino.Calendar.Controls;
public partial class WinoCalendarView : Control
namespace Wino.Calendar.Controls
{
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
public class WinoCalendarView : Control
{
get { return (Color)GetValue(TodayBackgroundColorProperty); }
set { SetValue(TodayBackgroundColorProperty, value); }
}
private const string PART_DayViewItemBorder = nameof(PART_DayViewItemBorder);
private const string PART_CalendarView = nameof(PART_CalendarView);
/// <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 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 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)
public Color TodayBackgroundColor
{
var clickedDate = args.AddedDates[0].Date;
SetInnerDisplayDate(clickedDate);
var clickArgs = new CalendarViewDayClickedEventArgs(clickedDate);
DateClickedCommand?.Execute(clickArgs);
get { return (Color)GetValue(TodayBackgroundColorProperty); }
set { SetValue(TodayBackgroundColorProperty, value); }
}
// 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)
/// <summary>
/// Gets or sets the command to execute when a date is picked.
/// Unused.
/// </summary>
public ICommand DateClickedCommand
{
control.UpdateVisibleDateRangeBackgrounds();
get { return (ICommand)GetValue(DateClickedCommandProperty); }
set { SetValue(DateClickedCommandProperty, value); }
}
}
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)
/// <summary>
/// Gets or sets the highlighted range of dates.
/// </summary>
public DateRange HighlightedDateRange
{
control.SetInnerDisplayDate(control.HighlightedDateRange.StartDate);
control.UpdateVisibleDateRangeBackgrounds();
get { return (DateRange)GetValue(HighlightedDateRangeProperty); }
set { SetValue(HighlightedDateRangeProperty, value); }
}
}
public void UpdateVisibleDateRangeBackgrounds()
{
if (HighlightedDateRange == null || VisibleDateBackground == null || TodayBackgroundColor == null || CalendarView == null) return;
var markDateCalendarDayItems = WinoVisualTreeHelper.FindDescendants<CalendarViewDayItem>(CalendarView);
foreach (var calendarDayItem in markDateCalendarDayItems)
public Brush VisibleDateBackground
{
var border = WinoVisualTreeHelper.GetChildObject<Border>(calendarDayItem, PART_DayViewItemBorder);
get { return (Brush)GetValue(VisibleDateBackgroundProperty); }
set { SetValue(VisibleDateBackgroundProperty, value); }
}
if (border == null) return;
if (calendarDayItem.Date.Date == DateTime.Today.Date)
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)
{
border.Background = new SolidColorBrush(TodayBackgroundColor);
var clickedDate = args.AddedDates[0].Date;
SetInnerDisplayDate(clickedDate);
var clickArgs = new CalendarViewDayClickedEventArgs(clickedDate);
DateClickedCommand?.Execute(clickArgs);
}
else if (calendarDayItem.Date.Date >= HighlightedDateRange.StartDate.Date && calendarDayItem.Date.Date < HighlightedDateRange.EndDate.Date)
// 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)
{
border.Background = VisibleDateBackground;
control.UpdateVisibleDateRangeBackgrounds();
}
else
}
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)
{
border.Background = null;
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;
}
}
}
}

View File

@@ -10,268 +10,269 @@ using Windows.UI.Xaml.Media;
using Wino.Calendar.Args;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Calendar.Controls;
public partial class WinoDayTimelineCanvas : Control, IDisposable
namespace Wino.Calendar.Controls
{
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 class WinoDayTimelineCanvas : Control, IDisposable
{
get { return (UIElement)GetValue(PositionerUIElementProperty); }
set { SetValue(PositionerUIElementProperty, value); }
}
public event EventHandler<TimelineCellSelectedArgs> TimelineCellSelected;
public event EventHandler<TimelineCellUnselectedArgs> TimelineCellUnselected;
public CalendarRenderOptions RenderOptions
{
get { return (CalendarRenderOptions)GetValue(RenderOptionsProperty); }
set { SetValue(RenderOptionsProperty, value); }
}
private const string PART_InternalCanvas = nameof(PART_InternalCanvas);
private CanvasControl Canvas;
public SolidColorBrush HalfHourSeperatorColor
{
get { return (SolidColorBrush)GetValue(HalfHourSeperatorColorProperty); }
set { SetValue(HalfHourSeperatorColorProperty, 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 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)
public UIElement PositionerUIElement
{
if (e.OldValue != null && e.NewValue == null)
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)
{
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));
}
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)
if (e.OldValue != null && e.NewValue == null)
{
var backgroundRectangle = new Rect(x + 1, y + 1, rectWidth - 1, rectHeight - 1);
args.DrawingSession.DrawRectangle(backgroundRectangle, strokeColor, strokeThickness);
args.DrawingSession.FillRectangle(backgroundRectangle, WorkingHourCellBackgroundColor.Color);
control.RaiseCellUnselected();
}
// Draw a line in the center of the rectangle for representing half hours.
double lineY = y + rectHeight / 2;
control.ForceDraw();
}
}
args.DrawingSession.DrawLine((float)x, (float)lineY, (float)(x + rectWidth), (float)lineY, HalfHourSeperatorColor.Color, strokeThickness, new CanvasStrokeStyle()
{
DashStyle = CanvasDashStyle.Dot
});
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));
}
// 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;
Debug.WriteLine($"Clicked: {clickedDateTime}");
}
// Second half of the hour is selected.
if (selectedDateTime.TimeOfDay.Minutes == 30)
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)
{
selectedY += rectHeight / 2;
var backgroundRectangle = new Rect(x + 1, y + 1, rectWidth - 1, rectHeight - 1);
args.DrawingSession.DrawRectangle(backgroundRectangle, strokeColor, strokeThickness);
args.DrawingSession.FillRectangle(backgroundRectangle, WorkingHourCellBackgroundColor.Color);
}
var selectedRectangle = new Rect(day * rectWidth, selectedY, rectWidth, selectionRectHeight);
args.DrawingSession.FillRectangle(selectedRectangle, SelectedCellBackgroundBrush.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;
public void Dispose()
{
if (Canvas == null) return;
Canvas.Draw -= OnCanvasDraw;
Canvas.PointerPressed -= OnCanvasPointerPressed;
Canvas.RemoveFromVisualTree();
Canvas.Draw -= OnCanvasDraw;
Canvas.PointerPressed -= OnCanvasPointerPressed;
Canvas.RemoveFromVisualTree();
Canvas = null;
Canvas = null;
}
}
}

View File

@@ -10,97 +10,98 @@ using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Calendar;
using Wino.Helpers;
namespace Wino.Calendar.Helpers;
public static class CalendarXamlHelpers
namespace Wino.Calendar.Helpers
{
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 class CalendarXamlHelpers
{
if (calendarItemViewModel == null || settings == null) return string.Empty;
public static CalendarItemViewModel GetFirstAllDayEvent(CalendarEventCollection collection)
=> (CalendarItemViewModel)collection.AllDayEvents.FirstOrDefault();
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)
/// <summary>
/// Returns full date + duration info in Event Details page details title.
/// </summary>
public static string GetEventDetailsDateString(CalendarItemViewModel calendarItemViewModel, CalendarSettings settings)
{
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)}";
}
}
if (calendarItemViewModel == null || settings == null) return string.Empty;
public static string GetRecurrenceString(CalendarItemViewModel calendarItemViewModel)
{
if (calendarItemViewModel == null || !calendarItemViewModel.IsRecurringChild) return string.Empty;
var start = calendarItemViewModel.Period.Start;
var end = calendarItemViewModel.Period.End;
// Parse recurrence rules
var calendarEvent = new CalendarEvent
{
Start = new CalDateTime(calendarItemViewModel.StartDate),
End = new CalDateTime(calendarItemViewModel.EndDate),
};
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";
var recurrenceLines = Regex.Split(calendarItemViewModel.CalendarItem.Recurrence, Constants.CalendarEventRecurrenceRuleSeperator);
foreach (var line in recurrenceLines)
{
calendarEvent.RecurrenceRules.Add(new RecurrencePattern(line));
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)}";
}
}
if (calendarEvent.RecurrenceRules == null || !calendarEvent.RecurrenceRules.Any())
public static string GetRecurrenceString(CalendarItemViewModel calendarItemViewModel)
{
return "No recurrence pattern.";
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}.";
}
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)
public static string GetDetailsPopupDurationString(CalendarItemViewModel calendarItemViewModel, CalendarSettings settings)
{
return $"{calendarItemViewModel.Period.Start.ToString("d", settings.CultureInfo)} {settings.GetTimeString(calendarItemViewModel.Period.Duration)}";
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})";
}
}
else if (calendarItemViewModel.IsMultiDayEvent)
public static PopupPlacementMode GetDesiredPlacementModeForEventsDetailsPopup(
CalendarItemViewModel calendarItemViewModel,
CalendarDisplayType calendarDisplayType)
{
return $"{calendarItemViewModel.Period.Start.ToString("d", settings.CultureInfo)} - {calendarItemViewModel.Period.End.ToString("d", settings.CultureInfo)}";
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);
}
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);
}
}

View File

@@ -15,15 +15,16 @@ 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;
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
namespace Wino.Calendar
{
public MainPage()
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
this.InitializeComponent();
public MainPage()
{
this.InitializeComponent();
}
}
}

View File

@@ -1,16 +1,17 @@
namespace Wino.Calendar.Models;
public struct CalendarItemMeasurement
namespace Wino.Calendar.Models
{
// Where to start?
public double Left { get; set; }
// Extend until where?
public double Right { get; set; }
public CalendarItemMeasurement(double left, double right)
public struct CalendarItemMeasurement
{
Left = left;
Right = 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;
}
}
}

View File

@@ -8,11 +8,6 @@
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">
@@ -21,8 +16,13 @@
</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>

View File

@@ -0,0 +1,29 @@
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)]

View File

@@ -0,0 +1,44 @@
<!--
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>

View File

@@ -1,7 +0,0 @@
{
"profiles": {
"Wino.Calendar": {
"commandName": "MsixPackage"
}
}
}

View File

@@ -2,20 +2,21 @@
using Windows.UI.Xaml.Controls;
using Wino.Calendar.ViewModels.Data;
namespace Wino.Calendar.Selectors;
public partial class CustomAreaCalendarItemSelector : DataTemplateSelector
namespace Wino.Calendar.Selectors
{
public DataTemplate AllDayTemplate { get; set; }
public DataTemplate MultiDayTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
public class CustomAreaCalendarItemSelector : DataTemplateSelector
{
if (item is CalendarItemViewModel calendarItemViewModel)
{
return calendarItemViewModel.IsMultiDayEvent ? MultiDayTemplate : AllDayTemplate;
}
public DataTemplate AllDayTemplate { get; set; }
public DataTemplate MultiDayTemplate { get; set; }
return base.SelectTemplateCore(item, container);
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
if (item is CalendarItemViewModel calendarItemViewModel)
{
return calendarItemViewModel.IsMultiDayEvent ? MultiDayTemplate : AllDayTemplate;
}
return base.SelectTemplateCore(item, container);
}
}
}

View File

@@ -2,32 +2,33 @@
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
namespace Wino.Calendar.Selectors;
public partial class WinoCalendarItemTemplateSelector : DataTemplateSelector
namespace Wino.Calendar.Selectors
{
public CalendarDisplayType DisplayType { get; set; }
public DataTemplate DayWeekWorkWeekTemplate { get; set; }
public DataTemplate MonthlyTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
public class WinoCalendarItemTemplateSelector : DataTemplateSelector
{
switch (DisplayType)
{
case CalendarDisplayType.Day:
case CalendarDisplayType.Week:
case CalendarDisplayType.WorkWeek:
return DayWeekWorkWeekTemplate;
case CalendarDisplayType.Month:
return MonthlyTemplate;
case CalendarDisplayType.Year:
break;
default:
break;
}
public CalendarDisplayType DisplayType { get; set; }
return base.SelectTemplateCore(item, container);
public DataTemplate DayWeekWorkWeekTemplate { get; set; }
public DataTemplate MonthlyTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
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);
}
}
}

View File

@@ -7,107 +7,108 @@ using Wino.Calendar.ViewModels.Data;
using Wino.Calendar.ViewModels.Interfaces;
using Wino.Core.Domain.Entities.Shared;
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
namespace Wino.Calendar.Services
{
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
/// <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
{
get
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
{
return GroupedAccountCalendars
.SelectMany(a => a.AccountCalendars)
.Where(b => b.IsChecked);
get
{
return GroupedAccountCalendars
.SelectMany(a => a.AccountCalendars)
.Where(b => b.IsChecked);
}
}
}
public IEnumerable<IGrouping<MailAccount, AccountCalendarViewModel>> GroupedAccountCalendarsEnumerable
{
get
public IEnumerable<IGrouping<MailAccount, AccountCalendarViewModel>> GroupedAccountCalendarsEnumerable
{
return GroupedAccountCalendars
.Select(a => a.AccountCalendars)
.SelectMany(b => b)
.GroupBy(c => c.Account);
get
{
return GroupedAccountCalendars
.Select(a => a.AccountCalendars)
.SelectMany(b => b)
.GroupBy(c => c.Account);
}
}
}
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)
public AccountCalendarStateService()
{
RemoveGroupedAccountCalendar(groupedAccountCalendar);
GroupedAccountCalendars = new ReadOnlyObservableCollection<GroupedAccountCalendarViewModel>(_internalGroupedAccountCalendars);
}
}
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 SingleGroupCalendarCollectiveStateChanged(object sender, EventArgs e)
=> CollectiveAccountGroupSelectionStateChanged?.Invoke(this, sender as GroupedAccountCalendarViewModel);
if (group == null)
private void SingleCalendarSelectionStateChanged(object sender, AccountCalendarViewModel e)
=> AccountCalendarSelectionStateChanged?.Invoke(this, e);
public void AddGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar)
{
// If the group doesn't exist, create it.
group = new GroupedAccountCalendarViewModel(accountCalendar.Account, new[] { accountCalendar });
AddGroupedAccountCalendar(group);
groupedAccountCalendar.CalendarSelectionStateChanged += SingleCalendarSelectionStateChanged;
groupedAccountCalendar.CollectiveSelectionStateChanged += SingleGroupCalendarCollectiveStateChanged;
_internalGroupedAccountCalendars.Add(groupedAccountCalendar);
}
else
public void RemoveGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar)
{
group.AccountCalendars.Add(accountCalendar);
groupedAccountCalendar.CalendarSelectionStateChanged -= SingleCalendarSelectionStateChanged;
groupedAccountCalendar.CollectiveSelectionStateChanged -= SingleGroupCalendarCollectiveStateChanged;
_internalGroupedAccountCalendars.Remove(groupedAccountCalendar);
}
}
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)
public void ClearGroupedAccountCalendar()
{
RemoveGroupedAccountCalendar(group);
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);
}
}
}
}

View File

@@ -2,13 +2,14 @@
using Wino.Core.Domain.Interfaces;
using Wino.Core.UWP.Services;
namespace Wino.Calendar.Services;
public class DialogService : DialogServiceBase, ICalendarDialogService
namespace Wino.Calendar.Services
{
public DialogService(IThemeService themeService,
IConfigurationService configurationService,
IApplicationResourceManager<ResourceDictionary> applicationResourceManager) : base(themeService, configurationService, applicationResourceManager)
public class DialogService : DialogServiceBase, ICalendarDialogService
{
public DialogService(IThemeService themeService,
IConfigurationService configurationService,
IApplicationResourceManager<ResourceDictionary> applicationResourceManager) : base(themeService, configurationService, applicationResourceManager)
{
}
}
}

View File

@@ -10,53 +10,54 @@ using Wino.Core.Domain.Models.Navigation;
using Wino.Core.UWP.Services;
using Wino.Views;
namespace Wino.Calendar.Services;
public class NavigationService : NavigationServiceBase, INavigationService
namespace Wino.Calendar.Services
{
public Type GetPageType(WinoPage winoPage)
public class NavigationService : NavigationServiceBase, INavigationService
{
return winoPage switch
public Type GetPageType(WinoPage winoPage)
{
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()
{
if (Window.Current.Content is Frame appFrame && appFrame.Content is AppShell shellPage)
{
var shellFrame = shellPage.GetShellFrame();
if (shellFrame.CanGoBack)
return winoPage switch
{
shellFrame.GoBack();
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()
{
if (Window.Current.Content is Frame appFrame && appFrame.Content is AppShell shellPage)
{
var shellFrame = shellPage.GetShellFrame();
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)
public bool Navigate(WinoPage page, object parameter = null, NavigationReferenceFrame frame = NavigationReferenceFrame.ShellFrame, NavigationTransitionType transition = NavigationTransitionType.None)
{
var shellFrame = shellPage.GetShellFrame();
// All navigations are performed on shell frame for calendar.
var pageType = GetPageType(page);
if (Window.Current.Content is Frame appFrame && appFrame.Content is AppShell shellPage)
{
var shellFrame = shellPage.GetShellFrame();
shellFrame.Navigate(pageType, parameter);
return true;
var pageType = GetPageType(page);
shellFrame.Navigate(pageType, parameter);
return true;
}
return false;
}
return false;
}
}

View File

@@ -4,32 +4,33 @@ using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Accounts;
namespace Wino.Calendar.Services;
public class ProviderService : IProviderService
namespace Wino.Calendar.Services
{
public IProviderDetail GetProviderDetail(MailProviderType type)
public class ProviderService : IProviderService
{
var details = GetAvailableProviders();
return details.FirstOrDefault(a => a.Type == type);
}
public List<IProviderDetail> GetAvailableProviders()
{
var providerList = new List<IProviderDetail>();
var providers = new MailProviderType[]
public IProviderDetail GetProviderDetail(MailProviderType type)
{
MailProviderType.Outlook,
MailProviderType.Gmail
};
var details = GetAvailableProviders();
foreach (var type in providers)
{
providerList.Add(new ProviderDetail(type, SpecialImapProvider.None));
return details.FirstOrDefault(a => a.Type == type);
}
return providerList;
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;
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,11 +1,12 @@
using Windows.UI.Xaml;
namespace Wino.Calendar.Styles;
public sealed partial class WinoCalendarResources : ResourceDictionary
namespace Wino.Calendar.Styles
{
public WinoCalendarResources()
public sealed partial class WinoCalendarResources : ResourceDictionary
{
this.InitializeComponent();
public WinoCalendarResources()
{
this.InitializeComponent();
}
}
}

View File

@@ -1,6 +1,7 @@
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> { }
}

View File

@@ -1,6 +1,7 @@
using Wino.Calendar.ViewModels;
using Wino.Core.UWP;
namespace Wino.Calendar.Views.Abstract;
public partial class AccountManagementPageAbstract : BasePage<AccountManagementViewModel> { }
namespace Wino.Calendar.Views.Abstract
{
public class AccountManagementPageAbstract : BasePage<AccountManagementViewModel> { }
}

View File

@@ -1,6 +1,7 @@
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> { }
}

View File

@@ -1,6 +1,7 @@
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> { }
}

View File

@@ -1,6 +1,7 @@
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> { }
}

View File

@@ -1,6 +1,7 @@
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> { }
}

View File

@@ -1,6 +1,7 @@
using Wino.Core.UWP;
using Wino.Core.ViewModels;
namespace Wino.Calendar.Views.Abstract;
public partial class PersonalizationPageAbstract : BasePage<PersonalizationPageViewModel> { }
namespace Wino.Calendar.Views.Abstract
{
public class PersonalizationPageAbstract : BasePage<PersonalizationPageViewModel> { }
}

View File

@@ -1,11 +1,12 @@
using Wino.Calendar.Views.Abstract;
namespace Wino.Calendar.Views.Account;
public sealed partial class AccountManagementPage : AccountManagementPageAbstract
namespace Wino.Calendar.Views.Account
{
public AccountManagementPage()
public sealed partial class AccountManagementPage : AccountManagementPageAbstract
{
InitializeComponent();
public AccountManagementPage()
{
InitializeComponent();
}
}
}

View File

@@ -5,49 +5,50 @@ using Wino.Calendar.Views.Abstract;
using Wino.Core.UWP;
using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.Views;
public sealed partial class AppShell : AppShellAbstract,
IRecipient<CalendarDisplayTypeChangedMessage>
namespace Wino.Calendar.Views
{
private const string STATE_HorizontalCalendar = "HorizontalCalendar";
private const string STATE_VerticalCalendar = "VerticalCalendar";
public Frame GetShellFrame() => ShellFrame;
public AppShell()
public sealed partial class AppShell : AppShellAbstract,
IRecipient<CalendarDisplayTypeChangedMessage>
{
InitializeComponent();
private const string STATE_HorizontalCalendar = "HorizontalCalendar";
private const string STATE_VerticalCalendar = "VerticalCalendar";
Window.Current.SetTitleBar(DragArea);
ManageCalendarDisplayType();
}
public Frame GetShellFrame() => ShellFrame;
private void ManageCalendarDisplayType()
{
// Go to different states based on the display type.
if (ViewModel.IsVerticalCalendar)
public AppShell()
{
VisualStateManager.GoToState(this, STATE_VerticalCalendar, false);
InitializeComponent();
Window.Current.SetTitleBar(DragArea);
ManageCalendarDisplayType();
}
else
private void ManageCalendarDisplayType()
{
VisualStateManager.GoToState(this, STATE_HorizontalCalendar, false);
// 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();
}
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();
}

View File

@@ -9,152 +9,153 @@ using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Calendar;
using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.Views;
public sealed partial class CalendarPage : CalendarPageAbstract,
IRecipient<ScrollToDateMessage>,
IRecipient<ScrollToHourMessage>,
IRecipient<GoNextDateRequestedMessage>,
IRecipient<GoPreviousDateRequestedMessage>
namespace Wino.Calendar.Views
{
private const int PopupDialogOffset = 12;
public CalendarPage()
public sealed partial class CalendarPage : CalendarPageAbstract,
IRecipient<ScrollToDateMessage>,
IRecipient<ScrollToHourMessage>,
IRecipient<GoNextDateRequestedMessage>,
IRecipient<GoPreviousDateRequestedMessage>
{
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
private const int PopupDialogOffset = 12;
ViewModel.DetailsShowCalendarItemChanged += CalendarItemDetailContextChanged;
}
private void CalendarItemDetailContextChanged(object sender, EventArgs e)
{
if (ViewModel.DisplayDetailsCalendarItemViewModel != null)
public CalendarPage()
{
var control = CalendarControl.GetCalendarItemControl(ViewModel.DisplayDetailsCalendarItemViewModel);
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
if (control != null)
ViewModel.DetailsShowCalendarItemChanged += CalendarItemDetailContextChanged;
}
private void CalendarItemDetailContextChanged(object sender, EventArgs e)
{
if (ViewModel.DisplayDetailsCalendarItemViewModel != null)
{
EventDetailsPopup.PlacementTarget = control;
var control = CalendarControl.GetCalendarItemControl(ViewModel.DisplayDetailsCalendarItemViewModel);
if (control != null)
{
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();
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)
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (args.RequestDefaultNavigation)
base.OnNavigatedTo(e);
if (e.NavigationMode == NavigationMode.Back) return;
if (e.Parameter is CalendarPageNavigationArgs args)
{
// 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));
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)
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)
{
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)
private void CalendarScrolling(object sender, EventArgs e)
{
// 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;
}
// In case of scrolling, we must dismiss the event details dialog.
ViewModel.DisplayDetailsCalendarItemViewModel = null;
}
}
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;
}
}

View File

@@ -1,12 +1,13 @@
using Wino.Calendar.Views.Abstract;
namespace Wino.Calendar.Views;
public sealed partial class EventDetailsPage : EventDetailsPageAbstract
namespace Wino.Calendar.Views
{
public EventDetailsPage()
public sealed partial class EventDetailsPage : EventDetailsPageAbstract
{
this.InitializeComponent();
public EventDetailsPage()
{
this.InitializeComponent();
}
}
}

View File

@@ -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.EditAccountDetailsCommand}" Content="{x:Bind domain:Translator.FolderOperation_Rename}" />
<Button Command="{x:Bind ViewModel.RenameAccountCommand}" Content="{x:Bind domain:Translator.FolderOperation_Rename}" />
</controls:SettingsCard>
<!-- TODO -->

View File

@@ -1,11 +1,12 @@
using Wino.Calendar.Views.Abstract;
namespace Wino.Calendar.Views.Settings;
public sealed partial class AccountDetailsPage : AccountDetailsPageAbstract
namespace Wino.Calendar.Views.Settings
{
public AccountDetailsPage()
public sealed partial class AccountDetailsPage : AccountDetailsPageAbstract
{
this.InitializeComponent();
public AccountDetailsPage()
{
this.InitializeComponent();
}
}
}

View File

@@ -1,12 +1,13 @@
using Wino.Calendar.Views.Abstract;
namespace Wino.Calendar.Views.Settings;
public sealed partial class CalendarSettingsPage : CalendarSettingsPageAbstract
namespace Wino.Calendar.Views.Settings
{
public CalendarSettingsPage()
public sealed partial class CalendarSettingsPage : CalendarSettingsPageAbstract
{
InitializeComponent();
public CalendarSettingsPage()
{
InitializeComponent();
}
}
}

View File

@@ -1,11 +1,12 @@
using Wino.Calendar.Views.Abstract;
namespace Wino.Calendar.Views.Settings;
public sealed partial class PersonalizationPage : PersonalizationPageAbstract
namespace Wino.Calendar.Views.Settings
{
public PersonalizationPage()
public sealed partial class PersonalizationPage : PersonalizationPageAbstract
{
this.InitializeComponent();
public PersonalizationPage()
{
this.InitializeComponent();
}
}
}

View File

@@ -1,31 +1,184 @@
<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>
<?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>
<ItemGroup>
<None Include="Package.StoreAssociation.xml" />
<Content Include="Assets\LargeTile.scale-100.png" />
@@ -73,6 +226,7 @@
<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" />
@@ -81,18 +235,120 @@
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Uwp.Controls.Primitives" />
<PackageReference Include="Microsoft.Identity.Client" />
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" />
<PackageReference Include="Win2D.uwp" />
<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>
</ItemGroup>
<ItemGroup>
<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" />
<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>
</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)' &lt; '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>

View File

@@ -1,14 +0,0 @@
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; }
}

View File

@@ -1,7 +0,0 @@
namespace Wino.Core.Domain.Enums;
public enum AccountCacheResetReason
{
AccountRemoval,
ExpiredCache
}

View File

@@ -1,7 +0,0 @@
namespace Wino.Core.Domain.Enums;
public enum InvalidMoveTargetReason
{
NonMoveTarget, // This folder does not allow moving mails.
MultipleAccounts // Multiple mails from different accounts cannot be moved.
}

View File

@@ -25,7 +25,7 @@ public enum WinoPage
AppPreferencesPage,
SettingOptionsPage,
AliasManagementPage,
EditAccountDetailsPage,
// Calendar
CalendarPage,
CalendarSettingsPage,

View File

@@ -1,3 +0,0 @@
namespace Wino.Core.Domain.Exceptions;
public class GmailServiceDisabledException : System.Exception { }

View File

@@ -20,7 +20,7 @@ public class ImapClientPoolException : Exception
ProtocolLog = protocolLog;
}
public ImapClientPoolException(Exception innerException, string protocolLog) : base(innerException.Message, innerException)
public ImapClientPoolException(Exception innerException, string protocolLog) : base(Translator.Exception_ImapClientPoolFailed, innerException)
{
ProtocolLog = protocolLog;
}

View File

@@ -1,9 +1,5 @@
using System;
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Exceptions;
public class InvalidMoveTargetException(InvalidMoveTargetReason reason) : Exception
{
public InvalidMoveTargetReason Reason { get; } = reason;
}
public class InvalidMoveTargetException : Exception { }

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Accounts;
namespace Wino.Core.Domain.Interfaces;
@@ -62,6 +61,15 @@ 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>
@@ -148,27 +156,4 @@ public interface IAccountService
/// <returns>Primary alias for the account.</returns>
Task<MailAccountAlias> GetPrimaryAccountAliasAsync(Guid accountId);
Task<bool> IsAccountFocusedEnabledAsync(Guid accountId);
/// <summary>
/// Deletes mail cache in the database for the given account.
/// </summary>
/// <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);
Task UpdateAccountCustomServerInformationAsync(CustomServerInformation customServerInformation);
}

View File

@@ -27,5 +27,5 @@ public interface IApplicationConfiguration
/// <summary>
/// Application insights instrumentation key.
/// </summary>
string SentryDNS { get; }
string ApplicationInsightsInstrumentationKey { get; }
}

View File

@@ -1,6 +1,7 @@
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;
@@ -16,6 +17,7 @@ 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,

View File

@@ -13,5 +13,4 @@ public interface IImapSynchronizer
Task<List<NewMailItemPackage>> CreateNewMailPackagesAsync(ImapMessageCreationPackage message, MailItemFolder assignedFolder, CancellationToken cancellationToken = default);
Task StartIdleClientAsync();
Task StopIdleClientAsync();
Task PreWarmClientPoolAsync();
}

View File

@@ -17,24 +17,16 @@ public interface IImapSynchronizerStrategy
/// <param name="synchronizer">Imap synchronizer that downloads messages.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>List of new downloaded message ids that don't exist locally.</returns>
Task<List<string>> HandleSynchronizationAsync(IImapClient client,
MailItemFolder folder,
IImapSynchronizer synchronizer,
CancellationToken cancellationToken = default);
Task<List<string>> HandleSynchronizationAsync(IImapClient client, MailItemFolder folder, IImapSynchronizer synchronizer, CancellationToken cancellationToken = default);
/// <summary>
/// Downloads given set of messages from the folder.
/// Folder is expected to be opened and synchronizer is connected.
/// </summary>
/// <param name="synchronizer">Synchronizer that performs the action.</param>
/// <param name="remoteFolder">Remote folder to download messages from.</param>
/// <param name="localFolder">Local folder to assign mails to.</param>
/// <param name="folder">Remote folder to download messages from.</param>
/// <param name="uniqueIdSet">Set of message uniqueids.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task DownloadMessagesAsync(IImapSynchronizer synchronizer,
IMailFolder remoteFolder,
MailItemFolder localFolder,
UniqueIdSet uniqueIdSet,
CancellationToken cancellationToken = default);
Task DownloadMessagesAsync(IImapSynchronizer synchronizer, IMailFolder folder, UniqueIdSet uniqueIdSet, CancellationToken cancellationToken = default);
}

View File

@@ -18,13 +18,9 @@ public interface IMailService
/// Returns the single mail item with the given mail copy id.
/// Caution: This method is not safe. Use other overrides.
/// </summary>
/// <param name="mailCopyId"></param>
/// <returns></returns>
Task<MailCopy> GetSingleMailItemAsync(string mailCopyId);
/// <summary>
/// Returns the multiple mail item with the given mail copy ids.
/// Caution: This method is not safe. Use other overrides.
/// </summary>
Task<List<MailCopy>> GetMailItemsAsync(IEnumerable<string> mailCopyIds);
Task<List<IMailItem>> FetchMailsAsync(MailListInitializationOptions options, CancellationToken cancellationToken = default);
/// <summary>
@@ -92,15 +88,6 @@ public interface IMailService
/// <param name="mailCopyId">Native mail id of the message.</param>
Task<bool> IsMailExistsAsync(string mailCopyId);
/// <summary>
/// Checks whether the given mail copy ids exists in the database.
/// Safely used for Outlook to prevent downloading the same mail twice.
/// For Gmail, it should be avoided since one mail may belong to multiple folders.
/// </summary>
/// <param name="mailCopyIds">Native mail id of the messages.</param>
/// <returns>List of Mail ids that already exists in the database.</returns>
Task<List<string>> AreMailsExistsAsync(IEnumerable<string> mailCopyIds);
/// <summary>
/// Returns all mails for given folder id.
/// </summary>
@@ -148,18 +135,4 @@ public interface IMailService
/// <param name="package">Mail creation package.</param>
/// <returns></returns>
Task CreateMailRawAsync(MailAccount account, MailItemFolder mailItemFolder, NewMailItemPackage package);
/// <summary>
/// Checks whether the account has any draft mail locally.
/// </summary>
/// <param name="accountId">Account id.</param>
Task<bool> HasAccountAnyDraftAsync(Guid accountId);
/// <summary>
/// Compares the ids returned from online search result for Archive folder against the local database.
/// </summary>
/// <param name="archiveFolderId">Archive folder id.</param>
/// <param name="onlineArchiveMailIds">Retrieved MailCopy ids from search result.</param>
/// <returns>Result model that contains added and removed mail copy ids.</returns>
Task<GmailArchiveComparisonResult> GetGmailArchiveComparisonResultAsync(Guid archiveFolderId, List<string> onlineArchiveMailIds);
}

View File

@@ -66,10 +66,4 @@ public interface IMimeFileService
/// <param name="mimeLocalPath">File path that physical MimeMessage is located.</param>
/// <param name="options">Rendering options</param>
MailRenderModel GetMailRenderModel(MimeMessage message, string mimeLocalPath, MailRenderingOptions options = null);
/// <summary>
/// Deletes every file in the mime cache for the given account.
/// </summary>
/// <param name="accountId">Account id.</param>
Task DeleteUserMimeCacheAsync(Guid accountId);
}

View File

@@ -22,9 +22,4 @@ public interface INotificationBuilder
/// Creates test notification for test purposes.
/// </summary>
Task CreateTestNotificationAsync(string title, string message);
/// <summary>
/// Removes the toast notification for a specific mail by unique id.
/// </summary>
void RemoveNotification(Guid mailUniqueId);
}

View File

@@ -1,12 +1,11 @@
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: INotifyPropertyChanged
public interface IPreferencesService
{
/// <summary>
/// When any of the preferences are changed.
@@ -51,12 +50,6 @@ public interface IPreferencesService: INotifyPropertyChanged
/// Local search will still offer online search at the end of local search results.
/// </summary>
SearchMode DefaultSearchMode { get; set; }
/// <summary>
/// Setting: Interval in minutes for background email synchronization.
/// </summary>
int EmailSyncIntervalMinutes { get; set; }
#endregion
#region Mail
@@ -195,21 +188,6 @@ public interface IPreferencesService: INotifyPropertyChanged
/// </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

View File

@@ -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

View File

@@ -1,20 +0,0 @@
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);
}

View File

@@ -56,12 +56,6 @@ 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)
@@ -72,11 +66,9 @@ public partial class AccountMenuItem : MenuItemBase<MailAccount, MenuItemBase<IM
public void UpdateAccount(MailAccount account)
{
Parameter = account;
OnPropertyChanged(nameof(AccountName));
OnPropertyChanged(nameof(Base64ProfilePicture));
OnPropertyChanged(nameof(AccountColorHex));
OnPropertyChanged(nameof(IsAttentionRequired));
AccountName = account.Name;
AttentionReason = account.AttentionReason;
Base64ProfilePicture = account.Base64ProfilePictureData;
if (SubMenuItems == null) return;

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
@@ -31,8 +32,6 @@ public partial class FolderMenuItem : MenuItemBase<IMailItemFolder, FolderMenuIt
return Translator.MoreFolderNameOverride;
else if (Parameter.SpecialFolderType == SpecialFolderType.Category)
return Translator.CategoriesFolderNameOverride;
else if (Parameter.SpecialFolderType == SpecialFolderType.Archive && ParentAccount.ProviderType == MailProviderType.Gmail)
return Translator.GmailArchiveFolderNameOverride;
else
return Parameter.FolderName;
}

View File

@@ -2,4 +2,4 @@
namespace Wino.Core.Domain.Models.Accounts;
public record AccountCreationDialogResult(MailProviderType ProviderType, string AccountName, SpecialImapProviderDetails SpecialImapProviderDetails, string AccountColorHex);
public record AccountCreationDialogResult(MailProviderType ProviderType, string AccountName, SpecialImapProviderDetails SpecialImapProviderDetails);

View File

@@ -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;

View File

@@ -1,8 +0,0 @@
namespace Wino.Core.Domain.Models.MailItem;
/// <summary>
/// Comparison result of the Gmail archive.
/// </summary>
/// <param name="Added">Mail copy ids to be added to Archive.</param>
/// <param name="Removed">Mail copy ids to be removed from Archive.</param>
public record GmailArchiveComparisonResult(string[] Added, string[] Removed);

View File

@@ -1,706 +0,0 @@
{
"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_SaveImapSuccess_Title": "Success",
"IMAPSetupDialog_SaveImapSuccess_Message": "IMAP settings saved successfuly.",
"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": "Should Wino ask you for confirmation every time you try to permanently delete a mail using Shift + Del keys?",
"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": "Показване на аватарите на подателите",
"SettingsEnableGravatarAvatars_Title": "Gravatar",
"SettingsEnableGravatarAvatars_Description": "Use gravatar (if available) as sender picture",
"SettingsEnableFavicons_Title": "Domain icons (Favicons)",
"SettingsEnableFavicons_Description": "Use domain favicons (if available) as sender picture",
"SettingsMailList_ClearAvatarsCache_Button": "Clear cached avatars",
"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": "Премахване на връзката между акаунтите",
"SettingsMailRendering_ActionLabels_Title": "Action labels",
"SettingsMailRendering_ActionLabels_Description": "Show action labels.",
"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": "Вчера",
"SettingsAppPreferences_EmailSyncInterval_Title": "Email sync interval",
"SettingsAppPreferences_EmailSyncInterval_Description": "Automatic email synchronization interval (minutes). This setting will be applied only after restarting Wino Mail."
}

View File

@@ -1,40 +1,29 @@
{
"AccountAlias_Column_Alias": "Alias",
"AccountAlias_Column_IsPrimaryAlias": "Primary",
"AccountAlias_Column_Verified": "Verified",
"AccountAlias_Disclaimer_FirstLine": "Wino can only import aliases for your Gmail accounts.",
"AccountAlias_Disclaimer_SecondLine": "If you want to use aliases for your Outlook or IMAP account, please add them yourself.",
"AccountCacheReset_Title": "Account Cache Reset",
"AccountCacheReset_Message": "This account requires full re-sychronization to continue working. Please wait while Wino re-synchronizes your messages...",
"AccountContactNameYou": "You",
"AccountCreationDialog_Completed": "all done",
"AccountCreationDialog_FetchingEvents": "Fetching calendar events.",
"AccountCreationDialog_Initializing": "initializing",
"AccountCreationDialog_PreparingFolders": "We are getting folder information at the moment.",
"AccountCreationDialog_SigninIn": "Account information is being saved.",
"AccountCreationDialog_FetchingProfileInformation": "Fetching profile details.",
"AccountCreationDialog_FetchingEvents": "Fetching calendar events.",
"AccountCreationDialog_GoogleAuthHelpClipboardText_Row0": "If your browser did not launch automatically to complete authentication:",
"AccountCreationDialog_GoogleAuthHelpClipboardText_Row1": "1) Click the button below to copy the authentication address",
"AccountCreationDialog_GoogleAuthHelpClipboardText_Row2": "2) Launch your web browser (Edge, Chrome, Firefox etc...)",
"AccountCreationDialog_GoogleAuthHelpClipboardText_Row3": "3) Paste the copied address and go to the website to complete authentication manually.",
"AccountCreationDialog_Initializing": "initializing",
"AccountCreationDialog_PreparingFolders": "We are getting folder information at the moment.",
"AccountCreationDialog_SigninIn": "Account information is being saved.",
"AccountEditDialog_Message": "Account Name",
"AccountEditDialog_Title": "Edit Account",
"AccountPickerDialog_Title": "Pick an account",
"AccountSettingsDialog_AccountName": "Sender Display Name",
"AccountSettingsDialog_AccountNamePlaceholder": "eg. John Doe",
"AccountDetailsPage_Title": "Account info",
"AccountDetailsPage_Description": "Change the name of the account in Wino and set desired sender name.",
"AccountDetailsPage_ColorPicker_Title": "Account color",
"AccountDetailsPage_ColorPicker_Description": "Assign a new account color to colorize its symbol in the list.",
"AddHyperlink": "Add",
"AccountContactNameYou": "You",
"AutoDiscoveryProgressMessage": "Searching for mail settings...",
"AppCloseBackgroundSynchronizationWarningTitle": "Background Synchronization",
"AppCloseStartupLaunchDisabledWarningMessageFirstLine": "Application has not been set to launch on Windows startup.",
"AppCloseStartupLaunchDisabledWarningMessageSecondLine": "This will cause you to miss notifications when you restart your computer.",
"AppCloseStartupLaunchDisabledWarningMessageThirdLine": "Do you want to go to App Preferences page to enable it?",
"AppCloseTerminateBehaviorWarningMessageFirstLine": "You are terminating Wino Mail and your app close behavior is set to 'Terminate'.",
"AppCloseTerminateBehaviorWarningMessageSecondLine": "This will stop all background synchronizations and notifications.",
"AppCloseTerminateBehaviorWarningMessageThirdLine": "Do you want to go to App Preferences to set Wino Mail to run minimized or in the background?",
"AutoDiscoveryProgressMessage": "Searching for mail settings...",
"AppCloseStartupLaunchDisabledWarningMessageFirstLine": "Application has not been set to launch on Windows startup.",
"AppCloseStartupLaunchDisabledWarningMessageSecondLine": "This will cause you to miss notifications when you restart your computer.",
"AppCloseStartupLaunchDisabledWarningMessageThirdLine": "Do you want to go to App Preferences page to enable it?",
"BasicIMAPSetupDialog_AdvancedConfiguration": "Advanced Configuration",
"BasicIMAPSetupDialog_CredentialLocalMessage": "Your credentials will only be stored locally on your computer.",
"BasicIMAPSetupDialog_Description": "Some accounts require additional steps to sign in",
@@ -45,63 +34,48 @@
"BasicIMAPSetupDialog_MailAddressPlaceholder": "johndoe@fabrikam.com",
"BasicIMAPSetupDialog_Password": "Password",
"BasicIMAPSetupDialog_Title": "IMAP Account",
"Busy": "Busy",
"Buttons_AddAccount": "Add Account",
"Buttons_AddNewAlias": "Add New Alias",
"Buttons_Allow": "Allow",
"Buttons_Deny": "Deny",
"Buttons_SyncAliases": "Synchronize Aliases",
"Buttons_ApplyTheme": "Apply Theme",
"Buttons_Browse": "Browse",
"Buttons_Cancel": "Cancel",
"Buttons_Close": "Close",
"Buttons_Copy": "Copy",
"Buttons_Create": "Create",
"Buttons_CreateAccount": "Create Account",
"Buttons_Copy": "Copy",
"Buttons_Delete": "Delete",
"Buttons_Deny": "Deny",
"Buttons_Discard": "Discard",
"Buttons_Edit": "Edit",
"Buttons_Discard": "Discard",
"Buttons_EnableImageRendering": "Enable",
"Buttons_Multiselect": "Select Multiple",
"Buttons_No": "No",
"Buttons_Open": "Open",
"Buttons_Purchase": "Purchase",
"Buttons_RateWino": "Rate Wino",
"Buttons_Reset": "Reset",
"Buttons_Save": "Save",
"Buttons_SaveConfiguration": "Save Configuration",
"Buttons_Send": "Send",
"Buttons_Share": "Share",
"Buttons_SignIn": "Sign In",
"Buttons_Sync": "Synchronize",
"Buttons_SyncAliases": "Synchronize Aliases",
"Buttons_TryAgain": "Try Again",
"Buttons_Yes": "Yes",
"CalendarAllDayEventSummary": "all-day events",
"CalendarDisplayOptions_Color": "Color",
"CalendarDisplayOptions_Expand": "Expand",
"CalendarItem_DetailsPopup_JoinOnline": "Join online",
"CalendarItem_DetailsPopup_ViewEventButton": "View event",
"CalendarItem_DetailsPopup_ViewSeriesButton": "View series",
"CalendarItemAllDay": "all day",
"CategoriesFolderNameOverride": "Categories",
"Buttons_Reset": "Reset",
"Buttons_Send": "Send",
"Buttons_Sync": "Synchronize",
"Buttons_Multiselect": "Select Multiple",
"Center": "Center",
"ComingSoon": "Coming soon...",
"ComposerFrom": "From: ",
"ComposerSubject": "Subject: ",
"ComposerTo": "To: ",
"ClipboardTextCopied_Message": "{0} copied to clipboard.",
"ClipboardTextCopied_Title": "Copied",
"ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.",
"ComingSoon": "Coming soon...",
"ComposerAttachmentsDragDropAttach_Message": "Attach",
"ComposerAttachmentsDropZone_Message": "Drop your files here",
"ComposerFrom": "From: ",
"ComposerImagesDropZone_Message": "Drop your images here",
"ComposerSubject": "Subject: ",
"ComposerTo": "To: ",
"ComposerToPlaceholder": "click enter to input addresses",
"CreateAccountAliasDialog_AliasAddress": "Address",
"CreateAccountAliasDialog_AliasAddressPlaceholder": "eg. support@mydomain.com",
"CreateAccountAliasDialog_Description": "Make sure your outgoing server allows sending mails from this alias.",
"CreateAccountAliasDialog_ReplyToAddress": "Reply-To Address",
"CreateAccountAliasDialog_ReplyToAddressPlaceholder": "admin@mydomain.com",
"CreateAccountAliasDialog_Title": "Create Account Alias",
"ComposerAttachmentsDropZone_Message": "Drop your files here",
"ComposerImagesDropZone_Message": "Drop your images here",
"ComposerAttachmentsDragDropAttach_Message": "Attach",
"CustomThemeBuilder_AccentColorDescription": "Set custom accent color if you wish. Not selecting a color will use your Windows accent color.",
"CustomThemeBuilder_AccentColorTitle": "Accent color",
"CustomThemeBuilder_PickColor": "Pick",
@@ -110,57 +84,70 @@
"CustomThemeBuilder_Title": "Custom Theme Builder",
"CustomThemeBuilder_WallpaperDescription": "Set a custom wallpaper for Wino",
"CustomThemeBuilder_WallpaperTitle": "Set custom wallpaper",
"Dialog_DontAskAgain": "Don't ask again",
"DialogMessage_AccountLimitMessage": "You have reached the account creation limit.\nWould you like to purchase 'Unlimited Account' add-on to continue?",
"DialogMessage_AccountLimitTitle": "Account Limit Reached",
"DialogMessage_AliasCreatedMessage": "New alias is succesfully created.",
"DialogMessage_AliasCreatedTitle": "Created New Alias",
"DialogMessage_AliasExistsMessage": "This alias is already in use.",
"DialogMessage_AliasExistsTitle": "Existing Alias",
"DialogMessage_AliasNotSelectedMessage": "You must select an alias before sending a message.",
"DialogMessage_AliasNotSelectedTitle": "Missing Alias",
"DialogMessage_CantDeleteRootAliasMessage": "Root alias can't be deleted. This is your main identity associated with your account setup.",
"DialogMessage_AliasNotSelectedMessage": "You must select an alias before sending a message.",
"DialogMessage_AliasExistsTitle": "Existing Alias",
"DialogMessage_AliasExistsMessage": "This alias is already in use.",
"DialogMessage_InvalidAliasTitle": "Invalid Alias",
"DialogMessage_InvalidAliasMessage": "This alias is not valid. Make sure all addresses of the alias are valid e-mail addresses.",
"DialogMessage_CantDeleteRootAliasTitle": "Can't Delete Alias",
"DialogMessage_CantDeleteRootAliasMessage": "Root alias can't be deleted. This is your main identity associated with your account setup.",
"DialogMessage_AliasCreatedTitle": "Created New Alias",
"DialogMessage_AliasCreatedMessage": "New alias is succesfully created.",
"DialogMessage_CleanupFolderMessage": "Do you want to permanently delete all the mails in this folder?",
"DialogMessage_CleanupFolderTitle": "Cleanup Folder",
"DialogMessage_ComposerMissingRecipientMessage": "Message has no recipient.",
"DialogMessage_ComposerValidationFailedTitle": "Validation Failed",
"DialogMessage_CreateLinkedAccountMessage": "Give this new link a name. Accounts will be merged under this name.",
"DialogMessage_CreateLinkedAccountTitle": "Account Link Name",
"DialogMessage_PrintingFailedMessage": "Failed to print this mail. Result: {0}",
"DialogMessage_PrintingFailedTitle": "Failed",
"DialogMessage_PrintingSuccessTitle": "Success",
"DialogMessage_PrintingSuccessMessage": "Mail is sent to printer.",
"DialogMessage_DeleteAccountConfirmationMessage": "Delete {0}?",
"DialogMessage_DeleteAccountConfirmationTitle": "All data associated with this account will be deleted from disk permanently.",
"DialogMessage_DiscardDraftConfirmationMessage": "This draft will be discarded. Do you want to continue?",
"DialogMessage_DiscardDraftConfirmationTitle": "Discard Draft",
"DialogMessage_EmptySubjectConfirmation": "Missing Subject",
"DialogMessage_EmptySubjectConfirmationMessage": "Message has no subject. Do you want to continue?",
"DialogMessage_EnableStartupLaunchDeniedMessage": "You can enable startup launch from Settings -> App Preferences.",
"DialogMessage_EnableStartupLaunchMessage": "Let Wino Mail automatically launch minimized on Windows startup to not miss any notifications.\n\nDo you want to enable startup launch?",
"DialogMessage_EnableStartupLaunchTitle": "Enable Startup Launch",
"DialogMessage_HardDeleteConfirmationMessage": "Permanent Delete",
"DialogMessage_HardDeleteConfirmationTitle": "Message(s) will be permanently deleted. Do you want to continue?",
"DialogMessage_InvalidAliasMessage": "This alias is not valid. Make sure all addresses of the alias are valid e-mail addresses.",
"DialogMessage_InvalidAliasTitle": "Invalid Alias",
"DialogMessage_NoAccountsForCreateMailMessage": "You don't have any accounts to create message from.",
"DialogMessage_NoAccountsForCreateMailTitle": "Account Missing",
"DialogMessage_PrintingFailedMessage": "Failed to print this mail. Result: {0}",
"DialogMessage_PrintingFailedTitle": "Failed",
"DialogMessage_PrintingSuccessMessage": "Mail is sent to printer.",
"DialogMessage_PrintingSuccessTitle": "Success",
"DialogMessage_RenameFolderMessage": "Enter new name for this folder",
"DialogMessage_RenameFolderTitle": "Rename Folder",
"DialogMessage_RenameLinkedAccountsMessage": "Enter new name for linked account",
"DialogMessage_RenameLinkedAccountsTitle": "Rename Linked Account",
"DialogMessage_UnlinkAccountsConfirmationMessage": "This operation will not delete your accounts but only break the link for shared folder connections. Do you want to continue?",
"DialogMessage_UnlinkAccountsConfirmationTitle": "Unlink Accounts",
"DialogMessage_UnsubscribeConfirmationGoToWebsiteConfirmButton": "Go to website",
"DialogMessage_UnsubscribeConfirmationGoToWebsiteMessage": "To stop getting messages from {0}, go to their website to unsubscribe.",
"DialogMessage_UnsubscribeConfirmationMailtoMessage": "Do you want to stop getting messages from {0}? Wino will unsubscribe for you by sending an email from your email account to {1}.",
"DialogMessage_UnsubscribeConfirmationOneClickMessage": "Do you want to stop getting messages from {0}?",
"DialogMessage_EmptySubjectConfirmation": "Missing Subject",
"DialogMessage_EmptySubjectConfirmationMessage": "Message has no subject. Do you want to continue?",
"DialogMessage_RenameFolderTitle": "Rename Folder",
"DialogMessage_RenameFolderMessage": "Enter new name for this folder",
"DialogMessage_UnsubscribeConfirmationTitle": "Unsubscribe",
"DialogMessage_UnsubscribeConfirmationOneClickMessage": "Do you want to stop getting messages from {0}?",
"DialogMessage_UnsubscribeConfirmationGoToWebsiteMessage": "To stop getting messages from {0}, go to their website to unsubscribe.",
"DialogMessage_UnsubscribeConfirmationGoToWebsiteConfirmButton": "Go to website",
"DialogMessage_UnsubscribeConfirmationMailtoMessage": "Do you want to stop getting messages from {0}? Wino will unsubscribe for you by sending an email from your email account to {1}.",
"DialogMessage_EnableStartupLaunchTitle": "Enable Startup Launch",
"DialogMessage_EnableStartupLaunchMessage": "Let Wino Mail automatically launch minimized on Windows startup to not miss any notifications.\n\nDo you want to enable startup launch?",
"DialogMessage_EnableStartupLaunchDeniedMessage": "You can enable startup launch from Settings -> App Preferences.",
"Dialog_DontAskAgain": "Don't ask again",
"CalendarAllDayEventSummary": "all-day events",
"CalendarItemAllDay": "all day",
"CalendarItem_DetailsPopup_JoinOnline": "Join online",
"CalendarItem_DetailsPopup_ViewEventButton": "View event",
"CalendarItem_DetailsPopup_ViewSeriesButton": "View series",
"CalendarDisplayOptions_Expand": "Expand",
"CalendarDisplayOptions_Color": "Color",
"CreateAccountAliasDialog_Title": "Create Account Alias",
"CreateAccountAliasDialog_Description": "Make sure your outgoing server allows sending mails from this alias.",
"CreateAccountAliasDialog_AliasAddress": "Address",
"CreateAccountAliasDialog_AliasAddressPlaceholder": "eg. support@mydomain.com",
"CreateAccountAliasDialog_ReplyToAddress": "Reply-To Address",
"CreateAccountAliasDialog_ReplyToAddressPlaceholder": "admin@mydomain.com",
"DiscordChannelDisclaimerMessage": "Wino doesn't have it's own Discord server, but special 'wino-mail' channel is hosted at 'Developer Sanctuary' server.\nTo get the updates about Wino please join Developer Sanctuary server and follow 'wino-mail' channel under 'Community Projects'\n\nYou will be directed to server URL since Discord doesn't support channel invites.",
"DiscordChannelDisclaimerTitle": "Important Discord Information",
"Draft": "Draft",
"DragMoveToFolderCaption": "Move to {0}",
"Busy": "Busy",
"EditorToolbarOption_Draw": "Draw",
"EditorToolbarOption_Format": "Format",
"EditorToolbarOption_Insert": "Insert",
@@ -171,26 +158,24 @@
"ElementTheme_Default": "Use system setting",
"ElementTheme_Light": "Light mode",
"Emoji": "Emoji",
"Error_FailedToSetupSystemFolders_Title": "Failed to setup system folders",
"Exception_WinoServerException": "Wino server failed.",
"Exception_MailProcessing": "This mail is still being processed. Please try again after few seconds.",
"Exception_ImapAutoDiscoveryFailed": "Couldn't find mailbox settings.",
"Exception_ImapClientPoolFailed": "IMAP Client Pool failed.",
"Exception_AuthenticationCanceled": "Authentication canceled",
"Exception_CustomThemeExists": "This theme already exists.",
"Exception_CustomThemeMissingName": "You must provide a name.",
"Exception_CustomThemeMissingWallpaper": "You must provide a custom background image.",
"Exception_FailedToSynchronizeAliases": "Failed to synchronize aliases",
"Exception_FailedToSynchronizeFolders": "Failed to synchronize folders",
"Exception_FailedToSynchronizeAliases": "Failed to synchronize aliases",
"Exception_MissingAlias": "Primary alias does not exist for this account. Creating draft failed.",
"Exception_FailedToSynchronizeProfileInformation": "Failed to synchronize profile information",
"Exception_GoogleAuthCallbackNull": "Callback uri is null on activation.",
"Exception_GoogleAuthCorruptedCode": "Corrupted authorization response.",
"Exception_GoogleAuthError": "OAuth authorization error: {0}",
"Exception_GoogleAuthInvalidResponse": "Received request with invalid state ({0})",
"Exception_GoogleAuthorizationCodeExchangeFailed": "Authorization code exchange failed.",
"Exception_ImapAutoDiscoveryFailed": "Couldn't find mailbox settings.",
"Exception_ImapClientPoolFailed": "IMAP Client Pool failed.",
"Exception_InboxNotAvailable": "Couldn't setup account folders.",
"Exception_InvalidSystemFolderConfiguration": "System folder configuration is not valid. Check configuration and try again.",
"Exception_InvalidMultiAccountMoveTarget": "You can't move multiple items that belong to different accounts in linked account.",
"Exception_MailProcessing": "This mail is still being processed. Please try again after few seconds.",
"Exception_MissingAlias": "Primary alias does not exist for this account. Creating draft failed.",
"Exception_NullAssignedAccount": "Assigned account is null",
"Exception_NullAssignedFolder": "Assigned folder is null",
"Exception_SynchronizerFailureHTTP": "Response handling failed with error HTTP code {0}",
@@ -201,12 +186,12 @@
"Exception_UnsupportedProvider": "This provider is not supported.",
"Exception_UnsupportedSynchronizerOperation": "This operation is not supported for {0}",
"Exception_UserCancelSystemFolderSetupDialog": "User canceled system folder config dialog.",
"Exception_WinoServerException": "Wino server failed.",
"Exception_InboxNotAvailable": "Couldn't setup account folders.",
"Files": "Files",
"FilteringOption_All": "All",
"FilteringOption_Files": "Has files",
"FilteringOption_Flagged": "Flagged",
"FilteringOption_Unread": "Unread",
"FilteringOption_Files": "Has files",
"Focused": "Focused",
"FolderOperation_CreateSubFolder": "Create sub folder",
"FolderOperation_Delete": "Delete",
@@ -214,73 +199,27 @@
"FolderOperation_Empty": "Empty this folder",
"FolderOperation_MarkAllAsRead": "Mark all as read",
"FolderOperation_Move": "Move",
"DragMoveToFolderCaption": "Move to {0}",
"FolderOperation_None": "None",
"FolderOperation_Pin": "Pin",
"FolderOperation_Rename": "Rename",
"FolderOperation_Unpin": "Unpin",
"GeneralTitle_Error": "Error",
"GeneralTitle_Info": "Information",
"GeneralTitle_Warning": "Warning",
"GmailServiceDisabled_Title": "Gmail Error",
"GmailServiceDisabled_Message": "Your Google Workspace account seems to be disabled for Gmail service. Please contact your administrator to enable Gmail service for your account.",
"GmailArchiveFolderNameOverride": "Archive",
"HoverActionOption_Archive": "Archive",
"HoverActionOption_Delete": "Delete",
"HoverActionOption_MoveJunk": "Move to Junk",
"HoverActionOption_ToggleFlag": "Flag / Unflag",
"HoverActionOption_ToggleRead": "Read / Unread",
"ImageRenderingDisabled": "Image rendering is disabled for this message.",
"ImapAdvancedSetupDialog_AuthenticationMethod": "Authentication method",
"ImapAdvancedSetupDialog_ConnectionSecurity": "Connection security",
"IMAPAdvancedSetupDialog_ValidationAuthMethodRequired": "Authentication method is required",
"IMAPAdvancedSetupDialog_ValidationConnectionSecurityRequired": "Connection security type is required",
"IMAPAdvancedSetupDialog_ValidationDisplayNameRequired": "Display name is required",
"IMAPAdvancedSetupDialog_ValidationEmailInvalid": "Please enter a valid email address",
"IMAPAdvancedSetupDialog_ValidationEmailRequired": "Email address is required",
"IMAPAdvancedSetupDialog_ValidationErrorTitle": "Please check the following:",
"IMAPAdvancedSetupDialog_ValidationIncomingPortInvalid": "Incoming port must be between 1-65535",
"IMAPAdvancedSetupDialog_ValidationIncomingPortRequired": "Incoming server port is required",
"IMAPAdvancedSetupDialog_ValidationIncomingServerRequired": "Incoming server address is required",
"IMAPAdvancedSetupDialog_ValidationOutgoingPasswordRequired": "Outgoing server password is required",
"IMAPAdvancedSetupDialog_ValidationOutgoingPortInvalid": "Outgoing port must be between 1-65535",
"IMAPAdvancedSetupDialog_ValidationOutgoingPortRequired": "Outgoing server port is required",
"IMAPAdvancedSetupDialog_ValidationOutgoingServerRequired": "Outgoing server address is required",
"IMAPAdvancedSetupDialog_ValidationOutgoingUsernameRequired": "Outgoing server username is required",
"IMAPAdvancedSetupDialog_ValidationPasswordRequired": "Password is required",
"IMAPAdvancedSetupDialog_ValidationUsernameRequired": "Username is required",
"ImapAuthenticationMethod_Auto": "Auto",
"ImapAuthenticationMethod_CramMD5": "CRAM-MD5",
"ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5",
"ImapAuthenticationMethod_EncryptedPassword": "Encrypted password",
"ImapAuthenticationMethod_None": "No authentication",
"ImapAuthenticationMethod_Ntlm": "NTLM",
"ImapAuthenticationMethod_Plain": "Normal password",
"ImapConnectionSecurity_Auto": "Auto",
"ImapConnectionSecurity_None": "None",
"ImapConnectionSecurity_SslTls": "SSL/TLS",
"ImapConnectionSecurity_StartTls": "STARTTLS",
"MergedAccountCommonFolderInbox": "Inbox",
"MergedAccountCommonFolderSent": "Sent",
"MergedAccountCommonFolderDraft": "Draft",
"MergedAccountCommonFolderJunk": "Junk",
"MergedAccountCommonFolderTrash": "Deleted",
"MergedAccountCommonFolderArchive": "Archive",
"IMAPSetupDialog_AccountType": "Account type",
"IMAPSetupDialog_ValidationSuccess_Title": "Success",
"IMAPSetupDialog_ValidationSuccess_Message": "Validation successful",
"IMAPSetupDialog_SaveImapSuccess_Title": "Success",
"IMAPSetupDialog_SaveImapSuccess_Message": "IMAP settings saved successfuly.",
"IMAPSetupDialog_ValidationFailed_Title": "IMAP Server validation failed.",
"IMAPSetupDialog_CertificateAllowanceRequired_Row0": "This server is requesting a SSL handshake to continue. Please confirm the certificate details below.",
"IMAPSetupDialog_CertificateAllowanceRequired_Row1": "Allow the handshake to continue setting up your account.",
"IMAPSetupDialog_CertificateDenied": "User didn't authorize the handshake with the certificate.",
"IMAPSetupDialog_CertificateIssuer": "Issuer",
"IMAPSetupDialog_CertificateSubject": "Subject",
"IMAPSetupDialog_CertificateValidFrom": "Valid from",
"IMAPSetupDialog_CertificateValidTo": "Valid to",
"IMAPSetupDialog_CertificateView": "View Certificate",
"IMAPSetupDialog_ConnectionFailedMessage": "IMAP connection failed.",
"IMAPSetupDialog_ConnectionFailedTitle": "Connection Failed",
"IMAPSetupDialog_DisplayName": "Display Name",
"IMAPSetupDialog_DisplayNamePlaceholder": "eg. John Doe",
"IMAPSetupDialog_IncomingMailServer": "Incoming mail server",
"IMAPSetupDialog_IncomingMailServerPort": "Port",
"IMAPSetupDialog_IMAPSettings": "IMAP Server Settings",
"IMAPSetupDialog_SMTPSettings": "SMTP Server Settings",
"IMAPSetupDialog_MailAddress": "Email address",
"IMAPSetupDialog_MailAddressPlaceholder": "someone@example.com",
"IMAPSetupDialog_OutgoingMailServer": "Outgoing (SMTP) mail server",
@@ -292,9 +231,26 @@
"IMAPSetupDialog_RequireSSLForIncomingMail": "Require SSL for incoming email",
"IMAPSetupDialog_RequireSSLForOutgoingMail": "Require SSL for outgoing email",
"IMAPSetupDialog_Title": "Advanced IMAP Configuration",
"IMAPSetupDialog_UseSameConfig": "Use the same username and password for sending email",
"IMAPSetupDialog_Username": "Username",
"IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domain/johndoe",
"IMAPSetupDialog_UseSameConfig": "Use the same username and password for sending email",
"IMAPSetupDialog_ConnectionFailedTitle": "Connection Failed",
"IMAPSetupDialog_ConnectionFailedMessage": "IMAP connection failed.",
"IMAPSetupDialog_CertificateAllowanceRequired_Row0": "This server is requesting a SSL handshake to continue. Please confirm the certificate details below.",
"IMAPSetupDialog_CertificateAllowanceRequired_Row1": "Allow the handshake to continue setting up your account.",
"IMAPSetupDialog_CertificateIssuer": "Issuer",
"IMAPSetupDialog_CertificateSubject": "Subject",
"IMAPSetupDialog_CertificateValidFrom": "Valid from",
"IMAPSetupDialog_CertificateValidTo": "Valid to",
"IMAPSetupDialog_CertificateDenied": "User didn't authorize the handshake with the certificate.",
"IMAPSetupDialog_CertificateView": "View Certificate",
"ImageRenderingDisabled": "Image rendering is disabled for this message.",
"InfoBarAction_Enable": "Enable",
"InfoBarMessage_SynchronizationDisabledFolder": "This folder is disabled for synchronization.",
"InfoBarTitle_SynchronizationDisabledFolder": "Disabled Folder",
"GeneralTitle_Error": "Error",
"GeneralTitle_Warning": "Warning",
"GeneralTitle_Info": "Information",
"Info_AccountCreatedMessage": "{0} is created",
"Info_AccountCreatedTitle": "Account Creation",
"Info_AccountCreationFailedTitle": "Account Creation Failed",
@@ -313,15 +269,14 @@
"Info_BackgroundExecutionDeniedTitle": "Denied Background Execution",
"Info_BackgroundExecutionUnknownErrorMessage": "Unknown exception occurred when registering background synchronizer.",
"Info_BackgroundExecutionUnknownErrorTitle": "Background Execution Failure",
"Info_CantDeletePrimaryAliasMessage": "Primary alias can't be deleted. Please change your alias before deleting this one",
"Info_FailedToOpenFileTitle": "Failed to launch file.",
"Info_FailedToOpenFileMessage": "File might be removed from the disk.",
"Info_ComposerMissingMIMEMessage": "Couldn't locate the MIME file. Synchronizing may help.",
"Info_ComposerMissingMIMETitle": "Failed",
"Info_ContactExistsMessage": "This contact is already in the recipient list.",
"Info_ContactExistsTitle": "Contact Exists",
"Info_DraftFolderMissingMessage": "Draft folder is missing for this account. Please check your account settings.",
"Info_DraftFolderMissingTitle": "Missing Draft Folder",
"Info_FailedToOpenFileMessage": "File might be removed from the disk.",
"Info_FailedToOpenFileTitle": "Failed to launch file.",
"Info_FileLaunchFailedTitle": "Failed to launch file",
"Info_InvalidAddressMessage": "'{0}' is not a valid e-mail address.",
"Info_InvalidAddressTitle": "Invalid Address",
@@ -331,16 +286,15 @@
"Info_LogsNotFoundTitle": "Logs Not Found",
"Info_LogsSavedMessage": "{0} is saved to selected folder.",
"Info_LogsSavedTitle": "Saved",
"Info_MailListSizeResetSuccessMessage": "The Mail List size has been reset.",
"Info_MailRenderingFailedMessage": "This mail is corrupted or can't be opened.\n{0}",
"Info_MailRenderingFailedTitle": "Render Failed",
"Info_MessageCorruptedMessage": "This message is corrupted.",
"Info_MessageCorruptedTitle": "Error",
"Info_MissingFolderMessage": "{0} doesn't exist for this account.",
"Info_MissingFolderTitle": "Missing Folder",
"Info_PDFSaveSuccessTitle": "Success",
"Info_PDFSaveFailedTitle": "Failed to save PDF file",
"Info_PDFSaveSuccessMessage": "PDF file is saved to {0}",
"Info_PDFSaveSuccessTitle": "Success",
"Info_PurchaseExistsMessage": "Looks like this product has already been purchased before.",
"Info_PurchaseExistsTitle": "Existing Product",
"Info_PurchaseThankYouMessage": "Thank You",
@@ -360,21 +314,32 @@
"Info_SyncCanceledMessage": "Canceled",
"Info_SyncCanceledTitle": "Synchronization",
"Info_SyncFailedTitle": "Synchronization Failed",
"Info_UnsubscribeErrorMessage": "Failed to unsubscribe",
"Info_UnsubscribeLinkInvalidMessage": "This unsubscribe link is invalid. Failed to unsubscribe from the list.",
"Info_UnsubscribeLinkInvalidTitle": "Invalid Unsubscribe Uri",
"Info_UnsubscribeSuccessMessage": "Successfully unsubscribed from {0}.",
"Info_UnsupportedFunctionalityDescription": "This functionality is not implemented yet.",
"Info_UnsupportedFunctionalityTitle": "Unsupported",
"InfoBarAction_Enable": "Enable",
"InfoBarMessage_SynchronizationDisabledFolder": "This folder is disabled for synchronization.",
"InfoBarTitle_SynchronizationDisabledFolder": "Disabled Folder",
"Info_UnsubscribeLinkInvalidTitle": "Invalid Unsubscribe Uri",
"Info_UnsubscribeLinkInvalidMessage": "This unsubscribe link is invalid. Failed to unsubscribe from the list.",
"Info_UnsubscribeSuccessMessage": "Successfully unsubscribed from {0}.",
"Info_UnsubscribeErrorMessage": "Failed to unsubscribe",
"Info_CantDeletePrimaryAliasMessage": "Primary alias can't be deleted. Please change your alias before deleting this one",
"Info_MailListSizeResetSuccessMessage": "The Mail List size has been reset.",
"ImapAdvancedSetupDialog_AuthenticationMethod": "Authentication method",
"ImapAdvancedSetupDialog_ConnectionSecurity": "Connection security",
"ImapAuthenticationMethod_Auto": "Auto",
"ImapAuthenticationMethod_CramMD5": "CRAM-MD5",
"ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5",
"ImapAuthenticationMethod_None": "No authentication",
"ImapAuthenticationMethod_Plain": "Normal password",
"ImapAuthenticationMethod_EncryptedPassword": "Encrypted password",
"ImapAuthenticationMethod_Ntlm": "NTLM",
"ImapConnectionSecurity_None": "None",
"ImapConnectionSecurity_SslTls": "SSL/TLS",
"ImapConnectionSecurity_StartTls": "STARTTLS",
"ImapConnectionSecurity_Auto": "Auto",
"Justify": "Justify",
"Left": "Left",
"Link": "Link",
"LinkedAccountsCreatePolicyMessage": "you must have at least 2 accounts to create link\nlink will be removed on save",
"LinkedAccountsTitle": "Linked Accounts",
"MailItemNoSubject": "No subject",
"MailOperation_AlwaysMoveFocused": "Always Move to Focused",
"MailOperation_AlwaysMoveOther": "Always Move to Other",
"MailOperation_Archive": "Archive",
@@ -401,32 +366,25 @@
"MailOperation_SaveAs": "Save As",
"MailOperation_SetFlag": "Set flag",
"MailOperation_Unarchive": "Unarchive",
"MailOperation_ViewMessageSource": "View message source",
"MailOperation_Zoom": "Zoom",
"MailOperation_ViewMessageSource": "View message source",
"MailsSelected": "{0} item(s) selected",
"MarkFlagUnflag": "Mark as flagged/unflagged",
"MarkReadUnread": "Mark as read/unread",
"MenuManageAccounts": "Manage Accounts",
"MenuMergedAccountItemAccountsSuffix": " accounts",
"MenuNewMail": "New Mail",
"MenuMergedAccountItemAccountsSuffix": " accounts",
"MenuRate": "Rate Wino",
"MenuSettings": "Settings",
"MergedAccountCommonFolderArchive": "Archive",
"MergedAccountCommonFolderDraft": "Draft",
"MergedAccountCommonFolderInbox": "Inbox",
"MergedAccountCommonFolderJunk": "Junk",
"MergedAccountCommonFolderSent": "Sent",
"MergedAccountCommonFolderTrash": "Deleted",
"MergedAccountsAvailableAccountsTitle": "Available Accounts",
"MessageSourceDialog_Title": "Message source",
"More": "More",
"MoreFolderNameOverride": "More",
"MoveMailDialog_InvalidFolderMessage": "{0} is not a valid folder for this mail.",
"MoveMailDialog_Title": "Pick a folder",
"NewAccountDialog_AccountName": "Account Name",
"NewAccountDialog_AccountNameDefaultValue": "Personal",
"NewAccountDialog_AccountNamePlaceholder": "eg. Personal Mail",
"NewAccountDialog_Title": "Add New Account",
"MessageSourceDialog_Title": "Message source",
"NoMailSelected": "No message selected",
"NoMessageCrieteria": "No messages match your search criteria",
"NoMessageEmptyFolder": "This folder is empty",
@@ -434,9 +392,6 @@
"Notifications_MultipleNotificationsTitle": "New Mail",
"Notifications_WinoUpdatedMessage": "Checkout new version {0}",
"Notifications_WinoUpdatedTitle": "Wino Mail has been updated.",
"OnlineSearchFailed_Message": "Failed to perform search\n{0}\n\nListing offline mails.",
"OnlineSearchTry_Line1": "Can't find what you are looking for?",
"OnlineSearchTry_Line2": "Try online search.",
"Other": "Other",
"PaneLengthOption_Default": "Default",
"PaneLengthOption_ExtraLarge": "Extra Large",
@@ -446,94 +401,64 @@
"PaneLengthOption_Small": "Small",
"Photos": "Photos",
"PreparingFoldersMessage": "Preparing folders",
"ProtocolLogAvailable_Message": "Protocol logs are available for diagnostics.",
"ProviderDetail_Gmail_Description": "Google Account",
"ProviderDetail_iCloud_Description": "Apple iCloud Account",
"ProviderDetail_iCloud_Title": "iCloud",
"ProviderDetail_IMAP_Description": "Custom IMAP/SMTP server",
"ProviderDetail_IMAP_Title": "IMAP Server",
"ProviderDetail_Yahoo_Description": "Yahoo Account",
"ProviderDetail_Yahoo_Title": "Yahoo Mail",
"QuickEventDialog_EventName": "Event name",
"QuickEventDialog_IsAllDay": "All day",
"QuickEventDialog_Location": "Location",
"QuickEventDialog_RemindMe": "Remind me",
"QuickEventDialogMoreDetailsButtonText": "More details",
"Reader_SaveAllAttachmentButtonText": "Save all attachments",
"ProviderDetail_Yahoo_Description": "Yahoo Account",
"ProviderDetail_iCloud_Title": "iCloud",
"ProviderDetail_iCloud_Description": "Apple iCloud Account",
"ProtocolLogAvailable_Message": "Protocol logs are available for diagnostics.",
"Results": "Results",
"Right": "Right",
"Reader_SaveAllAttachmentButtonText": "Save all attachments",
"SynchronizationFolderReport_Success": "up to date",
"SynchronizationFolderReport_Failed": "synchronization is failed",
"SearchBarPlaceholder": "Search",
"SearchingIn": "Searching in",
"SearchPivotName": "Results",
"SettingConfigureSpecialFolders_Button": "Configure",
"SettingsEditAccountDetails_IMAPConfiguration_Title": "IMAP/SMTP Configuration",
"SettingsEditAccountDetails_IMAPConfiguration_Description": "Change your incoming/outgoing server settings.",
"SettingsAbout_Description": "Learn more about Wino.",
"SettingsAbout_Title": "About",
"SettingsAboutGithub_Description": "Go to issue tracker GitHub repository.",
"SettingsAboutGithub_Title": "GitHub",
"SettingsAccountManagementAppendMessage_Title": "Append messages to Sent folder",
"SettingsAccountManagementAppendMessage_Description": "Create a copy of the message in Sent folder after the draft is sent. Enable this if you don't see your mails after you sent them in Sent folder.",
"SettingsEditLinkedInbox_Title": "Edit Linked Inbox",
"SettingsEditLinkedInbox_Description": "Add / remove accounts, rename or break the link between accounts.",
"SettingsAboutVersion": "Version ",
"SettingsAboutWinoDescription": "Lightweight mail client for Windows device families.",
"SettingsAbout_Description": "Learn more about Wino.",
"SettingsAbout_Title": "About",
"SettingsAccentColor_Description": "Change application's accent color",
"SettingsAccentColor_Title": "Accent Color",
"SettingsAccentColor_UseWindowsAccentColor": "Use my Windows accent color",
"SettingsAccountManagementAppendMessage_Description": "Create a copy of the message in Sent folder after the draft is sent. Enable this if you don't see your mails after you sent them in Sent folder.",
"SettingsAccountManagementAppendMessage_Title": "Append messages to Sent folder",
"SettingsAccountName_Description": "Change the name of the account.",
"SettingsAccountName_Title": "Account Name",
"SettingsApplicationTheme_Description": "Personalize Wino with different custom application themes for your like.",
"SettingsApplicationTheme_Title": "Application Theme",
"SettingsAppPreferences_CloseBehavior_Description": "What should happen when you close the app?",
"SettingsAppPreferences_CloseBehavior_Title": "Application close behavior",
"SettingsAppPreferences_Description": "General settings / preferences for Wino Mail.",
"SettingsAppPreferences_SearchMode_Description": "Set whether Wino should check fetched mails first while doing a search or ask your mail server online. Local search is always faster and you can always do an online search if your mail is not in the results.",
"SettingsAppPreferences_SearchMode_Local": "Local",
"SettingsAppPreferences_SearchMode_Online": "Online",
"SettingsAppPreferences_SearchMode_Title": "Default search mode",
"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Description": "Wino Mail will keep running in the background. You will be notified as new mails arrive.",
"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Title": "Run in the background",
"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Description": "Wino Mail will keep running on the system tray. Available to launch by clicking on an icon. You will be notified as new mails arrive.",
"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Title": "Minimize to system tray",
"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Description": "Wino Mail will not keep running anywhere. You will not be notified as new mails arrive. Launch Wino Mail again to continue mail synchronization.",
"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Title": "Terminate",
"SettingsAppPreferences_StartupBehavior_Description": "Allow Wino Mail to launch minimized when Windows starts. Always allow it to receive notifications.",
"SettingsAppPreferences_StartupBehavior_Disable": "Disable",
"SettingsAppPreferences_StartupBehavior_Disabled": "Wino Mail will not be launched on Windows startup. This will cause you to miss notifications when you restart your computer.",
"SettingsAppPreferences_StartupBehavior_DisabledByPolicy": "Your administrator or group policies disabled running applications on startup. Thus, Wino Mail can't be set to launch on Windows startup.",
"SettingsAppPreferences_StartupBehavior_DisabledByUser": "Please go to Task Manager -> Startup tab to allow Wino Mail to launch on Windows startup.",
"SettingsAppPreferences_StartupBehavior_Enable": "Enable",
"SettingsAppPreferences_StartupBehavior_Enabled": "Wino Mail successfully set to be launched in the background on Windows startup.",
"SettingsAppPreferences_StartupBehavior_FatalError": "Fatal error occurred while changing the startup mode for Wino Mail.",
"SettingsAppPreferences_StartupBehavior_Title": "Start minimized on Windows startup",
"SettingsAppPreferences_Title": "App Preferences",
"SettingsAutoSelectNextItem_Description": "Select the next item after you delete or move a mail.",
"SettingsAutoSelectNextItem_Title": "Auto select next item",
"SettingsAvailableThemes_Description": "Select a theme from Wino's own collection for your taste or apply your own themes.",
"SettingsAvailableThemes_Title": "Available Themes",
"SettingsCalendarSettings_Description": "Change first day of week, hour cell height and more...",
"SettingsAutoSelectNextItem_Title": "Auto select next item",
"SettingsAutoSelectNextItem_Description": "Select the next item after you delete or move a mail.",
"SettingsCalendarSettings_Title": "Calendar Settings",
"SettingsComposer_Title": "Composer",
"SettingsComposerFont_Title": "Default Composer Font",
"SettingsComposerFontFamily_Description": "Change the default font family and font size for composing mails.",
"SettingsConfigureSpecialFolders_Description": "Set folders with special functions. Folders such as Archive, Inbox, and Drafts are essential for Wino to function properly.",
"SettingsConfigureSpecialFolders_Title": "Configure System Folders",
"SettingsCalendarSettings_Description": "Change first day of week, hour cell height and more...",
"SettingsCustomTheme_Description": "Create your own custom theme with custom wallpaper and accent color.",
"SettingsCustomTheme_Title": "Custom Theme",
"SettingsConfigureSpecialFolders_Title": "Configure System Folders",
"SettingsConfigureSpecialFolders_Description": "Set folders with special functions. Folders such as Archive, Inbox, and Drafts are essential for Wino to function properly.",
"SettingConfigureSpecialFolders_Button": "Configure",
"Error_FailedToSetupSystemFolders_Title": "Failed to setup system folders",
"SettingsDeleteAccount_Description": "Delete all e-mails and credentials associated with this account.",
"SettingsDeleteAccount_Title": "Delete this account",
"SettingsDeleteProtection_Description": "Should Wino ask you for confirmation every time you try to permanently delete a mail using Shift + Del keys?",
"SettingsDeleteProtection_Description": "Should Wino ask you for comfirmation every time you try to permanently delete a mail using Shift + Del keys?",
"SettingsDeleteProtection_Title": "Permanent Delete Protection",
"SettingsDiagnostics_Description": "For developers",
"SettingsDiagnostics_DiagnosticId_Description": "Share this ID with the developers when asked to get help for the issues you experience in Wino Mail.",
"SettingsDiagnostics_DiagnosticId_Title": "Diagnostic ID",
"SettingsDiagnostics_Title": "Diagnostics",
"SettingsDiagnostics_DiagnosticId_Title": "Diagnostic Id",
"SettingsDiagnostics_DiagnosticId_Description": "Share this Id with the developers when asked to get help for the issues you experience in Wino Mail.",
"SettingsDiscord_Description": "Get regular development updates, join roadmap discussions and provide feedback.",
"SettingsDiscord_Title": "Discord Channel",
"SettingsEditLinkedInbox_Description": "Add / remove accounts, rename or break the link between accounts.",
"SettingsEditLinkedInbox_Title": "Edit Linked Inbox",
"SettingsElementThemeSelectionDisabled": "Element theme selection is disabled when application theme is selected other than Default.",
"SettingsElementTheme_Description": "Select a Windows theme for Wino",
"SettingsElementTheme_Title": "Element Theme",
"SettingsElementThemeSelectionDisabled": "Element theme selection is disabled when application theme is selected other than Default.",
"SettingsEnableHoverActions_Title": "Enable hover actions",
"SettingsEnableIMAPLogs_Description": "Enable this to provide details about IMAP connectivity issuses you had during IMAP server setup.",
"SettingsEnableIMAPLogs_Title": "Enable IMAP Protocol Logs",
@@ -546,15 +471,12 @@
"SettingsExternalContent_Title": "External Content",
"SettingsFocusedInbox_Description": "Set whether Inbox should be split into two as Focused - Other.",
"SettingsFocusedInbox_Title": "Focused Inbox",
"SettingsFolderMenuStyle_Description": "Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail",
"SettingsFolderMenuStyle_Title": "Create Nested Folders",
"SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.",
"SettingsFolderOptions_Title": "Folder Configuration",
"SettingsFolderSync_Description": "Enable or disable specific folders for synchronization.",
"SettingsFolderSync_Title": "Folder Synchronization",
"SettingsFontFamily_Title": "Font Family",
"SettingsFontPreview_Title": "Preview",
"SettingsFontSize_Title": "Font Size",
"SettingsFolderOptions_Title": "Folder Configuration",
"SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.",
"SettingsManageAliases_Title": "Aliases",
"SettingsManageAliases_Description": "See e-mail aliases assigned for this account, update or delete them.",
"SettingsHoverActionCenter": "Center Action",
"SettingsHoverActionLeft": "Left Action",
"SettingsHoverActionRight": "Right Action",
@@ -562,25 +484,44 @@
"SettingsHoverActions_Title": "Hover Actions",
"SettingsLanguage_Description": "Change display language for Wino.",
"SettingsLanguage_Title": "Display Language",
"SettingsLanguageTime_Description": "Wino display language, preferred time format.",
"SettingsLanguageTime_Title": "Language & Time",
"SettingsLanguageTime_Description": "Wino display language, preferred time format.",
"CategoriesFolderNameOverride": "Categories",
"AccountAlias_Column_Verified": "Verified",
"AccountAlias_Column_Alias": "Alias",
"AccountAlias_Column_IsPrimaryAlias": "Primary",
"AccountAlias_Disclaimer_FirstLine": "Wino can only import aliases for your Gmail accounts.",
"AccountAlias_Disclaimer_SecondLine": "If you want to use aliases for your Outlook or IMAP account, please add them yourself.",
"MoreFolderNameOverride": "More",
"SettingsOptions_Title": "Settings",
"SettingsLinkAccounts_Description": "Merge multiple accounts into one. See mails from one Inbox together.",
"SettingsLinkAccounts_Title": "Create Linked Accounts",
"SettingsLinkedAccountsSave_Description": "Modify the current link with the new accounts.",
"SettingsLinkedAccountsSave_Title": "Save Changes",
"SettingsLoadImages_Title": "Load images automatically",
"SettingsLoadPlaintextLinks_Title": "Convert plaintext links to clickable links",
"SettingsLoadStyles_Title": "Load styles automatically",
"SettingsMailListActionBar_Description": "Hide/show action bar at top of message list.",
"SettingsMailListActionBar_Title": "Show mail list actions",
"SettingsLoadPlaintextLinks_Title": "Convert plaintext links to clickable links",
"SettingsMailSpacing_Description": "Adjust the spacing for listing mails.",
"SettingsMailSpacing_Title": "Mail Spacing",
"SettingsFolderMenuStyle_Title": "Create Nested Folders",
"SettingsFolderMenuStyle_Description": "Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail",
"SettingsManageAccountSettings_Description": "Notifications, signatures, synchronization and other settings per account.",
"SettingsManageAccountSettings_Title": "Manage Account Settings",
"SettingsManageAliases_Description": "See e-mail aliases assigned for this account, update or delete them.",
"SettingsManageAliases_Title": "Aliases",
"SettingsEditAccountDetails_Title": "Edit Account Details",
"SettingsEditAccountDetails_Description": "Change account name, sender name and assign a new color if you like.",
"SettingsAppPreferences_Title": "App Preferences",
"SettingsAppPreferences_Description": "General settings / preferences for Wino Mail.",
"SettingsAppPreferences_CloseBehavior_Title": "Application close behavior",
"SettingsAppPreferences_CloseBehavior_Description": "What should happen when you close the app?",
"SettingsAppPreferences_StartupBehavior_Title": "Start minimized on Windows startup",
"SettingsAppPreferences_StartupBehavior_Description": "Allow Wino Mail to launch minimized when Windows starts. Always allow it to receive notifications.",
"SettingsAppPreferences_StartupBehavior_Enabled": "Wino Mail successfully set to be launched in the background on Windows startup.",
"SettingsAppPreferences_StartupBehavior_Disabled": "Wino Mail will not be launched on Windows startup. This will cause you to miss notifications when you restart your computer.",
"SettingsAppPreferences_StartupBehavior_DisabledByPolicy": "Your administrator or group policies disabled running applications on startup. Thus, Wino Mail can't be set to launch on Windows startup.",
"SettingsAppPreferences_StartupBehavior_DisabledByUser": "Please go to Task Manager -> Startup tab to allow Wino Mail to launch on Windows startup.",
"SettingsAppPreferences_StartupBehavior_FatalError": "Fatal error occurred while changing the startup mode for Wino Mail.",
"SettingsAppPreferences_StartupBehavior_Enable": "Enable",
"SettingsAppPreferences_StartupBehavior_Disable": "Disable",
"SettingsReorderAccounts_Title": "Reorder Accounts",
"SettingsReorderAccounts_Description": "Change the order of accounts in the account list.",
"SettingsManageLink_Description": "Move items to add new link or remove existing link.",
"SettingsManageLink_Title": "Manage Link",
"SettingsMarkAsRead_Description": "Change what should happen to the selected item.",
@@ -594,78 +535,59 @@
"SettingsNoAccountSetupMessage": "You didn't setup any accounts yet.",
"SettingsNotifications_Description": "Turn on or off notifications for this account.",
"SettingsNotifications_Title": "Notifications",
"SettingsNotificationsAndTaskbar_Description": "Change whether notifications should be displayed and taskbar badge for this account.",
"SettingsNotificationsAndTaskbar_Title": "Notifications & Taskbar",
"SettingsOptions_Title": "Settings",
"SettingsTaskbarBadge_Title": "Taskbar Badge",
"SettingsTaskbarBadge_Description": "Include unread mail count in taskbar icon.",
"SettingsNotificationsAndTaskbar_Description": "Change whether notifications should be displayed and taskbar badge for this account.",
"SettingsPaneLengthReset_Description": "Reset the size of the mail list to original if you have issues with it.",
"SettingsPaneLengthReset_Title": "Reset Mail List Size",
"SettingsPaypal_Description": "Show much more love ❤️ All donations are appreciated.",
"SettingsPaypal_Title": "Donate via PayPal",
"SettingsPersonalization_Description": "Change appearance of Wino as you like.",
"SettingsPersonalization_Title": "Personalization",
"SettingsPersonalizationMailDisplayCompactMode": "Compact Mode",
"SettingsPersonalizationMailDisplayMediumMode": "Medium Mode",
"SettingsPersonalizationMailDisplaySpaciousMode": "Spacious Mode",
"SettingsPrefer24HourClock_Description": "Mail recieve times will be displayed in 24 hour format instead of 12 (AM/PM)",
"SettingsPrefer24HourClock_Title": "Display Clock Format in 24 Hours",
"SettingsPersonalization_Description": "Change appearance of Wino as you like.",
"SettingsPersonalization_Title": "Personalization",
"SettingsPrivacyPolicy_Description": "Review privacy policy.",
"SettingsPrivacyPolicy_Title": "Privacy Policy",
"SettingsReader_Title": "Reader",
"SettingsComposer_Title": "Composer",
"SettingsReadComposePane_Description": "Fonts, external content.",
"SettingsReadComposePane_Title": "Reader & Composer",
"SettingsReader_Title": "Reader",
"SettingsReaderFont_Title": "Default Reader Font",
"SettingsReaderFontFamily_Description": "Change the default font family and font size for reading mails.",
"SettingsFontFamily_Title": "Font Family",
"SettingsFontSize_Title": "Font Size",
"SettingsFontPreview_Title": "Preview",
"SettingsComposerFont_Title": "Default Composer Font",
"SettingsComposerFontFamily_Description": "Change the default font family and font size for composing mails.",
"SettingsRenameMergeAccount_Description": "Change the display name of the linked accounts.",
"SettingsRenameMergeAccount_Title": "Rename",
"SettingsReorderAccounts_Description": "Change the order of accounts in the account list.",
"SettingsReorderAccounts_Title": "Reorder Accounts",
"SettingsSemanticZoom_Description": "This will allow you to click on the headers in messages list and go to specific date",
"SettingsSemanticZoom_Title": "Semantic Zoom for Date Headers",
"SettingsShowPreviewText_Description": "Hide/show the preview text.",
"SettingsShowPreviewText_Title": "Show Preview Text",
"SettingsShowSenderPictures_Description": "Hide/show the thumbnail sender pictures.",
"SettingsShowSenderPictures_Title": "Show Sender Avatars",
"SettingsEnableGravatarAvatars_Title": "Gravatar",
"SettingsEnableGravatarAvatars_Description": "Use gravatar (if available) as sender picture",
"SettingsEnableFavicons_Title": "Domain icons (Favicons)",
"SettingsEnableFavicons_Description": "Use domain favicons (if available) as sender picture",
"SettingsMailList_ClearAvatarsCache_Button": "Clear cached avatars",
"SettingsSignature_AddCustomSignature_Button": "Add signature",
"SettingsSignature_AddCustomSignature_Title": "Add custom signature",
"SettingsSignature_DeleteSignature_Title": "Delete signature",
"SettingsPrefer24HourClock_Title": "Display Clock Format in 24 Hours",
"SettingsPrefer24HourClock_Description": "Mail recieve times will be displayed in 24 hour format instead of 12 (AM/PM)",
"SettingsSignature_Description": "Manage account signatures",
"SettingsSignature_EditSignature_Title": "Edit signature",
"SettingsSignature_ForFollowingMessages_Title": "For Replies/Forwards",
"SettingsSignature_ForNewMessages_Title": "For New Messages",
"SettingsSignature_NoneSignatureName": "None",
"SettingsSignature_SignatureDefaults": "Signature defaults",
"SettingsSignature_Signatures": "Signatures",
"SettingsSignature_Title": "Signature",
"SettingsStartupItem_Description": "Primary account item to load Inbox at startup.",
"SettingsStartupItem_Title": "Startup Item",
"SettingsStore_Description": "Show some love ❤️",
"SettingsStore_Title": "Rate in Store",
"SettingsTaskbarBadge_Description": "Include unread mail count in taskbar icon.",
"SettingsTaskbarBadge_Title": "Taskbar Badge",
"SettingsThreads_Description": "Organize messages into conversation threads.",
"SettingsThreads_Title": "Conversation Threading",
"SettingsMailListActionBar_Description": "Hide/show action bar at top of message list.",
"SettingsMailListActionBar_Title": "Show mail list actions",
"SettingsUnlinkAccounts_Description": "Remove the link between accounts. his will not delete your accounts.",
"SettingsUnlinkAccounts_Title": "Unlink Accounts",
"SettingsMailRendering_ActionLabels_Title": "Action labels",
"SettingsMailRendering_ActionLabels_Description": "Show action labels.",
"SignatureDeleteDialog_Message": "Are you sure you want to delete \"{0}\" signature?",
"SignatureDeleteDialog_Title": "Delete signature",
"SignatureEditorDialog_SignatureName_Placeholder": "Name your signature",
"SignatureEditorDialog_SignatureName_TitleEdit": "Current signature name: {0}",
"SignatureEditorDialog_SignatureName_TitleNew": "Signature name",
"SignatureEditorDialog_Title": "Signature Editor",
"SortingOption_Date": "by date",
"SortingOption_Name": "by name",
"StoreRatingDialog_MessageFirstLine": "All feedbacks are appreciated and they will make much Wino better in the future. Would you like to rate Wino in Microsoft Store?",
"StoreRatingDialog_MessageSecondLine": "Would you like to rate Wino Mail in Microsoft Store?",
"StoreRatingDialog_Title": "Enjoying Wino?",
"SynchronizationFolderReport_Failed": "synchronization is failed",
"SynchronizationFolderReport_Success": "up to date",
"SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.",
"SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder",
"SystemFolderConfigDialog_DeletedFolderDescription": "Deleted messages will be moved to here.",
@@ -679,15 +601,11 @@
"SystemFolderConfigDialog_SentFolderDescription": "Folder that sent messages will be stored.",
"SystemFolderConfigDialog_SentFolderHeader": "Sent Folder",
"SystemFolderConfigDialog_Title": "Configure System Folders",
"SystemFolderConfigDialogValidation_DuplicateSystemFolders": "Some of the system folders are used more than once in the configuration.",
"SystemFolderConfigDialogValidation_InboxSelected": "You can't assign Inbox folder to any other system folder.",
"SystemFolderConfigSetupSuccess_Message": "System folders are successfully configured.",
"SystemFolderConfigDialogValidation_DuplicateSystemFolders": "Some of the system folders are used more than once in the configuration.",
"SystemFolderConfigSetupSuccess_Title": "System Folders Setup",
"SystemFolderConfigSetupSuccess_Message": "System folders are successfully configured.",
"TestingImapConnectionMessage": "Testing server connection...",
"TitleBarServerDisconnectedButton_Description": "Wino is disconnected from the network. Click reconnect to restore connection.",
"TitleBarServerDisconnectedButton_Title": "no connection",
"TitleBarServerReconnectButton_Title": "reconnect",
"TitleBarServerReconnectingButton_Title": "connecting",
"Today": "Today",
"UnknownAddress": "unknown address",
"UnknownDateHeader": "Unknown Date",
@@ -699,8 +617,36 @@
"WinoUpgradeMessage": "Upgrade to Unlimited Accounts",
"WinoUpgradeRemainingAccountsMessage": "{0} out of {1} free accounts used.",
"Yesterday": "Yesterday",
"SettingsAppPreferences_EmailSyncInterval_Title": "Email sync interval",
"SettingsAppPreferences_EmailSyncInterval_Description": "Automatic email synchronization interval (minutes). This setting will be applied only after restarting Wino Mail."
"SignatureEditorDialog_Title": "Signature Editor",
"SignatureEditorDialog_SignatureName_Placeholder": "Name your signature",
"SignatureEditorDialog_SignatureName_TitleNew": "Signature name",
"SignatureEditorDialog_SignatureName_TitleEdit": "Current signature name: {0}",
"SignatureDeleteDialog_Title": "Delete signature",
"SignatureDeleteDialog_Message": "Are you sure you want to delete \"{0}\" signature?",
"SettingsSignature_ForNewMessages_Title": "For New Messages",
"SettingsSignature_ForFollowingMessages_Title": "For Replies/Forwards",
"SettingsSignature_SignatureDefaults": "Signature defaults",
"SettingsSignature_Signatures": "Signatures",
"SettingsSignature_AddCustomSignature_Title": "Add custom signature",
"SettingsSignature_AddCustomSignature_Button": "Add signature",
"SettingsSignature_EditSignature_Title": "Edit signature",
"SettingsSignature_DeleteSignature_Title": "Delete signature",
"SettingsSignature_NoneSignatureName": "None",
"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Title": "Minimize to system tray",
"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Description": "Wino Mail will keep running on the system tray. Available to launch by clicking on an icon. You will be notified as new mails arrive.",
"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Title": "Run in the background",
"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Description": "Wino Mail will keep running in the background. You will be notified as new mails arrive.",
"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Title": "Terminate",
"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Description": "Wino Mail will not keep running anywhere. You will not be notified as new mails arrive. Launch Wino Mail again to continue mail synchronization.",
"TitleBarServerDisconnectedButton_Title": "no connection",
"TitleBarServerDisconnectedButton_Description": "Wino is disconnected from the network. Click reconnect to restore connection.",
"TitleBarServerReconnectButton_Title": "reconnect",
"TitleBarServerReconnectingButton_Title": "connecting",
"MailItemNoSubject": "No subject",
"QuickEventDialogMoreDetailsButtonText": "More details",
"QuickEventDialog_RemindMe": "Remind me",
"QuickEventDialog_Location": "Location",
"QuickEventDialog_EventName": "Event name",
"QuickEventDialog_IsAllDay": "All day"
}

View File

@@ -1,40 +1,29 @@
{
"AccountAlias_Column_Alias": "Alias",
"AccountAlias_Column_IsPrimaryAlias": "Primary",
"AccountAlias_Column_Verified": "Verified",
"AccountAlias_Disclaimer_FirstLine": "Wino can only import aliases for your Gmail accounts.",
"AccountAlias_Disclaimer_SecondLine": "If you want to use aliases for your Outlook or IMAP account, please add them yourself.",
"AccountCacheReset_Title": "Account Cache Reset",
"AccountCacheReset_Message": "This account requires full re-sychronization to continue working. Please wait while Wino re-synchronizes your messages...",
"AccountContactNameYou": "Vy",
"AccountCreationDialog_Completed": "hotovo",
"AccountCreationDialog_FetchingEvents": "Fetching calendar events.",
"AccountCreationDialog_Initializing": "inicializace",
"AccountCreationDialog_PreparingFolders": "Stahování informací o složkách.",
"AccountCreationDialog_SigninIn": "Probíhá ukládání informací o účtu.",
"AccountCreationDialog_FetchingProfileInformation": "Fetching profile details.",
"AccountCreationDialog_FetchingEvents": "Fetching calendar events.",
"AccountCreationDialog_GoogleAuthHelpClipboardText_Row0": "If your browser did not launch automatically to complete authentication:",
"AccountCreationDialog_GoogleAuthHelpClipboardText_Row1": "1) Click the button below to copy the authentication address",
"AccountCreationDialog_GoogleAuthHelpClipboardText_Row2": "2) Launch your web browser (Edge, Chrome, Firefox etc...)",
"AccountCreationDialog_GoogleAuthHelpClipboardText_Row3": "3) Paste the copied address and go to the website to complete authentication manually.",
"AccountCreationDialog_Initializing": "inicializace",
"AccountCreationDialog_PreparingFolders": "Stahování informací o složkách.",
"AccountCreationDialog_SigninIn": "Probíhá ukládání informací o účtu.",
"AccountEditDialog_Message": "Název účtu",
"AccountEditDialog_Title": "Upravit účet",
"AccountPickerDialog_Title": "Vybrat účet",
"AccountSettingsDialog_AccountName": "Jméno odesílatele",
"AccountSettingsDialog_AccountNamePlaceholder": "např. Jan Novák",
"AccountDetailsPage_Title": "Account info",
"AccountDetailsPage_Description": "Change the name of the account in Wino and set desired sender name.",
"AccountDetailsPage_ColorPicker_Title": "Account color",
"AccountDetailsPage_ColorPicker_Description": "Assign a new account color to colorize its symbol in the list.",
"AddHyperlink": "Přidat",
"AppCloseBackgroundSynchronizationWarningTitle": "Synchronizace na pozadí",
"AppCloseStartupLaunchDisabledWarningMessageFirstLine": "Application has not been set to launch on Windows startup.",
"AppCloseStartupLaunchDisabledWarningMessageSecondLine": "This will cause you to miss notifications when you restart your computer.",
"AppCloseStartupLaunchDisabledWarningMessageThirdLine": "Do you want to go to App Preferences page to enable it?",
"AccountContactNameYou": "You",
"AutoDiscoveryProgressMessage": "Vyhledávání v nastaveních mailu...",
"AppCloseBackgroundSynchronizationWarningTitle": "Background Synchronization",
"AppCloseTerminateBehaviorWarningMessageFirstLine": "You are terminating Wino Mail and your app close behavior is set to 'Terminate'.",
"AppCloseTerminateBehaviorWarningMessageSecondLine": "This will stop all background synchronizations and notifications.",
"AppCloseTerminateBehaviorWarningMessageThirdLine": "Do you want to go to App Preferences to set Wino Mail to run minimized or in the background?",
"AutoDiscoveryProgressMessage": "Vyhledávání v nastaveních mailu...",
"AppCloseStartupLaunchDisabledWarningMessageFirstLine": "Application has not been set to launch on Windows startup.",
"AppCloseStartupLaunchDisabledWarningMessageSecondLine": "This will cause you to miss notifications when you restart your computer.",
"AppCloseStartupLaunchDisabledWarningMessageThirdLine": "Do you want to go to App Preferences page to enable it?",
"BasicIMAPSetupDialog_AdvancedConfiguration": "Pokročilá nastavení",
"BasicIMAPSetupDialog_CredentialLocalMessage": "Vaše nastavení budou uložena pouze lokálně na vašem počítači.",
"BasicIMAPSetupDialog_Description": "Některé účty vyžadují další kroky k přihlášení",
@@ -45,63 +34,48 @@
"BasicIMAPSetupDialog_MailAddressPlaceholder": "jan.novak@seznam.cz",
"BasicIMAPSetupDialog_Password": "Heslo",
"BasicIMAPSetupDialog_Title": "IMAP účet",
"Busy": "Busy",
"Buttons_AddAccount": "Přidat účet",
"Buttons_AddNewAlias": "Add New Alias",
"Buttons_Allow": "Povolit",
"Buttons_Allow": "Allow",
"Buttons_Deny": "Deny",
"Buttons_SyncAliases": "Synchronize Aliases",
"Buttons_ApplyTheme": "Použít motiv",
"Buttons_Browse": "Procházet",
"Buttons_Cancel": "Zrušit",
"Buttons_Close": "Zavřít",
"Buttons_Copy": "Kopírovat",
"Buttons_Create": "Vytvořit",
"Buttons_CreateAccount": "Vytvořit účet",
"Buttons_Copy": "Kopírovat",
"Buttons_Delete": "Smazat",
"Buttons_Deny": "Deny",
"Buttons_Discard": "Zahodit",
"Buttons_Edit": "Upravit",
"Buttons_Discard": "Zahodit",
"Buttons_EnableImageRendering": "Povolit",
"Buttons_Multiselect": "Select Multiple",
"Buttons_No": "Ne",
"Buttons_Open": "Otevřít",
"Buttons_Purchase": "Koupit",
"Buttons_RateWino": "Ohodnotit Wino",
"Buttons_Reset": "Reset",
"Buttons_Save": "Uložit",
"Buttons_SaveConfiguration": "Uložit nastavení",
"Buttons_Send": "Send",
"Buttons_Share": "Sdílet",
"Buttons_SignIn": "Přihlásit se",
"Buttons_Sync": "Synchronizovat",
"Buttons_SyncAliases": "Synchronize Aliases",
"Buttons_TryAgain": "Zkusit znovu",
"Buttons_Yes": "Ano",
"CalendarAllDayEventSummary": "all-day events",
"CalendarDisplayOptions_Color": "Color",
"CalendarDisplayOptions_Expand": "Expand",
"CalendarItem_DetailsPopup_JoinOnline": "Join online",
"CalendarItem_DetailsPopup_ViewEventButton": "View event",
"CalendarItem_DetailsPopup_ViewSeriesButton": "View series",
"CalendarItemAllDay": "all day",
"CategoriesFolderNameOverride": "Kategorie",
"Buttons_Reset": "Reset",
"Buttons_Send": "Send",
"Buttons_Sync": "Synchronize",
"Buttons_Multiselect": "Select Multiple",
"Center": "Nastřed",
"ComingSoon": "Již brzy...",
"ComposerFrom": "Od: ",
"ComposerSubject": "Předmět: ",
"ComposerTo": "Komu: ",
"ClipboardTextCopied_Message": "\"{0}\" zkopírováno do schránky.",
"ClipboardTextCopied_Title": "Zkopírováno",
"ClipboardTextCopyFailed_Message": "Nepodařilo se zkopírovat \"{0}\" do schránky.",
"ComingSoon": "Již brzy...",
"ComposerAttachmentsDragDropAttach_Message": "Přiložit",
"ComposerAttachmentsDropZone_Message": "Sem přetáhněte soubory",
"ComposerFrom": "Od: ",
"ComposerImagesDropZone_Message": "Drop your images here",
"ComposerSubject": "Předmět: ",
"ComposerTo": "Komu: ",
"ComposerToPlaceholder": "pro vložení adresy zmáčkni Enter",
"CreateAccountAliasDialog_AliasAddress": "Address",
"CreateAccountAliasDialog_AliasAddressPlaceholder": "eg. support@mydomain.com",
"CreateAccountAliasDialog_Description": "Make sure your outgoing server allows sending mails from this alias.",
"CreateAccountAliasDialog_ReplyToAddress": "Odpověď na adresu",
"CreateAccountAliasDialog_ReplyToAddressPlaceholder": "admin@mydomain.com",
"CreateAccountAliasDialog_Title": "Vytvořit Alias účtu",
"ComposerAttachmentsDropZone_Message": "Sem přetáhněte soubory",
"ComposerImagesDropZone_Message": "Drop your images here",
"ComposerAttachmentsDragDropAttach_Message": "Přiložit",
"CustomThemeBuilder_AccentColorDescription": "Pokud chcete, můžete si nastavit barevný tón. Jinak se použije se výchozí barevný tón Windows.",
"CustomThemeBuilder_AccentColorTitle": "Barevný tón",
"CustomThemeBuilder_PickColor": "Vybrat",
@@ -110,57 +84,70 @@
"CustomThemeBuilder_Title": "Nástroj na vytvoření vlastního motivu",
"CustomThemeBuilder_WallpaperDescription": "Nastav vlastní pozadí pro Wino",
"CustomThemeBuilder_WallpaperTitle": "Nastav vlastní pozadí",
"Dialog_DontAskAgain": "Příště se neptat",
"DialogMessage_AccountLimitMessage": "Dosáhli jste limitu vytvořených účtů. Chcete si zakoupit doplněk \"Neomezený účet\", aby jste mohli pokračovat?",
"DialogMessage_AccountLimitTitle": "Dosažen limit počtu účtú",
"DialogMessage_AliasCreatedMessage": "New alias is succesfully created.",
"DialogMessage_AliasCreatedTitle": "Created New Alias",
"DialogMessage_AliasExistsMessage": "This alias is already in use.",
"DialogMessage_AliasExistsTitle": "Existing Alias",
"DialogMessage_AliasNotSelectedMessage": "You must select an alias before sending a message.",
"DialogMessage_AliasNotSelectedTitle": "Missing Alias",
"DialogMessage_CantDeleteRootAliasMessage": "Root alias can't be deleted. This is your main identity associated with your account setup.",
"DialogMessage_AliasNotSelectedMessage": "You must select an alias before sending a message.",
"DialogMessage_AliasExistsTitle": "Existing Alias",
"DialogMessage_AliasExistsMessage": "This alias is already in use.",
"DialogMessage_InvalidAliasTitle": "Invalid Alias",
"DialogMessage_InvalidAliasMessage": "This alias is not valid. Make sure all addresses of the alias are valid e-mail addresses.",
"DialogMessage_CantDeleteRootAliasTitle": "Can't Delete Alias",
"DialogMessage_CantDeleteRootAliasMessage": "Root alias can't be deleted. This is your main identity associated with your account setup.",
"DialogMessage_AliasCreatedTitle": "Created New Alias",
"DialogMessage_AliasCreatedMessage": "New alias is succesfully created.",
"DialogMessage_CleanupFolderMessage": "Přejete si trvale smazat všechny maily v této složce?",
"DialogMessage_CleanupFolderTitle": "Vyprázdnit složku",
"DialogMessage_ComposerMissingRecipientMessage": "Zpráva nemá žádného příjemce.",
"DialogMessage_ComposerValidationFailedTitle": "Ověření se nezdařilo",
"DialogMessage_CreateLinkedAccountMessage": "Dejte tomuto novému propojení název. Účty budou propojeny pod tímto názvem.",
"DialogMessage_CreateLinkedAccountTitle": "Název propojeného účtu",
"DialogMessage_PrintingFailedMessage": "Failed to print this mail. Result: {0}",
"DialogMessage_PrintingFailedTitle": "Failed",
"DialogMessage_PrintingSuccessTitle": "Success",
"DialogMessage_PrintingSuccessMessage": "Mail is sent to printer.",
"DialogMessage_DeleteAccountConfirmationMessage": "Odstranit {0}?",
"DialogMessage_DeleteAccountConfirmationTitle": "Všechna data spojená s tímto účtem budou trvale smazána z disku.",
"DialogMessage_DiscardDraftConfirmationMessage": "Tento koncept bude zahozen. Chcete pokračovat?",
"DialogMessage_DiscardDraftConfirmationTitle": "Zahodit koncept",
"DialogMessage_EmptySubjectConfirmation": "Chybějící Předmět",
"DialogMessage_EmptySubjectConfirmationMessage": "Zpráva nemá Předmět. Chcete pokračovat?",
"DialogMessage_EnableStartupLaunchDeniedMessage": "You can enable startup launch from Settings -> App Preferences.",
"DialogMessage_EnableStartupLaunchMessage": "Let Wino Mail automatically launch minimized on Windows startup to not miss any notifications.\n\nDo you want to enable startup launch?",
"DialogMessage_EnableStartupLaunchTitle": "Enable Startup Launch",
"DialogMessage_HardDeleteConfirmationMessage": "Trvalé smazání",
"DialogMessage_HardDeleteConfirmationTitle": "Zpráva(y) bude trvale odstraněna. Chcete pokračovat?",
"DialogMessage_InvalidAliasMessage": "This alias is not valid. Make sure all addresses of the alias are valid e-mail addresses.",
"DialogMessage_InvalidAliasTitle": "Invalid Alias",
"DialogMessage_NoAccountsForCreateMailMessage": "Nemáte žádný účet pro vytvoření zprávy.",
"DialogMessage_NoAccountsForCreateMailTitle": "Chybějící účet",
"DialogMessage_PrintingFailedMessage": "Failed to print this mail. Result: {0}",
"DialogMessage_PrintingFailedTitle": "Failed",
"DialogMessage_PrintingSuccessMessage": "Mail is sent to printer.",
"DialogMessage_PrintingSuccessTitle": "Success",
"DialogMessage_RenameFolderMessage": "Enter new name for this folder",
"DialogMessage_RenameFolderTitle": "Rename Folder",
"DialogMessage_RenameLinkedAccountsMessage": "Zadejte nový název pro propojený účet",
"DialogMessage_RenameLinkedAccountsTitle": "Přejmenovat propojený účet",
"DialogMessage_UnlinkAccountsConfirmationMessage": "Tato operace neodstraní vaše mailové účty, ale pouze zruší jejich propojení. Chcete pokračovat?",
"DialogMessage_UnlinkAccountsConfirmationTitle": "Rozpojit účty",
"DialogMessage_UnsubscribeConfirmationGoToWebsiteConfirmButton": "Přejít na web",
"DialogMessage_UnsubscribeConfirmationGoToWebsiteMessage": "Chcete-li přestat dostávat zprávy od {0}, přejděte na jejich webové stránky a odhlašte se.",
"DialogMessage_UnsubscribeConfirmationMailtoMessage": "Chcete přestat dostávat zprávy od {0}? Wino se za vás odhlásí zasláním e-mailu z Vašeho e-mailového účtu na {1}.",
"DialogMessage_UnsubscribeConfirmationOneClickMessage": "Chcete přestat dostávat zprávy od {0}?",
"DialogMessage_EmptySubjectConfirmation": "Missing Subject",
"DialogMessage_EmptySubjectConfirmationMessage": "Zpráva nemá Předmět. Chcete pokračovat?",
"DialogMessage_RenameFolderTitle": "Rename Folder",
"DialogMessage_RenameFolderMessage": "Enter new name for this folder",
"DialogMessage_UnsubscribeConfirmationTitle": "Odhlásit",
"DialogMessage_UnsubscribeConfirmationOneClickMessage": "Chcete přestat dostávat zprávy od {0}?",
"DialogMessage_UnsubscribeConfirmationGoToWebsiteMessage": "Chcete-li přestat dostávat zprávy od {0}, přejděte na jejich webové stránky a odhlašte se.",
"DialogMessage_UnsubscribeConfirmationGoToWebsiteConfirmButton": "Přejít na web",
"DialogMessage_UnsubscribeConfirmationMailtoMessage": "Chcete přestat dostávat zprávy od {0}? Wino se za vás odhlásí zasláním e-mailu z Vašeho e-mailového účtu na {1}.",
"DialogMessage_EnableStartupLaunchTitle": "Enable Startup Launch",
"DialogMessage_EnableStartupLaunchMessage": "Let Wino Mail automatically launch minimized on Windows startup to not miss any notifications.\n\nDo you want to enable startup launch?",
"DialogMessage_EnableStartupLaunchDeniedMessage": "You can enable startup launch from Settings -> App Preferences.",
"Dialog_DontAskAgain": "Příště se neptat",
"CalendarAllDayEventSummary": "all-day events",
"CalendarItemAllDay": "all day",
"CalendarItem_DetailsPopup_JoinOnline": "Join online",
"CalendarItem_DetailsPopup_ViewEventButton": "View event",
"CalendarItem_DetailsPopup_ViewSeriesButton": "View series",
"CalendarDisplayOptions_Expand": "Expand",
"CalendarDisplayOptions_Color": "Color",
"CreateAccountAliasDialog_Title": "Create Account Alias",
"CreateAccountAliasDialog_Description": "Make sure your outgoing server allows sending mails from this alias.",
"CreateAccountAliasDialog_AliasAddress": "Address",
"CreateAccountAliasDialog_AliasAddressPlaceholder": "eg. support@mydomain.com",
"CreateAccountAliasDialog_ReplyToAddress": "Reply-To Address",
"CreateAccountAliasDialog_ReplyToAddressPlaceholder": "admin@mydomain.com",
"DiscordChannelDisclaimerMessage": "Wino nemá vlastní Discord server, ale speciální kanál 'wino-mail' je hostován na serveru 'Developer Sanctuary'.\nChcete-li získat informace o Winu, připojte se k vývojářskému serveru a sledujte 'wino-mail' kanál v rámci 'Community Projects'. \n\nBudete přesměrováni na stránku serveru 'Developer Sanctuary', protože Discord nepodporuje pozvánky přímo do kanálů.",
"DiscordChannelDisclaimerTitle": "Důležité Discord informace",
"Draft": "Koncept",
"DragMoveToFolderCaption": "Přesunout do {0}",
"Busy": "Busy",
"EditorToolbarOption_Draw": "Nakreslit",
"EditorToolbarOption_Format": "Formátovat",
"EditorToolbarOption_Insert": "Vložit",
@@ -171,26 +158,24 @@
"ElementTheme_Default": "Použít nastavení systému",
"ElementTheme_Light": "Světlý režim",
"Emoji": "Emoji",
"Error_FailedToSetupSystemFolders_Title": "Nastavení systémových složek se nezdařilo",
"Exception_WinoServerException": "Wino server failed.",
"Exception_MailProcessing": "This mail is still being processed. Please try again after few seconds.",
"Exception_ImapAutoDiscoveryFailed": "Nastavení poštovní schránky se nepodařilo najít.",
"Exception_ImapClientPoolFailed": "IMAP Client Pool selhal.",
"Exception_AuthenticationCanceled": "Ověřování bylo zrušeno",
"Exception_CustomThemeExists": "Tento motiv už existuje.",
"Exception_CustomThemeMissingName": "Musíte zadat název.",
"Exception_CustomThemeMissingWallpaper": "Musíte zadat vlastní obrázek pozadí.",
"Exception_FailedToSynchronizeAliases": "Failed to synchronize aliases",
"Exception_FailedToSynchronizeFolders": "Synchronizace složek se nezdařila",
"Exception_FailedToSynchronizeAliases": "Failed to synchronize aliases",
"Exception_MissingAlias": "Primary alias does not exist for this account. Creating draft failed.",
"Exception_FailedToSynchronizeProfileInformation": "Failed to synchronize profile information",
"Exception_GoogleAuthCallbackNull": "Callback uri je při aktivaci null.",
"Exception_GoogleAuthCorruptedCode": "Odpověď z autorizačního serveru je chybná.",
"Exception_GoogleAuthError": "Chyba autorizace OAuth: {0}",
"Exception_GoogleAuthInvalidResponse": "Přijata chybová odpověď ({0})",
"Exception_GoogleAuthorizationCodeExchangeFailed": "Ověření autorizačního kódu selhalo.",
"Exception_ImapAutoDiscoveryFailed": "Nastavení poštovní schránky se nepodařilo najít.",
"Exception_ImapClientPoolFailed": "IMAP Client Pool selhal.",
"Exception_InboxNotAvailable": "Nelze nastavit složky účtu.",
"Exception_InvalidSystemFolderConfiguration": "Konfigurace systémové složky není správná. Zkontrolujte konfiguraci a zkuste to znovu.",
"Exception_InvalidMultiAccountMoveTarget": "You can't move multiple items that belong to different accounts in linked account.",
"Exception_MailProcessing": "This mail is still being processed. Please try again after few seconds.",
"Exception_MissingAlias": "Primary alias does not exist for this account. Creating draft failed.",
"Exception_NullAssignedAccount": "Přiřazený účet je \"null\"",
"Exception_NullAssignedFolder": "Přiřazená složka je \"null\"",
"Exception_SynchronizerFailureHTTP": "Zpracování odpovědi se nezdařilo. HTTP kód chyby: {0}",
@@ -201,12 +186,12 @@
"Exception_UnsupportedProvider": "Tento poskytovatel není podporován.",
"Exception_UnsupportedSynchronizerOperation": "Tato operace není podporována pro {0}",
"Exception_UserCancelSystemFolderSetupDialog": "Uživatel zrušil dialogové okno s konfigurací systémové složky.",
"Exception_WinoServerException": "Wino server failed.",
"Exception_InboxNotAvailable": "Nelze nastavit složky účtu.",
"Files": "Soubory",
"FilteringOption_All": "Všechny",
"FilteringOption_Files": "Obsahuje soubory",
"FilteringOption_Flagged": "Označené",
"FilteringOption_Unread": "Nepřečtené",
"FilteringOption_Files": "Obsahuje soubory",
"Focused": "Důležité",
"FolderOperation_CreateSubFolder": "Vytvořit podsložku",
"FolderOperation_Delete": "Odstranit",
@@ -214,73 +199,27 @@
"FolderOperation_Empty": "Vyprázdnit tuto složku",
"FolderOperation_MarkAllAsRead": "Označit vše jako přečtené",
"FolderOperation_Move": "Přesunout",
"DragMoveToFolderCaption": "Přesunout do {0}",
"FolderOperation_None": "Žádné",
"FolderOperation_Pin": "Připnout",
"FolderOperation_Rename": "Přejmenovat",
"FolderOperation_Unpin": "Odepnout",
"GeneralTitle_Error": "Chyba",
"GeneralTitle_Info": "Informace",
"GeneralTitle_Warning": "Upozornění",
"GmailServiceDisabled_Title": "Gmail Error",
"GmailServiceDisabled_Message": "Your Google Workspace account seems to be disabled for Gmail service. Please contact your administrator to enable Gmail service for your account.",
"GmailArchiveFolderNameOverride": "Archive",
"HoverActionOption_Archive": "Archivovat",
"HoverActionOption_Delete": "Smazat",
"HoverActionOption_MoveJunk": "Přesunout do Koše",
"HoverActionOption_ToggleFlag": "Označit / Zrušit označení",
"HoverActionOption_ToggleRead": "Přečtené / Nepřečtené",
"ImageRenderingDisabled": "Vykreslování obrázků je pro tuto zprávu zakázáno.",
"ImapAdvancedSetupDialog_AuthenticationMethod": "Způsob ověření",
"ImapAdvancedSetupDialog_ConnectionSecurity": "Zabezpečení připojení",
"IMAPAdvancedSetupDialog_ValidationAuthMethodRequired": "Authentication method is required",
"IMAPAdvancedSetupDialog_ValidationConnectionSecurityRequired": "Connection security type is required",
"IMAPAdvancedSetupDialog_ValidationDisplayNameRequired": "Display name is required",
"IMAPAdvancedSetupDialog_ValidationEmailInvalid": "Please enter a valid email address",
"IMAPAdvancedSetupDialog_ValidationEmailRequired": "Email address is required",
"IMAPAdvancedSetupDialog_ValidationErrorTitle": "Please check the following:",
"IMAPAdvancedSetupDialog_ValidationIncomingPortInvalid": "Incoming port must be between 1-65535",
"IMAPAdvancedSetupDialog_ValidationIncomingPortRequired": "Incoming server port is required",
"IMAPAdvancedSetupDialog_ValidationIncomingServerRequired": "Incoming server address is required",
"IMAPAdvancedSetupDialog_ValidationOutgoingPasswordRequired": "Outgoing server password is required",
"IMAPAdvancedSetupDialog_ValidationOutgoingPortInvalid": "Outgoing port must be between 1-65535",
"IMAPAdvancedSetupDialog_ValidationOutgoingPortRequired": "Outgoing server port is required",
"IMAPAdvancedSetupDialog_ValidationOutgoingServerRequired": "Outgoing server address is required",
"IMAPAdvancedSetupDialog_ValidationOutgoingUsernameRequired": "Outgoing server username is required",
"IMAPAdvancedSetupDialog_ValidationPasswordRequired": "Password is required",
"IMAPAdvancedSetupDialog_ValidationUsernameRequired": "Username is required",
"ImapAuthenticationMethod_Auto": "Auto",
"ImapAuthenticationMethod_CramMD5": "CRAM-MD5",
"ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5",
"ImapAuthenticationMethod_EncryptedPassword": "Zašifrované heslo",
"ImapAuthenticationMethod_None": "Žádné ověření",
"ImapAuthenticationMethod_Ntlm": "NTLM",
"ImapAuthenticationMethod_Plain": "Normální heslo",
"ImapConnectionSecurity_Auto": "Auto",
"ImapConnectionSecurity_None": "Žádné",
"ImapConnectionSecurity_SslTls": "SSL/TLS",
"ImapConnectionSecurity_StartTls": "STARTTLS",
"MergedAccountCommonFolderInbox": "Doručená pošta",
"MergedAccountCommonFolderSent": "Odesláno",
"MergedAccountCommonFolderDraft": "Koncepty",
"MergedAccountCommonFolderJunk": "Nevyžádaná pošta",
"MergedAccountCommonFolderTrash": "Koš",
"MergedAccountCommonFolderArchive": "Archív",
"IMAPSetupDialog_AccountType": "Typ účtu",
"IMAPSetupDialog_ValidationSuccess_Title": "Success",
"IMAPSetupDialog_ValidationSuccess_Message": "Validation successful",
"IMAPSetupDialog_SaveImapSuccess_Title": "Success",
"IMAPSetupDialog_SaveImapSuccess_Message": "IMAP settings saved successfuly.",
"IMAPSetupDialog_ValidationFailed_Title": "IMAP Server validation failed.",
"IMAPSetupDialog_CertificateAllowanceRequired_Row0": "This server is requesting a SSL handshake to continue. Please confirm the certificate details below.",
"IMAPSetupDialog_CertificateAllowanceRequired_Row1": "Allow the handshake to continue setting up your account.",
"IMAPSetupDialog_CertificateDenied": "User didn't authorize the handshake with the certificate.",
"IMAPSetupDialog_CertificateIssuer": "Issuer",
"IMAPSetupDialog_CertificateSubject": "Subject",
"IMAPSetupDialog_CertificateValidFrom": "Valid from",
"IMAPSetupDialog_CertificateValidTo": "Valid to",
"IMAPSetupDialog_CertificateView": "View Certificate",
"IMAPSetupDialog_ConnectionFailedMessage": "Připojení IMAP se nezdařilo.",
"IMAPSetupDialog_ConnectionFailedTitle": "Připojení se nezdařilo",
"IMAPSetupDialog_DisplayName": "Zobrazované jméno",
"IMAPSetupDialog_DisplayNamePlaceholder": "např. Jan Novák",
"IMAPSetupDialog_IncomingMailServer": "Server příchozí pošty",
"IMAPSetupDialog_IncomingMailServerPort": "Port",
"IMAPSetupDialog_IMAPSettings": "IMAP Server Settings",
"IMAPSetupDialog_SMTPSettings": "SMTP Server Settings",
"IMAPSetupDialog_MailAddress": "E-mailová adresa",
"IMAPSetupDialog_MailAddressPlaceholder": "jan.novák@seznam.cz",
"IMAPSetupDialog_OutgoingMailServer": "Server odchozí pošty (SMTP)",
@@ -292,9 +231,26 @@
"IMAPSetupDialog_RequireSSLForIncomingMail": "Vyžadovat SSL pro příchozí e-mail",
"IMAPSetupDialog_RequireSSLForOutgoingMail": "Vyžadovat SSL pro odchozí e-mail",
"IMAPSetupDialog_Title": "Pokročilé nastavení IMAP",
"IMAPSetupDialog_UseSameConfig": "Použít stejné uživatelské jméno a heslo pro odesílání e-mailu",
"IMAPSetupDialog_Username": "Uživatelské jméno",
"IMAPSetupDialog_UsernamePlaceholder": "jan.novak, jan.novak@seznam.cz",
"IMAPSetupDialog_UseSameConfig": "Použít stejné uživatelské jméno a heslo pro odesílání e-mailu",
"IMAPSetupDialog_ConnectionFailedTitle": "Připojení se nezdařilo",
"IMAPSetupDialog_ConnectionFailedMessage": "Připojení IMAP se nezdařilo.",
"IMAPSetupDialog_CertificateAllowanceRequired_Row0": "This server is requesting a SSL handshake to continue. Please confirm the certificate details below.",
"IMAPSetupDialog_CertificateAllowanceRequired_Row1": "Allow the handshake to continue setting up your account.",
"IMAPSetupDialog_CertificateIssuer": "Issuer",
"IMAPSetupDialog_CertificateSubject": "Subject",
"IMAPSetupDialog_CertificateValidFrom": "Valid from",
"IMAPSetupDialog_CertificateValidTo": "Valid to",
"IMAPSetupDialog_CertificateDenied": "User didn't authorize the handshake with the certificate.",
"IMAPSetupDialog_CertificateView": "View Certificate",
"ImageRenderingDisabled": "Vykreslování obrázků je pro tuto zprávu zakázáno.",
"InfoBarAction_Enable": "Zapnout",
"InfoBarMessage_SynchronizationDisabledFolder": "Synchronizace této složky je vypnuta.",
"InfoBarTitle_SynchronizationDisabledFolder": "Synchronizace složky vypnuta",
"GeneralTitle_Error": "Chyba",
"GeneralTitle_Warning": "Upozornění",
"GeneralTitle_Info": "Informace",
"Info_AccountCreatedMessage": "{0} je vytvořen",
"Info_AccountCreatedTitle": "Vytvoření účtu",
"Info_AccountCreationFailedTitle": "Vytvoření účtu selhalo",
@@ -313,15 +269,14 @@
"Info_BackgroundExecutionDeniedTitle": "Zakázané spuštění na pozadí",
"Info_BackgroundExecutionUnknownErrorMessage": "Při registraci procesu synchronizace na pozadí došlo k neznámé výjimce.",
"Info_BackgroundExecutionUnknownErrorTitle": "Chyba procesu na pozadí",
"Info_CantDeletePrimaryAliasMessage": "Primary alias can't be deleted. Please change your alias before deleting this one",
"Info_FailedToOpenFileTitle": "Failed to launch file.",
"Info_FailedToOpenFileMessage": "File might be removed from the disk.",
"Info_ComposerMissingMIMEMessage": "Nelze zjistit MIME typ souboru. Synchronizace může pomoci.",
"Info_ComposerMissingMIMETitle": "Chyba",
"Info_ContactExistsMessage": "Tento kontakt je již v seznamu příjemců.",
"Info_ContactExistsTitle": "Kontakt existuje",
"Info_DraftFolderMissingMessage": "Složka Koncepty pro tento účet chybí. Zkontrolujte prosím nastavení účtu.",
"Info_DraftFolderMissingTitle": "Chybí složka Koncepty",
"Info_FailedToOpenFileMessage": "File might be removed from the disk.",
"Info_FailedToOpenFileTitle": "Failed to launch file.",
"Info_FileLaunchFailedTitle": "Spuštění souboru se nezdařilo",
"Info_InvalidAddressMessage": "'{0}' není platná e-mailová adresa.",
"Info_InvalidAddressTitle": "Neplatná adresa",
@@ -331,16 +286,15 @@
"Info_LogsNotFoundTitle": "Logy nenalezeny",
"Info_LogsSavedMessage": "{0} je uložen do vybrané složky.",
"Info_LogsSavedTitle": "Uloženo",
"Info_MailListSizeResetSuccessMessage": "The Mail List size has been reset.",
"Info_MailRenderingFailedMessage": "Tento e-mail je poškozený nebo nelze otevřít.\n{0}",
"Info_MailRenderingFailedTitle": "Vykreslení selhalo",
"Info_MessageCorruptedMessage": "Tato zpráva je poškozena.",
"Info_MessageCorruptedTitle": "Chyba",
"Info_MissingFolderMessage": "{0} neexistuje pro tento účet.",
"Info_MissingFolderTitle": "Chybějící složka",
"Info_PDFSaveSuccessTitle": "Hotovo",
"Info_PDFSaveFailedTitle": "Uložení PDF souboru se nezdařilo",
"Info_PDFSaveSuccessMessage": "PDF soubor je uložen do {0}",
"Info_PDFSaveSuccessTitle": "Hotovo",
"Info_PurchaseExistsMessage": "Vypadá to, že tento produkt byl již zakoupen.",
"Info_PurchaseExistsTitle": "Stávající produkt",
"Info_PurchaseThankYouMessage": "Děkujeme",
@@ -360,21 +314,32 @@
"Info_SyncCanceledMessage": "Zrušeno",
"Info_SyncCanceledTitle": "Synchronizace",
"Info_SyncFailedTitle": "Synchronizace se nezdařila",
"Info_UnsubscribeErrorMessage": "Nepodařilo se odhlásit odběr",
"Info_UnsubscribeLinkInvalidMessage": "Tento odkaz pro odhlášení je neplatný. Nepodařilo se odhlásit automatizované zasílání e-mailu.",
"Info_UnsubscribeLinkInvalidTitle": "Neplatná URI pro odhlášení mailu",
"Info_UnsubscribeSuccessMessage": "Odběr {0} byl úspěšně odhlášen.",
"Info_UnsupportedFunctionalityDescription": "Tato funkce zatím není implementována.",
"Info_UnsupportedFunctionalityTitle": "Nepodporováno",
"InfoBarAction_Enable": "Zapnout",
"InfoBarMessage_SynchronizationDisabledFolder": "Synchronizace této složky je vypnuta.",
"InfoBarTitle_SynchronizationDisabledFolder": "Synchronizace složky vypnuta",
"Info_UnsubscribeLinkInvalidTitle": "Neplatná URI pro odhlášení mailu",
"Info_UnsubscribeLinkInvalidMessage": "Tento odkaz pro odhlášení je neplatný. Nepodařilo se odhlásit automatizované zasílání e-mailu.",
"Info_UnsubscribeSuccessMessage": "Odběr {0} byl úspěšně odhlášen.",
"Info_UnsubscribeErrorMessage": "Nepodařilo se odhlásit odběr",
"Info_CantDeletePrimaryAliasMessage": "Primary alias can't be deleted. Please change your alias before deleting this one",
"Info_MailListSizeResetSuccessMessage": "The Mail List size has been reset.",
"ImapAdvancedSetupDialog_AuthenticationMethod": "Způsob ověření",
"ImapAdvancedSetupDialog_ConnectionSecurity": "Zabezpečení připojení",
"ImapAuthenticationMethod_Auto": "Auto",
"ImapAuthenticationMethod_CramMD5": "CRAM-MD5",
"ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5",
"ImapAuthenticationMethod_None": "Žádné ověření",
"ImapAuthenticationMethod_Plain": "Normální heslo",
"ImapAuthenticationMethod_EncryptedPassword": "Zašifrované heslo",
"ImapAuthenticationMethod_Ntlm": "NTLM",
"ImapConnectionSecurity_None": "Žádné",
"ImapConnectionSecurity_SslTls": "SSL/TLS",
"ImapConnectionSecurity_StartTls": "STARTTLS",
"ImapConnectionSecurity_Auto": "Auto",
"Justify": "Do bloku",
"Left": "Zleva",
"Link": "Odkaz",
"LinkedAccountsCreatePolicyMessage": "musíte mít alespoň 2 účty, abyste vytvořili propojení\npropojení bude odstraněno po uložení",
"LinkedAccountsTitle": "Propojené účty",
"MailItemNoSubject": "No subject",
"MailOperation_AlwaysMoveFocused": "Vždy přesunout do složky \"Důležité\"",
"MailOperation_AlwaysMoveOther": "Vždy se přesunout do složky \"Ostatní\"",
"MailOperation_Archive": "Archivovat",
@@ -401,32 +366,25 @@
"MailOperation_SaveAs": "Uložit jako…",
"MailOperation_SetFlag": "Označit vlajkou",
"MailOperation_Unarchive": "Odarchivovat",
"MailOperation_ViewMessageSource": "View message source",
"MailOperation_Zoom": "Přiblížit",
"MailOperation_ViewMessageSource": "View message source",
"MailsSelected": "Vybráno {0} položek",
"MarkFlagUnflag": "Označit / Zrušit označení vlajkou",
"MarkReadUnread": "Označit jako přečtené/nepřečtené",
"MenuManageAccounts": "Nastavení účtů",
"MenuMergedAccountItemAccountsSuffix": " účty",
"MenuNewMail": "Nový e-mail",
"MenuMergedAccountItemAccountsSuffix": " účty",
"MenuRate": "Ohodnotit Wino",
"MenuSettings": "Nastavení",
"MergedAccountCommonFolderArchive": "Archív",
"MergedAccountCommonFolderDraft": "Koncepty",
"MergedAccountCommonFolderInbox": "Doručená pošta",
"MergedAccountCommonFolderJunk": "Nevyžádaná pošta",
"MergedAccountCommonFolderSent": "Odesláno",
"MergedAccountCommonFolderTrash": "Koš",
"MergedAccountsAvailableAccountsTitle": "Dostupné účty",
"MessageSourceDialog_Title": "Message source",
"More": "Více",
"MoreFolderNameOverride": "Více",
"MoveMailDialog_InvalidFolderMessage": "{0} není platná složka pro tento e-mail.",
"MoveMailDialog_Title": "Vyberte složku",
"NewAccountDialog_AccountName": "Název účtu",
"NewAccountDialog_AccountNameDefaultValue": "Osobní",
"NewAccountDialog_AccountNamePlaceholder": "např. Osobní účet",
"NewAccountDialog_Title": "Přidat nový účet",
"MessageSourceDialog_Title": "Message source",
"NoMailSelected": "Nebyly vybrány žádné zprávy",
"NoMessageCrieteria": "Žádná zpráva neodpovídá kritériím vyhledávání",
"NoMessageEmptyFolder": "Tato složka je prázdná",
@@ -434,9 +392,6 @@
"Notifications_MultipleNotificationsTitle": "New Mail",
"Notifications_WinoUpdatedMessage": "Vyzkoušejte novou verzi {0}",
"Notifications_WinoUpdatedTitle": "Wino Mail byl aktualizován.",
"OnlineSearchFailed_Message": "Failed to perform search\n{0}\n\nListing offline mails.",
"OnlineSearchTry_Line1": "Can't find what you are looking for?",
"OnlineSearchTry_Line2": "Try online search.",
"Other": "Ostatní",
"PaneLengthOption_Default": "Výchozí",
"PaneLengthOption_ExtraLarge": "Extra velké",
@@ -446,94 +401,64 @@
"PaneLengthOption_Small": "Malé",
"Photos": "Fotky",
"PreparingFoldersMessage": "Připravování složek",
"ProtocolLogAvailable_Message": "Protokoly jsou k dispozici pro diagnostiku.",
"ProviderDetail_Gmail_Description": "Google účet",
"ProviderDetail_iCloud_Description": "Apple iCloud Account",
"ProviderDetail_iCloud_Title": "iCloud",
"ProviderDetail_IMAP_Description": "Vlastní IMAP/SMTP server",
"ProviderDetail_IMAP_Title": "IMAP server",
"ProviderDetail_Yahoo_Description": "Yahoo Account",
"ProviderDetail_Yahoo_Title": "Yahoo Mail",
"QuickEventDialog_EventName": "Event name",
"QuickEventDialog_IsAllDay": "All day",
"QuickEventDialog_Location": "Location",
"QuickEventDialog_RemindMe": "Remind me",
"QuickEventDialogMoreDetailsButtonText": "More details",
"Reader_SaveAllAttachmentButtonText": "Save all attachments",
"ProviderDetail_Yahoo_Description": "Yahoo Account",
"ProviderDetail_iCloud_Title": "iCloud",
"ProviderDetail_iCloud_Description": "Apple iCloud Account",
"ProtocolLogAvailable_Message": "Protokoly jsou k dispozici pro diagnostiku.",
"Results": "Výsledky",
"Right": "Vpravo",
"Reader_SaveAllAttachmentButtonText": "Save all attachments",
"SynchronizationFolderReport_Success": "Aktuální",
"SynchronizationFolderReport_Failed": "Synchronizace se nezdařila.",
"SearchBarPlaceholder": "Vyhledávaný výraz",
"SearchingIn": "Vyhledávání v",
"SearchPivotName": "Výsledky",
"SettingConfigureSpecialFolders_Button": "Nastavit",
"SettingsEditAccountDetails_IMAPConfiguration_Title": "IMAP/SMTP Configuration",
"SettingsEditAccountDetails_IMAPConfiguration_Description": "Change your incoming/outgoing server settings.",
"SettingsAbout_Description": "Zjistěte více o Wino.",
"SettingsAbout_Title": "O aplikaci",
"SettingsAboutGithub_Description": "Přejít na seznam chyb na GitHub.",
"SettingsAboutGithub_Title": "GitHub",
"SettingsAccountManagementAppendMessage_Title": "Přidat zprávy do složky Odeslané",
"SettingsAccountManagementAppendMessage_Description": "Vytvořit kopii zprávy ve složce \"Odeslané\" po odeslání konceptu. Povolte tuto možnost, pokud nevidíte své e-maily po odeslání ve složky \"Odeslané\".",
"SettingsEditLinkedInbox_Title": "Upravit propojený účet",
"SettingsEditLinkedInbox_Description": "Přidat / odebrat účty, přejmenovat nebo zrušit propojení mezi účty.",
"SettingsAboutVersion": "Verze ",
"SettingsAboutWinoDescription": "Lehký e-mailový klient pro systém Windows.",
"SettingsAbout_Description": "Zjistěte více o Wino.",
"SettingsAbout_Title": "O aplikaci",
"SettingsAccentColor_Description": "Změna barevného tónu aplikace",
"SettingsAccentColor_Title": "Barevný tón",
"SettingsAccentColor_UseWindowsAccentColor": "Použít barevný tón mých Windows",
"SettingsAccountManagementAppendMessage_Description": "Vytvořit kopii zprávy ve složce \"Odeslané\" po odeslání konceptu. Povolte tuto možnost, pokud nevidíte své e-maily po odeslání ve složky \"Odeslané\".",
"SettingsAccountManagementAppendMessage_Title": "Přidat zprávy do složky Odeslané",
"SettingsAccountName_Description": "Změnit název účtu",
"SettingsAccountName_Title": "Název účtu",
"SettingsApplicationTheme_Description": "Přizpůsobte si Wino různými motivy dle vaší libosti.",
"SettingsApplicationTheme_Title": "Motiv aplikace",
"SettingsAppPreferences_CloseBehavior_Description": "What should happen when you close the app?",
"SettingsAppPreferences_CloseBehavior_Title": "Application close behavior",
"SettingsAppPreferences_Description": "General settings / preferences for Wino Mail.",
"SettingsAppPreferences_SearchMode_Description": "Set whether Wino should check fetched mails first while doing a search or ask your mail server online. Local search is always faster and you can always do an online search if your mail is not in the results.",
"SettingsAppPreferences_SearchMode_Local": "Local",
"SettingsAppPreferences_SearchMode_Online": "Online",
"SettingsAppPreferences_SearchMode_Title": "Default search mode",
"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Description": "Wino Mail will keep running in the background. You will be notified as new mails arrive.",
"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Title": "Run in the background",
"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Description": "Wino Mail will keep running on the system tray. Available to launch by clicking on an icon. You will be notified as new mails arrive.",
"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Title": "Minimize to system tray",
"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Description": "Wino Mail will not keep running anywhere. You will not be notified as new mails arrive. Launch Wino Mail again to continue mail synchronization.",
"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Title": "Terminate",
"SettingsAppPreferences_StartupBehavior_Description": "Allow Wino Mail to launch minimized when Windows starts. Always allow it to receive notifications.",
"SettingsAppPreferences_StartupBehavior_Disable": "Disable",
"SettingsAppPreferences_StartupBehavior_Disabled": "Wino Mail will not be launched on Windows startup. This will cause you to miss notifications when you restart your computer.",
"SettingsAppPreferences_StartupBehavior_DisabledByPolicy": "Your administrator or group policies disabled running applications on startup. Thus, Wino Mail can't be set to launch on Windows startup.",
"SettingsAppPreferences_StartupBehavior_DisabledByUser": "Please go to Task Manager -> Startup tab to allow Wino Mail to launch on Windows startup.",
"SettingsAppPreferences_StartupBehavior_Enable": "Enable",
"SettingsAppPreferences_StartupBehavior_Enabled": "Wino Mail successfully set to be launched in the background on Windows startup.",
"SettingsAppPreferences_StartupBehavior_FatalError": "Fatal error occurred while changing the startup mode for Wino Mail.",
"SettingsAppPreferences_StartupBehavior_Title": "Start minimized on Windows startup",
"SettingsAppPreferences_Title": "App Preferences",
"SettingsAutoSelectNextItem_Description": "Vyberat další položku poté, co se odstraní nebo přesune vybraný e-mail.",
"SettingsAutoSelectNextItem_Title": "Automaticky vybrat další položku",
"SettingsAvailableThemes_Description": "Vyberte si šablonu ze sbírky Wino dle vaší libosti nebo použijte vlastní motiv.",
"SettingsAvailableThemes_Title": "Dostupné motivy",
"SettingsCalendarSettings_Description": "Change first day of week, hour cell height and more...",
"SettingsAutoSelectNextItem_Title": "Automaticky vybrat další položku",
"SettingsAutoSelectNextItem_Description": "Vyberat další položku poté, co se odstraní nebo přesune vybraný e-mail.",
"SettingsCalendarSettings_Title": "Calendar Settings",
"SettingsComposer_Title": "Composer",
"SettingsComposerFont_Title": "Výchozí písmo editoru",
"SettingsComposerFontFamily_Description": "Změna výchozího fontu a jeho velikosti pro editor e-mailu",
"SettingsConfigureSpecialFolders_Description": "Nastavte složky se speciálními funkcemi. Složky jako Archív, Doručená pošta a Koncepty jsou nezbytné pro správné fungování Wina.",
"SettingsConfigureSpecialFolders_Title": "Nastavit systémové složky",
"SettingsCalendarSettings_Description": "Change first day of week, hour cell height and more...",
"SettingsCustomTheme_Description": "Vytvořte si vlastní motiv s vlastním pozadím a barevným tónem.",
"SettingsCustomTheme_Title": "Vlastní motiv",
"SettingsConfigureSpecialFolders_Title": "Nastavit systémové složky",
"SettingsConfigureSpecialFolders_Description": "Nastavte složky se speciálními funkcemi. Složky jako Archív, Doručená pošta a Koncepty jsou nezbytné pro správné fungování Wina.",
"SettingConfigureSpecialFolders_Button": "Nastavit",
"Error_FailedToSetupSystemFolders_Title": "Nastavení systémových složek se nezdařilo",
"SettingsDeleteAccount_Description": "Odstranit všechny e-maily a přihlašovací údaje spojené s tímto účtem.",
"SettingsDeleteAccount_Title": "Smazat tento účet",
"SettingsDeleteProtection_Description": "Should Wino ask you for confirmation every time you try to permanently delete a mail using Shift + Del keys?",
"SettingsDeleteProtection_Description": "Měl by vás Wino požádat o potvrzení pokaždé, když se pokoušíte trvale smazat e-mail pomocí kláves Shift + Del?",
"SettingsDeleteProtection_Title": "Ochrana proti trvalému smazání",
"SettingsDiagnostics_Description": "Pro vývojáře",
"SettingsDiagnostics_DiagnosticId_Description": "Share this ID with the developers when asked to get help for the issues you experience in Wino Mail.",
"SettingsDiagnostics_DiagnosticId_Title": "Diagnostic ID",
"SettingsDiagnostics_Title": "Diagnostika",
"SettingsDiagnostics_DiagnosticId_Title": "Diagnostic Id",
"SettingsDiagnostics_DiagnosticId_Description": "Share this Id with the developers when asked to get help for the issues you experience in Wino Mail.",
"SettingsDiscord_Description": "Získejte pravidelné aktualizace z vývoje, připojte se k diskusím o rozvoji aplikace a poskytněte zpětnou vazbu.",
"SettingsDiscord_Title": "Discord kanál",
"SettingsEditLinkedInbox_Description": "Přidat / odebrat účty, přejmenovat nebo zrušit propojení mezi účty.",
"SettingsEditLinkedInbox_Title": "Upravit propojený účet",
"SettingsElementThemeSelectionDisabled": "Výběr motivu prvku je zakázán, pokud je vybrán jiný motiv aplikace než výchozí.",
"SettingsElementTheme_Description": "Vyberte motiv Windows pro Wino",
"SettingsElementTheme_Title": "Motiv prvku",
"SettingsElementThemeSelectionDisabled": "Výběr motivu prvku je zakázán, pokud je vybrán jiný motiv aplikace než výchozí.",
"SettingsEnableHoverActions_Title": "Povolit akce při přejetí myší",
"SettingsEnableIMAPLogs_Description": "Povolte pro poskytnutí podrobností o problémech s připojením IMAP, které jste měli během nastavení serveru IMAP.",
"SettingsEnableIMAPLogs_Title": "Povolit logy protokolu IMAP",
@@ -546,15 +471,12 @@
"SettingsExternalContent_Title": "Externí obsah",
"SettingsFocusedInbox_Description": "Nastavte, zda by měla být doručená pošta rozdělena do dvou jako \"Důležité\" - \"Ostatní\".",
"SettingsFocusedInbox_Title": "Doporučené",
"SettingsFolderMenuStyle_Description": "Změnit, zda by složky účtu měly být vnořeny uvnitř nabídky účtu či nikoli. Vypněte tento režim, pokud se vám líbí starý systém menu Windows Mail",
"SettingsFolderMenuStyle_Title": "Zapnout vnořené složky",
"SettingsFolderOptions_Description": "Změnit nastavení jednotlivých složek, například povolit/zakázat synchronizaci, nebo zobrazit/skrýt počet nepřečtených e-mailů.",
"SettingsFolderOptions_Title": "Nastavení složky",
"SettingsFolderSync_Description": "Povolit nebo zakázat konkrétní složky pro synchronizaci.",
"SettingsFolderSync_Title": "Synchronizace složek",
"SettingsFontFamily_Title": "Font",
"SettingsFontPreview_Title": "Náhled",
"SettingsFontSize_Title": "Velikost",
"SettingsFolderOptions_Title": "Nastavení složky",
"SettingsFolderOptions_Description": "Změnit nastavení jednotlivých složek, například povolit/zakázat synchronizaci, nebo zobrazit/skrýt počet nepřečtených e-mailů.",
"SettingsManageAliases_Title": "Aliases",
"SettingsManageAliases_Description": "See e-mail aliases assigned for this account, update or delete them.",
"SettingsHoverActionCenter": "Prostřední akce",
"SettingsHoverActionLeft": "Levá akce",
"SettingsHoverActionRight": "Pravá akce",
@@ -562,25 +484,44 @@
"SettingsHoverActions_Title": "Akce při přejetí myší",
"SettingsLanguage_Description": "Změnit jazyk aplikace pro Wino.",
"SettingsLanguage_Title": "Jazyk aplikace",
"SettingsLanguageTime_Description": "Jazyk aplikace Wino, preferovaný formát času.",
"SettingsLanguageTime_Title": "Jazyk & čas",
"SettingsLanguageTime_Description": "Jazyk aplikace Wino, preferovaný formát času.",
"CategoriesFolderNameOverride": "Kategorie",
"AccountAlias_Column_Verified": "Verified",
"AccountAlias_Column_Alias": "Alias",
"AccountAlias_Column_IsPrimaryAlias": "Primary",
"AccountAlias_Disclaimer_FirstLine": "Wino can only import aliases for your Gmail accounts.",
"AccountAlias_Disclaimer_SecondLine": "If you want to use aliases for your Outlook or IMAP account, please add them yourself.",
"MoreFolderNameOverride": "Více",
"SettingsOptions_Title": "Nastavení",
"SettingsLinkAccounts_Description": "Sloučit více účtů do jednoho. Podívejte se na e-maily v jedné složce \"Doručená pošta\" společně.",
"SettingsLinkAccounts_Title": "Vytvořit propojené účty",
"SettingsLinkedAccountsSave_Description": "Změnit aktuální propojení s novými účty.",
"SettingsLinkedAccountsSave_Title": "Uložit změny",
"SettingsLoadImages_Title": "Automaticky načítat obrázky",
"SettingsLoadPlaintextLinks_Title": "Convert plaintext links to clickable links",
"SettingsLoadStyles_Title": "Automaticky načítat styly",
"SettingsMailListActionBar_Description": "Hide/show action bar at top of message list.",
"SettingsMailListActionBar_Title": "Show mail list actions",
"SettingsLoadPlaintextLinks_Title": "Convert plaintext links to clickable links",
"SettingsMailSpacing_Description": "Přizpůsobit rozestupy položek v seznamu e-mailů.",
"SettingsMailSpacing_Title": "Rozestupy e-mailů",
"SettingsFolderMenuStyle_Title": "Zapnout vnořené složky",
"SettingsFolderMenuStyle_Description": "Změnit, zda by složky účtu měly být vnořeny uvnitř nabídky účtu či nikoli. Vypněte tento režim, pokud se vám líbí starý systém menu Windows Mail",
"SettingsManageAccountSettings_Description": "Oznámení, podpisy, synchronizace a další nastavení pro jednotlivé účty.",
"SettingsManageAccountSettings_Title": "Správa nastavení účtů",
"SettingsManageAliases_Description": "See e-mail aliases assigned for this account, update or delete them.",
"SettingsManageAliases_Title": "Aliases",
"SettingsEditAccountDetails_Title": "Edit Account Details",
"SettingsEditAccountDetails_Description": "Change account name, sender name and assign a new color if you like.",
"SettingsAppPreferences_Title": "App Preferences",
"SettingsAppPreferences_Description": "General settings / preferences for Wino Mail.",
"SettingsAppPreferences_CloseBehavior_Title": "Application close behavior",
"SettingsAppPreferences_CloseBehavior_Description": "What should happen when you close the app?",
"SettingsAppPreferences_StartupBehavior_Title": "Start minimized on Windows startup",
"SettingsAppPreferences_StartupBehavior_Description": "Allow Wino Mail to launch minimized when Windows starts. Always allow it to receive notifications.",
"SettingsAppPreferences_StartupBehavior_Enabled": "Wino Mail successfully set to be launched in the background on Windows startup.",
"SettingsAppPreferences_StartupBehavior_Disabled": "Wino Mail will not be launched on Windows startup. This will cause you to miss notifications when you restart your computer.",
"SettingsAppPreferences_StartupBehavior_DisabledByPolicy": "Your administrator or group policies disabled running applications on startup. Thus, Wino Mail can't be set to launch on Windows startup.",
"SettingsAppPreferences_StartupBehavior_DisabledByUser": "Please go to Task Manager -> Startup tab to allow Wino Mail to launch on Windows startup.",
"SettingsAppPreferences_StartupBehavior_FatalError": "Fatal error occurred while changing the startup mode for Wino Mail.",
"SettingsAppPreferences_StartupBehavior_Enable": "Enable",
"SettingsAppPreferences_StartupBehavior_Disable": "Disable",
"SettingsReorderAccounts_Title": "Změna pořadí účtů",
"SettingsReorderAccounts_Description": "Change the order of accounts in the account list.",
"SettingsManageLink_Description": "Přesunout položky pro přidání nového propojení účtů nebo odstranění již existujícího.",
"SettingsManageLink_Title": "Spravovat propojení",
"SettingsMarkAsRead_Description": "Změnit, co by se mělo stát s vybranou položkou.",
@@ -594,78 +535,59 @@
"SettingsNoAccountSetupMessage": "Zatím jste nenastavili žádný účet.",
"SettingsNotifications_Description": "Zapnout nebo vypnout oznámení pro tento účet.",
"SettingsNotifications_Title": "Oznámení",
"SettingsNotificationsAndTaskbar_Description": "Change whether notifications should be displayed and taskbar badge for this account.",
"SettingsNotificationsAndTaskbar_Title": "Notifications & Taskbar",
"SettingsOptions_Title": "Nastavení",
"SettingsTaskbarBadge_Title": "Taskbar Badge",
"SettingsTaskbarBadge_Description": "Include unread mail count in taskbar icon.",
"SettingsNotificationsAndTaskbar_Description": "Change whether notifications should be displayed and taskbar badge for this account.",
"SettingsPaneLengthReset_Description": "Reset the size of the mail list to original if you have issues with it.",
"SettingsPaneLengthReset_Title": "Reset Mail List Size",
"SettingsPaypal_Description": "Ukažte mnohem více lásky ❤️ Všechny dary jsou vítany.",
"SettingsPaypal_Title": "Přispět přes PayPal",
"SettingsPersonalization_Description": "Změňte vzhled Wino, jak se vám líbí.",
"SettingsPersonalization_Title": "Přizpůsobení",
"SettingsPersonalizationMailDisplayCompactMode": "Kompaktní režim",
"SettingsPersonalizationMailDisplayMediumMode": "Střední režim",
"SettingsPersonalizationMailDisplaySpaciousMode": "Prostorový režim",
"SettingsPrefer24HourClock_Description": "Čas přijetí pošty bude zobrazen ve 24-hodinovém formátu času, namísto 12-hodinového (AM/PM)",
"SettingsPrefer24HourClock_Title": "Zobrazit 24-hodinový formát času",
"SettingsPersonalization_Description": "Změňte vzhled Wino, jak se vám líbí.",
"SettingsPersonalization_Title": "Přizpůsobení",
"SettingsPrivacyPolicy_Description": "Zkontrolujte zásady ochrany osobních údajů.",
"SettingsPrivacyPolicy_Title": "Zásady ochrany osobních údajů",
"SettingsReader_Title": "Reader",
"SettingsComposer_Title": "Composer",
"SettingsReadComposePane_Description": "Fonts, external content.",
"SettingsReadComposePane_Title": "Reader & Composer",
"SettingsReader_Title": "Reader",
"SettingsReaderFont_Title": "Výchozí font pro vykreslení e-mailu",
"SettingsReaderFontFamily_Description": "Změna výchozího fontu a jeho velikosti pro vykreslení e-mailu",
"SettingsFontFamily_Title": "Font",
"SettingsFontSize_Title": "Velikost",
"SettingsFontPreview_Title": "Náhled",
"SettingsComposerFont_Title": "Výchozí písmo editoru",
"SettingsComposerFontFamily_Description": "Změna výchozího fontu a jeho velikosti pro editor e-mailu",
"SettingsRenameMergeAccount_Description": "Změnit zobrazený název propojených účtů.",
"SettingsRenameMergeAccount_Title": "Přejmenovat",
"SettingsReorderAccounts_Description": "Change the order of accounts in the account list.",
"SettingsReorderAccounts_Title": "Změna pořadí účtů",
"SettingsSemanticZoom_Description": "Toto vám umožní kliknout na hlavičky v seznamu zpráv a přejít na konkrétní datum",
"SettingsSemanticZoom_Title": "Sémanické přiblížení pro záhlaví data",
"SettingsShowPreviewText_Description": "Hide/show the preview text.",
"SettingsShowPreviewText_Title": "Zobrazit náhled textu",
"SettingsShowSenderPictures_Description": "Skrýt/zobrazit náhled obrázku odesílatele.",
"SettingsShowSenderPictures_Title": "Zobrazit avatary odesílatele",
"SettingsEnableGravatarAvatars_Title": "Gravatar",
"SettingsEnableGravatarAvatars_Description": "Use gravatar (if available) as sender picture",
"SettingsEnableFavicons_Title": "Domain icons (Favicons)",
"SettingsEnableFavicons_Description": "Use domain favicons (if available) as sender picture",
"SettingsMailList_ClearAvatarsCache_Button": "Clear cached avatars",
"SettingsSignature_AddCustomSignature_Button": "Add signature",
"SettingsSignature_AddCustomSignature_Title": "Add custom signature",
"SettingsSignature_DeleteSignature_Title": "Delete signature",
"SettingsPrefer24HourClock_Title": "Zobrazit 24-hodinový formát času",
"SettingsPrefer24HourClock_Description": "Čas přijetí pošty bude zobrazen ve 24-hodinovém formátu času, namísto 12-hodinového (AM/PM)",
"SettingsSignature_Description": "Manage account signatures",
"SettingsSignature_EditSignature_Title": "Edit signature",
"SettingsSignature_ForFollowingMessages_Title": "For Replies/Forwards",
"SettingsSignature_ForNewMessages_Title": "For New Messages",
"SettingsSignature_NoneSignatureName": "None",
"SettingsSignature_SignatureDefaults": "Signature defaults",
"SettingsSignature_Signatures": "Signatures",
"SettingsSignature_Title": "Podpis",
"SettingsStartupItem_Description": "Primární účet zobrazený po startu",
"SettingsStartupItem_Title": "Primární účet",
"SettingsStore_Description": "Ukaž trochu lásky ❤️",
"SettingsStore_Title": "Ohodnotit v obchodě",
"SettingsTaskbarBadge_Description": "Include unread mail count in taskbar icon.",
"SettingsTaskbarBadge_Title": "Taskbar Badge",
"SettingsThreads_Description": "Uspořádat zprávy do konverzačních vláken.",
"SettingsThreads_Title": "Vlákna konverzací",
"SettingsMailListActionBar_Description": "Hide/show action bar at top of message list.",
"SettingsMailListActionBar_Title": "Show mail list actions",
"SettingsUnlinkAccounts_Description": "Remove the link between accounts. his will not delete your accounts.",
"SettingsUnlinkAccounts_Title": "Rozpojit účty",
"SettingsMailRendering_ActionLabels_Title": "Action labels",
"SettingsMailRendering_ActionLabels_Description": "Show action labels.",
"SignatureDeleteDialog_Message": "Are you sure you want to delete \"{0}\" signature?",
"SignatureDeleteDialog_Title": "Delete signature",
"SignatureEditorDialog_SignatureName_Placeholder": "Name your signature",
"SignatureEditorDialog_SignatureName_TitleEdit": "Current signature name: {0}",
"SignatureEditorDialog_SignatureName_TitleNew": "Signature name",
"SignatureEditorDialog_Title": "Signature Editor",
"SortingOption_Date": "podle data",
"SortingOption_Name": "podle jména",
"StoreRatingDialog_MessageFirstLine": "Veškerá zpětná vazba se cení a v bude mít vliv na zlepšení aplikace Wino. Chcete Wino ohodnotit v Microsoft Store?",
"StoreRatingDialog_MessageSecondLine": "Chcete ohodnotit Wino Mail v Microsoft Store?",
"StoreRatingDialog_Title": "Líbí se vám Wino?",
"SynchronizationFolderReport_Failed": "Synchronizace se nezdařila.",
"SynchronizationFolderReport_Success": "Aktuální",
"SystemFolderConfigDialog_ArchiveFolderDescription": "Archivované zprávy budou přesunuty do zde.",
"SystemFolderConfigDialog_ArchiveFolderHeader": "složka \"Archív\"",
"SystemFolderConfigDialog_DeletedFolderDescription": "Smazané zprávy budou přesunuty zde.",
@@ -679,15 +601,11 @@
"SystemFolderConfigDialog_SentFolderDescription": "Zprávy, které byly odeslány, skončí zde. ",
"SystemFolderConfigDialog_SentFolderHeader": "složka \"Odeslané\"",
"SystemFolderConfigDialog_Title": "Nastavit systémové složky",
"SystemFolderConfigDialogValidation_DuplicateSystemFolders": "Některé ze systémových složek se v konfiguraci používají více než jednou.",
"SystemFolderConfigDialogValidation_InboxSelected": "Složku Doručená pošta nelze přiřadit k žádné jiné systémové složce.",
"SystemFolderConfigSetupSuccess_Message": "Systémové složky jsou úspěšně nakonfigurovány.",
"SystemFolderConfigDialogValidation_DuplicateSystemFolders": "Některé ze systémových složek se v konfiguraci používají více než jednou.",
"SystemFolderConfigSetupSuccess_Title": "Nastavení systémových složek",
"SystemFolderConfigSetupSuccess_Message": "Systémové složky jsou úspěšně nakonfigurovány.",
"TestingImapConnectionMessage": "Testuji připojení k serveru...",
"TitleBarServerDisconnectedButton_Description": "Wino is disconnected from the network. Click reconnect to restore connection.",
"TitleBarServerDisconnectedButton_Title": "no connection",
"TitleBarServerReconnectButton_Title": "reconnect",
"TitleBarServerReconnectingButton_Title": "connecting",
"Today": "Dnes",
"UnknownAddress": "neznámá adresa",
"UnknownDateHeader": "Neznámé datum",
@@ -699,8 +617,36 @@
"WinoUpgradeMessage": "Přejít na neomezený počet účtů",
"WinoUpgradeRemainingAccountsMessage": "{0} z {1} použitých bezplatných účtů.",
"Yesterday": "Včera",
"SettingsAppPreferences_EmailSyncInterval_Title": "Email sync interval",
"SettingsAppPreferences_EmailSyncInterval_Description": "Automatic email synchronization interval (minutes). This setting will be applied only after restarting Wino Mail."
"SignatureEditorDialog_Title": "Signature Editor",
"SignatureEditorDialog_SignatureName_Placeholder": "Name your signature",
"SignatureEditorDialog_SignatureName_TitleNew": "Signature name",
"SignatureEditorDialog_SignatureName_TitleEdit": "Current signature name: {0}",
"SignatureDeleteDialog_Title": "Delete signature",
"SignatureDeleteDialog_Message": "Are you sure you want to delete \"{0}\" signature?",
"SettingsSignature_ForNewMessages_Title": "For New Messages",
"SettingsSignature_ForFollowingMessages_Title": "For Replies/Forwards",
"SettingsSignature_SignatureDefaults": "Signature defaults",
"SettingsSignature_Signatures": "Signatures",
"SettingsSignature_AddCustomSignature_Title": "Add custom signature",
"SettingsSignature_AddCustomSignature_Button": "Add signature",
"SettingsSignature_EditSignature_Title": "Edit signature",
"SettingsSignature_DeleteSignature_Title": "Delete signature",
"SettingsSignature_NoneSignatureName": "None",
"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Title": "Minimize to system tray",
"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Description": "Wino Mail will keep running on the system tray. Available to launch by clicking on an icon. You will be notified as new mails arrive.",
"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Title": "Run in the background",
"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Description": "Wino Mail will keep running in the background. You will be notified as new mails arrive.",
"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Title": "Terminate",
"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Description": "Wino Mail will not keep running anywhere. You will not be notified as new mails arrive. Launch Wino Mail again to continue mail synchronization.",
"TitleBarServerDisconnectedButton_Title": "no connection",
"TitleBarServerDisconnectedButton_Description": "Wino is disconnected from the network. Click reconnect to restore connection.",
"TitleBarServerReconnectButton_Title": "reconnect",
"TitleBarServerReconnectingButton_Title": "connecting",
"MailItemNoSubject": "No subject",
"QuickEventDialogMoreDetailsButtonText": "More details",
"QuickEventDialog_RemindMe": "Remind me",
"QuickEventDialog_Location": "Location",
"QuickEventDialog_EventName": "Event name",
"QuickEventDialog_IsAllDay": "All day"
}

Some files were not shown because too many files have changed in this diff Show More