refactor(@desktop/chat-communities): `ChannelIdentifierView` component updated

This commit is contained in:
Sale Djenic 2021-12-10 17:11:18 +01:00
parent 7f40ae0f57
commit bfaf1b5250
13 changed files with 457 additions and 351 deletions

View File

@ -3,6 +3,7 @@ import controller_interface
import io_interface
import ../../../../../../app_service/service/contacts/service as contact_service
import ../../../../../../app_service/service/chat/service as chat_service
import ../../../../../../app_service/service/message/service as message_service
import ../../../../../core/signals/types
@ -20,16 +21,19 @@ type
chatId: string
belongsToCommunity: bool
contactService: contact_service.Service
chatService: chat_service.Service
messageService: message_service.Service
proc newController*(delegate: io_interface.AccessInterface, events: EventEmitter, chatId: string, belongsToCommunity: bool,
contactService: contact_service.Service, messageService: message_service.Service): Controller =
contactService: contact_service.Service, chatService: chat_service.Service, messageService: message_service.Service):
Controller =
result = Controller()
result.delegate = delegate
result.events = events
result.chatId = chatId
result.belongsToCommunity = belongsToCommunity
result.contactService = contactService
result.chatService = chatService
result.messageService = messageService
method delete*(self: Controller) =
@ -57,6 +61,12 @@ method init*(self: Controller) =
method getChatId*(self: Controller): string =
return self.chatId
method getChatDetails*(self: Controller): ChatDto =
return self.chatService.getChatById(self.chatId)
method getOneToOneChatNameAndImage*(self: Controller): tuple[name: string, image: string, isIdenticon: bool] =
return self.chatService.getOneToOneChatNameAndImage(self.chatId)
method belongsToCommunity*(self: Controller): bool =
return self.belongsToCommunity

View File

@ -1,4 +1,5 @@
import ../../../../../../app_service/service/contacts/service as contact_service
import ../../../../../../app_service/service/contacts/dto/[contacts]
import ../../../../../../app_service/service/chat/dto/[chat]
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -13,6 +14,13 @@ method init*(self: AccessInterface) {.base.} =
method getChatId*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method getChatDetails*(self: AccessInterface): ChatDto {.base.} =
raise newException(ValueError, "No implementation available")
method getOneToOneChatNameAndImage*(self: AccessInterface): tuple[name: string, image: string, isIdenticon: bool]
{.base.} =
raise newException(ValueError, "No implementation available")
method belongsToCommunity*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -7,12 +7,15 @@ import ../../../../shared_models/message_item
import ../../../../../global/global_singleton
import ../../../../../../app_service/service/contacts/service as contact_service
import ../../../../../../app_service/service/chat/service as chat_service
import ../../../../../../app_service/service/message/service as message_service
import eventemitter
export io_interface
const CHAT_IDENTIFIER_MESSAGE_ID = "chat-identifier-message-id"
type
Module* = ref object of io_interface.AccessInterface
delegate: delegate_interface.AccessInterface
@ -22,16 +25,20 @@ type
moduleLoaded: bool
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, chatId: string,
belongsToCommunity: bool, contactService: contact_service.Service, messageService: message_service.Service):
belongsToCommunity: bool, contactService: contact_service.Service, chatService: chat_service.Service,
messageService: message_service.Service):
Module =
result = Module()
result.delegate = delegate
result.view = view.newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newController(result, events, chatId, belongsToCommunity, contactService,
result.controller = controller.newController(result, events, chatId, belongsToCommunity, contactService, chatService,
messageService)
result.moduleLoaded = false
# Forward declaration
proc createChatIdentifierItem(self: Module): Item
method delete*(self: Module) =
self.view.delete
self.viewVariant.delete
@ -45,15 +52,32 @@ method isLoaded*(self: Module): bool =
return self.moduleLoaded
method viewDidLoad*(self: Module) =
# The first message in the model must be always ChatIdentifier message.
self.view.model().appendItem(self.createChatIdentifierItem())
self.moduleLoaded = true
self.delegate.messagesDidLoad()
method getModuleAsVariant*(self: Module): QVariant =
return self.viewVariant
proc createChatIdentifierItem(self: Module): Item =
let chatDto = self.controller.getChatDetails()
var chatName = chatDto.name
var chatIcon = chatDto.identicon
var isIdenticon = false
if(chatDto.chatType == ChatType.OneToOne):
(chatName, chatIcon, isIdenticon) = self.controller.getOneToOneChatNameAndImage()
result = initItem(CHAT_IDENTIFIER_MESSAGE_ID, "", chatDto.id, chatName, "", chatIcon, isIdenticon, false, "", "", "",
true, 0, ContentType.ChatIdentifier, -1)
result.chatColorThisMessageBelongsTo = chatDto.color
result.chatTypeThisMessageBelongsTo = chatDto.chatType.int
method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: seq[ReactionDto],
pinnedMessages: seq[PinnedMessageDto]) =
var viewItems: seq[Item]
var viewItems: seq[Item]
for m in messages:
let sender = self.controller.getContactById(m.`from`)
let senderDisplayName = sender.userNameOrAlias()
@ -77,8 +101,13 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
item.pinned = true
# messages are sorted from the most recent to the least recent one
viewItems = item & viewItems
viewItems.add(item)
# ChatIdentifier message will be always the first message (the oldest one)
viewItems.add(self.createChatIdentifierItem())
# Delete the old ChatIdentifier message first
self.view.model().removeItem(CHAT_IDENTIFIER_MESSAGE_ID)
# Add new loaded messages
self.view.model().prependItems(viewItems)
method toggleReaction*(self: Module, messageId: string, emojiId: int) =

View File

@ -48,7 +48,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitt
result.inputAreaModule = input_area_module.newModule(result, chatId, belongsToCommunity, chatService, communityService)
result.messagesModule = messages_module.newModule(result, events, chatId, belongsToCommunity, contactService,
messageService)
chatService, messageService)
result.usersModule = users_module.newModule(result, events, sectionId, chatId, belongsToCommunity, isUsersListAvailable,
contactService, communityService, messageService)

