fix(chat): ensure messages ordering on model append/prepend
fixes: #8466
This commit is contained in:
parent
3acf7dbebf
commit
7ede3389ff
|
@ -84,7 +84,7 @@ QtObject:
|
||||||
read = getCount
|
read = getCount
|
||||||
notify = countChanged
|
notify = countChanged
|
||||||
|
|
||||||
method rowCount(self: Model, index: QModelIndex = nil): int =
|
method rowCount*(self: Model, index: QModelIndex = nil): int =
|
||||||
return self.items.len
|
return self.items.len
|
||||||
|
|
||||||
method roleNames(self: Model): Table[int, string] =
|
method roleNames(self: Model): Table[int, string] =
|
||||||
|
@ -252,45 +252,47 @@ QtObject:
|
||||||
elif clock == self.items[i].clock: # break ties by message id
|
elif clock == self.items[i].clock: # break ties by message id
|
||||||
if id > self.items[i].id:
|
if id > self.items[i].id:
|
||||||
return i
|
return i
|
||||||
return 0
|
return self.items.len
|
||||||
|
|
||||||
proc filterExistingItems(self: Model, items: seq[Item]): seq[Item] =
|
proc filterExistingItems(self: Model, items: seq[Item]): seq[Item] =
|
||||||
for item in items:
|
for item in items:
|
||||||
if(self.findIndexForMessageId(item.id) < 0):
|
if(self.findIndexForMessageId(item.id) < 0):
|
||||||
result &= item
|
result &= item
|
||||||
|
|
||||||
proc prependItems*(self: Model, items: seq[Item]) =
|
proc insertItemBasedOnClock*(self: Model, item: Item) =
|
||||||
let itemsToAppend = self.filterExistingItems(items)
|
if(self.findIndexForMessageId(item.id) != -1):
|
||||||
if(itemsToAppend.len == 0):
|
|
||||||
return
|
return
|
||||||
|
|
||||||
let parentModelIndex = newQModelIndex()
|
let parentModelIndex = newQModelIndex()
|
||||||
defer: parentModelIndex.delete
|
defer: parentModelIndex.delete
|
||||||
|
|
||||||
let first = 0
|
let position = self.findIndexBasedOnClockToInsertTo(item.clock, item.id)
|
||||||
let last = itemsToAppend.len - 1
|
|
||||||
self.beginInsertRows(parentModelIndex, first, last)
|
self.beginInsertRows(parentModelIndex, position, position)
|
||||||
self.items = itemsToAppend & self.items
|
self.items.insert(item, position)
|
||||||
self.endInsertRows()
|
self.endInsertRows()
|
||||||
|
|
||||||
|
if position > 0:
|
||||||
|
self.updateItemAtIndex(position - 1)
|
||||||
|
if position + 1 < self.items.len:
|
||||||
|
self.updateItemAtIndex(position + 1)
|
||||||
self.countChanged()
|
self.countChanged()
|
||||||
|
|
||||||
|
proc prependItems*(self: Model, items: seq[Item]) =
|
||||||
|
let itemsToAppend = self.filterExistingItems(items)
|
||||||
|
if(itemsToAppend.len == 0):
|
||||||
|
return
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
self.insertItemBasedOnClock(item)
|
||||||
|
|
||||||
proc appendItems*(self: Model, items: seq[Item]) =
|
proc appendItems*(self: Model, items: seq[Item]) =
|
||||||
let itemsToAppend = self.filterExistingItems(items)
|
let itemsToAppend = self.filterExistingItems(items)
|
||||||
if(itemsToAppend.len == 0):
|
if(itemsToAppend.len == 0):
|
||||||
return
|
return
|
||||||
|
|
||||||
let parentModelIndex = newQModelIndex()
|
for item in items:
|
||||||
defer: parentModelIndex.delete
|
self.insertItemBasedOnClock(item)
|
||||||
|
|
||||||
let first = self.items.len
|
|
||||||
let last = first + itemsToAppend.len - 1
|
|
||||||
self.beginInsertRows(parentModelIndex, first, last)
|
|
||||||
self.items.add(itemsToAppend)
|
|
||||||
self.endInsertRows()
|
|
||||||
|
|
||||||
if first > 0:
|
|
||||||
self.updateItemAtIndex(first - 1)
|
|
||||||
self.countChanged()
|
|
||||||
|
|
||||||
proc appendItem*(self: Model, item: Item) =
|
proc appendItem*(self: Model, item: Item) =
|
||||||
if(self.findIndexForMessageId(item.id) != -1):
|
if(self.findIndexForMessageId(item.id) != -1):
|
||||||
|
@ -324,25 +326,6 @@ QtObject:
|
||||||
self.updateItemAtIndex(1)
|
self.updateItemAtIndex(1)
|
||||||
self.countChanged()
|
self.countChanged()
|
||||||
|
|
||||||
proc insertItemBasedOnClock*(self: Model, item: Item) =
|
|
||||||
if(self.findIndexForMessageId(item.id) != -1):
|
|
||||||
return
|
|
||||||
|
|
||||||
let parentModelIndex = newQModelIndex()
|
|
||||||
defer: parentModelIndex.delete
|
|
||||||
|
|
||||||
let position = self.findIndexBasedOnClockToInsertTo(item.clock, item.id)
|
|
||||||
|
|
||||||
self.beginInsertRows(parentModelIndex, position, position)
|
|
||||||
self.items.insert(item, position)
|
|
||||||
self.endInsertRows()
|
|
||||||
|
|
||||||
if position > 0:
|
|
||||||
self.updateItemAtIndex(position - 1)
|
|
||||||
if position + 1 < self.items.len:
|
|
||||||
self.updateItemAtIndex(position + 1)
|
|
||||||
self.countChanged()
|
|
||||||
|
|
||||||
proc replyDeleted*(self: Model, messageIndex: int) {.signal.}
|
proc replyDeleted*(self: Model, messageIndex: int) {.signal.}
|
||||||
|
|
||||||
proc updateMessagesWithResponseTo(self: Model, messageId: string) =
|
proc updateMessagesWithResponseTo(self: Model, messageId: string) =
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import ../../../src/app_service/common/types
|
||||||
|
import ../../../src/app_service/service/contacts/dto/contacts
|
||||||
|
import ../../../src/app_service/service/message/dto/message
|
||||||
|
|
||||||
|
import ../../../src/app/modules/shared_models/message_model
|
||||||
|
import ../../../src/app/modules/shared_models/message_item
|
||||||
|
import ../../../src/app/modules/shared_models/message_transaction_parameters_item
|
||||||
|
|
||||||
|
proc createTestMessageItem(id: string, clock: int64): Item =
|
||||||
|
return initItem(
|
||||||
|
id = id,
|
||||||
|
communityId = "",
|
||||||
|
responseToMessageWithId = "",
|
||||||
|
senderId = "",
|
||||||
|
senderDisplayName = "",
|
||||||
|
senderOptionalName = "",
|
||||||
|
senderIcon = "",
|
||||||
|
amISender = false,
|
||||||
|
senderIsAdded = false,
|
||||||
|
outgoingStatus = "",
|
||||||
|
text = "",
|
||||||
|
image = "",
|
||||||
|
messageContainsMentions = false,
|
||||||
|
seen = true,
|
||||||
|
timestamp = 0,
|
||||||
|
clock = clock,
|
||||||
|
ContentType.NewMessagesMarker,
|
||||||
|
messageType = -1,
|
||||||
|
contactRequestState = 0,
|
||||||
|
sticker = "",
|
||||||
|
stickerPack = -1,
|
||||||
|
links = @[],
|
||||||
|
transactionParameters = newTransactionParametersItem("","","","","","",-1,""),
|
||||||
|
mentionedUsersPks = @[],
|
||||||
|
senderTrustStatus = TrustStatus.Unknown,
|
||||||
|
senderEnsVerified = false,
|
||||||
|
discordMessage = DiscordMessage(),
|
||||||
|
resendError = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
let message1 = createTestMessageItem("0xa", 1)
|
||||||
|
let message2 = createTestMessageItem("0xb", 2)
|
||||||
|
let message3 = createTestMessageItem("0xc", 3)
|
||||||
|
let message4 = createTestMessageItem("0xd", 3)
|
||||||
|
let message5 = createTestMessageItem("0xe", 4)
|
||||||
|
|
||||||
|
template checkOrder(model: Model) =
|
||||||
|
require(model.items.len == 5)
|
||||||
|
check(model.items[0].id == message5.id)
|
||||||
|
check(model.items[1].id == message4.id)
|
||||||
|
check(model.items[2].id == message3.id)
|
||||||
|
check(model.items[3].id == message2.id)
|
||||||
|
check(model.items[4].id == message1.id)
|
||||||
|
|
||||||
|
suite "empty model":
|
||||||
|
let model = newModel()
|
||||||
|
|
||||||
|
test "initial size":
|
||||||
|
require(model.rowCount() == 0)
|
||||||
|
|
||||||
|
# newest messages should be first, break ties by message id
|
||||||
|
suite "inserting new messages":
|
||||||
|
setup:
|
||||||
|
let model = newModel()
|
||||||
|
|
||||||
|
test "insert same message twice":
|
||||||
|
model.insertItemBasedOnClock(message1)
|
||||||
|
check(model.rowCount() == 1)
|
||||||
|
model.insertItemBasedOnClock(message1)
|
||||||
|
check(model.rowCount() == 1)
|
||||||
|
|
||||||
|
test "insert in order":
|
||||||
|
model.insertItemBasedOnClock(message1)
|
||||||
|
check(model.rowCount() == 1)
|
||||||
|
model.insertItemBasedOnClock(message2)
|
||||||
|
check(model.rowCount() == 2)
|
||||||
|
model.insertItemBasedOnClock(message3)
|
||||||
|
check(model.rowCount() == 3)
|
||||||
|
model.insertItemBasedOnClock(message4)
|
||||||
|
check(model.rowCount() == 4)
|
||||||
|
model.insertItemBasedOnClock(message5)
|
||||||
|
check(model.rowCount() == 5)
|
||||||
|
checkOrder(model)
|
||||||
|
|
||||||
|
test "insert out of order":
|
||||||
|
model.insertItemBasedOnClock(message5)
|
||||||
|
check(model.rowCount() == 1)
|
||||||
|
model.insertItemBasedOnClock(message4)
|
||||||
|
check(model.rowCount() == 2)
|
||||||
|
model.insertItemBasedOnClock(message3)
|
||||||
|
check(model.rowCount() == 3)
|
||||||
|
model.insertItemBasedOnClock(message2)
|
||||||
|
check(model.rowCount() == 4)
|
||||||
|
model.insertItemBasedOnClock(message1)
|
||||||
|
check(model.rowCount() == 5)
|
||||||
|
checkOrder(model)
|
||||||
|
|
||||||
|
test "insert out of order (randomly)":
|
||||||
|
model.insertItemBasedOnClock(message3)
|
||||||
|
check(model.rowCount() == 1)
|
||||||
|
model.insertItemBasedOnClock(message1)
|
||||||
|
check(model.rowCount() == 2)
|
||||||
|
model.insertItemBasedOnClock(message4)
|
||||||
|
check(model.rowCount() == 3)
|
||||||
|
model.insertItemBasedOnClock(message2)
|
||||||
|
check(model.rowCount() == 4)
|
||||||
|
model.insertItemBasedOnClock(message5)
|
||||||
|
check(model.rowCount() == 5)
|
||||||
|
checkOrder(model)
|
||||||
|
|
||||||
|
# assumption: each append sequence is already sorted
|
||||||
|
suite "appending new messages":
|
||||||
|
setup:
|
||||||
|
let model = newModel()
|
||||||
|
|
||||||
|
test "append empty model":
|
||||||
|
model.appendItems(@[message5,
|
||||||
|
message4,
|
||||||
|
message3,
|
||||||
|
message2,
|
||||||
|
message1])
|
||||||
|
checkOrder(model)
|
||||||
|
|
||||||
|
test "append to model with only newer messages":
|
||||||
|
model.insertItemBasedOnClock(message5)
|
||||||
|
model.insertItemBasedOnClock(message4)
|
||||||
|
model.appendItems(@[message3,
|
||||||
|
message2,
|
||||||
|
message1])
|
||||||
|
checkOrder(model)
|
||||||
|
|
||||||
|
test "append to model with newer and older messages":
|
||||||
|
model.insertItemBasedOnClock(message5)
|
||||||
|
model.insertItemBasedOnClock(message1)
|
||||||
|
model.appendItems(@[message4,
|
||||||
|
message3,
|
||||||
|
message2])
|
||||||
|
checkOrder(model)
|
||||||
|
|
||||||
|
test "append to model with newer and older messages and some in between":
|
||||||
|
model.insertItemBasedOnClock(message5)
|
||||||
|
model.insertItemBasedOnClock(message1)
|
||||||
|
model.insertItemBasedOnClock(message3) # in between
|
||||||
|
model.appendItems(@[message4,
|
||||||
|
message2])
|
||||||
|
checkOrder(model)
|
Loading…
Reference in New Issue