Some item templates and removal of sqlkata.

This commit is contained in:
Burak Kaan Köse
2025-11-15 13:29:02 +01:00
parent b356af8eb4
commit 12a39064dc
19 changed files with 313 additions and 356 deletions
-1
View File
@@ -46,7 +46,6 @@
<PackageVersion Include="Serilog.Sinks.ApplicationInsights" Version="4.0.0" /> <PackageVersion Include="Serilog.Sinks.ApplicationInsights" Version="4.0.0" />
<PackageVersion Include="SkiaSharp" Version="3.119.1" /> <PackageVersion Include="SkiaSharp" Version="3.119.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.10.196-beta" /> <PackageVersion Include="sqlite-net-pcl" Version="1.10.196-beta" />
<PackageVersion Include="SqlKata" Version="4.0.1" />
<PackageVersion Include="System.Drawing.Common" Version="10.0.0" /> <PackageVersion Include="System.Drawing.Common" Version="10.0.0" />
<PackageVersion Include="System.Private.Uri" Version="4.3.2" /> <PackageVersion Include="System.Private.Uri" Version="4.3.2" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.10" /> <PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.10" />
-1
View File
@@ -31,7 +31,6 @@
<PackageReference Include="NodaTime" /> <PackageReference Include="NodaTime" />
<PackageReference Include="Sentry.Serilog" /> <PackageReference Include="Sentry.Serilog" />
<PackageReference Include="SkiaSharp" /> <PackageReference Include="SkiaSharp" />
<PackageReference Include="SqlKata" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -32,9 +32,15 @@
<ComboBox <ComboBox
x:Name="MailOperationComboBox" x:Name="MailOperationComboBox"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberPath="DisplayName"
ItemsSource="{x:Bind AvailableMailOperations}" ItemsSource="{x:Bind AvailableMailOperations}"
SelectedItem="{x:Bind SelectedMailOperation, Mode=TwoWay}" /> SelectedItem="{x:Bind SelectedMailOperation, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="data:MailOperationViewModel">
<TextBlock Text="{x:Bind DisplayName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel> </StackPanel>
<!-- Key Input --> <!-- Key Input -->
+1 -1
View File
@@ -22,7 +22,7 @@
<Identity <Identity
Name="58272BurakKSE.WinoMailPreview" Name="58272BurakKSE.WinoMailPreview"
Publisher="CN=51FBDAF3-E212-4149-89A2-A2636B3BC911" Publisher="CN=51FBDAF3-E212-4149-89A2-A2636B3BC911"
Version="0.0.8.0" /> Version="2.0.13.0" />
<mp:PhoneIdentity PhoneProductId="3879fcfb-a561-4599-9103-e0c9b35a271f" PhonePublisherId="00000000-0000-0000-0000-000000000000"/> <mp:PhoneIdentity PhoneProductId="3879fcfb-a561-4599-9103-e0c9b35a271f" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
@@ -9,6 +9,7 @@
xmlns:data="using:Wino.Core.ViewModels.Data" xmlns:data="using:Wino.Core.ViewModels.Data"
xmlns:domain="using:Wino.Core.Domain" xmlns:domain="using:Wino.Core.Domain"
xmlns:helpers="using:Wino.Helpers" xmlns:helpers="using:Wino.Helpers"
xmlns:interfaces="using:Wino.Core.Domain.Interfaces"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls" xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:selectors="using:Wino.Selectors" xmlns:selectors="using:Wino.Selectors"
@@ -194,9 +195,14 @@
<winuiControls:SettingsCard Description="{x:Bind domain:Translator.SettingsStartupItem_Description}" Header="{x:Bind domain:Translator.SettingsStartupItem_Title}"> <winuiControls:SettingsCard Description="{x:Bind domain:Translator.SettingsStartupItem_Description}" Header="{x:Bind domain:Translator.SettingsStartupItem_Title}">
<ComboBox <ComboBox
MinWidth="150" MinWidth="150"
DisplayMemberPath="StartupEntityTitle"
ItemsSource="{x:Bind ViewModel.Accounts, Mode=OneTime}" ItemsSource="{x:Bind ViewModel.Accounts, Mode=OneTime}"
SelectedItem="{x:Bind ViewModel.StartupAccount, Mode=TwoWay}" /> SelectedItem="{x:Bind ViewModel.StartupAccount, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="interfaces:IAccountProviderDetailViewModel">
<TextBlock Text="{x:Bind StartupEntityTitle}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<winuiControls:SettingsCard.HeaderIcon> <winuiControls:SettingsCard.HeaderIcon>
<SymbolIcon Symbol="Account" /> <SymbolIcon Symbol="Account" />
</winuiControls:SettingsCard.HeaderIcon> </winuiControls:SettingsCard.HeaderIcon>
+8 -2
View File
@@ -12,6 +12,7 @@
xmlns:domain="using:Wino.Core.Domain" xmlns:domain="using:Wino.Core.Domain"
xmlns:entities="using:Wino.Core.Domain.Entities.Shared" xmlns:entities="using:Wino.Core.Domain.Entities.Shared"
xmlns:helpers="using:Wino.Helpers" xmlns:helpers="using:Wino.Helpers"
xmlns:mail="using:Wino.Core.Domain.Entities.Mail"
xmlns:mailkit="using:MimeKit" xmlns:mailkit="using:MimeKit"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls" xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
@@ -470,10 +471,15 @@
<ComboBox <ComboBox
x:Name="AccountsComboBox" x:Name="AccountsComboBox"
Grid.Column="1" Grid.Column="1"
DisplayMemberPath="AliasAddress"
IsEditable="False" IsEditable="False"
ItemsSource="{x:Bind ViewModel.AvailableAliases, Mode=OneWay}" ItemsSource="{x:Bind ViewModel.AvailableAliases, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.SelectedAlias, Mode=TwoWay}" /> SelectedItem="{x:Bind ViewModel.SelectedAlias, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="mail:MailAccountAlias">
<TextBlock Text="{x:Bind AliasAddress}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- To --> <!-- To -->
<TextBlock <TextBlock
@@ -2,6 +2,7 @@
x:Class="Wino.Views.ImapSetup.AdvancedImapSetupPage" x:Class="Wino.Views.ImapSetup.AdvancedImapSetupPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:accounts="using:Wino.Core.Domain.Models.Accounts"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:domain="using:Wino.Core.Domain" xmlns:domain="using:Wino.Core.Domain"
xmlns:helpers="using:Wino.Helpers" xmlns:helpers="using:Wino.Helpers"
@@ -101,9 +102,14 @@
<ComboBox <ComboBox
x:Name="IncomingConnectionSecurity" x:Name="IncomingConnectionSecurity"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberPath="DisplayName"
ItemsSource="{x:Bind AvailableConnectionSecurities}" ItemsSource="{x:Bind AvailableConnectionSecurities}"
SelectedIndex="0" /> SelectedIndex="0">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="accounts:ImapConnectionSecurityModel">
<TextBlock Text="{x:Bind DisplayName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel> </StackPanel>
<!-- Authentication --> <!-- Authentication -->
@@ -115,9 +121,14 @@
<ComboBox <ComboBox
x:Name="IncomingAuthenticationMethod" x:Name="IncomingAuthenticationMethod"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberPath="DisplayName"
ItemsSource="{x:Bind AvailableAuthenticationMethods}" ItemsSource="{x:Bind AvailableAuthenticationMethods}"
SelectedIndex="0" /> SelectedIndex="0">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="accounts:ImapAuthenticationMethodModel">
<TextBlock Text="{x:Bind DisplayName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel> </StackPanel>
</Grid> </Grid>
@@ -174,9 +185,15 @@
<ComboBox <ComboBox
x:Name="OutgoingConnectionSecurity" x:Name="OutgoingConnectionSecurity"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberPath="DisplayName"
ItemsSource="{x:Bind AvailableConnectionSecurities}" ItemsSource="{x:Bind AvailableConnectionSecurities}"
SelectedIndex="0" /> SelectedIndex="0">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="accounts:ImapConnectionSecurityModel">
<TextBlock Text="{x:Bind DisplayName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel> </StackPanel>
<!-- Authentication --> <!-- Authentication -->
@@ -185,9 +202,14 @@
<ComboBox <ComboBox
x:Name="OutgoingAuthenticationMethod" x:Name="OutgoingAuthenticationMethod"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberPath="DisplayName"
ItemsSource="{x:Bind AvailableAuthenticationMethods}" ItemsSource="{x:Bind AvailableAuthenticationMethods}"
SelectedIndex="0" /> SelectedIndex="0">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="accounts:ImapAuthenticationMethodModel">
<TextBlock Text="{x:Bind DisplayName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel> </StackPanel>
</Grid> </Grid>
</StackPanel> </StackPanel>
@@ -3,6 +3,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:abstract="using:Wino.Views.Abstract" xmlns:abstract="using:Wino.Views.Abstract"
xmlns:accounts="using:Wino.Core.Domain.Models.Accounts"
xmlns:controls="using:CommunityToolkit.WinUI.Controls" xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:converters="using:Wino.Core.WinUI.Converters" xmlns:converters="using:Wino.Core.WinUI.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -138,18 +139,28 @@
<ComboBox <ComboBox
Grid.Row="5" Grid.Row="5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberPath="DisplayName"
Header="{x:Bind domain:Translator.ImapAdvancedSetupDialog_ConnectionSecurity}" Header="{x:Bind domain:Translator.ImapAdvancedSetupDialog_ConnectionSecurity}"
ItemsSource="{x:Bind ViewModel.AvailableConnectionSecurities}" ItemsSource="{x:Bind ViewModel.AvailableConnectionSecurities}"
SelectedIndex="{x:Bind ViewModel.SelectedIncomingServerConnectionSecurityIndex, Mode=TwoWay}" /> SelectedIndex="{x:Bind ViewModel.SelectedIncomingServerConnectionSecurityIndex, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="accounts:ImapConnectionSecurityModel">
<TextBlock Text="{x:Bind DisplayName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox <ComboBox
Grid.Row="6" Grid.Row="6"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberPath="DisplayName"
Header="{x:Bind domain:Translator.ImapAdvancedSetupDialog_AuthenticationMethod}" Header="{x:Bind domain:Translator.ImapAdvancedSetupDialog_AuthenticationMethod}"
ItemsSource="{x:Bind ViewModel.AvailableAuthenticationMethods}" ItemsSource="{x:Bind ViewModel.AvailableAuthenticationMethods}"
SelectedIndex="{x:Bind ViewModel.SelectedIncomingServerAuthenticationMethodIndex, Mode=TwoWay}" /> SelectedIndex="{x:Bind ViewModel.SelectedIncomingServerAuthenticationMethodIndex, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="accounts:ImapAuthenticationMethodModel">
<TextBlock Text="{x:Bind DisplayName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Border <Border
Grid.RowSpan="6" Grid.RowSpan="6"
@@ -193,19 +204,29 @@
Grid.Row="5" Grid.Row="5"
Grid.Column="2" Grid.Column="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberPath="DisplayName"
Header="{x:Bind domain:Translator.ImapAdvancedSetupDialog_ConnectionSecurity}" Header="{x:Bind domain:Translator.ImapAdvancedSetupDialog_ConnectionSecurity}"
ItemsSource="{x:Bind ViewModel.AvailableConnectionSecurities}" ItemsSource="{x:Bind ViewModel.AvailableConnectionSecurities}"
SelectedIndex="{x:Bind ViewModel.SelectedOutgoingServerConnectionSecurityIndex, Mode=TwoWay}" /> SelectedIndex="{x:Bind ViewModel.SelectedOutgoingServerConnectionSecurityIndex, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="accounts:ImapConnectionSecurityModel">
<TextBlock Text="{x:Bind DisplayName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox <ComboBox
Grid.Row="6" Grid.Row="6"
Grid.Column="2" Grid.Column="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberPath="DisplayName"
Header="{x:Bind domain:Translator.ImapAdvancedSetupDialog_AuthenticationMethod}" Header="{x:Bind domain:Translator.ImapAdvancedSetupDialog_AuthenticationMethod}"
ItemsSource="{x:Bind ViewModel.AvailableAuthenticationMethods}" ItemsSource="{x:Bind ViewModel.AvailableAuthenticationMethods}"
SelectedIndex="{x:Bind ViewModel.SelectedOutgoingServerAuthenticationMethodIndex, Mode=TwoWay}" /> SelectedIndex="{x:Bind ViewModel.SelectedOutgoingServerAuthenticationMethodIndex, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="accounts:ImapAuthenticationMethodModel">
<TextBlock Text="{x:Bind DisplayName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<StackPanel <StackPanel
Grid.Row="7" Grid.Row="7"
File diff suppressed because one or more lines are too long
@@ -105,10 +105,15 @@
</controls:SettingsCard.HeaderIcon> </controls:SettingsCard.HeaderIcon>
<controls:SettingsCard.Content> <controls:SettingsCard.Content>
<ComboBox <ComboBox
DisplayMemberPath="Title"
IsEnabled="{x:Bind ViewModel.CanSelectElementTheme, Mode=OneWay}" IsEnabled="{x:Bind ViewModel.CanSelectElementTheme, Mode=OneWay}"
ItemsSource="{x:Bind ViewModel.ElementThemes}" ItemsSource="{x:Bind ViewModel.ElementThemes}"
SelectedItem="{x:Bind ViewModel.SelectedElementTheme, Mode=TwoWay}" /> SelectedItem="{x:Bind ViewModel.SelectedElementTheme, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="personalization1:ElementThemeContainer">
<TextBlock Text="{x:Bind Title}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</controls:SettingsCard.Content> </controls:SettingsCard.Content>
</controls:SettingsCard> </controls:SettingsCard>
+9 -9
View File
@@ -15,13 +15,20 @@
<!-- AOT / Trimming --> <!-- AOT / Trimming -->
<PublishAot Condition="'$(Configuration)' == 'Debug'">False</PublishAot> <PublishAot Condition="'$(Configuration)' == 'Debug'">False</PublishAot>
<PublishAot Condition="'$(Configuration)' != 'Debug'">True</PublishAot> <PublishAot Condition="'$(Configuration)' != 'Debug'">True</PublishAot>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors> <TreatWarningsAsErrors>False</TreatWarningsAsErrors>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
<!-- Trimming -->
<PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed>
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
<!-- Single instancing --> <!-- Single instancing -->
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants> <DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Remove="Assets\BadgeLogo.scale-100.png" /> <Content Remove="Assets\BadgeLogo.scale-100.png" />
<Content Remove="Assets\BadgeLogo.scale-125.png" /> <Content Remove="Assets\BadgeLogo.scale-125.png" />
@@ -269,13 +276,6 @@
<!-- Publish Properties --> <!-- Publish Properties -->
<PropertyGroup> <PropertyGroup>
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
<!-- Trimming -->
<PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed>
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
<GenerateTemporaryStoreCertificate>True</GenerateTemporaryStoreCertificate> <GenerateTemporaryStoreCertificate>True</GenerateTemporaryStoreCertificate>
<GenerateAppInstallerFile>False</GenerateAppInstallerFile> <GenerateAppInstallerFile>False</GenerateAppInstallerFile>
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled> <AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
+18 -53
View File
@@ -5,7 +5,6 @@ using System.Threading.Tasks;
using CommunityToolkit.Diagnostics; using CommunityToolkit.Diagnostics;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Serilog; using Serilog;
using SqlKata;
using Wino.Core.Domain.Entities.Mail; using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
@@ -55,14 +54,13 @@ public class AccountService : BaseDatabaseService, IAccountService
await Connection.ExecuteAsync("UPDATE MailAccount SET MergedInboxId = NULL WHERE MergedInboxId = ?", mergedInboxId); await Connection.ExecuteAsync("UPDATE MailAccount SET MergedInboxId = NULL WHERE MergedInboxId = ?", mergedInboxId);
// Then, add new accounts to merged inbox. // Then, add new accounts to merged inbox.
var query = new Query("MailAccount") var accountIdList = linkedAccountIds.ToList();
.WhereIn("Id", linkedAccountIds) var placeholders = string.Join(",", accountIdList.Select(_ => "?"));
.AsUpdate(new var sql = $"UPDATE MailAccount SET MergedInboxId = ? WHERE Id IN ({placeholders})";
{ var parameters = new List<object> { mergedInboxId };
MergedInboxId = mergedInboxId parameters.AddRange(accountIdList.Cast<object>());
});
await Connection.ExecuteAsync(query.GetRawQuery()); await Connection.ExecuteAsync(sql, parameters.ToArray());
WeakReferenceMessenger.Default.Send(new AccountsMenuRefreshRequested()); WeakReferenceMessenger.Default.Send(new AccountsMenuRefreshRequested());
} }
@@ -84,14 +82,7 @@ public class AccountService : BaseDatabaseService, IAccountService
return; return;
} }
var query = new Query("MailAccount") await Connection.ExecuteAsync("UPDATE MailAccount SET MergedInboxId = NULL WHERE MergedInboxId = ?", mergedInboxId).ConfigureAwait(false);
.Where("MergedInboxId", mergedInboxId)
.AsUpdate(new
{
MergedInboxId = (Guid?)null
});
await Connection.ExecuteAsync(query.GetRawQuery()).ConfigureAwait(false);
await Connection.DeleteAsync<MergedInbox>(mergedInbox).ConfigureAwait(false); await Connection.DeleteAsync<MergedInbox>(mergedInbox).ConfigureAwait(false);
// Change the startup entity id if it was the merged inbox. // Change the startup entity id if it was the merged inbox.
@@ -191,14 +182,7 @@ public class AccountService : BaseDatabaseService, IAccountService
public async Task RenameMergedAccountAsync(Guid mergedInboxId, string newName) public async Task RenameMergedAccountAsync(Guid mergedInboxId, string newName)
{ {
var query = new Query("MergedInbox") await Connection.ExecuteAsync("UPDATE MergedInbox SET Name = ? WHERE Id = ?", newName, mergedInboxId);
.Where("Id", mergedInboxId)
.AsUpdate(new
{
Name = newName
});
await Connection.ExecuteAsync(query.GetRawQuery());
ReportUIChange(new MergedInboxRenamed(mergedInboxId, newName)); ReportUIChange(new MergedInboxRenamed(mergedInboxId, newName));
} }
@@ -261,11 +245,9 @@ public class AccountService : BaseDatabaseService, IAccountService
public async Task<List<MailAccountAlias>> GetAccountAliasesAsync(Guid accountId) public async Task<List<MailAccountAlias>> GetAccountAliasesAsync(Guid accountId)
{ {
var query = new Query(nameof(MailAccountAlias)) return await Connection.QueryAsync<MailAccountAlias>(
.Where(nameof(MailAccountAlias.AccountId), accountId) "SELECT * FROM MailAccountAlias WHERE AccountId = ? ORDER BY IsRootAlias DESC",
.OrderByDesc(nameof(MailAccountAlias.IsRootAlias)); accountId).ConfigureAwait(false);
return await Connection.QueryAsync<MailAccountAlias>(query.GetRawQuery()).ConfigureAwait(false);
} }
private Task<MergedInbox> GetMergedInboxInformationAsync(Guid mergedInboxId) private Task<MergedInbox> GetMergedInboxInformationAsync(Guid mergedInboxId)
@@ -273,17 +255,9 @@ public class AccountService : BaseDatabaseService, IAccountService
public async Task DeleteAccountMailCacheAsync(Guid accountId, AccountCacheResetReason accountCacheResetReason) public async Task DeleteAccountMailCacheAsync(Guid accountId, AccountCacheResetReason accountCacheResetReason)
{ {
var deleteQuery = new Query("MailCopy") await Connection.ExecuteAsync(
.WhereIn("Id", q => q "DELETE FROM MailCopy WHERE Id IN (SELECT Id FROM MailCopy WHERE FolderId IN (SELECT Id FROM MailItemFolder WHERE MailAccountId = ?))",
.From("MailCopy") accountId);
.Select("Id")
.WhereIn("FolderId", q2 => q2
.From("MailItemFolder")
.Select("Id")
.Where("MailAccountId", accountId)
)).AsDelete();
await Connection.ExecuteAsync(deleteQuery.GetRawQuery());
WeakReferenceMessenger.Default.Send(new AccountCacheResetMessage(accountId, accountCacheResetReason)); WeakReferenceMessenger.Default.Send(new AccountCacheResetMessage(accountId, accountCacheResetReason));
} }
@@ -306,14 +280,9 @@ public class AccountService : BaseDatabaseService, IAccountService
// There will be only one account in the merged inbox. Remove the link for the other account as well. // There will be only one account in the merged inbox. Remove the link for the other account as well.
if (mergedInboxAccountCount == 2) if (mergedInboxAccountCount == 2)
{ {
var query = new Query("MailAccount") await Connection.ExecuteAsync(
.Where("MergedInboxId", account.MergedInboxId.Value) "UPDATE MailAccount SET MergedInboxId = NULL WHERE MergedInboxId = ?",
.AsUpdate(new account.MergedInboxId.Value).ConfigureAwait(false);
{
MergedInboxId = (Guid?)null
});
await Connection.ExecuteAsync(query.GetRawQuery()).ConfigureAwait(false);
} }
} }
@@ -494,11 +463,7 @@ public class AccountService : BaseDatabaseService, IAccountService
{ {
// Create query to delete alias. // Create query to delete alias.
var query = new Query("MailAccountAlias") await Connection.ExecuteAsync("DELETE FROM MailAccountAlias WHERE Id = ?", aliasId).ConfigureAwait(false);
.Where("Id", aliasId)
.AsDelete();
await Connection.ExecuteAsync(query.GetRawQuery()).ConfigureAwait(false);
} }
public async Task CreateAccountAsync(MailAccount account, CustomServerInformation customServerInformation) public async Task CreateAccountAsync(MailAccount account, CustomServerInformation customServerInformation)
+15 -35
View File
@@ -7,7 +7,6 @@ using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Ical.Net.CalendarComponents; using Ical.Net.CalendarComponents;
using Ical.Net.DataTypes; using Ical.Net.DataTypes;
using SqlKata;
using Wino.Core.Domain; using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Calendar; using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
@@ -43,14 +42,9 @@ public class CalendarService : BaseDatabaseService, ICalendarService
public async Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar) public async Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar)
{ {
var deleteCalendarItemsQuery = new Query() await Connection.ExecuteAsync(
.From(nameof(CalendarItem)) "DELETE FROM CalendarItem WHERE CalendarId = ? AND AccountId = ?",
.Where(nameof(CalendarItem.CalendarId), accountCalendar.Id) accountCalendar.Id, accountCalendar.AccountId);
.Where(nameof(AccountCalendar.AccountId), accountCalendar.AccountId);
var rawQuery = deleteCalendarItemsQuery.GetRawQuery();
await Connection.ExecuteAsync(rawQuery);
await Connection.DeleteAsync<AccountCalendar>(accountCalendar); await Connection.DeleteAsync<AccountCalendar>(accountCalendar);
WeakReferenceMessenger.Default.Send(new CalendarListDeleted(accountCalendar)); WeakReferenceMessenger.Default.Send(new CalendarListDeleted(accountCalendar));
@@ -182,24 +176,16 @@ public class CalendarService : BaseDatabaseService, ICalendarService
public Task<CalendarItem> GetCalendarItemAsync(Guid id) public Task<CalendarItem> GetCalendarItemAsync(Guid id)
{ {
var query = new Query() return Connection.FindWithQueryAsync<CalendarItem>(
.From(nameof(CalendarItem)) "SELECT * FROM CalendarItem WHERE Id = ?",
.Where(nameof(CalendarItem.Id), id); id);
var rawQuery = query.GetRawQuery();
return Connection.FindWithQueryAsync<CalendarItem>(rawQuery);
} }
public async Task<CalendarItem> GetCalendarItemAsync(Guid accountCalendarId, string remoteEventId) public async Task<CalendarItem> GetCalendarItemAsync(Guid accountCalendarId, string remoteEventId)
{ {
var query = new Query() var calendarItem = await Connection.FindWithQueryAsync<CalendarItem>(
.From(nameof(CalendarItem)) "SELECT * FROM CalendarItem WHERE CalendarId = ? AND RemoteEventId = ?",
.Where(nameof(CalendarItem.CalendarId), accountCalendarId) accountCalendarId, remoteEventId);
.Where(nameof(CalendarItem.RemoteEventId), remoteEventId);
var rawQuery = query.GetRawQuery();
var calendarItem = await Connection.FindWithQueryAsync<CalendarItem>(rawQuery);
// Load assigned calendar. // Load assigned calendar.
if (calendarItem != null) if (calendarItem != null)
@@ -212,12 +198,9 @@ public class CalendarService : BaseDatabaseService, ICalendarService
public Task UpdateCalendarDeltaSynchronizationToken(Guid calendarId, string deltaToken) public Task UpdateCalendarDeltaSynchronizationToken(Guid calendarId, string deltaToken)
{ {
var query = new Query() return Connection.ExecuteAsync(
.From(nameof(AccountCalendar)) "UPDATE AccountCalendar SET SynchronizationDeltaToken = ? WHERE Id = ?",
.Where(nameof(AccountCalendar.Id), calendarId) deltaToken, calendarId);
.AsUpdate(new { SynchronizationDeltaToken = deltaToken });
return Connection.ExecuteAsync(query.GetRawQuery());
} }
public Task<List<CalendarEventAttendee>> GetAttendeesAsync(Guid calendarEventTrackingId) public Task<List<CalendarEventAttendee>> GetAttendeesAsync(Guid calendarEventTrackingId)
@@ -228,12 +211,9 @@ public class CalendarService : BaseDatabaseService, ICalendarService
await Connection.RunInTransactionAsync((connection) => await Connection.RunInTransactionAsync((connection) =>
{ {
// Clear all attendees. // Clear all attendees.
var query = new Query() connection.Execute(
.From(nameof(CalendarEventAttendee)) "DELETE FROM CalendarEventAttendee WHERE CalendarItemId = ?",
.Where(nameof(CalendarEventAttendee.CalendarItemId), calendarItemId) calendarItemId);
.AsDelete();
connection.Execute(query.GetRawQuery());
// Insert new attendees. // Insert new attendees.
connection.InsertAll(allAttendees, typeof(CalendarEventAttendee)); connection.InsertAll(allAttendees, typeof(CalendarEventAttendee));
+6 -15
View File
@@ -4,7 +4,6 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using MimeKit; using MimeKit;
using Serilog; using Serilog;
using SqlKata;
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Services.Extensions; using Wino.Services.Extensions;
@@ -29,13 +28,9 @@ public class ContactService : BaseDatabaseService, IContactService
if (queryText == null || queryText.Length < 2) if (queryText == null || queryText.Length < 2)
return Task.FromResult<List<AccountContact>>(null); return Task.FromResult<List<AccountContact>>(null);
var query = new Query(nameof(AccountContact)); const string query = "SELECT * FROM AccountContact WHERE Address LIKE ? OR Name LIKE ?";
query.WhereContains("Address", queryText); var pattern = $"%{queryText}%";
query.OrWhereContains("Name", queryText); return Connection.QueryAsync<AccountContact>(query, pattern, pattern);
var rawLikeQuery = query.GetRawQuery();
return Connection.QueryAsync<AccountContact>(rawLikeQuery);
} }
public Task<AccountContact> GetAddressInformationByAddressAsync(string address) public Task<AccountContact> GetAddressInformationByAddressAsync(string address)
@@ -81,13 +76,9 @@ public class ContactService : BaseDatabaseService, IContactService
if (string.IsNullOrWhiteSpace(searchQuery)) if (string.IsNullOrWhiteSpace(searchQuery))
return GetAllContactsAsync(); return GetAllContactsAsync();
var query = new Query(nameof(AccountContact)); const string query = "SELECT * FROM AccountContact WHERE Address LIKE ? OR Name LIKE ?";
query.WhereContains("Address", searchQuery.Trim()); var pattern = $"%{searchQuery.Trim()}%";
query.OrWhereContains("Name", searchQuery.Trim()); return Connection.QueryAsync<AccountContact>(query, pattern, pattern);
var rawLikeQuery = query.GetRawQuery();
return Connection.QueryAsync<AccountContact>(rawLikeQuery);
} }
public async Task<AccountContact> UpdateContactAsync(AccountContact contact) public async Task<AccountContact> UpdateContactAsync(AccountContact contact)
@@ -1,14 +0,0 @@
using SqlKata;
using SqlKata.Compilers;
namespace Wino.Services.Extensions;
public static class SqlKataExtensions
{
private static SqliteCompiler Compiler = new SqliteCompiler();
public static string GetRawQuery(this Query query)
{
return Compiler.Compile(query).ToString();
}
}
+43 -49
View File
@@ -4,7 +4,6 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Serilog; using Serilog;
using SqlKata;
using Wino.Core.Domain; using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail; using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
@@ -53,23 +52,38 @@ public class FolderService : BaseDatabaseService, IFolderService
if (account == null) return default; if (account == null) return default;
var query = new Query("MailCopy") // Convert to raw SQL
.Where("FolderId", folderId) string sqlQuery;
.SelectRaw("count (DISTINCT Id)"); object[] parameters;
// If focused inbox is enabled, we need to check if this is the inbox folder.
if (account.Preferences.IsFocusedInboxEnabled.GetValueOrDefault() && folder.SpecialFolderType == SpecialFolderType.Inbox) if (account.Preferences.IsFocusedInboxEnabled.GetValueOrDefault() && folder.SpecialFolderType == SpecialFolderType.Inbox)
{ {
query.Where("IsFocused", 1);
}
// Draft and Junk folders are not counted as unread. They must return the item count instead.
if (folder.SpecialFolderType != SpecialFolderType.Draft && folder.SpecialFolderType != SpecialFolderType.Junk) if (folder.SpecialFolderType != SpecialFolderType.Draft && folder.SpecialFolderType != SpecialFolderType.Junk)
{ {
query.Where("IsRead", 0); sqlQuery = "SELECT COUNT(*) FROM MailCopy WHERE FolderId = ? AND IsFocused = ? AND IsRead = ?";
parameters = new object[] { folderId, 1, 0 };
}
else
{
sqlQuery = "SELECT COUNT(*) FROM MailCopy WHERE FolderId = ? AND IsFocused = ?";
parameters = new object[] { folderId, 1 };
}
}
else
{
if (folder.SpecialFolderType != SpecialFolderType.Draft && folder.SpecialFolderType != SpecialFolderType.Junk)
{
sqlQuery = "SELECT COUNT(*) FROM MailCopy WHERE FolderId = ? AND IsRead = ?";
parameters = new object[] { folderId, 0 };
}
else
{
sqlQuery = "SELECT COUNT(*) FROM MailCopy WHERE FolderId = ?";
parameters = new object[] { folderId };
}
} }
return await Connection.ExecuteScalarAsync<int>(query.GetRawQuery()); return await Connection.ExecuteScalarAsync<int>(sqlQuery, parameters);
} }
public async Task<AccountFolderTree> GetFolderStructureForAccountAsync(Guid accountId, bool includeHiddenFolders) public async Task<AccountFolderTree> GetFolderStructureForAccountAsync(Guid accountId, bool includeHiddenFolders)
@@ -186,13 +200,10 @@ public class FolderService : BaseDatabaseService, IFolderService
// Localize category folder name. // Localize category folder name.
if (parentFolder.SpecialFolderType == SpecialFolderType.Category) parentFolder.FolderName = Translator.CategoriesFolderNameOverride; if (parentFolder.SpecialFolderType == SpecialFolderType.Category) parentFolder.FolderName = Translator.CategoriesFolderNameOverride;
var query = new Query(nameof(MailItemFolder)) const string query = "SELECT * FROM MailItemFolder WHERE ParentRemoteFolderId = ? AND MailAccountId = ?";
.Where(nameof(MailItemFolder.ParentRemoteFolderId), parentFolder.RemoteFolderId)
.Where(nameof(MailItemFolder.MailAccountId), parentFolder.MailAccountId);
var preparedFolder = new FolderMenuItem(parentFolder, account, parentMenuItem); var preparedFolder = new FolderMenuItem(parentFolder, account, parentMenuItem);
var childFolders = await Connection.QueryAsync<MailItemFolder>(query.GetRawQuery()).ConfigureAwait(false); var childFolders = await Connection.QueryAsync<MailItemFolder>(query, parentFolder.RemoteFolderId, parentFolder.MailAccountId).ConfigureAwait(false);
if (childFolders.Any()) if (childFolders.Any())
{ {
@@ -348,21 +359,14 @@ public class FolderService : BaseDatabaseService, IFolderService
public Task<List<MailItemFolder>> GetFoldersAsync(Guid accountId) public Task<List<MailItemFolder>> GetFoldersAsync(Guid accountId)
{ {
var query = new Query(nameof(MailItemFolder)) const string query = "SELECT * FROM MailItemFolder WHERE MailAccountId = ? ORDER BY SpecialFolderType";
.Where(nameof(MailItemFolder.MailAccountId), accountId) return Connection.QueryAsync<MailItemFolder>(query, accountId);
.OrderBy(nameof(MailItemFolder.SpecialFolderType));
return Connection.QueryAsync<MailItemFolder>(query.GetRawQuery());
} }
public Task<List<MailItemFolder>> GetVisibleFoldersAsync(Guid accountId) public Task<List<MailItemFolder>> GetVisibleFoldersAsync(Guid accountId)
{ {
var query = new Query(nameof(MailItemFolder)) const string query = "SELECT * FROM MailItemFolder WHERE MailAccountId = ? AND IsHidden = ? ORDER BY SpecialFolderType";
.Where(nameof(MailItemFolder.MailAccountId), accountId) return Connection.QueryAsync<MailItemFolder>(query, accountId, 0);
.Where(nameof(MailItemFolder.IsHidden), false)
.OrderBy(nameof(MailItemFolder.SpecialFolderType));
return Connection.QueryAsync<MailItemFolder>(query.GetRawQuery());
} }
public async Task<IList<uint>> GetKnownUidsForFolderAsync(Guid folderId) public async Task<IList<uint>> GetKnownUidsForFolderAsync(Guid folderId)
@@ -516,25 +520,18 @@ public class FolderService : BaseDatabaseService, IFolderService
private Task<List<string>> GetMailCopyIdsByFolderIdAsync(Guid folderId) private Task<List<string>> GetMailCopyIdsByFolderIdAsync(Guid folderId)
{ {
var query = new Query("MailCopy") const string query = "SELECT Id FROM MailCopy WHERE FolderId = ?";
.Where("FolderId", folderId) return Connection.QueryScalarsAsync<string>(query, folderId);
.Select("Id");
return Connection.QueryScalarsAsync<string>(query.GetRawQuery());
} }
public async Task<List<MailFolderPairMetadata>> GetMailFolderPairMetadatasAsync(IEnumerable<string> mailCopyIds) public async Task<List<MailFolderPairMetadata>> GetMailFolderPairMetadatasAsync(IEnumerable<string> mailCopyIds)
{ {
// Get all assignments for all items. var mailCopyIdList = mailCopyIds.ToList();
var query = new Query(nameof(MailCopy)) var placeholders = string.Join(",", mailCopyIdList.Select(_ => "?"));
.Join(nameof(MailItemFolder), $"{nameof(MailCopy)}.FolderId", $"{nameof(MailItemFolder)}.Id") var query = $"SELECT DISTINCT MailCopy.Id as MailCopyId, MailItemFolder.Id as FolderId, MailItemFolder.RemoteFolderId as RemoteFolderId FROM MailCopy INNER JOIN MailItemFolder ON MailCopy.FolderId = MailItemFolder.Id WHERE MailCopy.Id IN ({placeholders})";
.WhereIn($"{nameof(MailCopy)}.Id", mailCopyIds) var parameters = mailCopyIdList.Cast<object>().ToArray();
.SelectRaw($"{nameof(MailCopy)}.Id as MailCopyId, {nameof(MailItemFolder)}.Id as FolderId, {nameof(MailItemFolder)}.RemoteFolderId as RemoteFolderId")
.Distinct();
var rowQuery = query.GetRawQuery(); return await Connection.QueryAsync<MailFolderPairMetadata>(query, parameters);
return await Connection.QueryAsync<MailFolderPairMetadata>(rowQuery);
} }
public Task<List<MailFolderPairMetadata>> GetMailFolderPairMetadatasAsync(string mailCopyId) public Task<List<MailFolderPairMetadata>> GetMailFolderPairMetadatasAsync(string mailCopyId)
@@ -687,14 +684,11 @@ public class FolderService : BaseDatabaseService, IFolderService
public Task<List<UnreadItemCountResult>> GetUnreadItemCountResultsAsync(IEnumerable<Guid> accountIds) public Task<List<UnreadItemCountResult>> GetUnreadItemCountResultsAsync(IEnumerable<Guid> accountIds)
{ {
var query = new Query(nameof(MailCopy)) var accountIdList = accountIds.ToList();
.Join(nameof(MailItemFolder), $"{nameof(MailCopy)}.FolderId", $"{nameof(MailItemFolder)}.Id") var placeholders = string.Join(",", accountIdList.Select(_ => "?"));
.WhereIn($"{nameof(MailItemFolder)}.MailAccountId", accountIds) var query = $"SELECT MailItemFolder.Id as FolderId, MailItemFolder.SpecialFolderType as SpecialFolderType, count(DISTINCT MailCopy.Id) as UnreadItemCount, MailItemFolder.MailAccountId as AccountId FROM MailCopy INNER JOIN MailItemFolder ON MailCopy.FolderId = MailItemFolder.Id WHERE MailItemFolder.MailAccountId IN ({placeholders}) AND MailCopy.IsRead = ? AND MailItemFolder.ShowUnreadCount = ? GROUP BY MailItemFolder.Id";
.Where($"{nameof(MailCopy)}.IsRead", 0) var parameters = accountIdList.Cast<object>().Concat(new object[] { 0, 1 }).ToArray();
.Where($"{nameof(MailItemFolder)}.ShowUnreadCount", 1)
.SelectRaw($"{nameof(MailItemFolder)}.Id as FolderId, {nameof(MailItemFolder)}.SpecialFolderType as SpecialFolderType, count (DISTINCT {nameof(MailCopy)}.Id) as UnreadItemCount, {nameof(MailItemFolder)}.MailAccountId as AccountId")
.GroupBy($"{nameof(MailItemFolder)}.Id");
return Connection.QueryAsync<UnreadItemCountResult>(query.GetRawQuery()); return Connection.QueryAsync<UnreadItemCountResult>(query, parameters);
} }
} }
+16 -24
View File
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using SqlKata;
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
@@ -24,10 +23,8 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
/// </summary> /// </summary>
public async Task<IEnumerable<KeyboardShortcut>> GetKeyboardShortcutsAsync() public async Task<IEnumerable<KeyboardShortcut>> GetKeyboardShortcutsAsync()
{ {
var query = new Query(nameof(KeyboardShortcut)) return await Connection.QueryAsync<KeyboardShortcut>(
.OrderBy(nameof(KeyboardShortcut.MailOperation)); "SELECT * FROM KeyboardShortcut ORDER BY MailOperation");
return await Connection.QueryAsync<KeyboardShortcut>(query.GetRawQuery());
} }
/// <summary> /// <summary>
@@ -35,11 +32,9 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
/// </summary> /// </summary>
public async Task<IEnumerable<KeyboardShortcut>> GetEnabledKeyboardShortcutsAsync() public async Task<IEnumerable<KeyboardShortcut>> GetEnabledKeyboardShortcutsAsync()
{ {
var query = new Query(nameof(KeyboardShortcut)) return await Connection.QueryAsync<KeyboardShortcut>(
.Where(nameof(KeyboardShortcut.IsEnabled), true) "SELECT * FROM KeyboardShortcut WHERE IsEnabled = ? ORDER BY MailOperation",
.OrderBy(nameof(KeyboardShortcut.MailOperation)); true);
return await Connection.QueryAsync<KeyboardShortcut>(query.GetRawQuery());
} }
/// <summary> /// <summary>
@@ -66,9 +61,6 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
/// </summary> /// </summary>
public async Task DeleteKeyboardShortcutAsync(Guid shortcutId) public async Task DeleteKeyboardShortcutAsync(Guid shortcutId)
{ {
var query = new Query(nameof(KeyboardShortcut))
.Where(nameof(KeyboardShortcut.Id), shortcutId);
await Connection.ExecuteAsync($"DELETE FROM {nameof(KeyboardShortcut)} WHERE {nameof(KeyboardShortcut.Id)} = ?", shortcutId); await Connection.ExecuteAsync($"DELETE FROM {nameof(KeyboardShortcut)} WHERE {nameof(KeyboardShortcut.Id)} = ?", shortcutId);
} }
@@ -77,12 +69,8 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
/// </summary> /// </summary>
public async Task<MailOperation?> GetMailOperationForKeyAsync(string key, ModifierKeys modifierKeys) public async Task<MailOperation?> GetMailOperationForKeyAsync(string key, ModifierKeys modifierKeys)
{ {
var query = new Query(nameof(KeyboardShortcut)) const string query = "SELECT * FROM KeyboardShortcut WHERE Key = ? AND ModifierKeys = ? AND IsEnabled = ? LIMIT 1";
.Where(nameof(KeyboardShortcut.Key), key) var shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query, key, (int)modifierKeys, 1);
.Where(nameof(KeyboardShortcut.ModifierKeys), (int)modifierKeys)
.Where(nameof(KeyboardShortcut.IsEnabled), true);
var shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query.GetRawQuery());
return shortcut?.MailOperation; return shortcut?.MailOperation;
} }
@@ -91,16 +79,20 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
/// </summary> /// </summary>
public async Task<bool> IsKeyCombinationInUseAsync(string key, ModifierKeys modifierKeys, Guid? excludeShortcutId = null) public async Task<bool> IsKeyCombinationInUseAsync(string key, ModifierKeys modifierKeys, Guid? excludeShortcutId = null)
{ {
var query = new Query(nameof(KeyboardShortcut)) string query;
.Where(nameof(KeyboardShortcut.Key), key) KeyboardShortcut shortcut;
.Where(nameof(KeyboardShortcut.ModifierKeys), (int)modifierKeys);
if (excludeShortcutId.HasValue) if (excludeShortcutId.HasValue)
{ {
query = query.WhereNot(nameof(KeyboardShortcut.Id), excludeShortcutId.Value); query = "SELECT * FROM KeyboardShortcut WHERE Key = ? AND ModifierKeys = ? AND Id != ? LIMIT 1";
shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query, key, (int)modifierKeys, excludeShortcutId.Value);
}
else
{
query = "SELECT * FROM KeyboardShortcut WHERE Key = ? AND ModifierKeys = ? LIMIT 1";
shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query, key, (int)modifierKeys);
} }
var shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query.GetRawQuery());
return shortcut != null; return shortcut != null;
} }
+92 -110
View File
@@ -2,12 +2,12 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using MimeKit; using MimeKit;
using Serilog; using Serilog;
using SqlKata;
using Wino.Core.Domain; using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail; using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
@@ -144,74 +144,80 @@ public class MailService : BaseDatabaseService, IMailService
return unreadMails; return unreadMails;
} }
private static string BuildMailFetchQuery(MailListInitializationOptions options) private static (string Query, object[] Parameters) BuildMailFetchQuery(MailListInitializationOptions options)
{ {
// If the search query is there, we should ignore some properties and trim it. var sql = new StringBuilder();
//if (!string.IsNullOrEmpty(options.SearchQuery)) sql.Append("SELECT MailCopy.* FROM MailCopy INNER JOIN MailItemFolder ON MailCopy.FolderId = MailItemFolder.Id");
//{
// options.IsFocusedOnly = null;
// filterType = FilterOptionType.All;
// searchQuery = searchQuery.Trim(); var whereClauses = new List<string>();
//} var parameters = new List<object>();
// SQLite PCL doesn't support joins. // Folder filter
// We make the query using SqlKata and execute it directly on SQLite-PCL. var folderPlaceholders = string.Join(",", options.Folders.Select(_ => "?"));
whereClauses.Add($"MailCopy.FolderId IN ({folderPlaceholders})");
parameters.AddRange(options.Folders.Select(f => (object)f.Id));
var query = new Query("MailCopy") // Filter type
.Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id")
.WhereIn("MailCopy.FolderId", options.Folders.Select(a => a.Id))
.Take(ItemLoadCount)
.SelectRaw("MailCopy.*");
if (options.SortingOptionType == SortingOptionType.ReceiveDate)
query.OrderByDesc("CreationDate");
else if (options.SortingOptionType == SortingOptionType.Sender)
query.OrderBy("FromName");
// Conditional where.
switch (options.FilterType) switch (options.FilterType)
{ {
case FilterOptionType.Unread: case FilterOptionType.Unread:
query.Where("MailCopy.IsRead", false); whereClauses.Add("MailCopy.IsRead = 0");
break; break;
case FilterOptionType.Flagged: case FilterOptionType.Flagged:
query.Where("MailCopy.IsFlagged", true); whereClauses.Add("MailCopy.IsFlagged = 1");
break; break;
case FilterOptionType.Files: case FilterOptionType.Files:
query.Where("MailCopy.HasAttachments", true); whereClauses.Add("MailCopy.HasAttachments = 1");
break; break;
} }
// Focused filter
if (options.IsFocusedOnly != null) if (options.IsFocusedOnly != null)
query.Where("MailCopy.IsFocused", options.IsFocusedOnly.Value); {
whereClauses.Add($"MailCopy.IsFocused = {(options.IsFocusedOnly.Value ? "1" : "0")}");
}
// Search query
if (!string.IsNullOrEmpty(options.SearchQuery)) if (!string.IsNullOrEmpty(options.SearchQuery))
query.Where(a => {
a.OrWhereContains("MailCopy.PreviewText", options.SearchQuery) whereClauses.Add("(MailCopy.PreviewText LIKE ? OR MailCopy.Subject LIKE ? OR MailCopy.FromName LIKE ? OR MailCopy.FromAddress LIKE ?)");
.OrWhereContains("MailCopy.Subject", options.SearchQuery) var searchPattern = $"%{options.SearchQuery}%";
.OrWhereContains("MailCopy.FromName", options.SearchQuery) parameters.Add(searchPattern);
.OrWhereContains("MailCopy.FromAddress", options.SearchQuery)); parameters.Add(searchPattern);
parameters.Add(searchPattern);
parameters.Add(searchPattern);
}
// Support pagination by excluding already fetched items // Exclude existing items
if (options.ExistingUniqueIds?.Any() ?? false) if (options.ExistingUniqueIds?.Any() ?? false)
{ {
query.WhereNotIn("MailCopy.UniqueId", options.ExistingUniqueIds); var excludePlaceholders = string.Join(",", options.ExistingUniqueIds.Select(_ => "?"));
whereClauses.Add($"MailCopy.UniqueId NOT IN ({excludePlaceholders})");
parameters.AddRange(options.ExistingUniqueIds.Select(id => (object)id));
} }
// Support skip for pagination if (whereClauses.Any())
{
sql.Append(" WHERE ");
sql.Append(string.Join(" AND ", whereClauses));
}
// Sorting
if (options.SortingOptionType == SortingOptionType.ReceiveDate)
sql.Append(" ORDER BY CreationDate DESC");
else if (options.SortingOptionType == SortingOptionType.Sender)
sql.Append(" ORDER BY FromName ASC");
// Pagination
var limit = options.Take > 0 ? options.Take : ItemLoadCount;
sql.Append($" LIMIT {limit}");
if (options.Skip > 0) if (options.Skip > 0)
{ {
query.Skip(options.Skip); sql.Append($" OFFSET {options.Skip}");
} }
// Support custom take count for pagination return (sql.ToString(), parameters.ToArray());
if (options.Take > 0)
{
query.Take(options.Take);
}
return query.GetRawQuery();
} }
public async Task<List<MailCopy>> FetchMailsAsync(MailListInitializationOptions options, CancellationToken cancellationToken = default) public async Task<List<MailCopy>> FetchMailsAsync(MailListInitializationOptions options, CancellationToken cancellationToken = default)
@@ -226,8 +232,8 @@ public class MailService : BaseDatabaseService, IMailService
else else
{ {
// If not just do the query. // If not just do the query.
var query = BuildMailFetchQuery(options); var (query, parameters) = BuildMailFetchQuery(options);
mails = await Connection.QueryAsync<MailCopy>(query); mails = await Connection.QueryAsync<MailCopy>(query, parameters);
} }
ConcurrentDictionary<Guid, MailItemFolder> folderCache = new(); ConcurrentDictionary<Guid, MailItemFolder> folderCache = new();
@@ -295,13 +301,12 @@ public class MailService : BaseDatabaseService, IMailService
if (string.IsNullOrEmpty(threadId)) if (string.IsNullOrEmpty(threadId))
return []; return [];
var query = new Query("MailCopy") var placeholders = string.Join(",", excludeMailIds.Select(_ => "?"));
.Where("ThreadId", threadId) var sql = $"SELECT MailCopy.* FROM MailCopy WHERE ThreadId = ? AND Id NOT IN ({placeholders})";
.WhereNotIn("Id", excludeMailIds) var parameters = new List<object> { threadId };
.SelectRaw("MailCopy.*") parameters.AddRange(excludeMailIds.Cast<object>());
.GetRawQuery();
return await Connection.QueryAsync<MailCopy>(query); return await Connection.QueryAsync<MailCopy>(sql, parameters.ToArray());
} }
private async Task<List<MailCopy>> GetMailsByThreadIdsAsync(List<string> threadIds, HashSet<string> excludeMailIds) private async Task<List<MailCopy>> GetMailsByThreadIdsAsync(List<string> threadIds, HashSet<string> excludeMailIds)
@@ -309,13 +314,14 @@ public class MailService : BaseDatabaseService, IMailService
if (threadIds?.Count == 0) if (threadIds?.Count == 0)
return []; return [];
var query = new Query("MailCopy") var threadPlaceholders = string.Join(",", threadIds.Select(_ => "?"));
.WhereIn("ThreadId", threadIds) var excludePlaceholders = string.Join(",", excludeMailIds.Select(_ => "?"));
.WhereNotIn("Id", excludeMailIds) var sql = $"SELECT MailCopy.* FROM MailCopy WHERE ThreadId IN ({threadPlaceholders}) AND Id NOT IN ({excludePlaceholders})";
.SelectRaw("MailCopy.*") var parameters = new List<object>();
.GetRawQuery(); parameters.AddRange(threadIds.Cast<object>());
parameters.AddRange(excludeMailIds.Cast<object>());
return await Connection.QueryAsync<MailCopy>(query).ConfigureAwait(false); return await Connection.QueryAsync<MailCopy>(sql, parameters.ToArray()).ConfigureAwait(false);
} }
/// <summary> /// <summary>
@@ -451,12 +457,9 @@ public class MailService : BaseDatabaseService, IMailService
/// <param name="mailCopyId">Mail copy id.</param> /// <param name="mailCopyId">Mail copy id.</param>
public async Task<MailCopy> GetSingleMailItemAsync(string mailCopyId) public async Task<MailCopy> GetSingleMailItemAsync(string mailCopyId)
{ {
var query = new Query("MailCopy") var mailCopy = await Connection.FindWithQueryAsync<MailCopy>(
.Where("MailCopy.Id", mailCopyId) "SELECT MailCopy.* FROM MailCopy WHERE MailCopy.Id = ?",
.SelectRaw("MailCopy.*") mailCopyId);
.GetRawQuery();
var mailCopy = await Connection.FindWithQueryAsync<MailCopy>(query);
if (mailCopy == null) return null; if (mailCopy == null) return null;
await LoadAssignedPropertiesAsync(mailCopy).ConfigureAwait(false); await LoadAssignedPropertiesAsync(mailCopy).ConfigureAwait(false);
@@ -466,14 +469,9 @@ public class MailService : BaseDatabaseService, IMailService
public async Task<MailCopy> GetSingleMailItemAsync(string mailCopyId, string remoteFolderId) public async Task<MailCopy> GetSingleMailItemAsync(string mailCopyId, string remoteFolderId)
{ {
var query = new Query("MailCopy") var mailItem = await Connection.FindWithQueryAsync<MailCopy>(
.Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id") "SELECT MailCopy.* FROM MailCopy INNER JOIN MailItemFolder ON MailCopy.FolderId = MailItemFolder.Id WHERE MailCopy.Id = ? AND MailItemFolder.RemoteFolderId = ?",
.Where("MailCopy.Id", mailCopyId) mailCopyId, remoteFolderId);
.Where("MailItemFolder.RemoteFolderId", remoteFolderId)
.SelectRaw("MailCopy.*")
.GetRawQuery();
var mailItem = await Connection.FindWithQueryAsync<MailCopy>(query);
if (mailItem == null) return null; if (mailItem == null) return null;
@@ -1030,14 +1028,9 @@ public class MailService : BaseDatabaseService, IMailService
public async Task<bool> MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId) public async Task<bool> MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId)
{ {
var query = new Query("MailCopy") var localDraftCopy = await Connection.FindWithQueryAsync<MailCopy>(
.Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id") "SELECT MailCopy.* FROM MailCopy INNER JOIN MailItemFolder ON MailCopy.FolderId = MailItemFolder.Id WHERE MailCopy.UniqueId = ? AND MailItemFolder.MailAccountId = ?",
.Where("MailCopy.UniqueId", localDraftCopyUniqueId) localDraftCopyUniqueId, accountId);
.Where("MailItemFolder.MailAccountId", accountId)
.SelectRaw("MailCopy.*")
.GetRawQuery();
var localDraftCopy = await Connection.FindWithQueryAsync<MailCopy>(query);
if (localDraftCopy == null) if (localDraftCopy == null)
{ {
@@ -1085,28 +1078,22 @@ public class MailService : BaseDatabaseService, IMailService
public Task<List<MailCopy>> GetDownloadedUnreadMailsAsync(Guid accountId, IEnumerable<string> downloadedMailCopyIds) public Task<List<MailCopy>> GetDownloadedUnreadMailsAsync(Guid accountId, IEnumerable<string> downloadedMailCopyIds)
{ {
var rawQuery = new Query("MailCopy") var placeholders = string.Join(",", downloadedMailCopyIds.Select(_ => "?"));
.Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id") var sql = $"SELECT MailCopy.* FROM MailCopy INNER JOIN MailItemFolder ON MailCopy.FolderId = MailItemFolder.Id WHERE MailCopy.Id IN ({placeholders}) AND MailCopy.IsRead = ? AND MailItemFolder.MailAccountId = ? AND MailItemFolder.SpecialFolderType = ?";
.WhereIn("MailCopy.Id", downloadedMailCopyIds) var parameters = new List<object>();
.Where("MailCopy.IsRead", false) parameters.AddRange(downloadedMailCopyIds.Cast<object>());
.Where("MailItemFolder.MailAccountId", accountId) parameters.Add(false);
.Where("MailItemFolder.SpecialFolderType", SpecialFolderType.Inbox) parameters.Add(accountId);
.SelectRaw("MailCopy.*") parameters.Add((int)SpecialFolderType.Inbox);
.GetRawQuery();
return Connection.QueryAsync<MailCopy>(rawQuery); return Connection.QueryAsync<MailCopy>(sql, parameters.ToArray());
} }
public Task<MailAccount> GetMailAccountByUniqueIdAsync(Guid uniqueMailId) public Task<MailAccount> GetMailAccountByUniqueIdAsync(Guid uniqueMailId)
{ {
var query = new Query("MailCopy") return Connection.FindWithQueryAsync<MailAccount>(
.Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id") "SELECT MailAccount.* FROM MailCopy INNER JOIN MailItemFolder ON MailCopy.FolderId = MailItemFolder.Id INNER JOIN MailAccount ON MailItemFolder.MailAccountId = MailAccount.Id WHERE MailCopy.UniqueId = ?",
.Join("MailAccount", "MailItemFolder.MailAccountId", "MailAccount.Id") uniqueMailId);
.Where("MailCopy.UniqueId", uniqueMailId)
.SelectRaw("MailAccount.*")
.GetRawQuery();
return Connection.FindWithQueryAsync<MailAccount>(query);
} }
public Task<bool> IsMailExistsAsync(string mailCopyId) public Task<bool> IsMailExistsAsync(string mailCopyId)
@@ -1116,11 +1103,10 @@ public class MailService : BaseDatabaseService, IMailService
{ {
var localMailIds = uniqueIds.Select(a => MailkitClientExtensions.CreateUid(folderId, a.Id)).ToArray(); var localMailIds = uniqueIds.Select(a => MailkitClientExtensions.CreateUid(folderId, a.Id)).ToArray();
var query = new Query(nameof(MailCopy)) var placeholders = string.Join(",", localMailIds.Select(_ => "?"));
.WhereIn("Id", localMailIds) var sql = $"SELECT * FROM MailCopy WHERE Id IN ({placeholders})";
.GetRawQuery();
return await Connection.QueryAsync<MailCopy>(query); return await Connection.QueryAsync<MailCopy>(sql, localMailIds.Cast<object>().ToArray());
} }
public Task<bool> IsMailExistsAsync(string mailCopyId, Guid folderId) public Task<bool> IsMailExistsAsync(string mailCopyId, Guid folderId)
@@ -1142,12 +1128,10 @@ public class MailService : BaseDatabaseService, IMailService
{ {
if (!mailCopyIds.Any()) return []; if (!mailCopyIds.Any()) return [];
var query = new Query("MailCopy") var placeholders = string.Join(",", mailCopyIds.Select(_ => "?"));
.WhereIn("MailCopy.Id", mailCopyIds) var sql = $"SELECT MailCopy.* FROM MailCopy WHERE MailCopy.Id IN ({placeholders})";
.SelectRaw("MailCopy.*")
.GetRawQuery();
var mailCopies = await Connection.QueryAsync<MailCopy>(query); var mailCopies = await Connection.QueryAsync<MailCopy>(sql, mailCopyIds.Cast<object>().ToArray());
if (mailCopies?.Count == 0) return []; if (mailCopies?.Count == 0) return [];
ConcurrentDictionary<Guid, MailItemFolder> folderCache = new(); ConcurrentDictionary<Guid, MailItemFolder> folderCache = new();
@@ -1164,11 +1148,9 @@ public class MailService : BaseDatabaseService, IMailService
public async Task<List<string>> AreMailsExistsAsync(IEnumerable<string> mailCopyIds) public async Task<List<string>> AreMailsExistsAsync(IEnumerable<string> mailCopyIds)
{ {
var query = new Query(nameof(MailCopy)) var placeholders = string.Join(",", mailCopyIds.Select(_ => "?"));
.WhereIn("Id", mailCopyIds) var sql = $"SELECT Id FROM MailCopy WHERE Id IN ({placeholders})";
.Select("Id")
.GetRawQuery();
return await Connection.QueryScalarsAsync<string>(query); return await Connection.QueryScalarsAsync<string>(sql, mailCopyIds.Cast<object>().ToArray());
} }
} }
-1
View File
@@ -22,7 +22,6 @@
<PackageReference Include="Serilog.Sinks.Debug" /> <PackageReference Include="Serilog.Sinks.Debug" />
<PackageReference Include="Serilog.Sinks.File" /> <PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="Serilog.Exceptions" /> <PackageReference Include="Serilog.Exceptions" />
<PackageReference Include="SqlKata" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Wino.Core.Domain\Wino.Core.Domain.csproj" /> <ProjectReference Include="..\Wino.Core.Domain\Wino.Core.Domain.csproj" />