fix(channels): sync preserve locally cleared dialogs

This commit is contained in:
iamxvbaba 2026-07-29 16:19:04 +08:00
parent e22fac1c3f
commit 73cf6a8184
50 changed files with 1525 additions and 327 deletions

View file

@ -2,6 +2,7 @@ package postgres
import (
"context"
"errors"
"fmt"
"github.com/jackc/pgx/v5"
"sort"
@ -223,19 +224,68 @@ func (s *ChannelStore) DeleteChannelHistory(ctx context.Context, req domain.Dele
}
if !req.ForEveryone {
appliedMinID := maxInt(member.AvailableMinID, maxID)
changed := appliedMinID > member.AvailableMinID
anchorID := member.HistoryClearAnchorID
anchorDate := member.HistoryClearAnchorDate
if changed {
anchorID = appliedMinID
anchorDate = req.Date
var messageDate int
err := tx.QueryRow(ctx, `
SELECT message_date
FROM channel_messages
WHERE channel_id = $1 AND id = $2`, req.ChannelID, appliedMinID).Scan(&messageDate)
switch {
case err == nil && messageDate > 0:
anchorDate = messageDate
case errors.Is(err, pgx.ErrNoRows):
// max_id may name an already-pruned/hole id. The owner-local
// marker is still a valid monotonic boundary; retain request
// time so its dialog projection remains loadable.
case err != nil:
return domain.DeleteChannelHistoryResult{}, fmt.Errorf("select channel clear anchor date: %w", err)
}
if anchorDate <= 0 {
anchorDate = channel.Date
}
}
topID, topDate, err := visibleChannelTopAfter(ctx, tx, req.ChannelID, appliedMinID, channel.Date)
if err != nil {
return domain.DeleteChannelHistoryResult{}, err
}
if topID == 0 && anchorID > 0 && anchorID == appliedMinID {
topID = anchorID
topDate = anchorDate
}
if _, err := tx.Exec(ctx, `
UPDATE channel_members
SET available_min_id = GREATEST(available_min_id, $3),
history_clear_anchor_id = CASE WHEN available_min_id < $3 THEN $3 ELSE history_clear_anchor_id END,
history_clear_anchor_date = CASE WHEN available_min_id < $3 THEN $4 ELSE history_clear_anchor_date END,
read_inbox_max_id = GREATEST(read_inbox_max_id, $3),
unread_mark = false,
updated_at = now()
WHERE channel_id = $1 AND user_id = $2`, req.ChannelID, req.UserID, appliedMinID); err != nil {
WHERE channel_id = $1 AND user_id = $2`, req.ChannelID, req.UserID, appliedMinID, anchorDate); err != nil {
return domain.DeleteChannelHistoryResult{}, fmt.Errorf("update channel local clear member: %w", err)
}
if changed {
member.AvailableMinID = appliedMinID
member.HistoryClearAnchorID = anchorID
member.HistoryClearAnchorDate = anchorDate
if err := upsertUserChannelMemberIndexTx(ctx, tx, channel, member); err != nil {
return domain.DeleteChannelHistoryResult{}, err
}
if _, err := tx.Exec(ctx, `
UPDATE user_channel_member_index
SET available_min_id = $3,
history_clear_anchor_id = $3,
history_clear_updated_at = $4,
updated_at = now()
WHERE user_id = $1 AND channel_id = $2`,
req.UserID, req.ChannelID, appliedMinID, req.Date); err != nil {
return domain.DeleteChannelHistoryResult{}, fmt.Errorf("update channel local clear recovery index: %w", err)
}
}
if err := deleteChannelUnreadMentionsUpToTx(ctx, tx, req.UserID, req.ChannelID, appliedMinID); err != nil {
return domain.DeleteChannelHistoryResult{}, err
}
@ -259,7 +309,17 @@ ON CONFLICT (user_id, channel_id) DO UPDATE SET
return domain.DeleteChannelHistoryResult{}, fmt.Errorf("commit local clear channel history: %w", err)
}
committed = true
return domain.DeleteChannelHistoryResult{Channel: channel, AvailableMinID: appliedMinID}, nil
if s.memberCacheActive(s.db) {
s.memberCache.delete(req.ChannelID, req.UserID)
}
if s.dialogCacheActive(s.db) {
s.dialogCache.delete(req.UserID, req.ChannelID)
}
return domain.DeleteChannelHistoryResult{
Channel: channel,
AvailableMinID: appliedMinID,
AvailableMinChanged: changed,
}, nil
}
if !canDeleteAnyChannelMessage(member) {
return domain.DeleteChannelHistoryResult{}, domain.ErrChannelAdminRequired