chore(AppMain): improve startup time

- wrap everything we can in AppMain with a (async) Loader
- do not access globals w/o a proper store
- drop some dead code

Although I wasn't able to completely fix the bug, the ~50% improvement in
startup time is still worth trying imho. On my machine, the startup time
went down from ~7s to under 4s.

Related: #7292
This commit is contained in:
Lukáš Tinkl 2022-10-18 11:06:18 +02:00 committed by Lukáš Tinkl
parent 38b6bdbfd3
commit 025a45d1a4
5 changed files with 203 additions and 186 deletions

View File

@ -23,13 +23,14 @@ StatusSectionLayout {
backButtonName: root.store.backButtonName backButtonName: root.store.backButtonName
notificationCount: root.store.unreadNotificationsCount notificationCount: root.store.unreadNotificationsCount
onNotificationButtonClicked: Global.openActivityCenterPopup() onNotificationButtonClicked: Global.openActivityCenterPopup()
onBackButtonClicked: { onBackButtonClicked: {
switch (profileContainer.currentIndex) { switch (profileContainer.currentIndex) {
case 1: case Constants.settingsSubsection.contacts:
Global.changeAppSectionBySectionType(Constants.appSection.profile, Constants.settingsSubsection.messaging) Global.changeAppSectionBySectionType(Constants.appSection.profile, Constants.settingsSubsection.messaging)
break; break;
case 4: case Constants.settingsSubsection.wallet:
walletView.resetStack(); walletView.resetStack();
break; break;
case Constants.settingsSubsection.keycard: case Constants.settingsSubsection.keycard:

View File

@ -44,7 +44,6 @@ import "activitycenter/stores" as AC
Item { Item {
id: appMain id: appMain
anchors.fill: parent
property alias appLayout: appLayout property alias appLayout: appLayout
property RootStore rootStore: RootStore {} property RootStore rootStore: RootStore {}
@ -64,11 +63,21 @@ Item {
onDestroyKeycardSharedModuleFlow: { onDestroyKeycardSharedModuleFlow: {
keycardPopup.active = false keycardPopup.active = false
} }
function onMailserverNotWorking() {
Global.openPopup(mailserverNotWorkingPopupComponent)
}
function onActiveSectionChanged() {
createChatView.opened = false
}
} }
Connections { Connections {
target: Global target: Global
onOpenLinkInBrowser: { onOpenLinkInBrowser: {
if (!browserLayoutContainer.active)
browserLayoutContainer.active = true;
browserLayoutContainer.item.openUrlInNewTab(link); browserLayoutContainer.item.openUrlInNewTab(link);
} }
onOpenChooseBrowserPopup: { onOpenChooseBrowserPopup: {
@ -85,6 +94,20 @@ Item {
return downloadPage return downloadPage
} }
onOpenImagePopup: {
var popup = imagePopupComponent.createObject(appMain)
popup.contextMenu = contextMenu
popup.openPopup(image)
}
onOpenCreateChatView: {
createChatView.opened = true
}
onCloseCreateChatView: {
createChatView.opened = false
}
onOpenProfilePopupRequested: { onOpenProfilePopupRequested: {
Global.openPopup(profilePopupComponent, {publicKey: publicKey, parentPopup: parentPopup}) Global.openPopup(profilePopupComponent, {publicKey: publicKey, parentPopup: parentPopup})
Global.profilePopupOpened = true Global.profilePopupOpened = true
@ -116,7 +139,7 @@ Item {
} }
function changeAppSectionBySectionId(sectionId) { function changeAppSectionBySectionId(sectionId) {
mainModule.setActiveSectionById(sectionId) appMain.rootStore.mainModuleInst.setActiveSectionById(sectionId)
} }
Component { Component {
@ -148,27 +171,24 @@ Item {
} }
} }
StatusImageModal { Component {
id: imagePopup id: imagePopupComponent
onClicked: { StatusImageModal {
if (mouse.button === Qt.LeftButton) { id: imagePopup
imagePopup.close() onClicked: {
} else if(mouse.button === Qt.RightButton) { if (mouse.button === Qt.LeftButton) {
contextMenu.imageSource = imagePopup.imageSource imagePopup.close()
contextMenu.hideEmojiPicker = true } else if(mouse.button === Qt.RightButton) {
contextMenu.isRightClickOnImage = true contextMenu.imageSource = imagePopup.imageSource
contextMenu.parent = imagePopup.contentItem contextMenu.hideEmojiPicker = true
contextMenu.setXPosition = function() { return mouse.x + Style.current.smallPadding } contextMenu.isRightClickOnImage = true
contextMenu.setYPosition = function() { return mouse.y - Style.current.smallPadding } contextMenu.parent = imagePopup.contentItem
contextMenu.show() contextMenu.setXPosition = function() { return mouse.x + Style.current.smallPadding }
} contextMenu.setYPosition = function() { return mouse.y - Style.current.smallPadding }
} contextMenu.show()
Connections { }
target: Global
onOpenImagePopup: {
imagePopup.contextMenu = contextMenu
imagePopup.openPopup(image)
} }
onClosed: destroy()
} }
} }
@ -230,9 +250,26 @@ Item {
} }
} }
AppSearch { Loader {
id: appSearch id: appSearch
store: appMain.rootStore.appSearchStore active: false
asynchronous: true
function openSearchPopup() {
if (!active)
active = true
item.openSearchPopup()
}
function closeSearchPopup() {
if (item)
item.closeSearchPopup()
active = false
}
sourceComponent: AppSearch {
store: appMain.rootStore.appSearchStore
}
} }
StatusEmojiPopup { StatusEmojiPopup {
@ -249,10 +286,10 @@ Item {
leftPanel: StatusAppNavBar { leftPanel: StatusAppNavBar {
communityTypeRole: "sectionType" communityTypeRole: "sectionType"
communityTypeValue: Constants.appSection.community communityTypeValue: Constants.appSection.community
sectionModel: mainModule.sectionsModel sectionModel: appMain.rootStore.mainModuleInst.sectionsModel
Component.onCompleted: { Component.onCompleted: {
mainModule.sectionsModel.sectionVisibilityUpdated.connect(function(){ appMain.rootStore.mainModuleInst.sectionsModel.sectionVisibilityUpdated.connect(function(){
triggerUpdate() triggerUpdate()
}) })
} }
@ -323,8 +360,8 @@ Item {
openHandler: function () { openHandler: function () {
// // we cannot return QVariant if we pass another parameter in a function call // // we cannot return QVariant if we pass another parameter in a function call
// // that's why we're using it this way // // that's why we're using it this way
mainModule.prepareCommunitySectionModuleForCommunityId(model.id) appMain.rootStore.mainModuleInst.prepareCommunitySectionModuleForCommunityId(model.id)
communityContextMenu.chatCommunitySectionModule = mainModule.getCommunitySectionModule() communityContextMenu.chatCommunitySectionModule = appMain.rootStore.mainModuleInst.getCommunitySectionModule()
} }
@ -418,7 +455,7 @@ Item {
property var updateBanner: null property var updateBanner: null
property var connectedBanner: null property var connectedBanner: null
readonly property bool isConnected: mainModule.isOnline readonly property bool isConnected: appMain.rootStore.mainModuleInst.isOnline
function processUpdateAvailable() { function processUpdateAvailable() {
if (!updateBanner) if (!updateBanner)
@ -658,60 +695,66 @@ Item {
anchors.fill: parent anchors.fill: parent
currentIndex: { currentIndex: {
if(mainModule.activeSection.sectionType === Constants.appSection.chat) { const activeSectionType = appMain.rootStore.mainModuleInst.activeSection.sectionType
if (activeSectionType === Constants.appSection.chat)
return Constants.appViewStackIndex.chat return Constants.appViewStackIndex.chat
} if (activeSectionType === Constants.appSection.community) {
else if(mainModule.activeSection.sectionType === Constants.appSection.community) {
for(let i = this.children.length - 1; i >=0; i--) for(let i = this.children.length - 1; i >=0; i--)
{ {
var obj = this.children[i]; var obj = this.children[i]
if(obj && obj.sectionId && obj.sectionId == mainModule.activeSection.id) if (obj && obj.sectionId && obj.sectionId === appMain.rootStore.mainModuleInst.activeSection.id)
{ {
obj.active = true
return i return i
} }
} }
// Should never be here, correct index must be returned from the for loop above // Should never be here, correct index must be returned from the for loop above
console.error("Wrong section type: ", mainModule.activeSection.sectionType, console.error("Wrong section type: ", appMain.rootStore.mainModuleInst.activeSection.sectionType,
" or section id: ", mainModule.activeSection.id) " or section id: ", appMain.rootStore.mainModuleInst.activeSection.id)
return Constants.appViewStackIndex.community return Constants.appViewStackIndex.community
} }
else if(mainModule.activeSection.sectionType === Constants.appSection.communitiesPortal) { if (activeSectionType === Constants.appSection.communitiesPortal)
return Constants.appViewStackIndex.communitiesPortal return Constants.appViewStackIndex.communitiesPortal
} if (activeSectionType === Constants.appSection.wallet)
else if(mainModule.activeSection.sectionType === Constants.appSection.wallet) {
return Constants.appViewStackIndex.wallet return Constants.appViewStackIndex.wallet
} if (activeSectionType === Constants.appSection.browser)
else if(mainModule.activeSection.sectionType === Constants.appSection.browser) {
return Constants.appViewStackIndex.browser return Constants.appViewStackIndex.browser
} if (activeSectionType === Constants.appSection.profile)
else if(mainModule.activeSection.sectionType === Constants.appSection.profile) {
return Constants.appViewStackIndex.profile return Constants.appViewStackIndex.profile
} if (activeSectionType === Constants.appSection.node)
else if(mainModule.activeSection.sectionType === Constants.appSection.node) {
return Constants.appViewStackIndex.node return Constants.appViewStackIndex.node
}
// We should never end up here // We should never end up here
console.error("Unknown section type") console.error("AppMain: Unknown section type")
} }
onCurrentIndexChanged: { onCurrentIndexChanged: {
var obj = this.children[currentIndex]; var obj = this.children[currentIndex]
if(!obj) if (!obj)
return return
if (obj.onActivated && typeof obj.onActivated === "function") { if (obj === browserLayoutContainer && browserLayoutContainer.active == false) {
this.children[currentIndex].onActivated()
}
if(obj === browserLayoutContainer && browserLayoutContainer.active == false){
browserLayoutContainer.active = true; browserLayoutContainer.active = true;
} }
if(obj === walletLayoutContainer){ if (obj === walletLayoutContainer && !walletLayoutContainer.active) {
walletLayoutContainer.showSigningPhrasePopup(); walletLayoutContainer.active = true
walletLayoutContainer.item.showSigningPhrasePopup()
}
if (obj === profileLayoutContainer && !profileLayoutContainer.active) {
profileLayoutContainer.active = true
}
if (obj === nodeLayoutContainer && !nodeLayoutContainer.active) {
nodeLayoutContainer.active = true
}
if (obj.onActivated && typeof obj.onActivated === "function") {
this.children[currentIndex].onActivated()
} }
} }
@ -745,41 +788,35 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
rootStore.chatCommunitySectionModule = mainModule.getChatSectionModule() rootStore.chatCommunitySectionModule = appMain.rootStore.mainModuleInst.getChatSectionModule()
} }
} }
CommunitiesPortalLayout { CommunitiesPortalLayout {
id: communitiesPortalLayoutContainer id: communitiesPortalLayoutContainer
Layout.fillWidth: true
Layout.fillHeight: true
contentPrefferedWidth: appView.width contentPrefferedWidth: appView.width
} }
WalletLayout { Loader {
id: walletLayoutContainer id: walletLayoutContainer
Layout.fillWidth: true active: false
Layout.fillHeight: true asynchronous: true
store: appMain.rootStore sourceComponent: WalletLayout {
contactsStore: appMain.rootStore.profileSectionStore.contactsStore store: appMain.rootStore
emojiPopup: statusEmojiPopup contactsStore: appMain.rootStore.profileSectionStore.contactsStore
sendModal: sendModal emojiPopup: statusEmojiPopup
} sendModal: sendModal
Component {
id: browserLayoutComponent
BrowserLayout {
globalStore: appMain.rootStore
sendTransactionModal: sendModal
} }
} }
Loader { Loader {
id: browserLayoutContainer id: browserLayoutContainer
sourceComponent: browserLayoutComponent
active: false active: false
Layout.fillWidth: true asynchronous: true
Layout.fillHeight: true sourceComponent: BrowserLayout {
globalStore: appMain.rootStore
sendTransactionModal: sendModal
}
// Loaders do not have access to the context, so props need to be set // Loaders do not have access to the context, so props need to be set
// Adding a "_" to avoid a binding loop // Adding a "_" to avoid a binding loop
// Not Refactored Yet // Not Refactored Yet
@ -788,59 +825,65 @@ Item {
// property var _walletModel: walletModel // property var _walletModel: walletModel
// Not Refactored Yet // Not Refactored Yet
// property var _utilsModel: utilsModel // property var _utilsModel: utilsModel
property var _web3Provider: BrowserStores.Web3ProviderStore.web3ProviderInst // property var _web3Provider: BrowserStores.Web3ProviderStore.web3ProviderInst
} }
ProfileLayout { Loader {
id: profileLayoutContainer id: profileLayoutContainer
Layout.fillWidth: true active: false
Layout.fillHeight: true asynchronous: true
sourceComponent: ProfileLayout {
store: appMain.rootStore.profileSectionStore store: appMain.rootStore.profileSectionStore
globalStore: appMain.rootStore globalStore: appMain.rootStore
systemPalette: appMain.sysPalette systemPalette: appMain.sysPalette
emojiPopup: statusEmojiPopup emojiPopup: statusEmojiPopup
}
} }
NodeLayout { Loader {
id: nodeLayoutContainer id: nodeLayoutContainer
Layout.fillWidth: true active: false
Layout.fillHeight: true asynchronous: true
sourceComponent: NodeLayout {}
} }
Repeater { Repeater {
model: mainModule.sectionsModel model: appMain.rootStore.mainModuleInst.sectionsModel
delegate: DelegateChooser { delegate: DelegateChooser {
id: delegateChooser
role: "sectionType" role: "sectionType"
DelegateChoice { DelegateChoice {
roleValue: Constants.appSection.community roleValue: Constants.appSection.community
delegate: ChatLayout {
delegate: Loader {
property string sectionId: model.id property string sectionId: model.id
active: false
asynchronous: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillHeight: true Layout.fillHeight: true
chatView.emojiPopup: statusEmojiPopup sourceComponent: ChatLayout {
chatView.emojiPopup: statusEmojiPopup
contactsStore: appMain.rootStore.contactStore contactsStore: appMain.rootStore.contactStore
rootStore.emojiReactionsModel: appMain.rootStore.emojiReactionsModel rootStore.emojiReactionsModel: appMain.rootStore.emojiReactionsModel
rootStore.openCreateChat: createChatView.opened rootStore.openCreateChat: createChatView.opened
chatView.onProfileButtonClicked: { chatView.onProfileButtonClicked: {
Global.changeAppSectionBySectionType(Constants.appSection.profile); Global.changeAppSectionBySectionType(Constants.appSection.profile);
} }
chatView.onOpenAppSearch: { chatView.onOpenAppSearch: {
appSearch.openSearchPopup() appSearch.openSearchPopup()
} }
Component.onCompleted: { Component.onCompleted: {
// we cannot return QVariant if we pass another parameter in a function call // we cannot return QVariant if we pass another parameter in a function call
// that's why we're using it this way // that's why we're using it this way
mainModule.prepareCommunitySectionModuleForCommunityId(model.id) appMain.rootStore.mainModuleInst.prepareCommunitySectionModuleForCommunityId(model.id)
rootStore.chatCommunitySectionModule = mainModule.getCommunitySectionModule() rootStore.chatCommunitySectionModule = appMain.rootStore.mainModuleInst.getCommunitySectionModule()
}
} }
} }
} }
@ -848,60 +891,32 @@ Item {
} }
} }
CreateChatView { Loader {
id: createChatView id: createChatView
property bool opened: false property bool opened: false
active: opened
rootStore: chatLayoutContainer.rootStore asynchronous: true
emojiPopup: statusEmojiPopup
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 8 anchors.topMargin: 8
anchors.rightMargin: 8 anchors.rightMargin: 8
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
width: parent.width - chatLayoutContainer.chatView.leftPanel.width - anchors.rightMargin - anchors.leftMargin width: parent.width - chatLayoutContainer.chatView.leftPanel.width - anchors.rightMargin - anchors.leftMargin
visible: createChatView.opened
Connections { sourceComponent: CreateChatView {
target: Global rootStore: chatLayoutContainer.rootStore
emojiPopup: statusEmojiPopup
function onOpenCreateChatView() {
createChatView.opened = true
}
function onCloseCreateChatView() {
createChatView.opened = false
}
}
Connections {
target: mainModule
function onActiveSectionChanged() {
Global.closeCreateChatView()
}
} }
} }
} }
Connections {
target: rootStore.mainModuleInst
function onMailserverNotWorking() {
if (!appLayout.mailserverNotWorkingPopup) {
appLayout.mailserverNotWorkingPopup = Global.openPopup(mailserverNotWorkingPopupComponent);
}
}
}
} // ColumnLayout } // ColumnLayout
property var mailserverNotWorkingPopup: null
Component { Component {
id: mailserverNotWorkingPopupComponent id: mailserverNotWorkingPopupComponent
MailserverConnectionDialog { MailserverConnectionDialog {
onClosed: { onClosed: {
appLayout.mailserverNotWorkingPopup = null
destroy() destroy()
} }
} }
@ -1089,10 +1104,14 @@ Item {
shortcut: "Ctrl+K" shortcut: "Ctrl+K"
onTriggered: { onTriggered: {
// FIXME the focus is no longer on the AppMain when the popup is opened, so this does not work to close // FIXME the focus is no longer on the AppMain when the popup is opened, so this does not work to close
if (channelPicker.opened) { if (!channelPickerLoader.active)
channelPicker.close() channelPickerLoader.active = true
if (channelPickerLoader.item.opened) {
channelPickerLoader.item.close()
channelPickerLoader.active = false
} else { } else {
channelPicker.open() channelPickerLoader.item.open()
} }
} }
} }
@ -1100,7 +1119,7 @@ Item {
shortcut: "Ctrl+F" shortcut: "Ctrl+F"
onTriggered: { onTriggered: {
// FIXME the focus is no longer on the AppMain when the popup is opened, so this does not work to close // FIXME the focus is no longer on the AppMain when the popup is opened, so this does not work to close
if (appSearch.opened) { if (appSearch.active) {
appSearch.closeSearchPopup() appSearch.closeSearchPopup()
} else { } else {
appSearch.openSearchPopup() appSearch.openSearchPopup()
@ -1108,40 +1127,40 @@ Item {
} }
} }
StatusSearchListPopup { Loader {
id: channelPicker id: channelPickerLoader
active: false
asynchronous: true
sourceComponent: StatusSearchListPopup {
searchBoxPlaceholder: qsTr("Where do you want to go?")
model: rootStore.chatSearchModel
delegate: StatusListItem {
property var modelData
property bool isCurrentItem: true
function filterAccepts(searchText) {
return title.includes(searchText)
}
x: parent.width / 2 - width / 2 title: modelData ? modelData.name : ""
y: parent.height / 2 - height / 2 label: modelData? modelData.sectionName : ""
highlighted: isCurrentItem
searchBoxPlaceholder: qsTr("Where do you want to go?") sensor.hoverEnabled: false
model: rootStore.chatSearchModel statusListItemIcon {
delegate: StatusListItem { name: modelData ? modelData.name : ""
property var modelData active: true
property bool isCurrentItem: true }
function filterAccepts(searchText) { asset.width: 30
return title.includes(searchText) asset.height: 30
asset.color: modelData ? modelData.color : ""
asset.name: modelData ? modelData.icon : ""
asset.isImage: asset.name.includes("data")
} }
title: modelData ? modelData.name : "" onAboutToShow: rootStore.rebuildChatSearchModel()
label: modelData? modelData.sectionName : "" onSelected: {
highlighted: isCurrentItem rootStore.setActiveSectionChat(modelData.sectionId, modelData.chatId)
sensor.hoverEnabled: false close()
statusListItemIcon {
name: modelData ? modelData.name : ""
active: true
} }
asset.width: 30
asset.height: 30
asset.color: modelData ? modelData.color : ""
asset.name: modelData ? modelData.icon : ""
asset.isImage: asset.name.includes("data")
}
onAboutToShow: rootStore.rebuildChatSearchModel()
onSelected: {
rootStore.setActiveSectionChat(modelData.sectionId, modelData.chatId)
close()
} }
} }
} }
@ -1230,14 +1249,14 @@ Item {
console.error('Could not parse the whitelist for sites', e) console.error('Could not parse the whitelist for sites', e)
} }
Global.privacyModuleInst = appMain.rootStore.profileSectionStore.privacyStore.privacyModule Global.privacyModuleInst = appMain.rootStore.profileSectionStore.privacyStore.privacyModule
Global.settingsHasLoaded(); Global.settingsLoaded()
} }
Loader { Loader {
id: keycardPopup id: keycardPopup
active: false active: false
sourceComponent: KeycardPopup { sourceComponent: KeycardPopup {
sharedKeycardModule: rootStore.mainModuleInst.keycardSharedModule sharedKeycardModule: appMain.rootStore.mainModuleInst.keycardSharedModule
} }
onLoaded: { onLoaded: {
@ -1251,7 +1270,7 @@ Item {
activeChatType: chatCommunitySectionModule && chatCommunitySectionModule.activeItem.type activeChatType: chatCommunitySectionModule && chatCommunitySectionModule.activeItem.type
enabled: !drag.source && ( enabled: !drag.source && (
// in chat view // in chat view
(mainModule.activeSection.sectionType === Constants.appSection.chat && (appMain.rootStore.mainModuleInst.activeSection.sectionType === Constants.appSection.chat &&
( (
// in a one-to-one chat // in a one-to-one chat
activeChatType === Constants.chatType.oneToOne || activeChatType === Constants.chatType.oneToOne ||
@ -1260,6 +1279,6 @@ Item {
) )
) || ) ||
// In community section // In community section
mainModule.activeSection.sectionType === Constants.appSection.community) appMain.rootStore.mainModuleInst.activeSection.sectionType === Constants.appSection.community)
} }
} }

View File

@ -13,6 +13,8 @@ import utils 1.0
Popup { Popup {
id: root id: root
anchors.centerIn: Overlay.overlay
width: 400 width: 400
height: 300 height: 300

View File

@ -19,7 +19,6 @@ Item {
property var toastMessage property var toastMessage
property var pinnedMessagesPopup property var pinnedMessagesPopup
property var communityProfilePopup property var communityProfilePopup
property var inviteFriendsToCommunityPopup
property bool profilePopupOpened: false property bool profilePopupOpened: false
property bool activityCenterPopupOpened: false property bool activityCenterPopupOpened: false
@ -31,7 +30,6 @@ Item {
signal openImagePopup(var image, var contextMenu) signal openImagePopup(var image, var contextMenu)
signal openLinkInBrowser(string link) signal openLinkInBrowser(string link)
signal openChooseBrowserPopup(string link) signal openChooseBrowserPopup(string link)
signal openPopupRequested(var popupComponent, var params)
signal openDownloadModalRequested(bool available, string version, string url) signal openDownloadModalRequested(bool available, string version, string url)
signal settingsLoaded() signal settingsLoaded()
signal openBackUpSeedPopup() signal openBackUpSeedPopup()
@ -181,10 +179,6 @@ Item {
errorSound.play(); errorSound.play();
} }
function settingsHasLoaded() {
settingsLoaded()
}
Component { Component {
id: sendContactRequestPopupComponent id: sendContactRequestPopupComponent

View File

@ -272,6 +272,7 @@ StatusWindow {
Loader { Loader {
id: loader id: loader
anchors.fill: parent anchors.fill: parent
asynchronous: true
opacity: active ? 1.0 : 0.0 opacity: active ? 1.0 : 0.0
visible: (opacity > 0.0001) visible: (opacity > 0.0001)
Behavior on opacity { NumberAnimation { duration: 120 }} Behavior on opacity { NumberAnimation { duration: 120 }}