parent
1ead1c3db5
commit
0767ce2443
|
@ -1,4 +1,5 @@
|
|||
import sugar, sequtils, times, strutils
|
||||
import ../../status/chat/chat as status_chat
|
||||
|
||||
proc handleChatEvents(self: ChatController) =
|
||||
# Display already saved messages
|
||||
|
@ -39,7 +40,10 @@ proc handleChatEvents(self: ChatController) =
|
|||
|
||||
self.status.events.on("channelLoaded") do(e: Args):
|
||||
var channel = ChannelArgs(e)
|
||||
discard self.view.chats.addChatItemToList(channel.chat)
|
||||
if channel.chat.chatType == ChatType.Timeline:
|
||||
self.view.setTimelineChat(channel.chat)
|
||||
elif channel.chat.chatType != ChatType.Profile:
|
||||
discard self.view.chats.addChatItemToList(channel.chat)
|
||||
self.status.chat.chatMessages(channel.chat.id)
|
||||
self.status.chat.chatReactions(channel.chat.id)
|
||||
|
||||
|
@ -50,13 +54,18 @@ proc handleChatEvents(self: ChatController) =
|
|||
|
||||
self.status.events.on("channelJoined") do(e: Args):
|
||||
var channel = ChannelArgs(e)
|
||||
discard self.view.chats.addChatItemToList(channel.chat)
|
||||
if channel.chat.chatType == ChatType.Timeline:
|
||||
self.view.setTimelineChat(channel.chat)
|
||||
elif channel.chat.chatType != ChatType.Profile:
|
||||
discard self.view.chats.addChatItemToList(channel.chat)
|
||||
self.view.setActiveChannel(channel.chat.id)
|
||||
self.status.chat.chatMessages(channel.chat.id)
|
||||
self.status.chat.chatReactions(channel.chat.id)
|
||||
self.view.setActiveChannel(channel.chat.id)
|
||||
|
||||
self.status.events.on("channelLeft") do(e: Args):
|
||||
self.view.removeChat(ChatIdArg(e).chatId)
|
||||
let chatId = ChatIdArg(e).chatId
|
||||
self.view.removeChat(chatId)
|
||||
self.view.removeMessagesFromTimeline(chatId)
|
||||
|
||||
self.status.events.on("activeChannelChanged") do(e: Args):
|
||||
self.view.setActiveChannel(ChatIdArg(e).chatId)
|
||||
|
|
|
@ -4,6 +4,7 @@ import ../../status/mailservers
|
|||
import ../../status/libstatus/accounts/constants
|
||||
import ../../status/libstatus/mailservers as status_mailservers
|
||||
import ../../status/libstatus/types
|
||||
import ../../status/libstatus/utils as status_utils
|
||||
import ../../status/accounts as status_accounts
|
||||
import ../../status/chat as status_chat
|
||||
import ../../status/messages as status_messages
|
||||
|
@ -33,12 +34,14 @@ QtObject:
|
|||
groups*: GroupsView
|
||||
transactions*: TransactionsView
|
||||
activeChannel*: ChatItemView
|
||||
previousActiveChannelIndex: int
|
||||
replyTo: string
|
||||
channelOpenTime*: Table[string, int64]
|
||||
connected: bool
|
||||
unreadMessageCnt: int
|
||||
oldestMessageTimestamp: int64
|
||||
loadingMessages: bool
|
||||
timelineChat: Chat
|
||||
pubKey*: string
|
||||
|
||||
proc setup(self: ChatsView) = self.QAbstractListModel.setup
|
||||
|
@ -71,6 +74,9 @@ QtObject:
|
|||
result.transactions = newTransactionsView(status)
|
||||
result.unreadMessageCnt = 0
|
||||
result.loadingMessages = false
|
||||
result.previousActiveChannelIndex = -1
|
||||
result.messageList[status_utils.getTimelineChatId()] = newChatMessageList(status_utils.getTimelineChatId(), result.status, false)
|
||||
|
||||
result.setup()
|
||||
|
||||
proc oldestMessageTimestampChanged*(self: ChatsView) {.signal.}
|
||||
|
@ -205,6 +211,7 @@ QtObject:
|
|||
if selectedChannel.chatType.isOneToOne and selectedChannel.id == selectedChannel.name:
|
||||
selectedChannel.name = self.userNameOrAlias(selectedChannel.id)
|
||||
|
||||
self.previousActiveChannelIndex = index
|
||||
self.activeChannel.setChatItem(selectedChannel)
|
||||
self.status.chat.setActiveChannel(selectedChannel.id)
|
||||
|
||||
|
@ -231,6 +238,16 @@ QtObject:
|
|||
write = setActiveChannel
|
||||
notify = activeChannelChanged
|
||||
|
||||
proc setActiveChannelToTimeline*(self: ChatsView) {.slot.} =
|
||||
if not self.activeChannel.chatItem.isNil:
|
||||
self.previousActiveChannelIndex = self.chats.chats.findIndexById(self.activeChannel.id)
|
||||
self.activeChannel.setChatItem(self.timelineChat)
|
||||
self.activeChannelChanged()
|
||||
|
||||
proc restorePreviousActiveChannel*(self: ChatsView) {.slot.} =
|
||||
if self.activeChannel.id == self.timelineChat.id and not self.previousActiveChannelIndex == -1:
|
||||
self.setActiveChannelByIndex(self.previousActiveChannelIndex)
|
||||
|
||||
proc getCurrentSuggestions(self: ChatsView): QVariant {.slot.} =
|
||||
return newQVariant(self.currentSuggestions)
|
||||
|
||||
|
@ -238,8 +255,11 @@ QtObject:
|
|||
read = getCurrentSuggestions
|
||||
|
||||
proc upsertChannel(self: ChatsView, channel: string) =
|
||||
var chat: Chat = nil
|
||||
if self.status.chat.channels.hasKey(channel):
|
||||
chat = self.status.chat.channels[channel]
|
||||
if not self.messageList.hasKey(channel):
|
||||
self.messageList[channel] = newChatMessageList(channel, self.status)
|
||||
self.messageList[channel] = newChatMessageList(channel, self.status, not chat.isNil and chat.chatType != ChatType.Profile)
|
||||
self.channelOpenTime[channel] = now().toTime.toUnix * 1000
|
||||
|
||||
proc messagePushed*(self: ChatsView) {.signal.}
|
||||
|
@ -257,10 +277,15 @@ QtObject:
|
|||
for msg in messages.mitems:
|
||||
self.upsertChannel(msg.chatId)
|
||||
msg.userName = self.status.chat.getUserName(msg.fromAuthor, msg.alias)
|
||||
self.messageList[msg.chatId].add(msg)
|
||||
if self.status.chat.channels.hasKey(msg.chatId):
|
||||
let chat = self.status.chat.channels[msg.chatId]
|
||||
if (chat.chatType == ChatType.Profile):
|
||||
self.messageList[status_utils.getTimelineChatId()].add(msg)
|
||||
else:
|
||||
self.messageList[msg.chatId].add(msg)
|
||||
self.messagePushed()
|
||||
if self.channelOpenTime.getOrDefault(msg.chatId, high(int64)) < msg.timestamp.parseFloat.fromUnixFloat.toUnix:
|
||||
let channel = self.chats.getChannelById(msg.chatId)
|
||||
let channel = self.status.chat.channels[msg.chatId]
|
||||
let isAddedContact = channel.chatType.isOneToOne and self.status.contacts.isAdded(channel.id)
|
||||
if not channel.muted:
|
||||
self.messageNotificationPushed(
|
||||
|
@ -274,12 +299,10 @@ QtObject:
|
|||
msg.hasMention,
|
||||
isAddedContact,
|
||||
channel.name)
|
||||
|
||||
else:
|
||||
discard self.status.chat.markMessagesSeen(msg.chatId, @[msg.id])
|
||||
self.newMessagePushed()
|
||||
|
||||
|
||||
proc updateUsernames*(self:ChatsView, contacts: seq[Profile]) =
|
||||
if contacts.len > 0:
|
||||
# Updating usernames for all the messages list
|
||||
|
@ -320,6 +343,9 @@ QtObject:
|
|||
discard self.chats.addChatItemToList(chatItem)
|
||||
self.messagePushed()
|
||||
|
||||
proc setTimelineChat*(self: ChatsView, chatItem: Chat) =
|
||||
self.timelineChat = chatItem
|
||||
|
||||
proc copyToClipboard*(self: ChatsView, content: string) {.slot.} =
|
||||
setClipBoardText(content)
|
||||
|
||||
|
@ -400,6 +426,10 @@ QtObject:
|
|||
self.messageList[chatId].delete
|
||||
self.messageList.del(chatId)
|
||||
|
||||
proc removeMessagesFromTimeline*(self: ChatsView, chatId: string) =
|
||||
self.messageList[status_utils.getTimelineChatId()].deleteMessagesByChatId(chatId)
|
||||
self.activeChannelChanged()
|
||||
|
||||
proc clearChatHistory*(self: ChatsView, id: string) {.slot.} =
|
||||
self.status.chat.clearHistory(id)
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ QtObject:
|
|||
proc upsertChannel(self: ChannelsList, channel: Chat): int =
|
||||
let idx = self.chats.findIndexById(channel.id)
|
||||
if idx == -1:
|
||||
if channel.isActive:
|
||||
if channel.isActive and channel.chatType != ChatType.Profile and channel.chatType != ChatType.Timeline:
|
||||
# We only want to add a channel to the list if it is active
|
||||
# otherwise, we'll end up with zombie channels on the list
|
||||
result = self.addChatItemToList(channel)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import NimQml, Tables, sets, json
|
||||
import NimQml, Tables, sets, json, sugar
|
||||
import ../../../status/status
|
||||
import ../../../status/accounts
|
||||
import ../../../status/chat
|
||||
|
@ -64,9 +64,12 @@ QtObject:
|
|||
result.contentType = ContentType.ChatIdentifier;
|
||||
result.chatId = chatId
|
||||
|
||||
proc newChatMessageList*(chatId: string, status: Status): ChatMessageList =
|
||||
proc newChatMessageList*(chatId: string, status: Status, addFakeMessages: bool = true): ChatMessageList =
|
||||
new(result, delete)
|
||||
result.messages = @[result.chatIdentifier(chatId), result.fetchMoreMessagesButton()]
|
||||
result.messages = @[]
|
||||
if addFakeMessages:
|
||||
result.messages.add(result.chatIdentifier(chatId))
|
||||
result.messages.add(result.fetchMoreMessagesButton())
|
||||
result.messageIndex = initTable[string, int]()
|
||||
result.timedoutMessages = initHashSet[string]()
|
||||
result.status = status
|
||||
|
@ -81,6 +84,11 @@ QtObject:
|
|||
self.messageReactions.del(messageId)
|
||||
self.endRemoveRows()
|
||||
|
||||
proc deleteMessagesByChatId*(self: ChatMessageList, chatId: string) =
|
||||
let messages = self.messages.filter(m => m.chatId == chatId)
|
||||
for message in messages:
|
||||
self.deleteMessage(message.id)
|
||||
|
||||
proc resetTimeOut*(self: ChatMessageList, messageId: string) =
|
||||
if not self.messageIndex.hasKey(messageId): return
|
||||
let msgIdx = self.messageIndex[messageId]
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import NimQml, chronicles, sequtils, sugar, strutils
|
||||
import ../../../status/libstatus/utils as status_utils
|
||||
import ../../../status/status
|
||||
import ../../../status/threads
|
||||
import ../../../status/chat/chat
|
||||
import contact_list
|
||||
import ../../../status/profile/profile
|
||||
import ../../../status/ens as status_ens
|
||||
|
@ -135,6 +137,7 @@ QtObject:
|
|||
|
||||
proc addContact*(self: ContactsView, publicKey: string): string {.slot.} =
|
||||
result = self.status.contacts.addContact(publicKey)
|
||||
self.status.chat.join(status_utils.getTimelineChatId(publicKey), ChatType.Profile, "", publicKey)
|
||||
self.contactChanged(publicKey, true)
|
||||
|
||||
proc changeContactNickname*(self: ContactsView, publicKey: string, nickname: string) {.slot.} =
|
||||
|
@ -153,4 +156,7 @@ QtObject:
|
|||
|
||||
proc removeContact*(self: ContactsView, publicKey: string) {.slot.} =
|
||||
self.status.contacts.removeContact(publicKey)
|
||||
let channelId = status_utils.getTimelineChatId(publicKey)
|
||||
if self.status.chat.hasChannel(channelId):
|
||||
self.status.chat.leave(channelId)
|
||||
self.contactChanged(publicKey, false)
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import json, strutils, sequtils, tables, chronicles, times
|
||||
import json, strutils, sequtils, tables, chronicles, times, sugar
|
||||
import libstatus/chat as status_chat
|
||||
import libstatus/mailservers as status_mailservers
|
||||
import libstatus/chatCommands as status_chat_commands
|
||||
import libstatus/accounts/constants as constants
|
||||
import libstatus/types
|
||||
import libstatus/utils as status_utils
|
||||
import libstatus/contacts as status_contacts
|
||||
import stickers
|
||||
import ../eventemitter
|
||||
|
||||
|
@ -89,12 +91,12 @@ proc hasChannel*(self: ChatModel, chatId: string): bool =
|
|||
proc getActiveChannel*(self: ChatModel): string =
|
||||
if (self.channels.len == 0): "" else: toSeq(self.channels.values)[self.channels.len - 1].id
|
||||
|
||||
proc join*(self: ChatModel, chatId: string, chatType: ChatType, ensName: string = "") =
|
||||
proc join*(self: ChatModel, chatId: string, chatType: ChatType, ensName: string = "", pubKey: string = "") =
|
||||
if self.hasChannel(chatId): return
|
||||
|
||||
var chat = newChat(chatId, ChatType(chatType))
|
||||
self.channels[chat.id] = chat
|
||||
status_chat.saveChat(chatId, chatType, true, chat.color, ensName)
|
||||
status_chat.saveChat(chatId, chatType, color=chat.color, ensName=ensName, profile=pubKey)
|
||||
if ensName != "":
|
||||
chat.name = ensName
|
||||
chat.ensName = ensName
|
||||
|
@ -127,6 +129,32 @@ proc updateContacts*(self: ChatModel, contacts: seq[Profile]) =
|
|||
|
||||
proc init*(self: ChatModel, pubKey: string) =
|
||||
var chatList = status_chat.loadChats()
|
||||
var contacts = getAddedContacts()
|
||||
|
||||
let profileUpdatesChatIds = chatList.filter(c => c.chatType == ChatType.Profile).map(c => c.id)
|
||||
|
||||
if chatList.filter(c => c.chatType == ChatType.Timeline).len == 0:
|
||||
var timelineChannel = newChat(status_utils.getTimelineChatId(), ChatType.Timeline)
|
||||
self.join(timelineChannel.id, timelineChannel.chatType)
|
||||
chatList.add(timelineChannel)
|
||||
|
||||
let timelineChatId = status_utils.getTimelineChatId(pubKey)
|
||||
|
||||
if not profileUpdatesChatIds.contains(timelineChatId):
|
||||
var profileUpdateChannel = newChat(timelineChatId, ChatType.Profile)
|
||||
status_chat.saveChat(profileUpdateChannel.id, profileUpdateChannel.chatType, profile=pubKey)
|
||||
chatList.add(profileUpdateChannel)
|
||||
|
||||
# For profile updates and timeline, we have to make sure that for
|
||||
# each added contact, a chat has been saved for the currently logged-in
|
||||
# user. Users that will use a version of Status with timeline support for the
|
||||
# first time, won't have any of those otherwise.
|
||||
if profileUpdatesChatIds.filter(id => id != timelineChatId).len != contacts.len:
|
||||
for contact in contacts:
|
||||
if not profileUpdatesChatIds.contains(status_utils.getTimelineChatId(contact.address)):
|
||||
let profileUpdatesChannel = newChat(status_utils.getTimelineChatId(contact.address), ChatType.Profile)
|
||||
status_chat.saveChat(profileUpdatesChannel.id, profileUpdatesChannel.chatType, ensName=contact.ensName, profile=contact.address)
|
||||
chatList.add(profileUpdatesChannel)
|
||||
|
||||
var filters:seq[JsonNode] = @[]
|
||||
for chat in chatList:
|
||||
|
|
|
@ -10,6 +10,7 @@ type ChatType* {.pure.}= enum
|
|||
Timeline = 5
|
||||
|
||||
proc isOneToOne*(self: ChatType): bool = self == ChatType.OneToOne
|
||||
proc isTimeline*(self: ChatType): bool = self == ChatType.Timeline
|
||||
|
||||
type ChatMember* = object
|
||||
admin*: bool
|
||||
|
|
|
@ -39,7 +39,7 @@ proc removeChatFilters(self: ChatModel, chatId: string) =
|
|||
for filter in filters:
|
||||
if filter["chatId"].getStr == chatId:
|
||||
status_chat.removeFilters(chatId, filter["filterId"].getStr)
|
||||
of ChatType.OneToOne:
|
||||
of ChatType.OneToOne, ChatType.Profile:
|
||||
# Check if user does not belong to any active chat group
|
||||
var inGroup = false
|
||||
for channel in self.channels.values:
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import json, sequtils, sugar
|
||||
import libstatus/contacts as status_contacts
|
||||
import libstatus/accounts as status_accounts
|
||||
import libstatus/chat as status_chat
|
||||
import libstatus/utils as status_utils
|
||||
import chat/chat
|
||||
#import chat/utils
|
||||
import profile/profile
|
||||
import ../eventemitter
|
||||
|
||||
|
@ -69,6 +73,7 @@ proc addContact*(self: ContactModel, id: string, localNickname: string): string
|
|||
let updating = contact.systemTags.contains(":contact/added")
|
||||
if not updating:
|
||||
contact.systemTags.add(":contact/added")
|
||||
status_chat.saveChat(getTimelineChatId(contact.id), ChatType.Profile, ensName=contact.ensName, profile=contact.id)
|
||||
let nickname =
|
||||
if (localNickname == ""):
|
||||
contact.localNickname
|
||||
|
|
|
@ -18,7 +18,7 @@ proc removeFilters*(chatId: string, filterId: string) =
|
|||
[{ "ChatID": chatId, "FilterID": filterId }]
|
||||
])
|
||||
|
||||
proc saveChat*(chatId: string, chatType: ChatType, active: bool = true, color: string, ensName: string = "", profile: string = "") =
|
||||
proc saveChat*(chatId: string, chatType: ChatType, active: bool = true, color: string = "#000000", ensName: string = "", profile: string = "") =
|
||||
# TODO: ideally status-go/stimbus should handle some of these fields instead of having the client
|
||||
# send them: lastMessage, unviewedMEssagesCount, timestamp, lastClockValue, name?
|
||||
discard callPrivateRPC("saveChat".prefix, %* [
|
||||
|
|
|
@ -4,6 +4,12 @@ from times import getTime, toUnix, nanosecond
|
|||
import accounts/signing_phrases
|
||||
from web3 import Address, fromHex
|
||||
|
||||
proc getTimelineChatId*(pubKey: string = ""): string =
|
||||
if pubKey == "":
|
||||
return "@timeline70bd746ddcc12beb96b2c9d572d0784ab137ffc774f5383e50585a932080b57cca0484b259e61cecbaa33a4c98a300a"
|
||||
else:
|
||||
return "@" & pubKey
|
||||
|
||||
proc isWakuEnabled(): bool =
|
||||
true # TODO:
|
||||
|
||||
|
|
|
@ -98,7 +98,8 @@ proc newChat*(id: string, chatType: ChatType): Chat =
|
|||
lastClockValue: 0,
|
||||
deletedAtClockValue: 0,
|
||||
unviewedMessagesCount: 0,
|
||||
hasMentions: false
|
||||
hasMentions: false,
|
||||
members: @[]
|
||||
)
|
||||
|
||||
if chatType == ChatType.OneToOne:
|
||||
|
|
|
@ -266,7 +266,7 @@ StackLayout {
|
|||
}
|
||||
onSendMessage: {
|
||||
if (chatInput.fileUrls.length > 0){
|
||||
chatsModel.sendImage(chatInput.fileUrls[0]);
|
||||
chatsModel.sendImage(chatInput.fileUrls[0], false);
|
||||
}
|
||||
var msg = chatsModel.plainText(Emoji.deparse(chatInput.textInput.text))
|
||||
if (msg.length > 0){
|
||||
|
|
|
@ -13,6 +13,7 @@ SplitView {
|
|||
property alias chatColumn: chatColumn
|
||||
|
||||
property var onActivated: function () {
|
||||
chatsModel.restorePreviousActiveChannel()
|
||||
chatColumn.onActivated()
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import QtQml.Models 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import "../../../imports"
|
||||
import "../../../shared"
|
||||
import "../../../shared/status"
|
||||
import "../Chat/data"
|
||||
import "../Chat/ChatColumn"
|
||||
import "../Chat/components"
|
||||
|
||||
ScrollView {
|
||||
id: root
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
contentHeight: timelineContainer.height + 40
|
||||
clip: true
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
property var onActivated: function () {
|
||||
chatsModel.setActiveChannelToTimeline()
|
||||
statusUpdateInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
statusUpdateInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
||||
}
|
||||
|
||||
function openProfilePopup(userNameParam, fromAuthorParam, identiconParam, textParam, nicknameParam, parentPopup){
|
||||
var popup = profilePopupComponent.createObject(root);
|
||||
if(parentPopup){
|
||||
popup.parentPopup = parentPopup;
|
||||
}
|
||||
popup.openPopup(profileModel.profile.pubKey !== fromAuthorParam, userNameParam, fromAuthorParam, identiconParam, textParam, nicknameParam);
|
||||
}
|
||||
|
||||
|
||||
MessageContextMenu {
|
||||
id: messageContextMenu
|
||||
}
|
||||
|
||||
StatusImageModal {
|
||||
id: imagePopup
|
||||
}
|
||||
|
||||
EmojiReactions {
|
||||
id: reactionModel
|
||||
}
|
||||
|
||||
property Component profilePopupComponent: ProfilePopup {
|
||||
id: profilePopup
|
||||
height: 450
|
||||
onClosed: {
|
||||
if(profilePopup.parentPopup){
|
||||
profilePopup.parentPopup.close();
|
||||
}
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: timelineContainer
|
||||
width: 624
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: childrenRect.height
|
||||
color: "transparent"
|
||||
|
||||
StatusChatInput {
|
||||
id: statusUpdateInput
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 40
|
||||
chatType: Constants.chatTypeStatusUpdate
|
||||
onSendMessage: {
|
||||
if (statusUpdateInput.fileUrls.length > 0){
|
||||
chatsModel.sendImage(statusUpdateInput.fileUrls[0], true);
|
||||
}
|
||||
var msg = chatsModel.plainText(Emoji.deparse(statusUpdateInput.textInput.text))
|
||||
if (msg.length > 0){
|
||||
msg = statusUpdateInput.interpretMessage(msg)
|
||||
chatsModel.sendMessage(msg, "", Utils.isOnlyEmoji(msg) ? Constants.emojiType : Constants.messageType, true);
|
||||
statusUpdateInput.textInput.text = "";
|
||||
if(event) event.accepted = true
|
||||
statusUpdateInput.messageSound.stop()
|
||||
Qt.callLater(statusUpdateInput.messageSound.play);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EmptyTimeline {
|
||||
id: emptyTimeline
|
||||
anchors.top: statusUpdateInput.bottom
|
||||
anchors.topMargin: 40
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: chatsModel.messageList.rowCount() === 0
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: chatLogView
|
||||
anchors.top: statusUpdateInput.bottom
|
||||
anchors.topMargin: 40
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: childrenRect.height + 40
|
||||
spacing: 10
|
||||
flickDeceleration: 10000
|
||||
interactive: false
|
||||
|
||||
model: messageListDelegate
|
||||
section.property: "sectionIdentifier"
|
||||
section.criteria: ViewSection.FullString
|
||||
}
|
||||
|
||||
DelegateModel {
|
||||
id: messageListDelegate
|
||||
property var moreThan: [
|
||||
function(left, right) { return left.clock > right.clock }
|
||||
]
|
||||
|
||||
property int sortOrder: 0
|
||||
onSortOrderChanged: items.setGroups(0, items.count, "unsorted")
|
||||
|
||||
function insertPosition(moreThan, item) {
|
||||
var lower = 0
|
||||
var upper = items.count
|
||||
while (lower < upper) {
|
||||
var middle = Math.floor(lower + (upper - lower) / 2)
|
||||
var result = moreThan(item.model, items.get(middle).model);
|
||||
if (result) {
|
||||
upper = middle
|
||||
} else {
|
||||
lower = middle + 1
|
||||
}
|
||||
}
|
||||
return lower
|
||||
}
|
||||
|
||||
function sort(moreThan) {
|
||||
while (unsortedItems.count > 0) {
|
||||
var item = unsortedItems.get(0)
|
||||
var index = insertPosition(moreThan, item)
|
||||
item.groups = "items"
|
||||
items.move(item.itemsIndex, index)
|
||||
}
|
||||
}
|
||||
|
||||
items.includeByDefault: false
|
||||
groups: DelegateModelGroup {
|
||||
id: unsortedItems
|
||||
name: "unsorted"
|
||||
includeByDefault: true
|
||||
onChanged: {
|
||||
if (messageListDelegate.sortOrder == messageListDelegate.moreThan.length)
|
||||
setGroups(0, count, "items")
|
||||
else {
|
||||
messageListDelegate.sort(messageListDelegate.moreThan[messageListDelegate.sortOrder])
|
||||
}
|
||||
}
|
||||
}
|
||||
model: chatsModel.messageList
|
||||
|
||||
delegate: Message {
|
||||
id: msgDelegate
|
||||
fromAuthor: model.fromAuthor
|
||||
chatId: model.chatId
|
||||
userName: model.userName
|
||||
localName: model.localName
|
||||
alias: model.alias
|
||||
message: model.message
|
||||
plainText: model.plainText
|
||||
identicon: model.identicon
|
||||
isCurrentUser: model.isCurrentUser
|
||||
timestamp: model.timestamp
|
||||
sticker: model.sticker
|
||||
contentType: model.contentType
|
||||
outgoingStatus: model.outgoingStatus
|
||||
responseTo: model.responseTo
|
||||
authorCurrentMsg: msgDelegate.ListView.section
|
||||
authorPrevMsg: msgDelegate.ListView.previousSection
|
||||
imageClick: imagePopup.openPopup.bind(imagePopup)
|
||||
messageId: model.messageId
|
||||
emojiReactions: model.emojiReactions
|
||||
isStatusUpdate: true
|
||||
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 > 0){
|
||||
return messageListDelegate.items.get(msgDelegate.DelegateModel.itemsIndex - 1).model.index
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
timeout: model.timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import "../imports"
|
|||
import "../shared"
|
||||
import "../shared/status"
|
||||
import "./AppLayouts"
|
||||
import "./AppLayouts/Timeline"
|
||||
import "./AppLayouts/Wallet"
|
||||
|
||||
RowLayout {
|
||||
|
@ -186,9 +187,16 @@ RowLayout {
|
|||
icon.name: "compass"
|
||||
}
|
||||
|
||||
StatusIconTabButton {
|
||||
id: timelineBtn
|
||||
anchors.top: browserBtn.enabled ? browserBtn.top : walletBtn.top
|
||||
enabled: isExperimental === "1" || appSettings.timelineEnabled
|
||||
icon.name: "timeline"
|
||||
}
|
||||
|
||||
StatusIconTabButton {
|
||||
id: profileBtn
|
||||
anchors.top: browserBtn.top
|
||||
anchors.top: timelineBtn.enabled ? timelineBtn.top : browserBtn.top
|
||||
icon.name: "profile"
|
||||
|
||||
Rectangle {
|
||||
|
@ -284,6 +292,13 @@ RowLayout {
|
|||
property var _web3Provider: web3Provider
|
||||
}
|
||||
|
||||
TimelineLayout {
|
||||
id: timelineLayoutContainer
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
ProfileLayout {
|
||||
id: profileLayoutContainer
|
||||
Layout.fillWidth: true
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20ZM10 18.5C11.3807 18.5 12.5 17.3807 12.5 16C12.5 14.6193 11.3807 13.5 10 13.5C8.61929 13.5 7.5 14.6193 7.5 16C7.5 17.3807 8.61929 18.5 10 18.5ZM18.5 10C18.5 11.8106 17.9339 13.4889 16.9691 14.8677C16.7804 15.1373 16.3583 14.8961 16.4121 14.5715C16.4699 14.2229 16.5 13.865 16.5 13.5C16.5 9.91015 13.5899 7 10 7C6.41015 7 3.5 9.91015 3.5 13.5C3.5 13.865 3.53008 14.2229 3.5879 14.5715C3.64173 14.896 3.21955 15.1372 3.03094 14.8677C2.06609 13.4889 1.5 11.8106 1.5 10C1.5 5.30558 5.30558 1.5 10 1.5C14.6944 1.5 18.5 5.30558 18.5 10ZM14 16C14 16.1504 14.1963 16.2238 14.2745 16.0954C14.7349 15.3388 15 14.4504 15 13.5C15 10.7386 12.7614 8.5 10 8.5C7.23858 8.5 5 10.7386 5 13.5C5 14.4504 5.26515 15.3388 5.7255 16.0954C5.80367 16.2238 6 16.1504 6 16C6 13.7909 7.79086 12 10 12C12.2091 12 14 13.7909 14 16Z" fill="#939BA1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -96,6 +96,7 @@ ApplicationWindow {
|
|||
property bool walletEnabled: false
|
||||
property bool browserEnabled: false
|
||||
property bool displayChatImages: false
|
||||
property bool timelineEnabled: true
|
||||
property bool compactMode
|
||||
property string locale: "en"
|
||||
property var recentEmojis: []
|
||||
|
@ -136,6 +137,7 @@ ApplicationWindow {
|
|||
property bool browserEnabled: defaultAppSettings.browserEnabled
|
||||
property bool displayChatImages: defaultAppSettings.displayChatImages
|
||||
property bool compactMode: defaultAppSettings.compactMode
|
||||
property bool timelineEnabled: defaultAppSettings.timelineEnabled
|
||||
property string locale: defaultAppSettings.locale
|
||||
property var recentEmojis: defaultAppSettings.recentEmojis
|
||||
property real volume: defaultAppSettings.volume
|
||||
|
|
|
@ -35,6 +35,7 @@ SOURCES = *.qml \
|
|||
app/AppLayouts/Profile/Sections/*.qml \
|
||||
app/AppLayouts/Profile/Sections/Contacts/*.qml \
|
||||
app/AppLayouts/Profile/Sections/Ens/*.qml \
|
||||
app/AppLayouts/Timeline/*.qml\
|
||||
app/AppLayouts/Wallet/*.qml \
|
||||
app/AppLayouts/Wallet/components/*.qml \
|
||||
app/AppLayouts/Wallet/components/collectiblesComponents/*.qml \
|
||||
|
|
Loading…
Reference in New Issue