View File

@ -1,4 +1,4 @@
import Tables, json
import Tables, json, strformat
type
ContentType* {.pure.} = enum
@ -41,9 +41,13 @@ type
reactions: OrderedTable[int, seq[tuple[publicKey: string, reactionId: string]]] # [emojiId, list of [user publicKey reacted with the emojiId, reaction id]]
reactionIds: seq[string]
pinned: bool
# used in case of ContentType.ChatIdentifier only
chatTypeThisMessageBelongsTo: int
chatColorThisMessageBelongsTo: string
proc initItem*(id, responseToMessageWithId, senderId, senderDisplayName, senderLocalName, senderIcon: string, isSenderIconIdenticon, amISender: bool,
outgoingStatus, text, image: string, seen: bool, timestamp: int64, contentType: ContentType, messageType: int): Item =
proc initItem*(id, responseToMessageWithId, senderId, senderDisplayName, senderLocalName, senderIcon: string,
isSenderIconIdenticon, amISender: bool, outgoingStatus, text, image: string, seen: bool, timestamp: int64,
contentType: ContentType, messageType: int): Item =
result = Item()
result.id = id
result.responseToMessageWithId = responseToMessageWithId
@ -113,6 +117,19 @@ proc pinned*(self: Item): bool {.inline.} =
proc `pinned=`*(self: Item, value: bool) {.inline.} =
self.pinned = value
proc chatTypeThisMessageBelongsTo*(self: Item): int {.inline.} =
self.chatTypeThisMessageBelongsTo
proc `chatTypeThisMessageBelongsTo=`*(self: Item, value: int) {.inline.} =
self.chatTypeThisMessageBelongsTo = value
proc chatColorThisMessageBelongsTo*(self: Item): string {.inline.} =
self.chatColorThisMessageBelongsTo
proc `chatColorThisMessageBelongsTo=`*(self: Item, value: string) {.inline.} =
self.chatColorThisMessageBelongsTo = value
proc shouldAddReaction*(self: Item, emojiId: int, publicKey: string): bool =
for k, values in self.reactions:
if(k != emojiId):
@ -175,6 +192,21 @@ proc getCountsForReactions*(self: Item): seq[JsonNode] =
result.add(%* {"emojiId": k, "counts": v.len})
proc `$`*(self: Item): string =
result = fmt"""MessageItem(
id: {self.id},
responseToMessageWithId: {self.responseToMessageWithId},
senderId: {self.senderId},
senderDisplayName: {self.senderDisplayName},
senderLocalName: {self.senderLocalName},
timestamp: {self.timestamp},
contentType: {self.contentType.int},
messageType:{self.messageType},
chatTypeThisMessageBelongsTo:{self.chatTypeThisMessageBelongsTo},
chatColorThisMessageBelongsTo:{self.chatColorThisMessageBelongsTo},
pinned:{self.pinned}
]"""
proc toJsonNode*(self: Item): JsonNode =
result = %* {
"id": self.id,

View File

@ -1,4 +1,4 @@
import NimQml, Tables, json, strutils
import NimQml, Tables, json, strutils, strformat
import message_item
@ -25,6 +25,8 @@ type
# GapTo
Pinned
CountsForReactions
ChatTypeThisMessageBelongsTo
ChatColorThisMessageBelongsTo
QtObject:
type
@ -42,6 +44,12 @@ QtObject:
new(result, delete)
result.setup
proc `$`*(self: Model): string =
for i in 0 ..< self.items.len:
result &= fmt"""MessageModel:
[{i}]:({$self.items[i]})
"""
proc countChanged(self: Model) {.signal.}
proc getCount(self: Model): int {.slot.} =
self.items.len
@ -75,6 +83,8 @@ QtObject:
# ModelRole.GapTo.int:"gapTo",
ModelRole.Pinned.int:"pinned",
ModelRole.CountsForReactions.int:"countsForReactions",
ModelRole.ChatTypeThisMessageBelongsTo.int:"chatTypeThisMessageBelongsTo",
ModelRole.ChatColorThisMessageBelongsTo.int:"chatColorThisMessageBelongsTo",
}.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -130,6 +140,10 @@ QtObject:
result = newQVariant(item.pinned)
of ModelRole.CountsForReactions:
result = newQVariant($(%* item.getCountsForReactions))
of ModelRole.ChatTypeThisMessageBelongsTo:
result = newQVariant(item.chatTypeThisMessageBelongsTo)
of ModelRole.ChatColorThisMessageBelongsTo:
result = newQVariant(item.chatColorThisMessageBelongsTo)
proc findIndexForMessageId(self: Model, messageId: string): int =
for i in 0 ..< self.items.len:

View File

@ -64,6 +64,9 @@ QtObject:
result.msgCursor = initTable[string, string]()
result.pinnedMsgCursor = initTable[string, string]()
proc initialMessagesFetched(self: Service, chatId: string): bool =
return self.msgCursor.hasKey(chatId)
proc getCurrentMessageCursor(self: Service, chatId: string): string =
if(not self.msgCursor.hasKey(chatId)):
self.msgCursor[chatId] = ""
@ -140,6 +143,9 @@ QtObject:
self.threadpool.start(arg)
proc asyncLoadInitialMessagesForChat*(self: Service, chatId: string) =
if(self.initialMessagesFetched(chatId)):
return
if(self.getCurrentMessageCursor(chatId).len > 0):
return

View File

