feat(chat/messages): implement new messages marker
closes: #8572 iterates: #7488
This commit is contained in:
parent
27b8924c6d
commit
87674064d0
|
@ -20,6 +20,7 @@ QtObject:
|
||||||
position: int
|
position: int
|
||||||
isUntrustworthy: bool
|
isUntrustworthy: bool
|
||||||
isContact: bool
|
isContact: bool
|
||||||
|
active: bool
|
||||||
|
|
||||||
proc delete*(self: ChatDetails) =
|
proc delete*(self: ChatDetails) =
|
||||||
self.QObject.delete
|
self.QObject.delete
|
||||||
|
@ -47,6 +48,7 @@ QtObject:
|
||||||
self.position = position
|
self.position = position
|
||||||
self.isUntrustworthy = isUntrustworthy
|
self.isUntrustworthy = isUntrustworthy
|
||||||
self.isContact = isContact
|
self.isContact = isContact
|
||||||
|
self.active = false
|
||||||
|
|
||||||
proc getId(self: ChatDetails): string {.slot.} =
|
proc getId(self: ChatDetails): string {.slot.} =
|
||||||
return self.id
|
return self.id
|
||||||
|
@ -188,3 +190,14 @@ QtObject:
|
||||||
proc setIsUntrustworthy*(self: ChatDetails, value: bool) = # this is not a slot
|
proc setIsUntrustworthy*(self: ChatDetails, value: bool) = # this is not a slot
|
||||||
self.isUntrustworthy = value
|
self.isUntrustworthy = value
|
||||||
self.isUntrustworthyChanged()
|
self.isUntrustworthyChanged()
|
||||||
|
|
||||||
|
proc activeChanged(self: ChatDetails) {.signal.}
|
||||||
|
proc isActive(self: ChatDetails): bool {.slot.} =
|
||||||
|
return self.active
|
||||||
|
QtProperty[bool] active:
|
||||||
|
read = isActive
|
||||||
|
notify = activeChanged
|
||||||
|
|
||||||
|
proc setActive*(self: ChatDetails, value: bool) =
|
||||||
|
self.active = value
|
||||||
|
self.activeChanged()
|
||||||
|
|
|
@ -117,3 +117,9 @@ method downloadMessages*(self: AccessInterface, filePath: string) =
|
||||||
|
|
||||||
method onMutualContactChanged*(self: AccessInterface) {.base.} =
|
method onMutualContactChanged*(self: AccessInterface) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method onMadeActive*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method onMadeInactive*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
|
@ -121,6 +121,12 @@ proc init*(self: Controller) =
|
||||||
return
|
return
|
||||||
self.delegate.toggleReactionFromOthers(args.messageId, args.emojiId, args.reactionId, args.reactionFrom)
|
self.delegate.toggleReactionFromOthers(args.messageId, args.emojiId, args.reactionId, args.reactionFrom)
|
||||||
|
|
||||||
|
self.events.on(SIGNAL_MESSAGES_MARKED_AS_READ) do(e: Args):
|
||||||
|
let args = MessagesMarkedAsReadArgs(e)
|
||||||
|
if(self.chatId != args.chatId):
|
||||||
|
return
|
||||||
|
self.delegate.markAllMessagesRead()
|
||||||
|
|
||||||
self.events.on(SIGNAL_CONTACT_NICKNAME_CHANGED) do(e: Args):
|
self.events.on(SIGNAL_CONTACT_NICKNAME_CHANGED) do(e: Args):
|
||||||
var args = ContactArgs(e)
|
var args = ContactArgs(e)
|
||||||
self.delegate.updateContactDetails(args.contactId)
|
self.delegate.updateContactDetails(args.contactId)
|
||||||
|
@ -265,6 +271,9 @@ proc setSearchedMessageId*(self: Controller, searchedMessageId: string) =
|
||||||
proc clearSearchedMessageId*(self: Controller) =
|
proc clearSearchedMessageId*(self: Controller) =
|
||||||
self.setSearchedMessageId("")
|
self.setSearchedMessageId("")
|
||||||
|
|
||||||
|
proc getFirstUnseenMessageId*(self: Controller): string =
|
||||||
|
self.messageService.getFirstUnseenMessageIdFor(self.chatId)
|
||||||
|
|
||||||
proc getLoadingMessagesPerPageFactor*(self: Controller): int =
|
proc getLoadingMessagesPerPageFactor*(self: Controller): int =
|
||||||
return self.loadingMessagesPerPageFactor
|
return self.loadingMessagesPerPageFactor
|
||||||
|
|
||||||
|
|
|
@ -151,3 +151,13 @@ method onMailserverSynced*(self: AccessInterface, syncedFrom: int64) =
|
||||||
|
|
||||||
method resendChatMessage*(self: AccessInterface, messageId: string): string =
|
method resendChatMessage*(self: AccessInterface, messageId: string): string =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method resetNewMessagesMarker*(self: AccessInterface) =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method scrollToNewMessagesMarker*(self: AccessInterface) =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method markAllMessagesRead*(self: AccessInterface) =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
|
|
@ -145,18 +145,18 @@ proc createChatIdentifierItem(self: Module): Item =
|
||||||
resendError = ""
|
resendError = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
proc checkIfMessageLoadedAndScrollToItIfItIs(self: Module): bool =
|
proc checkIfMessageLoadedAndScrollToItIfItIs(self: Module) =
|
||||||
let searchedMessageId = self.controller.getSearchedMessageId()
|
let searchedMessageId = self.controller.getSearchedMessageId()
|
||||||
if(searchedMessageId.len > 0):
|
if(searchedMessageId.len > 0):
|
||||||
self.view.emitScrollMessagesUpSignal()
|
|
||||||
let index = self.view.model().findIndexForMessageId(searchedMessageId)
|
let index = self.view.model().findIndexForMessageId(searchedMessageId)
|
||||||
self.controller.increaseLoadingMessagesPerPageFactor()
|
|
||||||
if(index != -1):
|
if(index != -1):
|
||||||
self.controller.clearSearchedMessageId()
|
self.controller.clearSearchedMessageId()
|
||||||
self.controller.resetLoadingMessagesPerPageFactor()
|
self.controller.resetLoadingMessagesPerPageFactor()
|
||||||
self.view.emitScrollToMessageSignal(index)
|
self.view.emitScrollToMessageSignal(index)
|
||||||
return true
|
self.view.setMessageSearchOngoing(false)
|
||||||
return false
|
else:
|
||||||
|
self.controller.increaseLoadingMessagesPerPageFactor()
|
||||||
|
self.loadMoreMessages()
|
||||||
|
|
||||||
method currentUserWalletContainsAddress(self: Module, address: string): bool =
|
method currentUserWalletContainsAddress(self: Module, address: string): bool =
|
||||||
if (address.len == 0):
|
if (address.len == 0):
|
||||||
|
@ -259,12 +259,12 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
|
||||||
self.view.model().removeItem(CHAT_IDENTIFIER_MESSAGE_ID)
|
self.view.model().removeItem(CHAT_IDENTIFIER_MESSAGE_ID)
|
||||||
# Add new loaded messages
|
# Add new loaded messages
|
||||||
self.view.model().appendItems(viewItems)
|
self.view.model().appendItems(viewItems)
|
||||||
|
self.view.model().resetNewMessagesMarker()
|
||||||
|
|
||||||
if(not self.view.getInitialMessagesLoaded()):
|
# check if this loading was caused by the click on a messages from the app search result
|
||||||
self.view.initialMessagesAreLoaded()
|
self.checkIfMessageLoadedAndScrollToItIfItIs()
|
||||||
|
|
||||||
# check if this loading was caused by the click on a messages from the app search result
|
self.view.initialMessagesAreLoaded()
|
||||||
discard self.checkIfMessageLoadedAndScrollToItIfItIs()
|
|
||||||
|
|
||||||
method messageAdded*(self: Module, message: MessageDto) =
|
method messageAdded*(self: Module, message: MessageDto) =
|
||||||
let sender = self.controller.getContactDetails(message.`from`)
|
let sender = self.controller.getContactDetails(message.`from`)
|
||||||
|
@ -328,9 +328,12 @@ method messageAdded*(self: Module, message: MessageDto) =
|
||||||
|
|
||||||
self.view.model().insertItemBasedOnClock(item)
|
self.view.model().insertItemBasedOnClock(item)
|
||||||
|
|
||||||
|
method removeNewMessagesMarker*(self: Module)
|
||||||
|
|
||||||
method onSendingMessageSuccess*(self: Module, message: MessageDto) =
|
method onSendingMessageSuccess*(self: Module, message: MessageDto) =
|
||||||
self.messageAdded(message)
|
self.messageAdded(message)
|
||||||
self.view.emitSendingMessageSuccessSignal()
|
self.view.emitSendingMessageSuccessSignal()
|
||||||
|
self.removeNewMessagesMarker()
|
||||||
|
|
||||||
method onSendingMessageError*(self: Module) =
|
method onSendingMessageError*(self: Module) =
|
||||||
self.view.emitSendingMessageErrorSignal()
|
self.view.emitSendingMessageErrorSignal()
|
||||||
|
@ -524,9 +527,16 @@ method switchToMessage*(self: Module, messageId: string) =
|
||||||
self.controller.setSearchedMessageId(messageId)
|
self.controller.setSearchedMessageId(messageId)
|
||||||
|
|
||||||
method scrollToMessage*(self: Module, messageId: string) =
|
method scrollToMessage*(self: Module, messageId: string) =
|
||||||
|
if(messageId == ""):
|
||||||
|
return
|
||||||
|
|
||||||
|
let scrollAlreadyOngoing = len(self.controller.getSearchedMessageId()) > 0
|
||||||
|
if(scrollAlreadyOngoing):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.view.setMessageSearchOngoing(true)
|
||||||
self.controller.setSearchedMessageId(messageId)
|
self.controller.setSearchedMessageId(messageId)
|
||||||
if(not self.checkIfMessageLoadedAndScrollToItIfItIs()):
|
self.checkIfMessageLoadedAndScrollToItIfItIs()
|
||||||
self.loadMoreMessages()
|
|
||||||
|
|
||||||
method requestMoreMessages*(self: Module) =
|
method requestMoreMessages*(self: Module) =
|
||||||
self.controller.requestMoreMessages()
|
self.controller.requestMoreMessages()
|
||||||
|
@ -611,3 +621,18 @@ method onMailserverSynced*(self: Module, syncedFrom: int64) =
|
||||||
|
|
||||||
method resendChatMessage*(self: Module, messageId: string): string =
|
method resendChatMessage*(self: Module, messageId: string): string =
|
||||||
return self.controller.resendChatMessage(messageId)
|
return self.controller.resendChatMessage(messageId)
|
||||||
|
|
||||||
|
method resetNewMessagesMarker*(self: Module) =
|
||||||
|
self.view.model().setFirstUnseenMessageId(self.controller.getFirstUnseenMessageId())
|
||||||
|
self.view.model().resetNewMessagesMarker()
|
||||||
|
|
||||||
|
method removeNewMessagesMarker*(self: Module) =
|
||||||
|
self.view.model().setFirstUnseenMessageId("")
|
||||||
|
self.view.model().resetNewMessagesMarker()
|
||||||
|
|
||||||
|
method scrollToNewMessagesMarker*(self: Module) =
|
||||||
|
self.scrollToMessage(self.view.model().getFirstUnseenMessageId())
|
||||||
|
|
||||||
|
method markAllMessagesRead*(self: Module) =
|
||||||
|
self.view.model().markAllAsSeen()
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ QtObject:
|
||||||
modelVariant: QVariant
|
modelVariant: QVariant
|
||||||
initialMessagesLoaded: bool
|
initialMessagesLoaded: bool
|
||||||
loadingHistoryMessagesInProgress: bool
|
loadingHistoryMessagesInProgress: bool
|
||||||
|
messageSearchOngoing: bool
|
||||||
|
|
||||||
proc delete*(self: View) =
|
proc delete*(self: View) =
|
||||||
self.model.delete
|
self.model.delete
|
||||||
|
@ -24,6 +25,7 @@ QtObject:
|
||||||
result.model = newModel()
|
result.model = newModel()
|
||||||
result.modelVariant = newQVariant(result.model)
|
result.modelVariant = newQVariant(result.model)
|
||||||
result.initialMessagesLoaded = false
|
result.initialMessagesLoaded = false
|
||||||
|
result.messageSearchOngoing = false
|
||||||
|
|
||||||
proc load*(self: View) =
|
proc load*(self: View) =
|
||||||
self.delegate.viewDidLoad()
|
self.delegate.viewDidLoad()
|
||||||
|
@ -96,7 +98,7 @@ QtObject:
|
||||||
|
|
||||||
proc initialMessagesLoadedChanged*(self: View) {.signal.}
|
proc initialMessagesLoadedChanged*(self: View) {.signal.}
|
||||||
|
|
||||||
proc getInitialMessagesLoaded*(self: View): bool {.slot.} =
|
proc getInitialMessagesLoaded(self: View): bool {.slot.} =
|
||||||
return self.initialMessagesLoaded
|
return self.initialMessagesLoaded
|
||||||
|
|
||||||
QtProperty[bool] initialMessagesLoaded:
|
QtProperty[bool] initialMessagesLoaded:
|
||||||
|
@ -164,10 +166,6 @@ QtObject:
|
||||||
proc emitScrollToMessageSignal*(self: View, messageIndex: int) =
|
proc emitScrollToMessageSignal*(self: View, messageIndex: int) =
|
||||||
self.scrollToMessage(messageIndex)
|
self.scrollToMessage(messageIndex)
|
||||||
|
|
||||||
proc scrollMessagesUp(self: View) {.signal.}
|
|
||||||
proc emitScrollMessagesUpSignal*(self: View) =
|
|
||||||
self.scrollMessagesUp()
|
|
||||||
|
|
||||||
proc requestMoreMessages(self: View) {.slot.} =
|
proc requestMoreMessages(self: View) {.slot.} =
|
||||||
self.delegate.requestMoreMessages()
|
self.delegate.requestMoreMessages()
|
||||||
|
|
||||||
|
@ -191,3 +189,19 @@ QtObject:
|
||||||
return
|
return
|
||||||
self.model.itemSending(messageId)
|
self.model.itemSending(messageId)
|
||||||
|
|
||||||
|
proc messageSearchOngoingChanged*(self: View) {.signal.}
|
||||||
|
proc getMessageSearchOngoing*(self: View): bool {.slot.} =
|
||||||
|
return self.messageSearchOngoing
|
||||||
|
|
||||||
|
QtProperty[bool] messageSearchOngoing:
|
||||||
|
read = getMessageSearchOngoing
|
||||||
|
notify = messageSearchOngoingChanged
|
||||||
|
|
||||||
|
proc setMessageSearchOngoing*(self: View, value: bool) =
|
||||||
|
self.messageSearchOngoing = value
|
||||||
|
self.messageSearchOngoingChanged()
|
||||||
|
|
||||||
|
proc addNewMessagesMarker*(self: View) {.slot.} =
|
||||||
|
if self.model.newMessagesMarkerIndex() == -1:
|
||||||
|
self.delegate.resetNewMessagesMarker()
|
||||||
|
|
||||||
|
|
|
@ -365,4 +365,12 @@ method onMutualContactChanged*(self: Module) =
|
||||||
self.view.onMutualContactChanged(isContact)
|
self.view.onMutualContactChanged(isContact)
|
||||||
|
|
||||||
method contactTrustStatusChanged*(self: Module, publicKey: string, isUntrustworthy: bool) =
|
method contactTrustStatusChanged*(self: Module, publicKey: string, isUntrustworthy: bool) =
|
||||||
self.view.updateTrustStatus(isUntrustworthy)
|
self.view.updateTrustStatus(isUntrustworthy)
|
||||||
|
|
||||||
|
method onMadeActive*(self: Module) =
|
||||||
|
self.messagesModule.resetNewMessagesMarker()
|
||||||
|
self.messagesModule.scrollToNewMessagesMarker()
|
||||||
|
self.view.setActive()
|
||||||
|
|
||||||
|
method onMadeInactive*(self: Module) =
|
||||||
|
self.view.setInactive()
|
||||||
|
|
|
@ -95,6 +95,12 @@ QtObject:
|
||||||
proc setMuted*(self: View, muted: bool) =
|
proc setMuted*(self: View, muted: bool) =
|
||||||
self.chatDetails.setMuted(muted)
|
self.chatDetails.setMuted(muted)
|
||||||
|
|
||||||
|
proc setActive*(self: View) =
|
||||||
|
self.chatDetails.setActive(true)
|
||||||
|
|
||||||
|
proc setInactive*(self: View) =
|
||||||
|
self.chatDetails.setActive(false)
|
||||||
|
|
||||||
proc updateChatDetailsNameAndIcon*(self: View, name, icon: string) =
|
proc updateChatDetailsNameAndIcon*(self: View, name, icon: string) =
|
||||||
self.chatDetails.setName(name)
|
self.chatDetails.setName(name)
|
||||||
self.chatDetails.setIcon(icon)
|
self.chatDetails.setIcon(icon)
|
||||||
|
@ -129,11 +135,9 @@ QtObject:
|
||||||
self.chatDetails.setEmoji(emoji)
|
self.chatDetails.setEmoji(emoji)
|
||||||
self.chatDetails.setColor(color)
|
self.chatDetails.setColor(color)
|
||||||
self.chatDetails.setIcon(icon)
|
self.chatDetails.setIcon(icon)
|
||||||
self.chatDetailsChanged()
|
|
||||||
|
|
||||||
proc updateChatDetailsName*(self: View, name: string) =
|
proc updateChatDetailsName*(self: View, name: string) =
|
||||||
self.chatDetails.setName(name)
|
self.chatDetails.setName(name)
|
||||||
self.chatDetailsChanged()
|
|
||||||
|
|
||||||
proc onMutualContactChanged*(self: View, value: bool) =
|
proc onMutualContactChanged*(self: View, value: bool) =
|
||||||
self.chatDetails.setIsMutualContact(value)
|
self.chatDetails.setIsMutualContact(value)
|
||||||
|
|
|
@ -350,11 +350,16 @@ method activeItemSubItemSet*(self: Module, itemId: string, subItemId: string) =
|
||||||
# update view maintained by this module
|
# update view maintained by this module
|
||||||
self.view.chatsModel().setActiveItemSubItem(itemId, subItemId)
|
self.view.chatsModel().setActiveItemSubItem(itemId, subItemId)
|
||||||
self.view.activeItemSubItemSet(item, subItem)
|
self.view.activeItemSubItemSet(item, subItem)
|
||||||
|
|
||||||
|
# update child modules
|
||||||
|
for chatId, chatContentModule in self.chatContentModules:
|
||||||
|
if chatId == self.controller.getActiveChatId():
|
||||||
|
chatContentModule.onMadeActive()
|
||||||
|
else:
|
||||||
|
chatContentModule.onMadeInactive()
|
||||||
|
|
||||||
# notify parent module about active chat/channel
|
# notify parent module about active chat/channel
|
||||||
self.delegate.onActiveChatChange(self.controller.getMySectionId(), self.controller.getActiveChatId())
|
self.delegate.onActiveChatChange(self.controller.getMySectionId(), self.controller.getActiveChatId())
|
||||||
# update notifications caused by setting active chat/channel
|
|
||||||
if singletonInstance.localAccountSensitiveSettings.getActiveSection() == self.controller.getMySectionId():
|
|
||||||
self.controller.markAllMessagesRead(self.controller.getActiveChatId())
|
|
||||||
|
|
||||||
method getModuleAsVariant*(self: Module): QVariant =
|
method getModuleAsVariant*(self: Module): QVariant =
|
||||||
return self.viewVariant
|
return self.viewVariant
|
||||||
|
@ -389,7 +394,6 @@ method onActiveSectionChange*(self: Module, sectionId: string) =
|
||||||
if(sectionId != self.controller.getMySectionId()):
|
if(sectionId != self.controller.getMySectionId()):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.updateBadgeNotifications(self.controller.getActiveChatId(), hasUnreadMessages=false, unviewedMentionsCount=0)
|
|
||||||
self.delegate.onActiveChatChange(self.controller.getMySectionId(), self.controller.getActiveChatId())
|
self.delegate.onActiveChatChange(self.controller.getMySectionId(), self.controller.getActiveChatId())
|
||||||
|
|
||||||
method chatsModel*(self: Module): chats_model.Model =
|
method chatsModel*(self: Module): chats_model.Model =
|
||||||
|
@ -729,12 +733,8 @@ method onNewMessagesReceived*(self: Module, sectionIdMsgBelongsTo: string, chatI
|
||||||
let chatDetails = self.controller.getChatDetails(chatIdMsgBelongsTo)
|
let chatDetails = self.controller.getChatDetails(chatIdMsgBelongsTo)
|
||||||
|
|
||||||
# Badge notification
|
# Badge notification
|
||||||
let messageBelongsToActiveSection = sectionIdMsgBelongsTo == self.controller.getMySectionId() and
|
let showBadge = (not chatDetails.muted and unviewedMessagesCount > 0) or unviewedMentionsCount > 0
|
||||||
self.controller.getMySectionId() == self.delegate.getActiveSectionId()
|
self.updateBadgeNotifications(chatIdMsgBelongsTo, showBadge, unviewedMentionsCount)
|
||||||
let messageBelongsToActiveChat = self.controller.getActiveChatId() == chatIdMsgBelongsTo
|
|
||||||
if(not messageBelongsToActiveSection or not messageBelongsToActiveChat):
|
|
||||||
let hasUnreadMessages = (not chatDetails.muted and unviewedMessagesCount > 0) or unviewedMentionsCount > 0
|
|
||||||
self.updateBadgeNotifications(chatIdMsgBelongsTo, hasUnreadMessages, unviewedMentionsCount)
|
|
||||||
|
|
||||||
if (chatDetails.muted):
|
if (chatDetails.muted):
|
||||||
# No need to send a notification
|
# No need to send a notification
|
||||||
|
@ -766,6 +766,10 @@ method onNewMessagesReceived*(self: Module, sectionIdMsgBelongsTo: string, chatI
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
let messageBelongsToActiveSection = sectionIdMsgBelongsTo == self.controller.getMySectionId() and
|
||||||
|
self.controller.getMySectionId() == self.delegate.getActiveSectionId()
|
||||||
|
let messageBelongsToActiveChat = self.controller.getActiveChatId() == chatIdMsgBelongsTo
|
||||||
|
|
||||||
singletonInstance.globalEvents.showMessageNotification(notificationTitle, plainText, sectionIdMsgBelongsTo,
|
singletonInstance.globalEvents.showMessageNotification(notificationTitle, plainText, sectionIdMsgBelongsTo,
|
||||||
self.controller.isCommunity(), messageBelongsToActiveSection, chatIdMsgBelongsTo, messageBelongsToActiveChat,
|
self.controller.isCommunity(), messageBelongsToActiveSection, chatIdMsgBelongsTo, messageBelongsToActiveChat,
|
||||||
message.id, notificationType.int, chatTypeMsgBelongsTo == ChatType.OneToOne,
|
message.id, notificationType.int, chatTypeMsgBelongsTo == ChatType.OneToOne,
|
||||||
|
|
|
@ -128,6 +128,38 @@ proc initItem*(
|
||||||
if attachment.contentType.contains("image"):
|
if attachment.contentType.contains("image"):
|
||||||
result.messageAttachments.add(attachment.localUrl)
|
result.messageAttachments.add(attachment.localUrl)
|
||||||
|
|
||||||
|
proc initNewMessagesMarkerItem*(timestamp: int64): Item =
|
||||||
|
return initItem(
|
||||||
|
id = "",
|
||||||
|
communityId = "",
|
||||||
|
responseToMessageWithId = "",
|
||||||
|
senderId = "",
|
||||||
|
senderDisplayName = "",
|
||||||
|
senderOptionalName = "",
|
||||||
|
senderIcon = "",
|
||||||
|
amISender = false,
|
||||||
|
senderIsAdded = false,
|
||||||
|
outgoingStatus = "",
|
||||||
|
text = "",
|
||||||
|
image = "",
|
||||||
|
messageContainsMentions = false,
|
||||||
|
seen = true,
|
||||||
|
timestamp = timestamp,
|
||||||
|
clock = 0,
|
||||||
|
ContentType.NewMessagesMarker,
|
||||||
|
messageType = -1,
|
||||||
|
contactRequestState = 0,
|
||||||
|
sticker = "",
|
||||||
|
stickerPack = -1,
|
||||||
|
links = @[],
|
||||||
|
transactionParameters = newTransactionParametersItem("","","","","","",-1,""),
|
||||||
|
mentionedUsersPks = @[],
|
||||||
|
senderTrustStatus = TrustStatus.Unknown,
|
||||||
|
senderEnsVerified = false,
|
||||||
|
discordMessage = DiscordMessage(),
|
||||||
|
resendError = ""
|
||||||
|
)
|
||||||
|
|
||||||
proc `$`*(self: Item): string =
|
proc `$`*(self: Item): string =
|
||||||
result = fmt"""Item(
|
result = fmt"""Item(
|
||||||
id: {$self.id},
|
id: {$self.id},
|
||||||
|
@ -246,6 +278,9 @@ proc sticker*(self: Item): string {.inline.} =
|
||||||
proc seen*(self: Item): bool {.inline.} =
|
proc seen*(self: Item): bool {.inline.} =
|
||||||
self.seen
|
self.seen
|
||||||
|
|
||||||
|
proc `seen=`*(self: Item, value: bool) {.inline.} =
|
||||||
|
self.seen = value
|
||||||
|
|
||||||
proc timestamp*(self: Item): int64 {.inline.} =
|
proc timestamp*(self: Item): int64 {.inline.} =
|
||||||
self.timestamp
|
self.timestamp
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ QtObject:
|
||||||
Model* = ref object of QAbstractListModel
|
Model* = ref object of QAbstractListModel
|
||||||
items*: seq[Item]
|
items*: seq[Item]
|
||||||
allKeys: seq[int]
|
allKeys: seq[int]
|
||||||
|
firstUnseenMessageId: string
|
||||||
|
|
||||||
proc delete(self: Model) =
|
proc delete(self: Model) =
|
||||||
self.items = @[]
|
self.items = @[]
|
||||||
|
@ -66,6 +67,8 @@ QtObject:
|
||||||
for i in result.roleNames().keys:
|
for i in result.roleNames().keys:
|
||||||
result.allKeys.add(i)
|
result.allKeys.add(i)
|
||||||
|
|
||||||
|
result.firstUnseenMessageId = ""
|
||||||
|
|
||||||
proc `$`*(self: Model): string =
|
proc `$`*(self: Model): string =
|
||||||
result = "MessageModel:\n"
|
result = "MessageModel:\n"
|
||||||
for i in 0 ..< self.items.len:
|
for i in 0 ..< self.items.len:
|
||||||
|
@ -529,3 +532,63 @@ QtObject:
|
||||||
if(ind == -1):
|
if(ind == -1):
|
||||||
return
|
return
|
||||||
self.updateItemAtIndex(ind)
|
self.updateItemAtIndex(ind)
|
||||||
|
|
||||||
|
proc setFirstUnseenMessageId*(self: Model, messageId: string) =
|
||||||
|
self.firstUnseenMessageId = messageId
|
||||||
|
|
||||||
|
proc getFirstUnseenMessageId*(self: Model): string =
|
||||||
|
self.firstUnseenMessageId
|
||||||
|
|
||||||
|
proc newMessagesMarkerIndex*(self: Model): int =
|
||||||
|
result = -1
|
||||||
|
for i in countdown(self.items.len - 1, 0):
|
||||||
|
if self.items[i].contentType == ContentType.NewMessagesMarker:
|
||||||
|
return i
|
||||||
|
|
||||||
|
proc removeNewMessagesMarker(self: Model) =
|
||||||
|
let index = self.newMessagesMarkerIndex()
|
||||||
|
if index == -1:
|
||||||
|
return
|
||||||
|
|
||||||
|
let parentModelIndex = newQModelIndex()
|
||||||
|
defer: parentModelIndex.delete
|
||||||
|
|
||||||
|
self.beginRemoveRows(parentModelIndex, index, index)
|
||||||
|
self.items.delete(index)
|
||||||
|
self.endRemoveRows()
|
||||||
|
self.countChanged()
|
||||||
|
|
||||||
|
# TODO: handle messages removal
|
||||||
|
proc resetNewMessagesMarker*(self: Model) =
|
||||||
|
self.removeNewMessagesMarker()
|
||||||
|
let messageId = self.firstUnseenMessageId
|
||||||
|
if messageId == "":
|
||||||
|
return
|
||||||
|
|
||||||
|
let index = self.findIndexForMessageId(messageId)
|
||||||
|
if index == -1:
|
||||||
|
return
|
||||||
|
|
||||||
|
let position = index + 1
|
||||||
|
|
||||||
|
let parentModelIndex = newQModelIndex()
|
||||||
|
defer: parentModelIndex.delete
|
||||||
|
|
||||||
|
self.beginInsertRows(parentModelIndex, position, position)
|
||||||
|
self.items.insert(initNewMessagesMarkerItem(self.items[index].timestamp), position)
|
||||||
|
self.endInsertRows()
|
||||||
|
self.countChanged()
|
||||||
|
|
||||||
|
proc getNewMessagesCount*(self: Model): int {.slot.} =
|
||||||
|
max(0, self.newMessagesMarkerIndex())
|
||||||
|
QtProperty[int]newMessagesCount:
|
||||||
|
read = getNewMessagesCount
|
||||||
|
notify = countChanged
|
||||||
|
|
||||||
|
proc markAllAsSeen*(self: Model) =
|
||||||
|
for i in 0 ..< self.items.len:
|
||||||
|
let item = self.items[i]
|
||||||
|
if not item.seen:
|
||||||
|
item.seen = true
|
||||||
|
let index = self.createIndex(i, 0, nil)
|
||||||
|
self.dataChanged(index, index, @[ModelRole.Seen.int])
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
type
|
type
|
||||||
ContentType* {.pure.} = enum
|
ContentType* {.pure.} = enum
|
||||||
|
NewMessagesMarker = -3
|
||||||
FetchMoreMessagesButton = -2
|
FetchMoreMessagesButton = -2
|
||||||
ChatIdentifier = -1
|
ChatIdentifier = -1
|
||||||
Unknown = 0
|
Unknown = 0
|
||||||
|
|
|
@ -657,6 +657,19 @@ QtObject:
|
||||||
|
|
||||||
self.threadpool.start(arg)
|
self.threadpool.start(arg)
|
||||||
|
|
||||||
|
proc getFirstUnseenMessageIdFor*(self: Service, chatId: string): string =
|
||||||
|
try:
|
||||||
|
let response = status_go.firstUnseenMessageID(chatId)
|
||||||
|
|
||||||
|
if(not response.error.isNil):
|
||||||
|
error "error getFirstUnseenMessageIdFor: ", errDescription = response.error.message
|
||||||
|
|
||||||
|
result = response.result.getStr()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error "error: ", procName = "getFirstUnseenMessageIdFor", errName = e.name,
|
||||||
|
errDesription = e.msg
|
||||||
|
|
||||||
proc onAsyncGetLinkPreviewData*(self: Service, response: string) {.slot.} =
|
proc onAsyncGetLinkPreviewData*(self: Service, response: string) {.slot.} =
|
||||||
self.events.emit(SIGNAL_MESSAGE_LINK_PREVIEW_DATA_LOADED, LinkPreviewDataArgs(response: response))
|
self.events.emit(SIGNAL_MESSAGE_LINK_PREVIEW_DATA_LOADED, LinkPreviewDataArgs(response: response))
|
||||||
|
|
||||||
|
|
|
@ -67,3 +67,8 @@ proc editMessage*(messageId: string, contentType: int, msg: string): RpcResponse
|
||||||
|
|
||||||
proc resendChatMessage*(messageId: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
proc resendChatMessage*(messageId: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
result = callPrivateRPC("reSendChatMessage".prefix, %* [messageId])
|
result = callPrivateRPC("reSendChatMessage".prefix, %* [messageId])
|
||||||
|
|
||||||
|
proc firstUnseenMessageID*(chatId: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
|
let payload = %* [chatId]
|
||||||
|
result = callPrivateRPC("firstUnseenMessageID".prefix, payload)
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,9 @@ QtObject {
|
||||||
property var messagesModel
|
property var messagesModel
|
||||||
property var chatSectionModule
|
property var chatSectionModule
|
||||||
|
|
||||||
property var loadingHistoryMessagesInProgress: root.chatSectionModule? root.chatSectionModule.loadingHistoryMessagesInProgress : false
|
readonly property bool loadingHistoryMessagesInProgress: root.chatSectionModule? root.chatSectionModule.loadingHistoryMessagesInProgress : false
|
||||||
|
readonly property int newMessagesCount: messagesModel ? messagesModel.newMessagesCount : 0
|
||||||
|
readonly property bool messageSearchOngoing: messageModule ? messageModule.messageSearchOngoing : false
|
||||||
|
|
||||||
onMessageModuleChanged: {
|
onMessageModuleChanged: {
|
||||||
if(!messageModule)
|
if(!messageModule)
|
||||||
|
@ -225,6 +227,12 @@ QtObject {
|
||||||
messageModule.leaveChat()
|
messageModule.leaveChat()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addNewMessagesMarker() {
|
||||||
|
if(!messageModule)
|
||||||
|
return
|
||||||
|
messageModule.addNewMessagesMarker()
|
||||||
|
}
|
||||||
|
|
||||||
property bool playAnimation: {
|
property bool playAnimation: {
|
||||||
if(!Global.applicationWindow.active)
|
if(!Global.applicationWindow.active)
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -42,39 +42,63 @@ Item {
|
||||||
|
|
||||||
property var messageContextMenu
|
property var messageContextMenu
|
||||||
|
|
||||||
property real scrollY: chatLogView.visibleArea.yPosition * chatLogView.contentHeight
|
|
||||||
property int newMessages: 0
|
|
||||||
|
|
||||||
property int countOnStartUp: 0
|
|
||||||
signal openStickerPackPopup(string stickerPackId)
|
signal openStickerPackPopup(string stickerPackId)
|
||||||
signal showReplyArea(string messageId, string author)
|
signal showReplyArea(string messageId, string author)
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
readonly property real scrollY: chatLogView.visibleArea.yPosition * chatLogView.contentHeight
|
||||||
|
readonly property bool isMostRecentMessageInViewport: chatLogView.visibleArea.yPosition >= 0.999 - chatLogView.visibleArea.heightRatio
|
||||||
|
readonly property var chatDetails: chatContentModule.chatDetails
|
||||||
|
|
||||||
|
function markAllMessagesReadIfMostRecentMessageIsInViewport() {
|
||||||
|
if (!isMostRecentMessageInViewport) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chatDetails.active && chatDetails.hasUnreadMessages && !messageStore.messageSearchOngoing) {
|
||||||
|
chatContentModule.markAllMessagesRead()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onIsMostRecentMessageInViewportChanged: markAllMessagesReadIfMostRecentMessageIsInViewport()
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root.messageStore.messageModule
|
target: root.messageStore.messageModule
|
||||||
|
|
||||||
function onMessageSuccessfullySent() {
|
function onMessageSuccessfullySent() {
|
||||||
chatLogView.scrollToBottom(true)
|
chatLogView.positionViewAtBeginning()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSendingMessageFailed() {
|
function onSendingMessageFailed() {
|
||||||
sendingMsgFailedPopup.open()
|
sendingMsgFailedPopup.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onScrollMessagesUp() {
|
|
||||||
chatLogView.positionViewAtEnd()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onScrollToMessage(messageIndex) {
|
function onScrollToMessage(messageIndex) {
|
||||||
chatLogView.positionViewAtIndex(messageIndex, ListView.Center);
|
chatLogView.positionViewAtIndex(messageIndex, ListView.Center)
|
||||||
chatLogView.itemAtIndex(messageIndex).startMessageFoundAnimation();
|
chatLogView.itemAtIndex(messageIndex).startMessageFoundAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not Refactored Yet
|
function onMessageSearchOngoingChanged() {
|
||||||
// onNewMessagePushed: {
|
d.markAllMessagesReadIfMostRecentMessageIsInViewport()
|
||||||
// if (!chatLogView.scrollToBottom()) {
|
}
|
||||||
// newMessages++
|
}
|
||||||
// }
|
|
||||||
// }
|
Connections {
|
||||||
|
target: d.chatDetails
|
||||||
|
|
||||||
|
function onActiveChanged() {
|
||||||
|
d.markAllMessagesReadIfMostRecentMessageIsInViewport()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onHasUnreadMessagesChanged() {
|
||||||
|
if (d.chatDetails.hasUnreadMessages && d.chatDetails.active && !d.isMostRecentMessageInViewport) {
|
||||||
|
// HACK: we call it later because messages model may not be yet propagated with unread messages when this signal is emitted
|
||||||
|
Qt.callLater(() => messageStore.addNewMessagesMarker())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -120,34 +144,15 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrollToBottom(force, caller) {
|
|
||||||
if (!force && !chatLogView.atYEnd) {
|
|
||||||
// User has scrolled up, we don't want to scroll back
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (caller && caller !== chatLogView.itemAtIndex(chatLogView.count - 1)) {
|
|
||||||
// If we have a caller, only accept its request if it's the last message
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Call this twice and with a timer since the first scroll to bottom might have happened before some stuff loads
|
|
||||||
// meaning that the scroll will not actually be at the bottom on switch
|
|
||||||
// Add a small delay because images, even though they say they say they are loaed, they aren't shown yet
|
|
||||||
Qt.callLater(chatLogView.positionViewAtBeginning)
|
|
||||||
timer.setTimeout(function() {
|
|
||||||
Qt.callLater(chatLogView.positionViewAtBeginning)
|
|
||||||
}, 100);
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
model: messageStore.messagesModel
|
model: messageStore.messagesModel
|
||||||
|
|
||||||
Component.onCompleted: chatLogView.scrollToBottom(true)
|
|
||||||
|
|
||||||
onContentYChanged: {
|
onContentYChanged: {
|
||||||
scrollDownButton.visible = contentHeight - (scrollY + height) > 400
|
scrollDownButton.visible = contentHeight - (d.scrollY + height) > 400
|
||||||
if(scrollY < 500) messageStore.loadMoreMessages()
|
if(d.scrollY < 500) messageStore.loadMoreMessages()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onCountChanged: d.markAllMessagesReadIfMostRecentMessageIsInViewport()
|
||||||
|
|
||||||
ScrollBar.vertical: StatusScrollBar {
|
ScrollBar.vertical: StatusScrollBar {
|
||||||
visible: chatLogView.visibleArea.heightRatio < 1
|
visible: chatLogView.visibleArea.heightRatio < 1
|
||||||
}
|
}
|
||||||
|
@ -160,18 +165,6 @@ Item {
|
||||||
width: chatLogView.width
|
width: chatLogView.width
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connections {
|
|
||||||
// id: contentHeightConnection
|
|
||||||
// enabled: true
|
|
||||||
// target: chatLogView
|
|
||||||
// onContentHeightChanged: {
|
|
||||||
// chatLogView.checkHeaderHeight()
|
|
||||||
// }
|
|
||||||
// onHeightChanged: {
|
|
||||||
// chatLogView.checkHeaderHeight()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: timer
|
id: timer
|
||||||
}
|
}
|
||||||
|
@ -181,12 +174,14 @@ Item {
|
||||||
|
|
||||||
readonly property int buttonPadding: 5
|
readonly property int buttonPadding: 5
|
||||||
|
|
||||||
visible: false
|
|
||||||
height: 32
|
|
||||||
width: nbMessages.width + arrowImage.width + 2 * Style.current.halfPadding + (nbMessages.visible ? scrollDownButton.buttonPadding : 0)
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Style.current.padding
|
anchors.rightMargin: Style.current.padding
|
||||||
|
|
||||||
|
visible: false
|
||||||
|
height: 32
|
||||||
|
width: arrowImage.width + 2 * Style.current.halfPadding
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Style.current.buttonSecondaryColor
|
color: Style.current.buttonSecondaryColor
|
||||||
border.width: 0
|
border.width: 0
|
||||||
|
@ -194,31 +189,16 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
newMessages = 0
|
|
||||||
scrollDownButton.visible = false
|
scrollDownButton.visible = false
|
||||||
chatLogView.scrollToBottom(true)
|
chatLogView.positionViewAtBeginning()
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: nbMessages
|
|
||||||
visible: newMessages > 0
|
|
||||||
width: visible ? implicitWidth : 0
|
|
||||||
text: newMessages
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: parent.left
|
|
||||||
color: Style.current.pillButtonTextColor
|
|
||||||
font.pixelSize: 15
|
|
||||||
anchors.leftMargin: Style.current.halfPadding
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusIcon {
|
StatusIcon {
|
||||||
id: arrowImage
|
id: arrowImage
|
||||||
|
anchors.centerIn: parent
|
||||||
width: 24
|
width: 24
|
||||||
height: 24
|
height: 24
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: nbMessages.right
|
|
||||||
icon: "arrow-down"
|
icon: "arrow-down"
|
||||||
anchors.leftMargin: nbMessages.visible ? scrollDownButton.buttonPadding : 0
|
|
||||||
color: Style.current.pillButtonTextColor
|
color: Style.current.pillButtonTextColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,15 +209,6 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connections {
|
|
||||||
// Not Refactored Yet
|
|
||||||
// target: root.rootStore.chatsModelInst
|
|
||||||
|
|
||||||
// onAppReady: {
|
|
||||||
// chatLogView.scrollToBottom(true)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: chatLogView.model || null
|
target: chatLogView.model || null
|
||||||
function onDataChanged(topLeft, bottomRight, roles) {
|
function onDataChanged(topLeft, bottomRight, roles) {
|
||||||
|
|
|
@ -205,6 +205,8 @@ Loader {
|
||||||
|
|
||||||
z: (typeof chatLogView === "undefined") ? 1 : (chatLogView.count - index)
|
z: (typeof chatLogView === "undefined") ? 1 : (chatLogView.count - index)
|
||||||
|
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
sourceComponent: {
|
sourceComponent: {
|
||||||
switch(messageContentType) {
|
switch(messageContentType) {
|
||||||
case Constants.messageContentType.chatIdentifier:
|
case Constants.messageContentType.chatIdentifier:
|
||||||
|
@ -215,6 +217,8 @@ Loader {
|
||||||
return privateGroupHeaderComponent
|
return privateGroupHeaderComponent
|
||||||
case Constants.messageContentType.gapType:
|
case Constants.messageContentType.gapType:
|
||||||
return gapComponent
|
return gapComponent
|
||||||
|
case Constants.messageContentType.newMessagesMarker:
|
||||||
|
return newMessagesMarkerComponent
|
||||||
default:
|
default:
|
||||||
return messageComponent
|
return messageComponent
|
||||||
}
|
}
|
||||||
|
@ -902,4 +906,13 @@ Loader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: newMessagesMarkerComponent
|
||||||
|
|
||||||
|
NewMessagesMarker {
|
||||||
|
count: root.messageStore.newMessagesCount
|
||||||
|
timestamp: root.messageTimestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
import QtQuick 2.14
|
||||||
|
import QtQuick.Layouts 1.14
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property double timestamp
|
||||||
|
property int count
|
||||||
|
|
||||||
|
implicitHeight: 28
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: 16
|
||||||
|
rightMargin: 16
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: 1
|
||||||
|
color: Theme.palette.primaryColor1
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
text: qsTr("%n missed message(s) since %1", "", count).arg(new Date(timestamp).toLocaleDateString())
|
||||||
|
color: Theme.palette.primaryColor1
|
||||||
|
font.weight: Font.Bold
|
||||||
|
font.pixelSize: 13
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
width: parent.width
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: 1
|
||||||
|
color: Theme.palette.primaryColor1
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
implicitHeight: 16
|
||||||
|
implicitWidth: newLabel.width + 2*4
|
||||||
|
|
||||||
|
radius: 4
|
||||||
|
color: Theme.palette.primaryColor1
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: newLabel
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: qsTr("NEW", "new message(s)")
|
||||||
|
color: Theme.palette.indirectColor1
|
||||||
|
font.weight: Font.DemiBold
|
||||||
|
font.pixelSize: 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,3 +8,4 @@ MessageView 1.0 MessageView.qml
|
||||||
NormalMessageView 1.0 NormalMessageView.qml
|
NormalMessageView 1.0 NormalMessageView.qml
|
||||||
ProfileHeaderContextMenuView 1.0 ProfileHeaderContextMenuView.qml
|
ProfileHeaderContextMenuView 1.0 ProfileHeaderContextMenuView.qml
|
||||||
TransactionBubbleView 1.0 TransactionBubbleView.qml
|
TransactionBubbleView 1.0 TransactionBubbleView.qml
|
||||||
|
NewMessagesMarker 1.0 NewMessagesMarker.qml
|
||||||
|
|
|
@ -348,6 +348,7 @@ QtObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property QtObject messageContentType: QtObject {
|
readonly property QtObject messageContentType: QtObject {
|
||||||
|
readonly property int newMessagesMarker: -3
|
||||||
readonly property int fetchMoreMessagesButton: -2
|
readonly property int fetchMoreMessagesButton: -2
|
||||||
readonly property int chatIdentifier: -1
|
readonly property int chatIdentifier: -1
|
||||||
readonly property int unknownContentType: 0
|
readonly property int unknownContentType: 0
|
||||||
|
|
Loading…
Reference in New Issue