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
+19 -54
View File
@@ -5,7 +5,6 @@ using System.Threading.Tasks;
using CommunityToolkit.Diagnostics;
using CommunityToolkit.Mvvm.Messaging;
using Serilog;
using SqlKata;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
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);
// Then, add new accounts to merged inbox.
var query = new Query("MailAccount")
.WhereIn("Id", linkedAccountIds)
.AsUpdate(new
{
MergedInboxId = mergedInboxId
});
await Connection.ExecuteAsync(query.GetRawQuery());
var accountIdList = linkedAccountIds.ToList();
var placeholders = string.Join(",", accountIdList.Select(_ => "?"));
var sql = $"UPDATE MailAccount SET MergedInboxId = ? WHERE Id IN ({placeholders})";
var parameters = new List<object> { mergedInboxId };
parameters.AddRange(accountIdList.Cast<object>());
await Connection.ExecuteAsync(sql, parameters.ToArray());
WeakReferenceMessenger.Default.Send(new AccountsMenuRefreshRequested());
}
@@ -84,14 +82,7 @@ public class AccountService : BaseDatabaseService, IAccountService
return;
}
var query = new Query("MailAccount")
.Where("MergedInboxId", mergedInboxId)
.AsUpdate(new
{
MergedInboxId = (Guid?)null
});
await Connection.ExecuteAsync(query.GetRawQuery()).ConfigureAwait(false);
await Connection.ExecuteAsync("UPDATE MailAccount SET MergedInboxId = NULL WHERE MergedInboxId = ?", mergedInboxId).ConfigureAwait(false);
await Connection.DeleteAsync<MergedInbox>(mergedInbox).ConfigureAwait(false);
// 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)
{
var query = new Query("MergedInbox")
.Where("Id", mergedInboxId)
.AsUpdate(new
{
Name = newName
});
await Connection.ExecuteAsync(query.GetRawQuery());
await Connection.ExecuteAsync("UPDATE MergedInbox SET Name = ? WHERE Id = ?", newName, mergedInboxId);
ReportUIChange(new MergedInboxRenamed(mergedInboxId, newName));
}
@@ -261,11 +245,9 @@ public class AccountService : BaseDatabaseService, IAccountService
public async Task<List<MailAccountAlias>> GetAccountAliasesAsync(Guid accountId)
{
var query = new Query(nameof(MailAccountAlias))
.Where(nameof(MailAccountAlias.AccountId), accountId)
.OrderByDesc(nameof(MailAccountAlias.IsRootAlias));
return await Connection.QueryAsync<MailAccountAlias>(query.GetRawQuery()).ConfigureAwait(false);
return await Connection.QueryAsync<MailAccountAlias>(
"SELECT * FROM MailAccountAlias WHERE AccountId = ? ORDER BY IsRootAlias DESC",
accountId).ConfigureAwait(false);
}
private Task<MergedInbox> GetMergedInboxInformationAsync(Guid mergedInboxId)
@@ -273,17 +255,9 @@ public class AccountService : BaseDatabaseService, IAccountService
public async Task DeleteAccountMailCacheAsync(Guid accountId, AccountCacheResetReason accountCacheResetReason)
{
var deleteQuery = new Query("MailCopy")
.WhereIn("Id", q => q
.From("MailCopy")
.Select("Id")
.WhereIn("FolderId", q2 => q2
.From("MailItemFolder")
.Select("Id")
.Where("MailAccountId", accountId)
)).AsDelete();
await Connection.ExecuteAsync(deleteQuery.GetRawQuery());
await Connection.ExecuteAsync(
"DELETE FROM MailCopy WHERE Id IN (SELECT Id FROM MailCopy WHERE FolderId IN (SELECT Id FROM MailItemFolder WHERE MailAccountId = ?))",
accountId);
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.
if (mergedInboxAccountCount == 2)
{
var query = new Query("MailAccount")
.Where("MergedInboxId", account.MergedInboxId.Value)
.AsUpdate(new
{
MergedInboxId = (Guid?)null
});
await Connection.ExecuteAsync(query.GetRawQuery()).ConfigureAwait(false);
await Connection.ExecuteAsync(
"UPDATE MailAccount SET MergedInboxId = NULL WHERE MergedInboxId = ?",
account.MergedInboxId.Value).ConfigureAwait(false);
}
}
@@ -494,11 +463,7 @@ public class AccountService : BaseDatabaseService, IAccountService
{
// Create query to delete alias.
var query = new Query("MailAccountAlias")
.Where("Id", aliasId)
.AsDelete();
await Connection.ExecuteAsync(query.GetRawQuery()).ConfigureAwait(false);
await Connection.ExecuteAsync("DELETE FROM MailAccountAlias WHERE Id = ?", aliasId).ConfigureAwait(false);
}
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 Ical.Net.CalendarComponents;
using Ical.Net.DataTypes;
using SqlKata;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Enums;
@@ -43,14 +42,9 @@ public class CalendarService : BaseDatabaseService, ICalendarService
public async Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar)
{
var deleteCalendarItemsQuery = new Query()
.From(nameof(CalendarItem))
.Where(nameof(CalendarItem.CalendarId), accountCalendar.Id)
.Where(nameof(AccountCalendar.AccountId), accountCalendar.AccountId);
var rawQuery = deleteCalendarItemsQuery.GetRawQuery();
await Connection.ExecuteAsync(rawQuery);
await Connection.ExecuteAsync(
"DELETE FROM CalendarItem WHERE CalendarId = ? AND AccountId = ?",
accountCalendar.Id, accountCalendar.AccountId);
await Connection.DeleteAsync<AccountCalendar>(accountCalendar);
WeakReferenceMessenger.Default.Send(new CalendarListDeleted(accountCalendar));
@@ -182,24 +176,16 @@ public class CalendarService : BaseDatabaseService, ICalendarService
public Task<CalendarItem> GetCalendarItemAsync(Guid id)
{
var query = new Query()
.From(nameof(CalendarItem))
.Where(nameof(CalendarItem.Id), id);
var rawQuery = query.GetRawQuery();
return Connection.FindWithQueryAsync<CalendarItem>(rawQuery);
return Connection.FindWithQueryAsync<CalendarItem>(
"SELECT * FROM CalendarItem WHERE Id = ?",
id);
}
public async Task<CalendarItem> GetCalendarItemAsync(Guid accountCalendarId, string remoteEventId)
{
var query = new Query()
.From(nameof(CalendarItem))
.Where(nameof(CalendarItem.CalendarId), accountCalendarId)
.Where(nameof(CalendarItem.RemoteEventId), remoteEventId);
var rawQuery = query.GetRawQuery();
var calendarItem = await Connection.FindWithQueryAsync<CalendarItem>(rawQuery);
var calendarItem = await Connection.FindWithQueryAsync<CalendarItem>(
"SELECT * FROM CalendarItem WHERE CalendarId = ? AND RemoteEventId = ?",
accountCalendarId, remoteEventId);
// Load assigned calendar.
if (calendarItem != null)
@@ -212,12 +198,9 @@ public class CalendarService : BaseDatabaseService, ICalendarService
public Task UpdateCalendarDeltaSynchronizationToken(Guid calendarId, string deltaToken)
{
var query = new Query()
.From(nameof(AccountCalendar))
.Where(nameof(AccountCalendar.Id), calendarId)
.AsUpdate(new { SynchronizationDeltaToken = deltaToken });
return Connection.ExecuteAsync(query.GetRawQuery());
return Connection.ExecuteAsync(
"UPDATE AccountCalendar SET SynchronizationDeltaToken = ? WHERE Id = ?",
deltaToken, calendarId);
}
public Task<List<CalendarEventAttendee>> GetAttendeesAsync(Guid calendarEventTrackingId)
@@ -228,12 +211,9 @@ public class CalendarService : BaseDatabaseService, ICalendarService
await Connection.RunInTransactionAsync((connection) =>
{
// Clear all attendees.
var query = new Query()
.From(nameof(CalendarEventAttendee))
.Where(nameof(CalendarEventAttendee.CalendarItemId), calendarItemId)
.AsDelete();
connection.Execute(query.GetRawQuery());
connection.Execute(
"DELETE FROM CalendarEventAttendee WHERE CalendarItemId = ?",
calendarItemId);
// Insert new attendees.
connection.InsertAll(allAttendees, typeof(CalendarEventAttendee));
+6 -15
View File
@@ -4,7 +4,6 @@ using System.Linq;
using System.Threading.Tasks;
using MimeKit;
using Serilog;
using SqlKata;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Interfaces;
using Wino.Services.Extensions;
@@ -29,13 +28,9 @@ public class ContactService : BaseDatabaseService, IContactService
if (queryText == null || queryText.Length < 2)
return Task.FromResult<List<AccountContact>>(null);
var query = new Query(nameof(AccountContact));
query.WhereContains("Address", queryText);
query.OrWhereContains("Name", queryText);
var rawLikeQuery = query.GetRawQuery();
return Connection.QueryAsync<AccountContact>(rawLikeQuery);
const string query = "SELECT * FROM AccountContact WHERE Address LIKE ? OR Name LIKE ?";
var pattern = $"%{queryText}%";
return Connection.QueryAsync<AccountContact>(query, pattern, pattern);
}
public Task<AccountContact> GetAddressInformationByAddressAsync(string address)
@@ -81,13 +76,9 @@ public class ContactService : BaseDatabaseService, IContactService
if (string.IsNullOrWhiteSpace(searchQuery))
return GetAllContactsAsync();
var query = new Query(nameof(AccountContact));
query.WhereContains("Address", searchQuery.Trim());
query.OrWhereContains("Name", searchQuery.Trim());
var rawLikeQuery = query.GetRawQuery();
return Connection.QueryAsync<AccountContact>(rawLikeQuery);
const string query = "SELECT * FROM AccountContact WHERE Address LIKE ? OR Name LIKE ?";
var pattern = $"%{searchQuery.Trim()}%";
return Connection.QueryAsync<AccountContact>(query, pattern, pattern);
}
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();
}
}
+46 -52
View File
@@ -4,7 +4,6 @@ using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Serilog;
using SqlKata;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
@@ -53,23 +52,38 @@ public class FolderService : BaseDatabaseService, IFolderService
if (account == null) return default;
var query = new Query("MailCopy")
.Where("FolderId", folderId)
.SelectRaw("count (DISTINCT Id)");
// If focused inbox is enabled, we need to check if this is the inbox folder.
// Convert to raw SQL
string sqlQuery;
object[] parameters;
if (account.Preferences.IsFocusedInboxEnabled.GetValueOrDefault() && folder.SpecialFolderType == SpecialFolderType.Inbox)
{
query.Where("IsFocused", 1);
if (folder.SpecialFolderType != SpecialFolderType.Draft && folder.SpecialFolderType != SpecialFolderType.Junk)
{
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 };
}
}
// 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)
else
{
query.Where("IsRead", 0);
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)
@@ -186,13 +200,10 @@ public class FolderService : BaseDatabaseService, IFolderService
// Localize category folder name.
if (parentFolder.SpecialFolderType == SpecialFolderType.Category) parentFolder.FolderName = Translator.CategoriesFolderNameOverride;
var query = new Query(nameof(MailItemFolder))
.Where(nameof(MailItemFolder.ParentRemoteFolderId), parentFolder.RemoteFolderId)
.Where(nameof(MailItemFolder.MailAccountId), parentFolder.MailAccountId);
const string query = "SELECT * FROM MailItemFolder WHERE ParentRemoteFolderId = ? AND MailAccountId = ?";
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())
{
@@ -348,21 +359,14 @@ public class FolderService : BaseDatabaseService, IFolderService
public Task<List<MailItemFolder>> GetFoldersAsync(Guid accountId)
{
var query = new Query(nameof(MailItemFolder))
.Where(nameof(MailItemFolder.MailAccountId), accountId)
.OrderBy(nameof(MailItemFolder.SpecialFolderType));
return Connection.QueryAsync<MailItemFolder>(query.GetRawQuery());
const string query = "SELECT * FROM MailItemFolder WHERE MailAccountId = ? ORDER BY SpecialFolderType";
return Connection.QueryAsync<MailItemFolder>(query, accountId);
}
public Task<List<MailItemFolder>> GetVisibleFoldersAsync(Guid accountId)
{
var query = new Query(nameof(MailItemFolder))
.Where(nameof(MailItemFolder.MailAccountId), accountId)
.Where(nameof(MailItemFolder.IsHidden), false)
.OrderBy(nameof(MailItemFolder.SpecialFolderType));
return Connection.QueryAsync<MailItemFolder>(query.GetRawQuery());
const string query = "SELECT * FROM MailItemFolder WHERE MailAccountId = ? AND IsHidden = ? ORDER BY SpecialFolderType";
return Connection.QueryAsync<MailItemFolder>(query, accountId, 0);
}
public async Task<IList<uint>> GetKnownUidsForFolderAsync(Guid folderId)
@@ -516,25 +520,18 @@ public class FolderService : BaseDatabaseService, IFolderService
private Task<List<string>> GetMailCopyIdsByFolderIdAsync(Guid folderId)
{
var query = new Query("MailCopy")
.Where("FolderId", folderId)
.Select("Id");
return Connection.QueryScalarsAsync<string>(query.GetRawQuery());
const string query = "SELECT Id FROM MailCopy WHERE FolderId = ?";
return Connection.QueryScalarsAsync<string>(query, folderId);
}
public async Task<List<MailFolderPairMetadata>> GetMailFolderPairMetadatasAsync(IEnumerable<string> mailCopyIds)
{
// Get all assignments for all items.
var query = new Query(nameof(MailCopy))
.Join(nameof(MailItemFolder), $"{nameof(MailCopy)}.FolderId", $"{nameof(MailItemFolder)}.Id")
.WhereIn($"{nameof(MailCopy)}.Id", mailCopyIds)
.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>(rowQuery);
var mailCopyIdList = mailCopyIds.ToList();
var placeholders = string.Join(",", mailCopyIdList.Select(_ => "?"));
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})";
var parameters = mailCopyIdList.Cast<object>().ToArray();
return await Connection.QueryAsync<MailFolderPairMetadata>(query, parameters);
}
public Task<List<MailFolderPairMetadata>> GetMailFolderPairMetadatasAsync(string mailCopyId)
@@ -687,14 +684,11 @@ public class FolderService : BaseDatabaseService, IFolderService
public Task<List<UnreadItemCountResult>> GetUnreadItemCountResultsAsync(IEnumerable<Guid> accountIds)
{
var query = new Query(nameof(MailCopy))
.Join(nameof(MailItemFolder), $"{nameof(MailCopy)}.FolderId", $"{nameof(MailItemFolder)}.Id")
.WhereIn($"{nameof(MailItemFolder)}.MailAccountId", accountIds)
.Where($"{nameof(MailCopy)}.IsRead", 0)
.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());
var accountIdList = accountIds.ToList();
var placeholders = string.Join(",", accountIdList.Select(_ => "?"));
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";
var parameters = accountIdList.Cast<object>().Concat(new object[] { 0, 1 }).ToArray();
return Connection.QueryAsync<UnreadItemCountResult>(query, parameters);
}
}
+18 -26
View File
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SqlKata;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
@@ -24,10 +23,8 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
/// </summary>
public async Task<IEnumerable<KeyboardShortcut>> GetKeyboardShortcutsAsync()
{
var query = new Query(nameof(KeyboardShortcut))
.OrderBy(nameof(KeyboardShortcut.MailOperation));
return await Connection.QueryAsync<KeyboardShortcut>(query.GetRawQuery());
return await Connection.QueryAsync<KeyboardShortcut>(
"SELECT * FROM KeyboardShortcut ORDER BY MailOperation");
}
/// <summary>
@@ -35,11 +32,9 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
/// </summary>
public async Task<IEnumerable<KeyboardShortcut>> GetEnabledKeyboardShortcutsAsync()
{
var query = new Query(nameof(KeyboardShortcut))
.Where(nameof(KeyboardShortcut.IsEnabled), true)
.OrderBy(nameof(KeyboardShortcut.MailOperation));
return await Connection.QueryAsync<KeyboardShortcut>(query.GetRawQuery());
return await Connection.QueryAsync<KeyboardShortcut>(
"SELECT * FROM KeyboardShortcut WHERE IsEnabled = ? ORDER BY MailOperation",
true);
}
/// <summary>
@@ -66,9 +61,6 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
/// </summary>
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);
}
@@ -77,12 +69,8 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
/// </summary>
public async Task<MailOperation?> GetMailOperationForKeyAsync(string key, ModifierKeys modifierKeys)
{
var query = new Query(nameof(KeyboardShortcut))
.Where(nameof(KeyboardShortcut.Key), key)
.Where(nameof(KeyboardShortcut.ModifierKeys), (int)modifierKeys)
.Where(nameof(KeyboardShortcut.IsEnabled), true);
var shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query.GetRawQuery());
const string query = "SELECT * FROM KeyboardShortcut WHERE Key = ? AND ModifierKeys = ? AND IsEnabled = ? LIMIT 1";
var shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query, key, (int)modifierKeys, 1);
return shortcut?.MailOperation;
}
@@ -91,16 +79,20 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
/// </summary>
public async Task<bool> IsKeyCombinationInUseAsync(string key, ModifierKeys modifierKeys, Guid? excludeShortcutId = null)
{
var query = new Query(nameof(KeyboardShortcut))
.Where(nameof(KeyboardShortcut.Key), key)
.Where(nameof(KeyboardShortcut.ModifierKeys), (int)modifierKeys);
string query;
KeyboardShortcut shortcut;
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);
}
var shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query.GetRawQuery());
else
{
query = "SELECT * FROM KeyboardShortcut WHERE Key = ? AND ModifierKeys = ? LIMIT 1";
shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query, key, (int)modifierKeys);
}
return shortcut != null;
}
+100 -118
View File
@@ -2,12 +2,12 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using MimeKit;
using Serilog;
using SqlKata;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
@@ -144,74 +144,80 @@ public class MailService : BaseDatabaseService, IMailService
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.
//if (!string.IsNullOrEmpty(options.SearchQuery))
//{
// options.IsFocusedOnly = null;
// filterType = FilterOptionType.All;
// searchQuery = searchQuery.Trim();
//}
// SQLite PCL doesn't support joins.
// We make the query using SqlKata and execute it directly on SQLite-PCL.
var query = new Query("MailCopy")
.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.
var sql = new StringBuilder();
sql.Append("SELECT MailCopy.* FROM MailCopy INNER JOIN MailItemFolder ON MailCopy.FolderId = MailItemFolder.Id");
var whereClauses = new List<string>();
var parameters = new List<object>();
// Folder filter
var folderPlaceholders = string.Join(",", options.Folders.Select(_ => "?"));
whereClauses.Add($"MailCopy.FolderId IN ({folderPlaceholders})");
parameters.AddRange(options.Folders.Select(f => (object)f.Id));
// Filter type
switch (options.FilterType)
{
case FilterOptionType.Unread:
query.Where("MailCopy.IsRead", false);
whereClauses.Add("MailCopy.IsRead = 0");
break;
case FilterOptionType.Flagged:
query.Where("MailCopy.IsFlagged", true);
whereClauses.Add("MailCopy.IsFlagged = 1");
break;
case FilterOptionType.Files:
query.Where("MailCopy.HasAttachments", true);
whereClauses.Add("MailCopy.HasAttachments = 1");
break;
}
// Focused filter
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))
query.Where(a =>
a.OrWhereContains("MailCopy.PreviewText", options.SearchQuery)
.OrWhereContains("MailCopy.Subject", options.SearchQuery)
.OrWhereContains("MailCopy.FromName", options.SearchQuery)
.OrWhereContains("MailCopy.FromAddress", options.SearchQuery));
// Support pagination by excluding already fetched items
{
whereClauses.Add("(MailCopy.PreviewText LIKE ? OR MailCopy.Subject LIKE ? OR MailCopy.FromName LIKE ? OR MailCopy.FromAddress LIKE ?)");
var searchPattern = $"%{options.SearchQuery}%";
parameters.Add(searchPattern);
parameters.Add(searchPattern);
parameters.Add(searchPattern);
parameters.Add(searchPattern);
}
// Exclude existing items
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)
{
query.Skip(options.Skip);
sql.Append($" OFFSET {options.Skip}");
}
// Support custom take count for pagination
if (options.Take > 0)
{
query.Take(options.Take);
}
return query.GetRawQuery();
return (sql.ToString(), parameters.ToArray());
}
public async Task<List<MailCopy>> FetchMailsAsync(MailListInitializationOptions options, CancellationToken cancellationToken = default)
@@ -226,8 +232,8 @@ public class MailService : BaseDatabaseService, IMailService
else
{
// If not just do the query.
var query = BuildMailFetchQuery(options);
mails = await Connection.QueryAsync<MailCopy>(query);
var (query, parameters) = BuildMailFetchQuery(options);
mails = await Connection.QueryAsync<MailCopy>(query, parameters);
}
ConcurrentDictionary<Guid, MailItemFolder> folderCache = new();
@@ -295,13 +301,12 @@ public class MailService : BaseDatabaseService, IMailService
if (string.IsNullOrEmpty(threadId))
return [];
var query = new Query("MailCopy")
.Where("ThreadId", threadId)
.WhereNotIn("Id", excludeMailIds)
.SelectRaw("MailCopy.*")
.GetRawQuery();
var placeholders = string.Join(",", excludeMailIds.Select(_ => "?"));
var sql = $"SELECT MailCopy.* FROM MailCopy WHERE ThreadId = ? AND Id NOT IN ({placeholders})";
var parameters = new List<object> { threadId };
parameters.AddRange(excludeMailIds.Cast<object>());
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)
@@ -309,13 +314,14 @@ public class MailService : BaseDatabaseService, IMailService
if (threadIds?.Count == 0)
return [];
var query = new Query("MailCopy")
.WhereIn("ThreadId", threadIds)
.WhereNotIn("Id", excludeMailIds)
.SelectRaw("MailCopy.*")
.GetRawQuery();
var threadPlaceholders = string.Join(",", threadIds.Select(_ => "?"));
var excludePlaceholders = string.Join(",", excludeMailIds.Select(_ => "?"));
var sql = $"SELECT MailCopy.* FROM MailCopy WHERE ThreadId IN ({threadPlaceholders}) AND Id NOT IN ({excludePlaceholders})";
var parameters = new List<object>();
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>
@@ -451,12 +457,9 @@ public class MailService : BaseDatabaseService, IMailService
/// <param name="mailCopyId">Mail copy id.</param>
public async Task<MailCopy> GetSingleMailItemAsync(string mailCopyId)
{
var query = new Query("MailCopy")
.Where("MailCopy.Id", mailCopyId)
.SelectRaw("MailCopy.*")
.GetRawQuery();
var mailCopy = await Connection.FindWithQueryAsync<MailCopy>(query);
var mailCopy = await Connection.FindWithQueryAsync<MailCopy>(
"SELECT MailCopy.* FROM MailCopy WHERE MailCopy.Id = ?",
mailCopyId);
if (mailCopy == null) return null;
await LoadAssignedPropertiesAsync(mailCopy).ConfigureAwait(false);
@@ -466,14 +469,9 @@ public class MailService : BaseDatabaseService, IMailService
public async Task<MailCopy> GetSingleMailItemAsync(string mailCopyId, string remoteFolderId)
{
var query = new Query("MailCopy")
.Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id")
.Where("MailCopy.Id", mailCopyId)
.Where("MailItemFolder.RemoteFolderId", remoteFolderId)
.SelectRaw("MailCopy.*")
.GetRawQuery();
var mailItem = await Connection.FindWithQueryAsync<MailCopy>(query);
var mailItem = await Connection.FindWithQueryAsync<MailCopy>(
"SELECT MailCopy.* FROM MailCopy INNER JOIN MailItemFolder ON MailCopy.FolderId = MailItemFolder.Id WHERE MailCopy.Id = ? AND MailItemFolder.RemoteFolderId = ?",
mailCopyId, remoteFolderId);
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)
{
var query = new Query("MailCopy")
.Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id")
.Where("MailCopy.UniqueId", localDraftCopyUniqueId)
.Where("MailItemFolder.MailAccountId", accountId)
.SelectRaw("MailCopy.*")
.GetRawQuery();
var localDraftCopy = await Connection.FindWithQueryAsync<MailCopy>(query);
var localDraftCopy = await Connection.FindWithQueryAsync<MailCopy>(
"SELECT MailCopy.* FROM MailCopy INNER JOIN MailItemFolder ON MailCopy.FolderId = MailItemFolder.Id WHERE MailCopy.UniqueId = ? AND MailItemFolder.MailAccountId = ?",
localDraftCopyUniqueId, accountId);
if (localDraftCopy == null)
{
@@ -1085,28 +1078,22 @@ public class MailService : BaseDatabaseService, IMailService
public Task<List<MailCopy>> GetDownloadedUnreadMailsAsync(Guid accountId, IEnumerable<string> downloadedMailCopyIds)
{
var rawQuery = new Query("MailCopy")
.Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id")
.WhereIn("MailCopy.Id", downloadedMailCopyIds)
.Where("MailCopy.IsRead", false)
.Where("MailItemFolder.MailAccountId", accountId)
.Where("MailItemFolder.SpecialFolderType", SpecialFolderType.Inbox)
.SelectRaw("MailCopy.*")
.GetRawQuery();
var placeholders = string.Join(",", downloadedMailCopyIds.Select(_ => "?"));
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 = ?";
var parameters = new List<object>();
parameters.AddRange(downloadedMailCopyIds.Cast<object>());
parameters.Add(false);
parameters.Add(accountId);
parameters.Add((int)SpecialFolderType.Inbox);
return Connection.QueryAsync<MailCopy>(rawQuery);
return Connection.QueryAsync<MailCopy>(sql, parameters.ToArray());
}
public Task<MailAccount> GetMailAccountByUniqueIdAsync(Guid uniqueMailId)
{
var query = new Query("MailCopy")
.Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id")
.Join("MailAccount", "MailItemFolder.MailAccountId", "MailAccount.Id")
.Where("MailCopy.UniqueId", uniqueMailId)
.SelectRaw("MailAccount.*")
.GetRawQuery();
return Connection.FindWithQueryAsync<MailAccount>(query);
return Connection.FindWithQueryAsync<MailAccount>(
"SELECT MailAccount.* FROM MailCopy INNER JOIN MailItemFolder ON MailCopy.FolderId = MailItemFolder.Id INNER JOIN MailAccount ON MailItemFolder.MailAccountId = MailAccount.Id WHERE MailCopy.UniqueId = ?",
uniqueMailId);
}
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 query = new Query(nameof(MailCopy))
.WhereIn("Id", localMailIds)
.GetRawQuery();
var placeholders = string.Join(",", localMailIds.Select(_ => "?"));
var sql = $"SELECT * FROM MailCopy WHERE Id IN ({placeholders})";
return await Connection.QueryAsync<MailCopy>(query);
return await Connection.QueryAsync<MailCopy>(sql, localMailIds.Cast<object>().ToArray());
}
public Task<bool> IsMailExistsAsync(string mailCopyId, Guid folderId)
@@ -1142,12 +1128,10 @@ public class MailService : BaseDatabaseService, IMailService
{
if (!mailCopyIds.Any()) return [];
var query = new Query("MailCopy")
.WhereIn("MailCopy.Id", mailCopyIds)
.SelectRaw("MailCopy.*")
.GetRawQuery();
var placeholders = string.Join(",", mailCopyIds.Select(_ => "?"));
var sql = $"SELECT MailCopy.* FROM MailCopy WHERE MailCopy.Id IN ({placeholders})";
var mailCopies = await Connection.QueryAsync<MailCopy>(query);
var mailCopies = await Connection.QueryAsync<MailCopy>(sql, mailCopyIds.Cast<object>().ToArray());
if (mailCopies?.Count == 0) return [];
ConcurrentDictionary<Guid, MailItemFolder> folderCache = new();
@@ -1164,11 +1148,9 @@ public class MailService : BaseDatabaseService, IMailService
public async Task<List<string>> AreMailsExistsAsync(IEnumerable<string> mailCopyIds)
{
var query = new Query(nameof(MailCopy))
.WhereIn("Id", mailCopyIds)
.Select("Id")
.GetRawQuery();
var placeholders = string.Join(",", mailCopyIds.Select(_ => "?"));
var sql = $"SELECT Id FROM MailCopy WHERE Id IN ({placeholders})";
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.File" />
<PackageReference Include="Serilog.Exceptions" />
<PackageReference Include="SqlKata" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Wino.Core.Domain\Wino.Core.Domain.csproj" />