Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .agent-docs/2026-01-17-unarchive-chat-on-receive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Unarchive Chat On Incoming Message

## Plan
- [x] Inspect current dialog/archive handling across server, protocol, and clients.
- [x] Add server-side unarchiveIfNeeded module and call it during message send with user-bucket persistence.
- [x] Add dialog-archive update to protos, regenerate code, and wire server sync + client update application.
- [x] Remove client-side unarchive-on-receive logic and add tests for server unarchive behavior.
30 changes: 0 additions & 30 deletions apple/InlineKit/Sources/InlineKit/Models/Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -399,12 +399,6 @@ public extension Message {
// Save the message
let message = try saveAndFetch(db, onConflict: .ignore)

// Handle unarchiving for incoming messages
if !isExisting, out != true {
// TODO: move this out of here
try unarchiveIncomingMessagesChat(db, peerId: peerId)
}

// Publish changes if needed
if publishChanges {
let message = self // Create an immutable copy
Expand All @@ -425,30 +419,6 @@ public extension Message {
return message
}

func unarchiveIncomingMessagesChat(
_ db: Database,
peerId: Peer
) throws {
if let dialog = try Dialog.fetchOne(db, id: Dialog.getDialogId(peerId: peerId)),
dialog.archived == true
{
var updatedDialog = dialog
updatedDialog.archived = false
try updatedDialog.save(db, onConflict: .replace)

// Schedule API update after transaction
let peer = peerId
db.afterNextTransaction { _ in
Task {
try? await ApiClient.shared.updateDialog(
peerId: peer,
pinned: nil,
archived: false
)
}
}
}
}
}

public extension ApiMessage {
Expand Down
17 changes: 17 additions & 0 deletions apple/InlineKit/Sources/InlineKit/RealtimeAPI/Updates.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ public actor UpdatesEngine: Sendable {
case let .updateReadMaxID(updateReadMaxID):
try updateReadMaxID.apply(db)

case let .dialogArchived(dialogArchived):
try dialogArchived.apply(db)

default:
break
}
Expand Down Expand Up @@ -751,6 +754,20 @@ extension InlineProtocol.UpdateMarkAsUnread {
}
}

extension InlineProtocol.UpdateDialogArchived {
func apply(_ db: Database) throws {
Log.shared.debug("update dialog archived for peer \(peerID.toPeer()) archived: \(archived)")

if var dialog = try Dialog.get(peerId: peerID.toPeer()).fetchOne(db) {
dialog.archived = archived
try dialog.update(db)
Log.shared.debug("Updated dialog archived to \(archived)")
} else {
Log.shared.warning("Could not find dialog for peer \(peerID.toPeer()) to update archived state")
}
}
}

extension InlineProtocol.UpdateReadMaxId {
func apply(_ db: Database) throws {
Log.shared.debug(
Expand Down
1 change: 0 additions & 1 deletion apple/InlineKit/Sources/InlineProtocol/client.pb.swift

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

95 changes: 95 additions & 0 deletions apple/InlineKit/Sources/InlineProtocol/core.pb.swift

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion apple/InlineKit/Sources/RealtimeV2/Sync/Sync.swift
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ actor Sync {
.space(id: payload.member.spaceID)
case let .joinSpace(payload):
.space(id: payload.space.id)
case .updateUserStatus, .updateUserSettings, .newChat:
case .updateUserStatus, .updateUserSettings, .newChat, .dialogArchived:
.user
case let .participantAdd(payload):
.chat(peer: .with { $0.chat = .with { $0.chatID = payload.chatID } })
Expand Down Expand Up @@ -506,6 +506,8 @@ actor BucketActor {
true
case .spaceMemberAdd:
true
case .dialogArchived:
true
case .newMessage, .editMessage, .messageAttachment:
enableMessageUpdates
default:
Expand Down
10 changes: 10 additions & 0 deletions proto/core.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,7 @@ message Update {
UpdateSpaceHasNewUpdates space_has_new_updates = 26;
UpdateSpaceMemberUpdate space_member_update = 27;
UpdateChatVisibility chat_visibility = 28;
UpdateDialogArchived dialog_archived = 29;
}
}

Expand Down Expand Up @@ -1206,6 +1207,15 @@ message UpdateMarkAsUnread {
bool unread_mark = 2;
}

// Update when a dialog is archived or unarchived
message UpdateDialogArchived {
// Peer ID of the dialog that changed
Peer peer_id = 1;

// Whether it's archived (true) or unarchived (false)
bool archived = 2;
}

// Update when a new chat is created either in space or a private chat
message UpdateNewChat {
// Chat
Expand Down
7 changes: 7 additions & 0 deletions proto/server.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ message ServerUpdate {
// User bucket updates
ServerUserUpdateSpaceMemberDelete user_space_member_delete = 10;
ServerUserUpdateChatParticipantDelete user_chat_participant_delete = 11;
ServerUserUpdateDialogArchived user_dialog_archived = 14;
}
}

Expand Down Expand Up @@ -88,3 +89,9 @@ message ServerUserUpdateSpaceMemberDelete { int64 space_id = 1; }

// Update for a user when they were removed from a chat
message ServerUserUpdateChatParticipantDelete { int64 chat_id = 1; }

// Update for a user when a dialog is archived or unarchived
message ServerUserUpdateDialogArchived {
Peer peer_id = 1;
bool archived = 2;
}
Loading
Loading