fix(categories): correct all 7 smart-folder bugs at source (PHASE 2) (#38)
CI / backend (push) Successful in 52s
CI / frontend (push) Successful in 11s
CI / format (push) Successful in 1m8s
CI / db-tests (push) Successful in 49s
Deploy Staging / deploy (push) Successful in 24s
CI / backend (pull_request) Successful in 53s
CI / frontend (pull_request) Successful in 11s
CI / format (pull_request) Successful in 49s
CI / db-tests (pull_request) Successful in 52s
Security / secrets (push) Successful in 3s
Security / dependencies (push) Successful in 58s
Security / sast (push) Successful in 33s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 58s
Security / sast (pull_request) Successful in 37s
CI / backend (push) Successful in 52s
CI / frontend (push) Successful in 11s
CI / format (push) Successful in 1m8s
CI / db-tests (push) Successful in 49s
Deploy Staging / deploy (push) Successful in 24s
CI / backend (pull_request) Successful in 53s
CI / frontend (pull_request) Successful in 11s
CI / format (pull_request) Successful in 49s
CI / db-tests (pull_request) Successful in 52s
Security / secrets (push) Successful in 3s
Security / dependencies (push) Successful in 58s
Security / sast (push) Successful in 33s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 58s
Security / sast (pull_request) Successful in 37s
This commit was merged in pull request #38.
This commit is contained in:
@@ -228,9 +228,34 @@ public class SyncService : ISyncService
|
||||
await _db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
/// <summary>Resolves domain/sender/thread, then inserts the email and attachment metadata.</summary>
|
||||
// Per-sync-run cache of the user's GmailLabelId -> local Label.Id, so email/label linkage
|
||||
// costs no extra query per message. Populated lazily; labels are synced before messages.
|
||||
private Dictionary<string, Guid>? _labelCache;
|
||||
private Guid _labelCacheUserId;
|
||||
|
||||
private async Task<Dictionary<string, Guid>> GetLabelMapAsync(Guid userId, CancellationToken ct)
|
||||
{
|
||||
if (_labelCache is null || _labelCacheUserId != userId)
|
||||
{
|
||||
_labelCache = await _db.Labels
|
||||
.Where(l => l.UserId == userId)
|
||||
.ToDictionaryAsync(l => l.GmailLabelId, l => l.Id, ct);
|
||||
_labelCacheUserId = userId;
|
||||
}
|
||||
return _labelCache;
|
||||
}
|
||||
|
||||
/// <summary>Resolves domain/sender/thread, then upserts the email, its labels, and
|
||||
/// attachment metadata. Idempotent: re-syncing a message replaces the prior row (and its
|
||||
/// labels) rather than duplicating it.</summary>
|
||||
private async Task UpsertMessageAsync(Guid userId, GmailMessageDetail d, CancellationToken ct)
|
||||
{
|
||||
// True upsert: drop any existing copy first so labels/flags re-populate cleanly and
|
||||
// an incrementally-changed message can't be duplicated. EmailLabels/Attachments cascade.
|
||||
var prior = await _db.Emails.FirstOrDefaultAsync(
|
||||
e => e.UserId == userId && e.GmailMessageId == d.GmailMessageId, ct);
|
||||
if (prior is not null) _db.Emails.Remove(prior);
|
||||
|
||||
var sender = await ResolveSenderAsync(userId, d.FromAddress, d.FromDisplayName, ct);
|
||||
var thread = await ResolveThreadAsync(userId, d.GmailThreadId, d.Subject, d.Snippet, d.SentAtUtc, ct);
|
||||
|
||||
@@ -256,6 +281,7 @@ public class SyncService : ISyncService
|
||||
IsInInbox = d.LabelIds.Contains("INBOX"),
|
||||
IsStarred = d.LabelIds.Contains("STARRED"),
|
||||
IsImportant = d.LabelIds.Contains("IMPORTANT"),
|
||||
IsTrashed = d.LabelIds.Contains("TRASH"),
|
||||
HasAttachments = d.HasAttachments,
|
||||
HasListUnsubscribe = d.HasListUnsubscribe,
|
||||
ListUnsubscribeRaw = Trunc(d.ListUnsubscribeRaw, 2048),
|
||||
@@ -264,6 +290,14 @@ public class SyncService : ISyncService
|
||||
};
|
||||
_db.Emails.Add(email);
|
||||
|
||||
// Link the email to its Gmail labels (system + user) so label-based folders — Sent,
|
||||
// Spam, and "Unlabelled" (no user label) — resolve correctly. Previously no EmailLabel
|
||||
// rows were ever created, so every label folder was empty and every email looked unlabelled.
|
||||
var labelMap = await GetLabelMapAsync(userId, ct);
|
||||
foreach (var gmailLabelId in d.LabelIds.Distinct())
|
||||
if (labelMap.TryGetValue(gmailLabelId, out var localLabelId))
|
||||
_db.Set<EmailLabel>().Add(new EmailLabel { EmailId = email.Id, LabelId = localLabelId });
|
||||
|
||||
foreach (var (fileName, mime, size, attId) in d.Attachments)
|
||||
{
|
||||
_db.Attachments.Add(new Attachment
|
||||
|
||||
Reference in New Issue
Block a user