feat: introduce timeline

Closes #1489 #1490 #1491
This commit is contained in:
Pascal Precht 2020-12-17 11:40:37 +01:00 committed by Iuri Matias
parent 1ead1c3db5
commit 0767ce2443
19 changed files with 335 additions and 21 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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]

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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, %* [

View File

@ -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:

View File

@ -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:

View File

@ -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){

View File

@ -13,6 +13,7 @@ SplitView {
property alias chatColumn: chatColumn
property var onActivated: function () {
chatsModel.restorePreviousActiveChannel()
chatColumn.onActivated()
}

View File

@ -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
}
}
}
}

View File

@ -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

4
ui/app/img/timeline.svg Normal file
View File

@ -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

View File

@ -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

View File

@ -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 \