Skip to content

Commit ec5f159

Browse files
committedMar 7, 2025
Fix delete message
- Create transaction for delete message - Fix updates in server
1 parent 4bb6b23 commit ec5f159

File tree

8 files changed

+181
-15
lines changed

8 files changed

+181
-15
lines changed
 

‎apple/InlineIOS/Chat/ComposeView.swift

-2
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,6 @@ class ComposeView: UIView, NSTextLayoutManagerDelegate,
580580
} catch {
581581
Log.shared.error("Failed to save photo", error: error)
582582
}
583-
print("Attachment items", self.attachmentItems)
584583

585584
for (index, (_, attachment)) in self.attachmentItems.enumerated() {
586585
_ = index == 0
@@ -595,7 +594,6 @@ class ComposeView: UIView, NSTextLayoutManagerDelegate,
595594
)
596595
)
597596
)
598-
print("Sent attachment", attachment)
599597
}
600598

601599
DispatchQueue.main.async { [weak self] in

‎apple/InlineIOS/Chat/UIMessageView.swift

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Auth
22
import ContextMenuAuxiliaryPreview
3+
import GRDB
34
import InlineKit
45
import Logger
56
import Nuke
@@ -683,10 +684,14 @@ extension UIMessageView: UIContextMenuInteractionDelegate, ContextMenuManagerDel
683684
attributes: .destructive
684685
) { _ in
685686
Task {
686-
try? await DataManager.shared.deleteMessage(
687-
messageId: self.message.messageId,
688-
chatId: self.message.chatId,
689-
peerId: self.message.peerId
687+
let _ = Transactions.shared.mutate(
688+
transaction: .deleteMessage(
689+
.init(
690+
messageIds: [self.message.messageId],
691+
peerId: self.message.peerId,
692+
chatId: self.message.chatId
693+
)
694+
)
690695
)
691696
}
692697
}

‎apple/InlineKit/Sources/InlineKit/RealtimeHelpers/RealtimeUpdates.swift

+6-5
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ extension InlineProtocol.UpdateComposeAction {
140140
default:
141141
nil
142142
}
143-
143+
144144
print("action: \(action)")
145145

146146
if let action {
@@ -161,10 +161,11 @@ extension InlineProtocol.UpdateDeleteMessages {
161161

162162
let prevChatLastMsgId = chat.lastMsgId
163163

164-
try Message
165-
.filter(ids: messageIds)
166-
.filter(Column("chatId") == chat.id)
167-
.deleteAll(db)
164+
for messageId in messageIds {
165+
try Message
166+
.filter(Column("messageId") == messageId && Column("chatId") == chat.id)
167+
.deleteAll(db)
168+
}
168169

169170
// Update last message
170171
for messageId in messageIds {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import Auth
2+
import Foundation
3+
import GRDB
4+
import InlineProtocol
5+
import Logger
6+
import MultipartFormDataKit
7+
import RealtimeAPI
8+
9+
public struct TransactionDeleteMessage: Transaction {
10+
// Properties
11+
var messageIds: [Int64]
12+
var peerId: Peer
13+
var chatId: Int64
14+
15+
// Config
16+
public var id = UUID().uuidString
17+
var config = TransactionConfig.default
18+
var date = Date()
19+
20+
public init(messageIds: [Int64], peerId: Peer, chatId: Int64) {
21+
self.messageIds = messageIds
22+
self.peerId = peerId
23+
self.chatId = chatId
24+
}
25+
26+
// Methods
27+
func optimistic() {
28+
Log.shared.debug("Optimistic delete message \(messageIds) \(peerId) \(chatId)")
29+
do {
30+
try AppDatabase.shared.dbWriter.write { db in
31+
let chat = try Chat.fetchOne(db, id: chatId)
32+
33+
let prevChatLastMsgId = chat?.lastMsgId
34+
35+
for messageId in messageIds {
36+
try Message.filter(Column("messageId") == messageId && Column("chatId") == chatId).deleteAll(db)
37+
}
38+
39+
// Update last message
40+
for messageId in messageIds {
41+
guard prevChatLastMsgId == messageId else { continue }
42+
43+
let previousMessage = try Message
44+
.filter(Column("chatId") == chat?.id)
45+
.order(Column("date").desc)
46+
.limit(1, offset: 1)
47+
.fetchOne(db)
48+
49+
var updatedChat = chat
50+
updatedChat?.lastMsgId = previousMessage?.messageId
51+
try updatedChat?.save(db)
52+
}
53+
}
54+
} catch {
55+
Log.shared.error("Failed to delete message \(error)")
56+
}
57+
58+
DispatchQueue.main.async {
59+
MessagesPublisher.shared.messagesDeleted(messageIds: messageIds, peer: peerId)
60+
}
61+
}
62+
63+
func execute() async throws -> [InlineProtocol.Update] {
64+
let result = try await Realtime.shared.invoke(
65+
.deleteMessages,
66+
input: .deleteMessages(DeleteMessagesInput.with {
67+
$0.peerID = peerId.toInputPeer()
68+
$0.messageIds = messageIds
69+
})
70+
)
71+
72+
guard case let .deleteMessages(response) = result else {
73+
throw DeleteMessageError.failed
74+
}
75+
76+
return response.updates
77+
}
78+
79+
func shouldRetryOnFail(error: Error) -> Bool {
80+
if let error = error as? RealtimeAPIError {
81+
switch error {
82+
case let .rpcError(_, _, code):
83+
switch code {
84+
case 400, 401:
85+
return false
86+
87+
default:
88+
return true
89+
}
90+
default:
91+
return true
92+
}
93+
}
94+
95+
return true
96+
}
97+
98+
func didSucceed(result: [InlineProtocol.Update]) async {
99+
await Realtime.shared.updates.applyBatch(updates: result)
100+
}
101+
102+
func didFail(error: Error?) async {
103+
Log.shared.error("Failed to delete message", error: error)
104+
105+
try? await AppDatabase.shared.dbWriter.write { db in
106+
var chat = try Chat.fetchOne(db, id: chatId)
107+
}
108+
109+
DispatchQueue.main.async {
110+
MessagesPublisher.shared.messagesDeleted(messageIds: messageIds, peer: peerId)
111+
}
112+
}
113+
114+
func rollback() async {
115+
let _ = try? await AppDatabase.shared.dbWriter.write { db in
116+
var chat = try Chat.fetchOne(db, id: chatId)
117+
118+
let prevChatLastMsgId = chat?.lastMsgId
119+
120+
for messageId in messageIds {
121+
try Message.filter(Column("messageId") == messageId && Column("chatId") == chatId).deleteAll(db)
122+
}
123+
124+
// Update last message
125+
for messageId in messageIds {
126+
guard prevChatLastMsgId == messageId else { continue }
127+
128+
let previousMessage = try Message
129+
.filter(Column("chatId") == chat?.id)
130+
.order(Column("date").desc)
131+
.limit(1, offset: 1)
132+
.fetchOne(db)
133+
134+
var updatedChat = chat
135+
updatedChat?.lastMsgId = previousMessage?.messageId
136+
try updatedChat?.save(db)
137+
138+
break
139+
}
140+
}
141+
}
142+
143+
enum DeleteMessageError: Error {
144+
case failed
145+
}
146+
}

‎apple/InlineKit/Sources/InlineKit/Transactions/Methods/SendMessage.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public struct TransactionSendMessage: Transaction {
183183
func shouldRetryOnFail(error: Error) -> Bool {
184184
if let error = error as? RealtimeAPIError {
185185
switch error {
186-
case let .rpcError(_, _, code):
186+
case let .rpcError(ec, msg, code):
187187
switch code {
188188
case 400, 401:
189189
return false

‎apple/InlineKit/Sources/InlineKit/Transactions/Transactions.swift

+11-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public class Transactions: @unchecked Sendable {
8484
await actor.cancel(transactionId: transactionId)
8585
}
8686
}
87-
87+
8888
public func clearAll() {
8989
Task {
9090
await actor.clearAll()
@@ -96,6 +96,7 @@ public class Transactions: @unchecked Sendable {
9696
public enum TransactionType: Codable {
9797
case sendMessage(TransactionSendMessage)
9898
case mockMessage(MockMessageTransaction)
99+
case deleteMessage(TransactionDeleteMessage)
99100

100101
var id: String {
101102
transaction.id
@@ -111,6 +112,8 @@ public enum TransactionType: Codable {
111112
t
112113
case let .mockMessage(t):
113114
t
115+
case let .deleteMessage(t):
116+
t
114117
}
115118
}
116119

@@ -140,6 +143,9 @@ public enum TransactionType: Codable {
140143
case let .mockMessage(transaction):
141144
try container.encode("mockMessage", forKey: .type)
142145
try container.encode(transaction, forKey: .transaction)
146+
case let .deleteMessage(transaction):
147+
try container.encode("deleteMessage", forKey: .type)
148+
try container.encode(transaction, forKey: .transaction)
143149
}
144150
}
145151

@@ -154,6 +160,9 @@ public enum TransactionType: Codable {
154160
case "mockMessage":
155161
let transaction = try container.decode(MockMessageTransaction.self, forKey: .transaction)
156162
self = .mockMessage(transaction)
163+
case "deleteMessage":
164+
let transaction = try container.decode(TransactionDeleteMessage.self, forKey: .transaction)
165+
self = .deleteMessage(transaction)
157166
default:
158167
throw DecodingError.dataCorruptedError(
159168
forKey: .type,
@@ -189,7 +198,7 @@ protocol Transaction: Codable, Sendable, Identifiable {
189198
func optimistic()
190199
/// Optionally apply additional changes in cache after success. For example mark an status as completed.
191200
func didSucceed(result: R) async
192-
201+
193202
func shouldRetryOnFail(error: Error) -> Bool
194203

195204
/// If execute fails, apply appropriate logic. By default it calls `rollback()` method

‎server/src/db/models/messages.ts

+2
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ async function deleteMessages(messageIds: bigint[], chatId: number) {
200200
)
201201
.returning()
202202

203+
console.log("deleted", deleted)
204+
203205
if (deleted.length === 0) {
204206
log.trace("messages not found", { messageIds, chatId })
205207
throw ModelError.MessageInvalid

‎server/src/functions/messages.deleteMessage.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@ export const deleteMessage = async (input: Input, context: FunctionContext): Pro
1818
const chatId = await ChatModel.getChatIdFromInputPeer(input.peer, context)
1919
await MessageModel.deleteMessages(input.messageIds, chatId)
2020

21+
const encodingForInputPeer: InputPeer =
22+
input.peer.type.oneofKind === "user" && BigInt(context.currentUserId) === input.peer.type.user.userId
23+
? input.peer
24+
: { type: { oneofKind: "user", user: { userId: BigInt(context.currentUserId) } } }
25+
2126
const update: Update = {
2227
update: {
2328
oneofKind: "deleteMessages",
2429
deleteMessages: {
2530
messageIds: input.messageIds.map((id) => BigInt(id)),
26-
peerId: Encoders.peerFromInputPeer({ inputPeer: input.peer, currentUserId: context.currentUserId }),
31+
peerId: Encoders.peerFromInputPeer({ inputPeer: encodingForInputPeer, currentUserId: context.currentUserId }),
2732
},
2833
},
2934
}

0 commit comments

Comments
 (0)