@ -31,7 +31,6 @@ Item {
property real scrollY: chatLogView.visibleArea.yPosition * chatLogView.contentHeight
property int newMessages: 0
property int countOnStartUp: 0
ListView {
id: chatLogView
@ -286,41 +285,16 @@ Item {
// }
// }
model: messageListDelegate
model: messageStore.messageModule.model
section.property: "sectionIdentifier"
section.criteria: ViewSection.FullString
// Not Refactored Yet
//Component.onCompleted: scrollToBottom(true)
}
// MessageDialog {
// id: sendingMsgFailedPopup
// standardButtons: StandardButton.Ok
// //% "Failed to send message."
// text: qsTrId("failed-to-send-message-")
// icon: StandardIcon.Critical
// }
Timer {
id: modelLoadingDelayTimer
interval: 1000
onTriggered: {
root.countOnStartUp = messageListDelegate.count;
}
}
DelegateModelGeneralized {
id: messageListDelegate
lessThan: [
function(left, right) { return left.clock > right.clock }
]
model: messageStore.messageModule.model
delegate: MessageView {
id: msgDelegate
rootStore: root.store
messageStore: root.messageStore
messageId: model.id
@ -338,6 +312,10 @@ Item {
messageContentType: model.contentType
pinnedMessage: model.pinned
// Used only in case of ChatIdentifier
chatTypeThisMessageBelongsTo: model.chatTypeThisMessageBelongsTo
chatColorThisMessageBelongsTo: model.chatColorThisMessageBelongsTo
// This is possible since we have all data loaded before we load qml.
// When we fetch messages to fulfill a gap we have to set them at once.
prevMessageIndex: index - 1
@ -345,125 +323,16 @@ Item {
nextMessageIndex: index + 1
nextMessageAsJsonObj: messageStore.getMessageByIndexAsJson(index + 1)
/////////////TODO Remove
// fromAuthor: model.fromAuthor
// chatId: model.chatId
// userName: model.userName
// alias: model.alias
// localName: model.localName
// message: model.message
// plainText: model.plainText
// identicon: model.identicon
// isCurrentUser: model.isCurrentUser
// timestamp: model.timestamp
// sticker: model.sticker
// contentType: model.contentType
// replaces: model.replaces
// isEdited: model.isEdited
// outgoingStatus: model.outgoingStatus
// responseTo: model.responseTo
// authorCurrentMsg: msgDelegate.ListView.section
// // The previous message is actually the nextSection since we reversed the list order
// authorPrevMsg: msgDelegate.ListView.nextSection
// imageClick: imagePopup.openPopup.bind(imagePopup)
// messageId: model.messageId
// emojiReactions: model.emojiReactions
// linkUrls: model.linkUrls
// communityId: model.communityId
// hasMention: model.hasMention
// stickerPackId: model.stickerPackId
// pinnedMessage: model.isPinned
// pinnedBy: model.pinnedBy
// gapFrom: model.gapFrom
// gapTo: model.gapTo
// visible: !model.hide
// messageContextMenu: root.messageContextMenuInst
// prevMessageIndex: {
// // This is used in order to have access to the previous message and determine the timestamp
// // we can't rely on the index because the sequence of messages is not ordered on the nim side
// if (msgDelegate.DelegateModel.itemsIndex < messageListDelegate.items.count - 1) {
// return messageListDelegate.items.get(msgDelegate.DelegateModel.itemsIndex + 1).model.index
// }
// return -1;
// }
// nextMessageIndex: {
// if (msgDelegate.DelegateModel.itemsIndex < 1) {
// return -1
// }
// return messageListDelegate.items.get(msgDelegate.DelegateModel.itemsIndex - 1).model.index
// }
// scrollToBottom: chatLogView.scrollToBottom
// timeout: model.timeout
Component.onCompleted: {
if ((root.countOnStartUp > 0) && (root.countOnStartUp - 1) < index) {
//new message, increment z order
z = index;
}
// messageStore.messageId = model.id
// messageStore.responseToMessageWithId = model.responseToMessageWithId
// messageStore.senderId = model.senderId
// messageStore.senderDisplayName = model.senderDisplayName
// messageStore.senderLocalName = model.senderLocalName
// messageStore.senderIcon = model.senderIcon
// messageStore.isSenderIconIdenticon = model.isSenderIconIdenticon
// messageStore.amISender = model.amISender
// messageStore.message = model.messageText
// messageStore.messageImage = model.messageImage
// messageStore.messageTimestamp = model.timestamp
// messageStore.messageOutgoingStatus = model.outgoingStatus
// messageStore.messageContentType = model.contentType
// messageStore.pinnedMessage = model.pinned
// messageStore.fromAuthor = model.fromAuthor;
// messageStore.chatId = model.chatId;
// messageStore.userName = model.userName;
// messageStore.alias = model.alias;
// messageStore.localName = model.localName;
// messageStore.message = model.message;
// messageStore.plainText = model.plainText;
// messageStore.identicon = model.identicon;
// messageStore.isCurrentUser = model.isCurrentUser;
// messageStore.timestamp = model.timestamp;
// messageStore.sticker = model.sticker;
// messageStore.contentType = model.contentType;
// messageStore.replaces = model.replaces;
// messageStore.isEdited = model.isEdited;
// messageStore.outgoingStatus = model.outgoingStatus;
// messageStore.responseTo = model.responseTo;
// messageStore.authorCurrentMsg = msgDelegate.ListView.section;
// // The previous message is actually the nextSection since we reversed the list order
// messageStore.authorPrevMsg = msgDelegate.ListView.nextSection;
// messageStore.imageClick = imagePopup.openPopup.bind(imagePopup);
// messageStore.messageId = model.messageId;
// messageStore.emojiReactions = model.emojiReactions;
// messageStore.linkUrls = model.linkUrls;
// messageStore.communityId = model.communityId;
// messageStore.hasMention = model.hasMention;
// messageStore.stickerPackId = model.stickerPackId;
// messageStore.pinnedMessage = model.isPinned;
// messageStore.pinnedBy = model.pinnedBy;
// messageStore.gapFrom = model.gapFrom;
// messageStore.gapTo = model.gapTo;
// messageStore.messageContextMenu = root.messageContextMenuInst;
// messageStore.prevMessageIndex =
// // This is used in order to have access to the previous message and determine the timestamp
// // we can't rely on the index because the sequence of messages is not ordered on the nim side
// (msgDelegate.DelegateModel.itemsIndex < messageListDelegate.items.count - 1) ?
// messageListDelegate.items.get(msgDelegate.DelegateModel.itemsIndex + 1).model.index
// : -1;
// messageStore.nextMessageIndex = (msgDelegate.DelegateModel.itemsIndex < 1) ?
// -1 : messageListDelegate.items.get(msgDelegate.DelegateModel.itemsIndex - 1).model.index;
// messageStore.scrollToBottom = chatLogView.scrollToBottom;
// messageStore.timeout = model.timeout;
}
}
Component.onCompleted: {
modelLoadingDelayTimer.start();
}
}
// MessageDialog {
// id: sendingMsgFailedPopup
// standardButtons: StandardButton.Ok
// //% "Failed to send message."
// text: qsTrId("failed-to-send-message-")
// icon: StandardIcon.Critical
// }
}

View File

@ -7,14 +7,15 @@ import utils 1.0
Column {
id: root
spacing: Style.current.padding
visible: authorCurrentMsg === ""
anchors.horizontalCenter: parent.horizontalCenter
topPadding: visible ? Style.current.bigPadding : 0
bottomPadding: visible? 50 : 0
property var store
property string authorCurrentMsg: "authorCurrentMsg"
property string profileImage
property string chatName: ""
property int chatType: -1
property string chatColor: ""
property string chatIcon: ""
property bool chatIconIsIdenticon: true
Rectangle {
id: circleId
@ -22,38 +23,24 @@ Column {
width: 120
height: 120
radius: 120
border.width: root.store.chatsModelInst.channelView.activeChannel.chatType === Constants.chatType.oneToOne ? 2 : 0
border.width: root.chatType === Constants.chatType.oneToOne ? 2 : 0
border.color: Style.current.border
color: {
if (root.store.chatsModelInst.channelView.activeChannel.chatType === Constants.chatType.oneToOne) {
return Style.current.transparent
}
if (root.store.chatsModelInst.channelView.activeChannel.color) {
return root.store.chatsModelInst.channelView.activeChannel.color
}
const color = root.store.chatsModelInst.channelView.getChannelColor(chatId)
if (!color) {
return Style.current.orange
}
return color
}
color: root.chatColor
RoundedImage {
visible: root.store.chatsModelInst.channelView.activeChannel.chatType === Constants.chatType.oneToOne
visible: root.chatType === Constants.chatType.oneToOne
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
width: 120
height: 120
source: root.profileImage || root.store.chatsModelInst.channelView.activeChannel.identicon
source: root.chatIcon
smooth: false
antialiasing: true
}
StyledText {
visible: root.store.chatsModelInst.channelView.activeChannel.chatType !== Constants.chatType.oneToOne
text: Utils.removeStatusEns((root.store.chatsModelInst.channelView.activeChannel.name.charAt(0) === "#" ?
root.store.chatsModelInst.channelView.activeChannel.name.charAt(1) :
root.store.chatsModelInst.channelView.activeChannel.name.charAt(0)).toUpperCase())
visible: root.chatType !== Constants.chatType.oneToOne
text: root.chatName.charAt(0).toUpperCase()
opacity: 0.7
font.weight: Font.Bold
font.pixelSize: 51
@ -66,13 +53,7 @@ Column {
StyledText {
id: channelName
wrapMode: Text.Wrap
text: {
switch(root.store.chatsModelInst.channelView.activeChannel.chatType) {
case Constants.chatType.publicChat: return "#" + root.store.chatsModelInst.channelView.activeChannel.name;
case Constants.chatType.oneToOne: return Utils.removeStatusEns(root.store.chatsModelInst.userNameOrAlias(chatsModel.channelView.activeChannel.id))
default: return root.store.chatsModelInst.channelView.activeChannel.name
}
}
text: root.chatName
font.weight: Font.Bold
font.pixelSize: 22
color: Style.current.textColor
@ -85,11 +66,13 @@ Column {
anchors.horizontalCenter: parent.horizontalCenter
width: 310
text: {
switch(root.store.chatsModelInst.channelView.activeChannel.chatType) {
//% "Welcome to the beginning of the <span style='color: %1'>%2</span> group!"
case Constants.chatType.privateGroupChat: return qsTrId("welcome-to-the-beginning-of-the--span-style--color---1---2--span--group-").arg(Style.current.textColor).arg(root.store.chatsModelInst.channelView.activeChannel.name);
//% "Any messages you send here are encrypted and can only be read by you and <span style='color: %1'>%2</span>"
case Constants.chatType.oneToOne: return qsTrId("any-messages-you-send-here-are-encrypted-and-can-only-be-read-by-you-and--span-style--color---1---2--span-").arg(Style.current.textColor).arg(channelName.text)
switch(root.chatType) {
case Constants.chatType.privateGroupChat:
//% "Welcome to the beginning of the <span style='color: %1'>%2</span> group!"
return qsTrId("welcome-to-the-beginning-of-the--span-style--color---1---2--span--group-").arg(Style.current.textColor).arg(root.chatName);
case Constants.chatType.oneToOne:
//% "Any messages you send here are encrypted and can only be read by you and <span style='color: %1'>%2</span>"
return qsTrId("any-messages-you-send-here-are-encrypted-and-can-only-be-read-by-you-and--span-style--color---1---2--span-").arg(Style.current.textColor).arg(root.chatName)
default: return "";
}
}
@ -100,8 +83,10 @@ Column {
}
Item {
visible: root.store.chatsModelInst.channelView.activeChannel.chatType === Constants.chatType.privateGroupChat
&& root.store.chatsModelInst.channelView.activeChannel.isMemberButNotJoined
//NEED TO CHECK THIS
// visible: root.store.chatsModelInst.channelView.activeChannel.chatType === Constants.chatType.privateGroupChat
// && root.store.chatsModelInst.channelView.activeChannel.isMemberButNotJoined
visible: root.chatType === Constants.chatType.privateGroupChat
anchors.horizontalCenter: parent.horizontalCenter
width: visible ? joinChat.width : 0
height: visible ? 100 : 0
@ -119,7 +104,8 @@ Column {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked: {
root.store.chatsModelInst.groups.join()
//NEED TO CHECK THIS
// root.store.chatsModelInst.groups.join()
}
}
}
@ -136,7 +122,8 @@ Column {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked: {
root.store.chatsModelInst.channelView.leaveActiveChat()
//NEED TO CHECK THIS
// root.store.chatsModelInst.channelView.leaveActiveChat()
}
}
}

View File

@ -51,57 +51,58 @@ Item {
height: root.veryLongChatText && !root.readMore ? Math.min(implicitHeight, 200) : implicitHeight
clip: height < implicitHeight
onLinkActivated: {
root.linkActivated(link)
if(link.startsWith("#")) {
const channelName = link.substring(1);
const foundChannelObj = root.store.chatsModelInst.getChannel(channelName);
// Not Refactored Yet
// root.linkActivated(link)
// if(link.startsWith("#")) {
// const channelName = link.substring(1);
// const foundChannelObj = root.store.chatsModelInst.getChannel(channelName);
if (!foundChannelObj)
{
root.store.chatsModelInst.channelView.joinPublicChat(channelName)
if(root.store.chatsModelInst.communities.activeCommunity.active)
{
root.store.chatsModelInst.channelView.joinPublicChat(channelName)
Global.changeAppSectionBySectionType(Constants.appSection.chat)
}
return
}
// if (!foundChannelObj)
// {
// root.store.chatsModelInst.channelView.joinPublicChat(channelName)
// if(root.store.chatsModelInst.communities.activeCommunity.active)
// {
// root.store.chatsModelInst.channelView.joinPublicChat(channelName)
// Global.changeAppSectionBySectionType(Constants.appSection.chat)
// }
// return
// }
let obj = JSON.parse(foundChannelObj)
// let obj = JSON.parse(foundChannelObj)
if(obj.chatType === -1 || obj.chatType === Constants.chatTypePublic)
{
if(root.store.chatsModelInst.communities.activeCommunity.active) {
root.store.chatsModelInst.channelView.joinPublicChat(channelName)
Global.changeAppSectionBySectionType(Constants.appSection.chat)
}
root.store.chatsModelInst.channelView.setActiveChannel(channelName);
}
else if(obj.communityId === root.store.chatsModelInst.communities.activeCommunity.id &&
obj.chatType === Constants.chatTypeCommunity &&
root.store.chatsModelInst.channelView.activeChannel.id !== obj.id
)
{
root.store.chatsModelInst.channelView.setActiveChannel(channelName);
}
// if(obj.chatType === -1 || obj.chatType === Constants.chatTypePublic)
// {
// if(root.store.chatsModelInst.communities.activeCommunity.active) {
// root.store.chatsModelInst.channelView.joinPublicChat(channelName)
// Global.changeAppSectionBySectionType(Constants.appSection.chat)
// }
// root.store.chatsModelInst.channelView.setActiveChannel(channelName);
// }
// else if(obj.communityId === root.store.chatsModelInst.communities.activeCommunity.id &&
// obj.chatType === Constants.chatTypeCommunity &&
// root.store.chatsModelInst.channelView.activeChannel.id !== obj.id
// )
// {
// root.store.chatsModelInst.channelView.setActiveChannel(channelName);
// }
return
}
// return
// }
if (link.startsWith('//')) {
let pk = link.replace("//", "");
const userProfileImage = appMain.getProfileImage(pk)
openProfilePopup(root.store.chatsModelInst.userNameOrAlias(pk), pk, userProfileImage || root.store.utilsModelInst.generateIdenticon(pk))
return;
}
// if (link.startsWith('//')) {
// let pk = link.replace("//", "");
// const userProfileImage = appMain.getProfileImage(pk)
// openProfilePopup(root.store.chatsModelInst.userNameOrAlias(pk), pk, userProfileImage || root.store.utilsModelInst.generateIdenticon(pk))
// return;
// }
const data = Utils.getLinkDataForStatusLinks(link)
if (data && data.callback) {
return data.callback()
}
// const data = Utils.getLinkDataForStatusLinks(link)
// if (data && data.callback) {
// return data.callback()
// }
Global.openLink(link)
// Global.openLink(link)
}
onLinkHovered: {

View File

@ -26,18 +26,13 @@ Item {
// Not Refactored Yet
return false
// if (!!rootStore) {
// switch (rootStore.chatsModelInst.channelView.activeChannel.chatType) {
// case Constants.chatType.oneToOne: return true
// case Constants.chatType.privateGroupChat: return rootStore.chatsModelInst.channelView.activeChannel.isAdmin(userProfile.pubKey) ? true : isCurrentUser
// case Constants.chatType.publicChat: return isCurrentUser
// case Constants.chatType.communityChat: return rootStore.chatsModelInst.communities.activeCommunity.admin ? true : isCurrentUser
// case Constants.chatType.profile: return false
// default: return false
// }
// }
// else {
// return false;
// switch (chatTypeThisMessageBelongsTo) {
// case Constants.chatType.oneToOne: return true
// case Constants.chatType.privateGroupChat: return rootStore.chatsModelInst.channelView.activeChannel.isAdmin(userProfile.pubKey) ? true : isCurrentUser
// case Constants.chatType.publicChat: return isCurrentUser
// case Constants.chatType.communityChat: return rootStore.chatsModelInst.communities.activeCommunity.admin ? true : isCurrentUser
// case Constants.chatType.profile: return false
// default: return false
// }
}
@ -206,8 +201,10 @@ Item {
}
StyledText {
//% "Pinned by %1"
text: qsTrId("pinned-by--1").arg(rootStore.chatsModelInst.alias(pinnedBy))
// Not Refactored Yet
text: ""
// //% "Pinned by %1"
// text: qsTrId("pinned-by--1").arg(rootStore.chatsModelInst.alias(pinnedBy))
anchors.left: pinImage.right
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 13
@ -217,14 +214,15 @@ Item {
}
Connections {
enabled: !!rootStore
target: enabled ? rootStore.chatsModelInst.messageView : null
onMessageEdited: {
if(chatReply.item)
chatReply.item.messageEdited(editedMessageId, editedMessageContent)
}
}
// Not Refactored Yet
// Connections {
// enabled: !!rootStore
// target: enabled ? rootStore.chatsModelInst.messageView : null
// onMessageEdited: {
// if(chatReply.item)
// chatReply.item.messageEdited(editedMessageId, editedMessageContent)
// }
// }
ChatReplyPanel {
id: chatReply
@ -303,7 +301,7 @@ Item {
anchors.left: chatName.right
anchors.leftMargin: 4
color: Style.current.secondaryText
//timestamp: timestamp
timestamp: messageTimestamp
}
Loader {
@ -376,7 +374,7 @@ Item {
StatusChatInput {
id: editTextInput
chatInputPlaceholder: qsTrId("type-a-message-")
chatType: rootStore.chatsModelInst.channelView.activeChannel.chatType
chatType: chatTypeThisMessageBelongsTo
isEdit: true
textInput.text: editMessageLoader.sourceText
onSendMessage: {
@ -437,7 +435,8 @@ Item {
visible: !isEdit
ChatTextView {
id: chatText
store: rootStore
// Not Refactored Yet
// store: rootStore
readonly property int leftPadding: chatImage.anchors.leftMargin + chatImage.width + chatHorizontalPadding
visible: {
const urls = linkUrls.split(" ")
@ -544,7 +543,8 @@ Item {
sourceComponent: Component {
LinksMessageView {
store: rootStore
// Not Refactored Yet
// store: rootStore
linkUrls: linkUrls
container: root.container
isCurrentUser: isCurrentUser
@ -585,7 +585,8 @@ Item {
sourceComponent: Component {
id: invitationBubble
InvitationBubbleView {
store: rootStore
// Not Refactored Yet
// store: rootStore
communityId: root.container.communityId
}
}

View File

@ -16,9 +16,7 @@ Column {
anchors.right: !isCurrentUser ? undefined : parent.right
z: (typeof chatLogView === "undefined") ? 1 : (chatLogView.count - index)
property var rootStore
property var messageStore
//property var chatsModel: !!root.rootStore ? root.rootStore.chatsModelInst : null
property string messageId: ""
property string responseToMessageWithId: ""
@ -35,6 +33,10 @@ Column {
property int messageContentType: 1
property bool pinnedMessage: false
// Used only in case of ChatIdentifier
property int chatTypeThisMessageBelongsTo: -1
property string chatColorThisMessageBelongsTo: ""
property int prevMessageIndex: -1
property var prevMessageAsJsonObj
property int nextMessageIndex: -1
@ -91,35 +93,18 @@ Column {
property bool shouldRepeatHeader: ((parseInt(timestamp, 10) - parseInt(prevMsgTimestamp, 10)) / 60 / 1000) > Constants.repeatHeaderInterval
//////////////////////////////////////
//TODO REMOVE
// property string fromAuthor: "0x0011223344556677889910"
// property string userName: "Jotaro Kujo"
// property string alias: ""
// property string localName: ""
// property string message: "That's right. We're friends... Of justice, that is."
//TODO CHECCK - REMOVE
property string plainText: "That's right. We're friends... Of justice, that is."
// property string identicon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQAQMAAAC6caSPAAAABlBMVEXMzMz////TjRV2AAAAAWJLR0QB/wIt3gAAACpJREFUGBntwYEAAAAAw6D7Uw/gCtUAAAAAAAAAAAAAAAAAAAAAAAAAgBNPsAABAjKCqQAAAABJRU5ErkJggg=="
// property bool isCurrentUser: false
// property string timestamp: "1234567"
property string sticker: "Qme8vJtyrEHxABcSVGPF95PtozDgUyfr1xGjePmFdZgk9v"
// property int contentType: 1 // constants don't work in default props
property string chatId: "chatId"
// property string outgoingStatus: ""
// property string responseTo: ""
// property string messageId: ""
property string emojiReactions: ""
// property int prevMessageIndex: -1
// property int nextMessageIndex: -1
property bool timeout: false
property bool hasMention: false
property string linkUrls: ""
property bool placeholderMessage: false
property bool activityCenterMessage: false
// property bool pinnedMessage: false
property bool read: true
property string pinnedBy
property bool forceHoverHandler: false // Used to force the HoverHandler to be active (useful for messages in popups)
property string communityId: ""
property int stickerPackId: -1
property int gapFrom: 0
property int gapTo: 0
@ -128,29 +113,8 @@ Column {
property bool isEdited: false
property bool showEdit: true
property var messageContextMenu
// property string displayUserName: {
// if (isCurrentUser) {
// //% "You"
// return qsTrId("You")
// }
//////////////////////////////////////
// if (localName !== "") {
// return localName
// }
// if (userName !== "") {
// return Utils.removeStatusEns(userName)
// }
// return Utils.removeStatusEns(alias)
// }
// property string authorCurrentMsg: "authorCurrentMsg"
// property string authorPrevMsg: "authorPrevMsg"
// property string prevMsgTimestamp: !!root.chatsModel ? root.chatsModel.messageView.messageList.getMessageData(prevMessageIndex, "timestamp") : ""
// property string nextMsgTimestamp: !!root.chatsModel ? root.chatsModel.messageView.messageList.getMessageData(nextMessageIndex, "timestamp"): ""
// property bool shouldRepeatHeader: ((parseInt(timestamp, 10) - parseInt(prevMsgTimestamp, 10)) / 60 / 1000) > Constants.repeatHeaderInterval
property bool isEmoji: contentType === Constants.messageContentType.emojiType
property bool isImage: contentType === Constants.messageContentType.imageType
@ -165,30 +129,8 @@ Column {
property bool isStatusUpdate: false
property int statusAgeEpoch: 0
// TODO: we don't use replyMessageIndex any more, but messageId
// property int replyMessageIndex: !!root.chatsModel ? root.chatsModel.messageView.messageList.getMessageIndex(responseTo) : -1
// property string repliedMessageAuthor: replyMessageIndex > -1 ? !!root.chatsModel ? root.chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "userName") : "" : "";
// property string repliedMessageAuthorPubkey: replyMessageIndex > -1 ? root.chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "publicKey") : "";
// property bool repliedMessageAuthorIsCurrentUser: replyMessageIndex > -1 ? repliedMessageAuthorPubkey === userProfile.pubKey : "";
// property bool repliedMessageIsEdited: replyMessageIndex > -1 ? !!root.chatsModel ? root.chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "isEdited") === "true" : false : false;
// property string repliedMessageContent: replyMessageIndex > -1 ? !!root.chatsModel ? root.chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "message") : "" : "";
// property int repliedMessageType: replyMessageIndex > -1 ? !!root.chatsModel ? parseInt(root.chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "contentType")) : 0 : 0;
// property string repliedMessageImage: replyMessageIndex > -1 ? !!root.chatsModel ? root.chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "image") : "" : "";
// property string repliedMessageUserIdenticon: replyMessageIndex > -1 ? !!root.chatsModel ? root.chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "identicon") : "" : "";
// property string repliedMessageUserImage: replyMessageIndex > -1 ? appMain.getProfileImage(repliedMessageAuthorPubkey, repliedMessageAuthorIsCurrentUser , false) || "" : "";
property var imageClick: function () {}
property var scrollToBottom: function () {}
// property string userPubKey: {
// if (contentType === Constants.messageContentType.chatIdentifier) {
// return chatId
// }
// return fromAuthor
// }
// property bool useLargeImage: contentType === Constants.messageContentType.chatIdentifier
// Not Refactored Yet - This will be determined on the backend
// property string profileImageSource: !placeholderMessage && appMain.getProfileImage(userPubKey, isCurrentUser, useLargeImage) || ""
property var emojiReactionsModel: {
// Not Refactored Yet
@ -327,7 +269,7 @@ Column {
id: gapComponent
GapComponent {
onClicked: {
// Not Refactored Yet
// Not Refactored Yet - Should do it via messageStore
// root.chatsModel.messageView.fillGaps(messageStore.messageId);
// root.visible = false;
// root.height = 0;
@ -338,14 +280,14 @@ Column {
Component {
id: fetchMoreMessagesButtonComponent
FetchMoreMessagesButton {
// nextMessageIndex: root.messageStore.nextMessageIndex
// nextMsgTimestamp: root.messageStore.nextMsgTimestamp
nextMessageIndex: root.nextMessageIndex
nextMsgTimestamp: root.nextMsgTimestamp
onClicked: {
// Not Refactored Yet
// Not Refactored Yet - Should do it via messageStore
// root.chatsModel.messageView.hideLoadingIndicator();
}
onTimerTriggered: {
// Not Refactored Yet
// Not Refactored Yet - Should do it via messageStore
// root.chatsModel.requestMoreMessages(Constants.fetchRangeLast24Hours);
}
}
@ -353,17 +295,13 @@ Column {
Component {
id: channelIdentifierComponent
Rectangle {
color: "blue"
width: 100
height: 100
ChannelIdentifierView {
chatName: root.senderDisplayName
chatType: root.chatTypeThisMessageBelongsTo
chatColor: root.chatColorThisMessageBelongsTo
chatIcon: root.senderIcon
chatIconIsIdenticon: root.isSenderIconIdenticon
}
// Not Refactored Yet
// ChannelIdentifierView {
// store: root.rootStore
// profileImage: profileImageSource
// authorCurrentMsg: root.authorCurrentMsg
// }
}
// Private group Messages
@ -402,25 +340,31 @@ Column {
StatusUpdateView {
statusAgeEpoch: root.statusAgeEpoch
container: root
store: root.rootStore
// Not Refactored Yet
// store: root.rootStore
messageContextMenu: root.messageContextMenu
onAddEmoji: {
root.clickMessage(isProfileClick, isSticker, isImage , image, emojiOnly, hideEmojiPicker);
}
onChatImageClicked: {
messageStore.imageClick(image);
// Not Refactored Yet - Should do it via messageStore
// messageStore.imageClick(image);
}
onUserNameClicked: {
root.parent.clickMessage(isProfileClick);
// Not Refactored Yet - Should do it via messageStore
// root.parent.clickMessage(isProfileClick);
}
onEmojiBtnClicked: {
root.parent.clickMessage(isProfileClick, isSticker, isImage, image, emojiOnly);
// Not Refactored Yet - Should do it via messageStore
// root.parent.clickMessage(isProfileClick, isSticker, isImage, image, emojiOnly);
}
onClickMessage: {
root.parent.clickMessage(isProfileClick, isSticker, isImage, image, emojiOnly, hideEmojiPicker, isReply);
// Not Refactored Yet - Should do it via messageStore
// root.parent.clickMessage(isProfileClick, isSticker, isImage, image, emojiOnly, hideEmojiPicker, isReply);
}
onSetMessageActive: {
root.messageStore.setMessageActive(messageId, active);;
// Not Refactored Yet - Should do it via messageStore
// root.messageStore.setMessageActive(messageId, active);;
}
}
}

View File

@ -0,0 +1,205 @@
import QtQuick 2.3
import QtGraphicalEffects 1.13
import utils 1.0
import shared 1.0
import shared.panels 1.0
import shared.status 1.0
import shared.panels.chat 1.0
import shared.controls.chat 1.0
import StatusQ.Controls 0.1
MouseArea {
id: root
// property var store
property bool hovered: containsMouse
property var container
property int statusAgeEpoch: 0
property var messageContextMenu
signal userNameClicked(bool isProfileClick)
signal setMessageActive(string messageId, bool active)
signal emojiBtnClicked(bool isProfileClick, bool isSticker, bool isImage, var image, bool emojiOnly)
signal clickMessage(bool isProfileClick, bool isSticker, bool isImage, var image, bool emojiOnly, bool hideEmojiPicker, bool isReply)
// TODO bring those back and remove dynamic scoping
// property var emojiReactionsModel
// property string timestamp: ""
// property bool isCurrentUser: false
// property bool isMessageActive: false
// property string userName: ""
// property string localName: ""
// property string displayUserName: ""
// property bool isImage: false
// property bool isMessage: false
// property string profileImageSource: ""
// property string userIdenticon: ""
anchors.top: parent.top
anchors.topMargin: 0
height: (isImage ? chatImageContent.height : chatText.height) + chatName.height + 2* Style.current.padding + (emojiReactionsModel.length ? 20 : 0)
width: parent.width
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
propagateComposedEvents: true
signal chatImageClicked(string image)
signal addEmoji(bool isProfileClick, bool isSticker, bool isImage , var image, bool emojiOnly, bool hideEmojiPicker)
onClicked: {
mouse.accepted = false
}
Rectangle {
id: rootRect
anchors.fill: parent
radius: Style.current.radius
color: root.hovered ? Style.current.border : Style.current.background
UserImage {
id: chatImage
active: isMessage || isImage
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
anchors.top: parent.top
anchors.topMargin: Style.current.halfPadding
// messageContextMenu: root.messageContextMenu
// profileImage: root.profileImageSource
// isMessage: root.isMessage
// identiconImageSource: root.userIdenticon
onClickMessage: {
root.clickMessage(true, false, false, null, false, false, isReplyImage)
}
}
UsernameLabel {
id: chatName
z: 51
visible: chatImage.visible
anchors.leftMargin: Style.current.halfPadding
anchors.top: chatImage.top
anchors.left: chatImage.right
label.font.pixelSize: Style.current.primaryTextFontSize
// messageContextMenu: root.messageContextMenu
// isCurrentUser: root.isCurrentUser
// userName: root.userName
// localName: root.localName
// displayUserName: root.displayUserName
onClickMessage: {
root.userNameClicked(true);
}
}
ChatTimePanel {
id: chatTime
// statusAgeEpoch is used to trigger Qt property update
// since the returned string will be the same in 99% cases, this should not trigger ChatTime re-rendering
text: Utils.formatAgeFromTime(timestamp, statusAgeEpoch)
visible: chatName.visible
anchors.verticalCenter: chatName.verticalCenter
anchors.left: chatName.right
anchors.leftMargin: Style.current.halfPadding
//timestamp: timestamp
}
ChatTextView {
id: chatText
anchors.top: chatName.visible ? chatName.bottom : chatImage.top
anchors.topMargin: chatName.visible ? 6 : 0
anchors.left: chatImage.right
anchors.leftMargin: Style.current.halfPadding
anchors.right: parent.right
anchors.rightMargin: Style.current.padding
// store: root.store
}
Loader {
id: chatImageContent
active: isImage
anchors.left: chatImage.right
anchors.leftMargin: Style.current.halfPadding
anchors.top: chatText.bottom
z: 51
sourceComponent: Component {
StatusChatImage {
imageSource: image
imageWidth: 200
container: root.container
onClicked: {
root.chatImageClicked(image);
}
}
}
}
StatusFlatRoundButton {
id: emojiBtn
width: 32
height: 32
anchors.top: rootRect.top
anchors.topMargin: -height / 4
anchors.right: rootRect.right
anchors.rightMargin: Style.current.halfPadding
visible: root.hovered
icon.name: "reaction-b"
icon.width: 20
icon.height: 20
type: StatusFlatRoundButton.Type.Tertiary
backgroundHoverColor: Style.current.background
onClicked: {
// Set parent, X & Y positions for the messageContextMenu
messageContextMenu.parent = emojiBtn
messageContextMenu.setXPosition = function() { return -messageContextMenu.width + emojiBtn.width}
messageContextMenu.setYPosition = function() { return -messageContextMenu.height - 4}
root.emojiBtnClicked(false, false, false, null, true)
}
}
DropShadow {
anchors.fill: emojiBtn
horizontalOffset: 0
verticalOffset: 2
radius: 10
samples: 12
color: "#22000000"
source: emojiBtn
}
Loader {
id: emojiReactionLoader
active: emojiReactionsModel.length
sourceComponent: emojiReactionsComponent
anchors.left: chatImage.right
anchors.leftMargin: Style.current.halfPadding
anchors.top: isImage ? chatImageContent.bottom : chatText.bottom
anchors.topMargin: Style.current.halfPadding
}
Component {
id: emojiReactionsComponent
EmojiReactionsPanel {
// isMessageActive: root.isMessageActive
// emojiReactionsModel: root.emojiReactionsModel
onAddEmojiClicked: {
root.addEmoji(false, false, false, null, true, false);
// Set parent, X & Y positions for the messageContextMenu
messageContextMenu.parent = emojiReactionLoader
messageContextMenu.setXPosition = function() { return (messageContextMenu.parent.x + 4)}
messageContextMenu.setYPosition = function() { return (-messageContextMenu.height - 4)}
}
onToggleReaction: chatsModel.toggleReaction(messageId, emojiID)
// onSetMessageActive: {
// root.setMessageActive(messageId, active);;
// }
}
}
Separator {
anchors.bottom: parent.bottom
visible: !root.hovered
}
}
}