Merge outgoing photo echo attachments

This commit is contained in:
sevenhill
2026-07-05 08:58:38 +03:00
parent b518f22a09
commit 5d33320e3d
4 changed files with 355 additions and 3 deletions
@@ -51,6 +51,66 @@ public static class QMaxDatabaseCleanup
await db.Database.ExecuteSqlRawAsync("DROP TABLE IF EXISTS temp.LegacyWebFileMessageIds;");
}
public static async Task MergeOutgoingRemoteAttachmentEchoesAsync(QMaxDbContext db)
{
var connection = db.Database.GetDbConnection();
if (connection.State != System.Data.ConnectionState.Open)
{
await connection.OpenAsync();
}
await db.Database.ExecuteSqlRawAsync("DROP TABLE IF EXISTS temp.OutgoingAttachmentEchoMerges;");
await db.Database.ExecuteSqlRawAsync("""
CREATE TEMP TABLE OutgoingAttachmentEchoMerges (
LocalId TEXT NOT NULL PRIMARY KEY,
RemoteId TEXT NOT NULL,
ExternalId TEXT NULL,
RemoteUrl TEXT NULL
);
""");
await db.Database.ExecuteSqlRawAsync("""
INSERT OR IGNORE INTO OutgoingAttachmentEchoMerges (LocalId, RemoteId, ExternalId, RemoteUrl)
SELECT localAttachment.Id,
remoteAttachment.Id,
remoteAttachment.ExternalId,
remoteAttachment.RemoteUrl
FROM Messages AS message
INNER JOIN MessageAttachments AS localAttachment ON localAttachment.MessageId = message.Id
INNER JOIN MessageAttachments AS remoteAttachment ON remoteAttachment.MessageId = message.Id
WHERE message.Direction = 'Outgoing'
AND localAttachment.Id <> remoteAttachment.Id
AND localAttachment.SortOrder = remoteAttachment.SortOrder
AND localAttachment.Kind = remoteAttachment.Kind
AND (localAttachment.ExternalId IS NULL OR trim(localAttachment.ExternalId) = '')
AND (localAttachment.RemoteUrl IS NULL OR trim(localAttachment.RemoteUrl) = '')
AND localAttachment.StorageFileName NOT LIKE 'remote-%'
AND (
(remoteAttachment.ExternalId IS NOT NULL AND trim(remoteAttachment.ExternalId) <> '') OR
(remoteAttachment.RemoteUrl IS NOT NULL AND trim(remoteAttachment.RemoteUrl) <> '') OR
remoteAttachment.StorageFileName LIKE 'remote-%'
);
""");
await db.Database.ExecuteSqlRawAsync("""
DELETE FROM MessageAttachments
WHERE Id IN (SELECT RemoteId FROM OutgoingAttachmentEchoMerges);
""");
await db.Database.ExecuteSqlRawAsync("""
UPDATE MessageAttachments
SET ExternalId = COALESCE(NULLIF(ExternalId, ''), (
SELECT ExternalId
FROM OutgoingAttachmentEchoMerges
WHERE LocalId = MessageAttachments.Id
)),
RemoteUrl = COALESCE(NULLIF(RemoteUrl, ''), (
SELECT RemoteUrl
FROM OutgoingAttachmentEchoMerges
WHERE LocalId = MessageAttachments.Id
))
WHERE Id IN (SELECT LocalId FROM OutgoingAttachmentEchoMerges);
""");
await db.Database.ExecuteSqlRawAsync("DROP TABLE IF EXISTS temp.OutgoingAttachmentEchoMerges;");
}
public static async Task ClearUnreadCountsForLatestOutgoingChatsAsync(QMaxDbContext db)
{
var unreadChats = await db.Chats
@@ -90,4 +150,5 @@ public static class QMaxDatabaseCleanup
await db.SaveChangesAsync();
}
}
}
+1
View File
@@ -162,6 +162,7 @@ static async Task EnsureCompatibilitySchemaAsync(QMaxDbContext db)
await RemoveDuplicateMessageAttachmentsAsync(db);
await QMaxDatabaseCleanup.RemoveLegacyWebFileAttachmentEchoesAsync(db);
await QMaxDatabaseCleanup.MergeOutgoingRemoteAttachmentEchoesAsync(db);
await QMaxDatabaseCleanup.ClearUnreadCountsForLatestOutgoingChatsAsync(db);
await db.Database.ExecuteSqlRawAsync("DROP INDEX IF EXISTS IX_MessageAttachments_MessageId_ExternalId;");
await db.Database.ExecuteSqlRawAsync("""
@@ -133,7 +133,7 @@ public sealed class MaxBridgeSyncService(
update.LastMessageIsOutgoing != true &&
update.Messages.Count == 0 &&
HasListClockTime(update.LastMessageTimeText);
var lastKnownOutgoingMessages = !shouldLoadLastKnownMessage
var lastKnownOutgoingCandidates = !shouldLoadLastKnownMessage
? []
: await db.Messages
.AsNoTracking()
@@ -142,9 +142,11 @@ public sealed class MaxBridgeSyncService(
x.ChatId == chat.Id &&
x.DeletedAt == null &&
x.Direction == MessageDirection.Outgoing)
.OrderByDescending(x => x.SentAt)
.Take(5)
.ToListAsync(cancellationToken);
var lastKnownOutgoingMessages = lastKnownOutgoingCandidates
.OrderByDescending(x => x.SentAt)
.Take(5)
.ToList();
var lastKnownMessage = lastKnownOutgoingMessages
.Select(x => new LastKnownMessageSnapshot(
x.Direction,
@@ -605,6 +607,17 @@ public sealed class MaxBridgeSyncService(
foreach (var incomingAttachment in incomingAttachments)
{
if (TryMergeOutgoingRemoteEchoIntoLocalAttachment(
message,
incoming,
knownAttachments,
incomingAttachment,
out var echoChanged))
{
changed |= echoChanged;
continue;
}
if (HasAttachment(knownAttachments, incomingAttachment))
{
continue;
@@ -621,6 +634,54 @@ public sealed class MaxBridgeSyncService(
return new MessageMergeResult(changed, addedAttachments);
}
private static bool TryMergeOutgoingRemoteEchoIntoLocalAttachment(
Message message,
MaxMessageUpdate incoming,
IEnumerable<MessageAttachment> attachments,
MaxAttachmentUpdate incomingAttachment,
out bool changed)
{
changed = false;
if (message.Direction != MessageDirection.Outgoing || !incoming.IsOutgoing)
{
return false;
}
if (string.IsNullOrWhiteSpace(incomingAttachment.ExternalId) &&
string.IsNullOrWhiteSpace(incomingAttachment.RemoteUrl))
{
return false;
}
var kind = ResolveKind(incomingAttachment) ?? AttachmentKind.File;
var localUpload = attachments.FirstOrDefault(attachment =>
attachment.SortOrder == incomingAttachment.SortOrder &&
attachment.Kind == kind &&
string.IsNullOrWhiteSpace(attachment.ExternalId) &&
string.IsNullOrWhiteSpace(attachment.RemoteUrl));
if (localUpload is null)
{
return false;
}
if (!string.IsNullOrWhiteSpace(incomingAttachment.ExternalId) &&
!string.Equals(localUpload.ExternalId, incomingAttachment.ExternalId, StringComparison.Ordinal))
{
localUpload.ExternalId = incomingAttachment.ExternalId;
changed = true;
}
if (!string.IsNullOrWhiteSpace(incomingAttachment.RemoteUrl) &&
!string.Equals(localUpload.RemoteUrl, incomingAttachment.RemoteUrl, StringComparison.Ordinal))
{
localUpload.RemoteUrl = incomingAttachment.RemoteUrl;
changed = true;
}
return true;
}
private static IReadOnlyList<MaxAttachmentUpdate> DistinctAttachments(IReadOnlyList<MaxAttachmentUpdate> attachments)
{
if (attachments.Count <= 1)