chore: consolidate & refactor popup handling

- all remaining global popup components moved into a separate Popups
entity
- removed some static objects from the Global singleton (appMain,
pinnedMessagesPopup, communityProfilePopup, sounds); rationale:
singletons should not contain any state
- fixed support for popups in storybook
- fixed some warnings (most of them broke the popups in one way or the other)
This commit is contained in:
Lukáš Tinkl 2023-02-07 15:21:32 +01:00 committed by Lukáš Tinkl
parent 1481790183
commit f5d6c538c2
28 changed files with 376 additions and 404 deletions

View File

@ -21,6 +21,8 @@ SplitView {
contentWidth: parent.width contentWidth: parent.width
store: QtObject { store: QtObject {
readonly property bool isProduction: false
function checkForUpdates() { function checkForUpdates() {
logs.logEvent("store::checkForUpdates") logs.logEvent("store::checkForUpdates")
} }

View File

@ -10,10 +10,17 @@ import Storybook 1.0
import Models 1.0 import Models 1.0
import utils 1.0 import utils 1.0
import mainui 1.0
SplitView { SplitView {
id: root
Logs { id: logs } Logs { id: logs }
Popups {
popupParent: root
rootStore: QtObject {}
}
SplitView { SplitView {
orientation: Qt.Vertical orientation: Qt.Vertical
SplitView.fillWidth: true SplitView.fillWidth: true
@ -40,16 +47,6 @@ SplitView {
} }
} }
// TODO: onCompleted handler and localAccountSensitiveSettings are here to allow opening
// "Import Community" and "Create New Community" popups. However those popups shouldn't
// be tightly coupled with `CommunitiesPortalLayout` so it should be refactored in the next step.
// Pressing buttons "Import using key" and "Create new community" should only request for opening
// dialogs, and in Storybook it should be logged in the same way as calls to stores.
// Mentioned popups should have their own pages in the Storybook.
Component.onCompleted: {
Global.appMain = this
}
QtObject { QtObject {
id: localAccountSensitiveSettings id: localAccountSensitiveSettings
readonly property bool isDiscordImportToolEnabled: false readonly property bool isDiscordImportToolEnabled: false

View File

@ -6,6 +6,9 @@ import Storybook 1.0
import utils 1.0 import utils 1.0
import shared.views 1.0 import shared.views 1.0
import mainui 1.0
import StatusQ 0.1
SplitView { SplitView {
id: root id: root
@ -91,6 +94,11 @@ SplitView {
Logs { id: logs } Logs { id: logs }
Popups {
popupParent: root
rootStore: QtObject {}
}
SplitView { SplitView {
orientation: Qt.Vertical orientation: Qt.Vertical
SplitView.fillWidth: true SplitView.fillWidth: true
@ -114,10 +122,6 @@ SplitView {
publicKey: switchOwnProfile.checked ? "0xdeadbeef" : "0xrandomguy" publicKey: switchOwnProfile.checked ? "0xdeadbeef" : "0xrandomguy"
Component.onCompleted: {
Global.appMain = root // FIXME this is here for the popups to work
}
profileStore: QtObject { profileStore: QtObject {
readonly property string pubkey: "0xdeadbeef" readonly property string pubkey: "0xdeadbeef"
readonly property string ensName: name.text readonly property string ensName: name.text
@ -286,7 +290,11 @@ SplitView {
TextField { TextField {
Layout.fillWidth: true Layout.fillWidth: true
id: bio id: bio
text: "Hello from MockMainModule, I am a mock user and this is my bio." text: "Hi, I am Alex. I'm an indie developer who mainly works on web products.
I worked for several different companies and created a couple of my own products from scratch. Currently building Telescope and Prepacked.
Say hi, or find me on Twitter, GitHub, or Mastodon."
} }
} }
} }

View File

@ -9,8 +9,6 @@ Feature: User Identity
And the user signs up with username "tester123" and password "TesTEr16843/!@00" And the user signs up with username "tester123" and password "TesTEr16843/!@00"
And the user lands on the signed in app And the user lands on the signed in app
@mayfail
# FIXME test is broken on step `the user's social links are empty`. Issue #9499
Scenario Outline: The user sets display name, bio and social links Scenario Outline: The user sets display name, bio and social links
Given the user opens app settings screen Given the user opens app settings screen
And the user opens the profile settings And the user opens the profile settings

View File

@ -2,7 +2,6 @@ import StatusQ.Core.Utils 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
StatusValidator { StatusValidator {
name: "url" name: "url"
errorMessage: qsTr("Please enter a valid URL") errorMessage: qsTr("Please enter a valid URL")
@ -11,5 +10,3 @@ StatusValidator {
return Utils.isURL(value); return Utils.isURL(value);
} }
} }

View File

@ -1,9 +1,6 @@
import QtQuick 2.13 import QtQuick 2.14
import StatusQ.Controls 0.1
QtObject { QtObject {
id: statusValidator
property string name: "" property string name: ""
property string errorMessage: qsTr("invalid input") property string errorMessage: qsTr("invalid input")
property var validatorObj property var validatorObj

View File

@ -3,6 +3,7 @@
#include "StatusQ/QClipboardProxy.h" #include "StatusQ/QClipboardProxy.h"
#include "StatusQ/statussyntaxhighlighter.h" #include "StatusQ/statussyntaxhighlighter.h"
#include "StatusQ/statuswindow.h" #include "StatusQ/statuswindow.h"
#include "StatusQ/rxvalidator.h"
#include <QQmlEngine> #include <QQmlEngine>
@ -12,4 +13,5 @@ void registerStatusQTypes()
qmlRegisterSingletonType<QClipboardProxy>("StatusQ", 0 , 1, "QClipboardProxy", qmlRegisterSingletonType<QClipboardProxy>("StatusQ", 0 , 1, "QClipboardProxy",
&QClipboardProxy::qmlInstance); &QClipboardProxy::qmlInstance);
qmlRegisterType<StatusSyntaxHighlighter>("StatusQ", 0 , 1, "StatusSyntaxHighlighter"); qmlRegisterType<StatusSyntaxHighlighter>("StatusQ", 0 , 1, "StatusSyntaxHighlighter");
qmlRegisterType<RXValidator>("StatusQ", 0 , 1, "RXValidator");
} }

View File

@ -79,8 +79,6 @@ ModalPopup {
id: urlInput id: urlInput
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
leftPadding: 0
rightPadding: 0
label: qsTr("URL") label: qsTr("URL")
input.text: ogUrl input.text: ogUrl
placeholderText: qsTr("Paste URL") placeholderText: qsTr("Paste URL")

View File

@ -61,16 +61,12 @@ StackLayout {
hasAddedContacts: root.contactsStore.myContactsModel.count > 0 hasAddedContacts: root.contactsStore.myContactsModel.count > 0
chatCommunitySectionModule: root.rootStore.chatCommunitySectionModule chatCommunitySectionModule: root.rootStore.chatCommunitySectionModule
community: root.rootStore.mainModuleInst ? root.rootStore.mainModuleInst.activeSection community: root.rootStore.mainModuleInst ? root.rootStore.mainModuleInst.activeSection
|| {} : {} || ({}) : ({})
onBackToCommunityClicked: root.currentIndex = 0 onBackToCommunityClicked: root.currentIndex = 0
// TODO: remove me when migration to new settings is done // TODO: remove me when migration to new settings is done
onOpenLegacyPopupClicked: Global.openPopup(Global.communityProfilePopup, { onOpenLegacyPopupClicked: Global.openCommunityProfilePopupRequested(root.rootStore, community, chatCommunitySectionModule)
"store": root.rootStore,
"community": community,
"communitySectionModule": chatCommunitySectionModule
})
} }
} }
} }

View File

@ -1,7 +1,6 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13 import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13 import QtQuick.Layouts 1.13
import QtQuick.Dialogs 1.3
import utils 1.0 import utils 1.0
import shared.controls 1.0 import shared.controls 1.0
@ -18,8 +17,8 @@ StatusModal {
property var store property var store
property var contactsStore property var contactsStore
property string commandTitle: "Send" property string commandTitle: qsTr("Send")
property string finalButtonLabel: "Request address" property string finalButtonLabel: qsTr("Request address")
property var sendChatCommand: function () {} property var sendChatCommand: function () {}
property bool isRequested: false property bool isRequested: false

View File

@ -37,7 +37,7 @@ QtObject {
// Contact requests related part // Contact requests related part
property var contactRequestsModel: chatCommunitySectionModule.contactRequestsModel property var contactRequestsModel: chatCommunitySectionModule.contactRequestsModel
property var loadingHistoryMessagesInProgress: chatCommunitySectionModule.loadingHistoryMessagesInProgress property bool loadingHistoryMessagesInProgress: chatCommunitySectionModule.loadingHistoryMessagesInProgress
property var advancedModule: profileSectionModule.advancedModule property var advancedModule: profileSectionModule.advancedModule

View File

@ -99,12 +99,7 @@ ColumnLayout {
console.warn("error on open pinned messages limit reached from message context menu - chat content module is not set") console.warn("error on open pinned messages limit reached from message context menu - chat content module is not set")
return return
} }
Global.openPopup(Global.pinnedMessagesPopup, { Global.openPinnedMessagesPopupRequested(rootStore, messageStore, chatContentModule.pinnedMessagesModel, messageId)
store: rootStore,
messageStore: messageStore,
pinnedMessagesModel: chatContentModule.pinnedMessagesModel,
messageToPin: messageId
})
} }
onToggleReaction: { onToggleReaction: {
@ -243,8 +238,7 @@ ColumnLayout {
chatInput.fileUrlsAndSources chatInput.fileUrlsAndSources
)) ))
{ {
Global.sendMessageSound.stop(); Global.playSendMessageSound()
Qt.callLater(Global.sendMessageSound.play);
chatInput.textInput.clear(); chatInput.textInput.clear();
chatInput.textInput.textFormat = TextEdit.PlainText; chatInput.textInput.textFormat = TextEdit.PlainText;

View File

@ -309,12 +309,7 @@ Item {
console.warn("error on open pinned messages - chat content module is not set") console.warn("error on open pinned messages - chat content module is not set")
return return
} }
Global.openPopup(Global.pinnedMessagesPopup, { Global.openPinnedMessagesPopupRequested(rootStore, messageStore, chatContentModule.pinnedMessagesModel, "")
store: rootStore,
messageStore: messageStore,
pinnedMessagesModel: chatContentModule.pinnedMessagesModel,
messageToPin: ""
})
} }
onUnmute: { onUnmute: {
if(!chatContentModule) { if(!chatContentModule) {

View File

@ -66,6 +66,7 @@ StatusDialog {
StatusScrollView { StatusScrollView {
id: scrollView id: scrollView
implicitHeight: contentHeight + topPadding + bottomPadding
anchors.fill: parent anchors.fill: parent
padding: 0 padding: 0

View File

@ -13,7 +13,6 @@ QtObject {
property bool isTelemetryEnabled: advancedModule? advancedModule.isTelemetryEnabled : false property bool isTelemetryEnabled: advancedModule? advancedModule.isTelemetryEnabled : false
property bool isAutoMessageEnabled: advancedModule? advancedModule.isAutoMessageEnabled : false property bool isAutoMessageEnabled: advancedModule? advancedModule.isAutoMessageEnabled : false
property bool isDebugEnabled: advancedModule? advancedModule.isDebugEnabled : false property bool isDebugEnabled: advancedModule? advancedModule.isDebugEnabled : false
property bool isCommunityHistoryArchiveSupportEnabled: advancedModule? advancedModule.isCommunityHistoryArchiveSupportEnabled : false
property bool isWakuV2StoreEnabled: advancedModule ? advancedModule.isWakuV2StoreEnabled : false property bool isWakuV2StoreEnabled: advancedModule ? advancedModule.isWakuV2StoreEnabled : false
property var customNetworksModel: advancedModule? advancedModule.customNetworksModel : [] property var customNetworksModel: advancedModule? advancedModule.customNetworksModel : []

View File

@ -471,8 +471,7 @@ SettingsContentBase {
value = appSettings.volume value = appSettings.volume
volumeSlider.valueChanged.connect(() => { volumeSlider.valueChanged.connect(() => {
// play a sound preview, but not on startup // play a sound preview, but not on startup
Global.notificationSound.stop() Global.playNotificationSound()
Global.notificationSound.play()
}); });
} }
} }

View File

@ -4,17 +4,14 @@ import QtQuick.Layouts 1.13
import QtMultimedia 5.13 import QtMultimedia 5.13
import Qt.labs.qmlmodels 1.0 import Qt.labs.qmlmodels 1.0
import Qt.labs.platform 1.1 import Qt.labs.platform 1.1
import Qt.labs.settings 1.0
import QtQml.Models 2.14 import QtQml.Models 2.14
import AppLayouts.Wallet 1.0 import AppLayouts.Wallet 1.0
import AppLayouts.Node 1.0 import AppLayouts.Node 1.0
import AppLayouts.Browser 1.0 import AppLayouts.Browser 1.0
import AppLayouts.Chat 1.0 import AppLayouts.Chat 1.0
import AppLayouts.Chat.popups 1.0
import AppLayouts.Chat.views 1.0 import AppLayouts.Chat.views 1.0
import AppLayouts.Profile 1.0 import AppLayouts.Profile 1.0
import AppLayouts.Profile.popups 1.0
import AppLayouts.CommunitiesPortal 1.0 import AppLayouts.CommunitiesPortal 1.0
import utils 1.0 import utils 1.0
@ -51,15 +48,11 @@ Item {
// set from main.qml // set from main.qml
property var sysPalette property var sysPalette
property var activePopupComponents: []
signal closeProfilePopup()
Connections { Connections {
target: rootStore.mainModuleInst target: rootStore.mainModuleInst
function onDisplayUserProfile(publicKey: string) { function onDisplayUserProfile(publicKey: string) {
Global.openProfilePopup(publicKey) popups.openProfilePopup(publicKey)
} }
function onDisplayKeycardSharedModuleFlow() { function onDisplayKeycardSharedModuleFlow() {
@ -83,47 +76,24 @@ Item {
} }
function onOpenActivityCenter() { function onOpenActivityCenter() {
Global.openPopup(activityCenterPopupComponent) popups.openPopup(activityCenterPopupComponent)
} }
} }
Popups { Popups {
id: popups
popupParent: appMain
rootStore: appMain.rootStore rootStore: appMain.rootStore
Component.onCompleted: {
Global.openSendIDRequestPopup.connect(openSendIDRequestPopup)
Global.openOutgoingIDRequestPopup.connect(openOutgoingIDRequestPopup)
Global.openIncomingIDRequestPopup.connect(openIncomingIDRequestPopup)
Global.openInviteFriendsToCommunityPopup.connect(openInviteFriendsToCommunityPopup)
Global.openContactRequestPopup.connect(openContactRequestPopup)
}
} }
Connections { Connections {
id: globalConns
target: Global target: Global
function onOpenLinkInBrowser(link: string) { function onOpenLinkInBrowser(link: string) {
changeAppSectionBySectionId(Constants.appSection.browser) changeAppSectionBySectionId(Constants.appSection.browser)
Qt.callLater(() => browserLayoutContainer.item.openUrlInNewTab(link)); Qt.callLater(() => browserLayoutContainer.item.openUrlInNewTab(link));
} }
function onOpenChooseBrowserPopup(link: string) {
Global.openPopup(chooseBrowserPopupComponent, {link: link});
}
function onOpenDownloadModalRequested(available: bool, version: string, url: string) {
const downloadPage = downloadPageComponent.createObject(appMain,
{
newVersionAvailable: available,
downloadURL: url,
currentVersion: appMain.rootStore.profileSectionStore.getCurrentVersion(),
newVersion: version
})
return downloadPage
}
function onOpenImagePopup(image, contextMenu) {
var popup = imagePopupComponent.createObject(appMain)
popup.contextMenu = contextMenu
popup.openPopup(image)
}
function onOpenCreateChatView() { function onOpenCreateChatView() {
createChatView.opened = true createChatView.opened = true
@ -133,76 +103,44 @@ Item {
createChatView.opened = false createChatView.opened = false
} }
function onOpenProfilePopupRequested(publicKey: string, parentPopup) { function onOpenActivityCenterPopupRequested() {
if (Global.profilePopupOpened) { popups.openPopup(activityCenterPopupComponent)
appMain.closeProfilePopup()
}
Global.openPopup(profilePopupComponent, {publicKey: publicKey, parentPopup: parentPopup})
Global.profilePopupOpened = true
}
function onOpenNicknamePopupRequested(publicKey: string,nickname: string, subtitle: string) {
Global.openPopup(nicknamePopupComponent, {publicKey: publicKey, nickname: nickname, "header.subTitle": subtitle})
}
function onBlockContactRequested(publicKey: string, contactName: string) {
Global.openPopup(blockContactConfirmationComponent, {contactName: contactName, contactAddress: publicKey})
}
function onUnblockContactRequested(publicKey: string, contactName: string) {
Global.openPopup(unblockContactConfirmationComponent, {contactName: contactName, contactAddress: publicKey})
} }
function onOpenActivityCenterPopupRequested(publicKey: string, contactName: string) {
Global.openPopup(activityCenterPopupComponent)
}
function onOpenChangeProfilePicPopup(cb) {
var popup = changeProfilePicComponent.createObject(appMain, {callback: cb});
popup.chooseImageToCrop();
}
function onOpenBackUpSeedPopup() {
Global.openPopup(backupSeedModalComponent)
}
function onDisplayToastMessage(title: string, subTitle: string, icon: string, loading: bool, ephNotifType: int, url: string) { function onDisplayToastMessage(title: string, subTitle: string, icon: string, loading: bool, ephNotifType: int, url: string) {
appMain.rootStore.mainModuleInst.displayEphemeralNotification(title, subTitle, icon, loading, ephNotifType, url); appMain.rootStore.mainModuleInst.displayEphemeralNotification(title, subTitle, icon, loading, ephNotifType, url)
}
function onOpenEditDisplayNamePopup() {
Global.openPopup(displayNamePopupComponent)
}
function onOpenPopupRequested(popupComponent, params) {
if (activePopupComponents.includes(popupComponent)) {
return;
}
const popup = popupComponent.createObject(appMain, params);
popup.open();
activePopupComponents.push(popupComponent);
popup.closed.connect(() => {
const removeIndex = activePopupComponents.indexOf(popupComponent);
if (removeIndex !== -1) {
activePopupComponents.splice(removeIndex, 1);
}
})
return popup;
} }
function onOpenLink(link: string) { function onOpenLink(link: string) {
// Qt sometimes inserts random HTML tags; and this will break on invalid URL inside QDesktopServices::openUrl(link) // Qt sometimes inserts random HTML tags; and this will break on invalid URL inside QDesktopServices::openUrl(link)
link = appMain.rootStore.plainText(link); link = appMain.rootStore.plainText(link)
if (appMain.rootStore.showBrowserSelector) { if (appMain.rootStore.showBrowserSelector) {
Global.openChooseBrowserPopup(link); popups.openChooseBrowserPopup(link)
} else { } else {
if (appMain.rootStore.openLinksInStatus) { if (appMain.rootStore.openLinksInStatus) {
Global.changeAppSectionBySectionType(Constants.appSection.browser); globalConns.onAppSectionBySectionTypeChanged(Constants.appSection.browser)
Global.openLinkInBrowser(link); globalConns.onOpenLinkInBrowser(link)
} else { } else {
Qt.openUrlExternally(link); Qt.openUrlExternally(link)
} }
} }
} }
function onPlaySendMessageSound() {
sendMessageSound.stop()
sendMessageSound.play()
}
function onPlayNotificationSound() {
notificationSound.stop()
notificationSound.play()
}
function onPlayErrorSound() {
errorSound.stop()
errorSound.play()
}
function onSetNthEnabledSectionActive(nthSection: int) { function onSetNthEnabledSectionActive(nthSection: int) {
if(!appMain.rootStore.mainModuleInst) if(!appMain.rootStore.mainModuleInst)
return return
@ -224,124 +162,22 @@ Item {
appMain.rootStore.mainModuleInst.setActiveSectionById(sectionId) appMain.rootStore.mainModuleInst.setActiveSectionById(sectionId)
} }
Component {
id: backupSeedModalComponent
BackupSeedModal {
anchors.centerIn: parent
privacyStore: appMain.rootStore.profileSectionStore.privacyStore
onClosed: destroy()
}
}
Component {
id: displayNamePopupComponent
DisplayNamePopup {
anchors.centerIn: parent
profileStore: appMain.rootStore.profileSectionStore.profileStore
onClosed: {
destroy()
}
}
}
Component {
id: downloadPageComponent
DownloadPage {
onClosed: {
destroy();
}
}
}
Component {
id: imagePopupComponent
StatusImageModal {
id: imagePopup
onClicked: {
if (mouse.button === Qt.LeftButton) {
imagePopup.close()
} else if(mouse.button === Qt.RightButton) {
contextMenu.imageSource = imagePopup.imageSource
contextMenu.hideEmojiPicker = true
contextMenu.isRightClickOnImage = true
contextMenu.parent = imagePopup.contentItem
contextMenu.show()
}
}
onClosed: destroy()
}
}
Component {
id: profilePopupComponent
ProfileDialog {
id: profilePopup
profileStore: appMain.rootStore.profileSectionStore.profileStore
contactsStore: appMain.rootStore.profileSectionStore.contactsStore
onClosed: {
if (profilePopup.parentPopup) {
profilePopup.parentPopup.close()
}
Global.profilePopupOpened = false
destroy()
}
Component.onCompleted: {
appMain.closeProfilePopup.connect(profilePopup.close)
}
}
}
Component {
id: changeProfilePicComponent
ImageCropWorkflow {
title: qsTr("Profile Picture")
acceptButtonText: qsTr("Make this my Profile Pic")
onImageCropped: {
if (callback) {
callback(image,
cropRect.x.toFixed(),
cropRect.y.toFixed(),
(cropRect.x + cropRect.width).toFixed(),
(cropRect.y + cropRect.height).toFixed())
return
}
appMain.rootStore.profileSectionStore.profileStore.uploadImage(image,
cropRect.x.toFixed(),
cropRect.y.toFixed(),
(cropRect.x + cropRect.width).toFixed(),
(cropRect.y + cropRect.height).toFixed());
}
}
}
Audio { Audio {
id: sendMessageSound id: sendMessageSound
store: rootStore store: rootStore
source: "qrc:/imports/assets/audio/send_message.wav" source: "qrc:/imports/assets/audio/send_message.wav"
Component.onCompleted: {
Global.sendMessageSound = this;
}
} }
Audio { Audio {
id: notificationSound id: notificationSound
store: rootStore store: rootStore
source: "qrc:/imports/assets/audio/notification.wav" source: "qrc:/imports/assets/audio/notification.wav"
Component.onCompleted: {
Global.notificationSound = this;
}
} }
Audio { Audio {
id: errorSound id: errorSound
source: "qrc:/imports/assets/audio/error.mp3" source: "qrc:/imports/assets/audio/error.mp3"
store: rootStore store: rootStore
Component.onCompleted: {
Global.errorSound = this;
}
} }
Loader { Loader {
@ -452,7 +288,7 @@ Item {
icon.name: "share-ios" icon.name: "share-ios"
enabled: model.canManageUsers enabled: model.canManageUsers
onTriggered: { onTriggered: {
Global.openInviteFriendsToCommunityPopup(model, popups.openInviteFriendsToCommunityPopup(model,
communityContextMenu.chatCommunitySectionModule, communityContextMenu.chatCommunitySectionModule,
null) null)
} }
@ -461,11 +297,7 @@ Item {
StatusAction { StatusAction {
text: qsTr("View Community") text: qsTr("View Community")
icon.name: "group-chat" icon.name: "group-chat"
onTriggered: Global.openPopup(communityProfilePopup, { onTriggered: popups.openCommunityProfilePopup(appMain.rootStore, model, communityContextMenu.chatCommunitySectionModule)
store: appMain.rootStore,
community: model,
communitySectionModule: communityContextMenu.chatCommunitySectionModule
})
} }
StatusMenuSeparator {} StatusMenuSeparator {}
@ -670,9 +502,7 @@ Item {
text: qsTr("Secure your seed phrase") text: qsTr("Secure your seed phrase")
buttonText: qsTr("Back up now") buttonText: qsTr("Back up now")
onClicked: { onClicked: popups.openBackUpSeedPopup()
Global.openBackUpSeedPopup();
}
onCloseClicked: { onCloseClicked: {
appMain.rootStore.profileSectionStore.profileStore.userDeclinedBackupBanner = true appMain.rootStore.profileSectionStore.profileStore.userDeclinedBackupBanner = true
@ -719,7 +549,7 @@ Item {
return "" return ""
} }
onLinkActivated: Global.openPopup(communitiesPortalLayoutContainer.discordImportProgressPopup) onLinkActivated: popups.openPopup(communitiesPortalLayoutContainer.discordImportProgressPopup)
progressValue: progress progressValue: progress
closeBtnVisible: finished || stopped closeBtnVisible: finished || stopped
buttonText: finished && !errors ? qsTr("Visit your Community") : "" buttonText: finished && !errors ? qsTr("Visit your Community") : ""
@ -880,6 +710,8 @@ Item {
Loader { Loader {
id: personalChatLayoutLoader id: personalChatLayoutLoader
asynchronous: true
active: appView.currentIndex === Constants.appViewStackIndex.chat
sourceComponent: { sourceComponent: {
if (appMain.rootStore.mainModuleInst.chatsLoadingFailed) { if (appMain.rootStore.mainModuleInst.chatsLoadingFailed) {
return errorStateComponent return errorStateComponent
@ -939,11 +771,11 @@ Item {
} }
onImportCommunityClicked: { onImportCommunityClicked: {
Global.openPopup(communitiesPortalLayoutContainer.importCommunitiesPopup); popups.openPopup(communitiesPortalLayoutContainer.importCommunitiesPopup);
} }
onCreateCommunityClicked: { onCreateCommunityClicked: {
Global.openPopup(communitiesPortalLayoutContainer.createCommunitiesPopup); popups.openPopup(communitiesPortalLayoutContainer.createCommunitiesPopup);
} }
Component.onCompleted: { Component.onCompleted: {
@ -1075,51 +907,9 @@ Item {
} }
} // ColumnLayout } // ColumnLayout
Component {
id: chooseBrowserPopupComponent
ChooseBrowserPopup {
onClosed: {
destroy()
}
}
}
Component {
id: communityProfilePopup
CommunityProfilePopup {
anchors.centerIn: parent
contactsStore: appMain.rootStore.contactStore
hasAddedContacts: appMain.rootStore.hasAddedContacts
onClosed: {
destroy()
}
}
}
Component {
id: pinnedMessagesPopupComponent
PinnedMessagesPopup {
id: pinnedMessagesPopup
emojiReactionsModel: appMain.rootStore.emojiReactionsModel
onClosed: destroy()
}
}
Component {
id: genericConfirmationDialog
ConfirmationDialog {
onClosed: {
destroy()
}
}
}
Component { Component {
id: activityCenterPopupComponent id: activityCenterPopupComponent
ActivityCenterPopup { ActivityCenterPopup {
id: activityCenter
// TODO get screen size // Taken from old code top bar height was fixed there to 56 // TODO get screen size // Taken from old code top bar height was fixed there to 56
property int _buttonSize: 56 property int _buttonSize: 56
@ -1131,41 +921,6 @@ Item {
} }
} }
Component {
id: nicknamePopupComponent
NicknamePopup {
onEditDone: {
if (nickname !== newNickname) {
appMain.rootStore.contactStore.changeContactNickname(publicKey, newNickname)
}
close()
}
onClosed: destroy()
}
}
Component {
id: unblockContactConfirmationComponent
UnblockContactConfirmationDialog {
onUnblockButtonClicked: {
appMain.rootStore.contactStore.unblockContact(contactAddress)
close()
}
onClosed: destroy()
}
}
Component {
id: blockContactConfirmationComponent
BlockContactConfirmationDialog {
onBlockButtonClicked: {
appMain.rootStore.contactStore.blockContact(contactAddress)
close()
}
onClosed: destroy()
}
}
// Add SendModal here as it is used by the Wallet as well as the Browser // Add SendModal here as it is used by the Wallet as well as the Browser
Loader { Loader {
id: sendModal id: sendModal
@ -1354,9 +1109,6 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
Global.appMain = this;
Global.pinnedMessagesPopup = pinnedMessagesPopupComponent;
Global.communityProfilePopup = communityProfilePopup;
const whitelist = appMain.rootStore.messagingStore.getLinkPreviewWhitelist() const whitelist = appMain.rootStore.messagingStore.getLinkPreviewWhitelist()
try { try {
const whiteListedSites = JSON.parse(whitelist) const whiteListedSites = JSON.parse(whitelist)

View File

@ -1,18 +1,119 @@
import QtQuick 2.14 import QtQuick 2.15
import AppLayouts.Chat.popups 1.0 import AppLayouts.Chat.popups 1.0
import AppLayouts.Profile.popups 1.0
import shared.popups 1.0 import shared.popups 1.0
import shared.status 1.0
import utils 1.0 import utils 1.0
QtObject { QtObject {
id: root id: root
/* required */ property var rootStore required property var popupParent
required property var rootStore
property var activePopupComponents: []
Component.onCompleted: {
Global.openSendIDRequestPopup.connect(openSendIDRequestPopup)
Global.openOutgoingIDRequestPopup.connect(openOutgoingIDRequestPopup)
Global.openIncomingIDRequestPopup.connect(openIncomingIDRequestPopup)
Global.openInviteFriendsToCommunityPopup.connect(openInviteFriendsToCommunityPopup)
Global.openContactRequestPopup.connect(openContactRequestPopup)
Global.openChooseBrowserPopup.connect(openChooseBrowserPopup)
Global.openDownloadModalRequested.connect(openDownloadModal)
Global.openImagePopup.connect(openImagePopup)
Global.openProfilePopupRequested.connect(openProfilePopup)
Global.openNicknamePopupRequested.connect(openNicknamePopup)
Global.blockContactRequested.connect(openBlockContactPopup)
Global.unblockContactRequested.connect(openUnblockContactPopup)
Global.openChangeProfilePicPopup.connect(openChangeProfilePicPopup)
Global.openBackUpSeedPopup.connect(openBackUpSeedPopup)
Global.openEditDisplayNamePopup.connect(openEditDisplayNamePopup)
Global.openPinnedMessagesPopupRequested.connect(openPinnedMessagesPopup)
Global.openCommunityProfilePopupRequested.connect(openCommunityProfilePopup)
Global.openPopupRequested.connect(openPopup)
}
function openPopup(popupComponent, params = {}, cb = null) {
if (activePopupComponents.includes(popupComponent)) {
return
}
const popup = popupComponent.createObject(popupParent, params)
popup.open()
if (cb)
cb(popup)
activePopupComponents.push(popupComponent)
popup.closed.connect(() => {
const removeIndex = activePopupComponents.indexOf(popupComponent)
if (removeIndex !== -1) {
activePopupComponents.splice(removeIndex, 1)
}
})
}
function openChooseBrowserPopup(link: string) {
openPopup(chooseBrowserPopupComponent, {link: link})
}
function openDownloadModal(available: bool, version: string, url: string) {
const popupProperties = {
newVersionAvailable: available,
downloadURL: url,
currentVersion: rootStore.profileSectionStore.getCurrentVersion(),
newVersion: version
}
openPopup(downloadPageComponent, popupProperties)
}
function openImagePopup(image, contextMenu) {
var popup = imagePopupComponent.createObject(popupParent)
popup.contextMenu = contextMenu
popup.openPopup(image)
}
function openProfilePopup(publicKey: string, parentPopup) {
openPopup(profilePopupComponent, {publicKey: publicKey, parentPopup: parentPopup})
}
function openNicknamePopup(publicKey: string, nickname: string, subtitle: string) {
openPopup(nicknamePopupComponent, {publicKey: publicKey, nickname: nickname, "header.subTitle": subtitle})
}
function openBlockContactPopup(publicKey: string, contactName: string) {
openPopup(blockContactConfirmationComponent, {contactName: contactName, contactAddress: publicKey})
}
function openUnblockContactPopup(publicKey: string, contactName: string) {
openPopup(unblockContactConfirmationComponent, {contactName: contactName, contactAddress: publicKey})
}
function openChangeProfilePicPopup(cb) {
var popup = changeProfilePicComponent.createObject(popupParent, {callback: cb});
popup.chooseImageToCrop()
}
function openBackUpSeedPopup() {
openPopup(backupSeedModalComponent)
}
function openEditDisplayNamePopup() {
openPopup(displayNamePopupComponent)
}
function openCommunityProfilePopup(store, community, communitySectionModule) {
openPopup(communityProfilePopup, { store: store, community: community, communitySectionModule: communitySectionModule})
}
function openSendIDRequestPopup(publicKey, cb) { function openSendIDRequestPopup(publicKey, cb) {
const contactDetails = Utils.getContactDetailsAsJson(publicKey, false) const contactDetails = Utils.getContactDetailsAsJson(publicKey, false)
const popup = Global.openPopup(sendIDRequestPopupComponent, { openPopup(sendIDRequestPopupComponent, {
userPublicKey: publicKey, userPublicKey: publicKey,
userDisplayName: contactDetails.displayName, userDisplayName: contactDetails.displayName,
userIcon: contactDetails.largeImage, userIcon: contactDetails.largeImage,
@ -20,9 +121,7 @@ QtObject {
"header.title": qsTr("Verify %1's Identity").arg(contactDetails.displayName), "header.title": qsTr("Verify %1's Identity").arg(contactDetails.displayName),
challengeText: qsTr("Ask a question that only the real %1 will be able to answer e.g. a question about a shared experience, or ask %1 to enter a code or phrase you have sent to them via a different communication channel (phone, post, etc...).").arg(contactDetails.displayName), challengeText: qsTr("Ask a question that only the real %1 will be able to answer e.g. a question about a shared experience, or ask %1 to enter a code or phrase you have sent to them via a different communication channel (phone, post, etc...).").arg(contactDetails.displayName),
buttonText: qsTr("Send verification request") buttonText: qsTr("Send verification request")
}) }, cb)
if (cb)
cb(popup)
} }
function openOutgoingIDRequestPopup(publicKey, cb) { function openOutgoingIDRequestPopup(publicKey, cb) {
@ -38,9 +137,7 @@ QtObject {
verificationRequestedAt: verificationDetails.requestedAt, verificationRequestedAt: verificationDetails.requestedAt,
verificationRepliedAt: verificationDetails.repliedAt verificationRepliedAt: verificationDetails.repliedAt
} }
const popup = Global.openPopup(contactOutgoingVerificationRequestPopupComponent, popupProperties) openPopup(contactOutgoingVerificationRequestPopupComponent, popupProperties, cb)
if (cb)
cb(popup)
} catch (e) { } catch (e) {
console.error("Error getting or parsing verification data", e) console.error("Error getting or parsing verification data", e)
} }
@ -52,16 +149,11 @@ QtObject {
publicKey: publicKey publicKey: publicKey
} }
const popup = Global.openPopup(contactVerificationRequestPopupComponent, popupProperties) openPopup(contactVerificationRequestPopupComponent, popupProperties, cb)
if (cb) {
cb(popup)
}
} }
function openInviteFriendsToCommunityPopup(community, communitySectionModule, cb) { function openInviteFriendsToCommunityPopup(community, communitySectionModule, cb) {
const popup = Global.openPopup(inviteFriendsToCommunityPopup, { community, communitySectionModule }) openPopup(inviteFriendsToCommunityPopup, { community: community, communitySectionModule: communitySectionModule }, cb)
if (cb)
cb(popup)
} }
function openContactRequestPopup(publicKey, cb) { function openContactRequestPopup(publicKey, cb) {
@ -73,12 +165,19 @@ QtObject {
userIsEnsVerified: contactDetails.ensVerified userIsEnsVerified: contactDetails.ensVerified
} }
const popup = Global.openPopup(sendContactRequestPopupComponent, popupProperties) openPopup(sendContactRequestPopupComponent, popupProperties, cb)
if (cb)
cb(popup)
} }
readonly property list<Component> _d: [ function openPinnedMessagesPopup(store, messageStore, pinnedMessagesModel, messageToPin) {
openPopup(pinnedMessagesPopup, { store: store, messageStore: messageStore,
pinnedMessagesModel: pinnedMessagesModel, messageToPin: messageToPin})
}
function openCommunityPopup(store, community, chatCommunitySectionModule) {
openPopup(communityProfilePopup, {store: store, community: community, chatCommunitySectionModule: chatCommunitySectionModule})
}
readonly property list<Component> _components: [
Component { Component {
id: contactVerificationRequestPopupComponent id: contactVerificationRequestPopupComponent
ContactVerificationRequestPopup { ContactVerificationRequestPopup {
@ -136,6 +235,153 @@ QtObject {
onAccepted: root.rootStore.profileSectionStore.contactsStore.sendContactRequest(userPublicKey, message) onAccepted: root.rootStore.profileSectionStore.contactsStore.sendContactRequest(userPublicKey, message)
onClosed: destroy() onClosed: destroy()
} }
},
Component {
id: backupSeedModalComponent
BackupSeedModal {
anchors.centerIn: parent
privacyStore: rootStore.profileSectionStore.privacyStore
onClosed: destroy()
}
},
Component {
id: displayNamePopupComponent
DisplayNamePopup {
anchors.centerIn: parent
profileStore: rootStore.profileSectionStore.profileStore
onClosed: destroy()
}
},
Component {
id: downloadPageComponent
DownloadPage {
onClosed: destroy()
}
},
Component {
id: imagePopupComponent
StatusImageModal {
id: imagePopup
onClicked: {
if (mouse.button === Qt.LeftButton) {
imagePopup.close()
} else if(mouse.button === Qt.RightButton) {
contextMenu.imageSource = imagePopup.imageSource
contextMenu.hideEmojiPicker = true
contextMenu.isRightClickOnImage = true
contextMenu.parent = imagePopup.contentItem
contextMenu.show()
}
}
onClosed: destroy()
}
},
Component {
id: profilePopupComponent
ProfileDialog {
id: profilePopup
profileStore: rootStore.profileSectionStore.profileStore
contactsStore: rootStore.profileSectionStore.contactsStore
onClosed: {
if (profilePopup.parentPopup) {
profilePopup.parentPopup.close()
}
destroy()
}
}
},
Component {
id: changeProfilePicComponent
ImageCropWorkflow {
title: qsTr("Profile Picture")
acceptButtonText: qsTr("Make this my Profile Pic")
onImageCropped: {
if (callback) {
callback(image,
cropRect.x.toFixed(),
cropRect.y.toFixed(),
(cropRect.x + cropRect.width).toFixed(),
(cropRect.y + cropRect.height).toFixed())
return
}
rootStore.profileSectionStore.profileStore.uploadImage(image,
cropRect.x.toFixed(),
cropRect.y.toFixed(),
(cropRect.x + cropRect.width).toFixed(),
(cropRect.y + cropRect.height).toFixed());
}
onDone: destroy()
}
},
Component {
id: chooseBrowserPopupComponent
ChooseBrowserPopup {
onClosed: destroy()
}
},
Component {
id: communityProfilePopup
CommunityProfilePopup {
anchors.centerIn: parent
contactsStore: rootStore.contactStore
hasAddedContacts: rootStore.hasAddedContacts
onClosed: destroy()
}
},
Component {
id: pinnedMessagesPopup
PinnedMessagesPopup {
emojiReactionsModel: rootStore.emojiReactionsModel
onClosed: destroy()
}
},
Component {
id: nicknamePopupComponent
NicknamePopup {
onEditDone: {
if (nickname !== newNickname) {
rootStore.contactStore.changeContactNickname(publicKey, newNickname)
}
close()
}
onClosed: destroy()
}
},
Component {
id: unblockContactConfirmationComponent
UnblockContactConfirmationDialog {
onUnblockButtonClicked: {
rootStore.contactStore.unblockContact(contactAddress)
close()
}
onClosed: destroy()
}
},
Component {
id: blockContactConfirmationComponent
BlockContactConfirmationDialog {
onBlockButtonClicked: {
rootStore.contactStore.blockContact(contactAddress)
close()
}
onClosed: destroy()
}
} }
] ]
} }

View File

@ -1,2 +1,3 @@
AppMain 1.0 AppMain.qml AppMain 1.0 AppMain.qml
SplashScreen 1.0 SplashScreen.qml SplashScreen 1.0 SplashScreen.qml
Popups 1.0 Popups.qml

View File

@ -23,6 +23,7 @@ Item {
property bool roundedImage: true property bool roundedImage: true
signal imageCropped(var image, var cropRect) signal imageCropped(var image, var cropRect)
signal done()
function chooseImageToCrop() { function chooseImageToCrop() {
fileDialog.open() fileDialog.open()
@ -87,6 +88,7 @@ Item {
} }
} }
] ]
onClosed: root.done()
} // StatusModal } // StatusModal
} // Item } // Item

View File

@ -1,5 +1,4 @@
import QtQuick 2.13 import QtQuick 2.14
import QtQuick.Controls 2.13
import utils 1.0 import utils 1.0
import shared.controls 1.0 import shared.controls 1.0
@ -61,11 +60,9 @@ StatusModal {
charLimit: maxNicknameLength charLimit: maxNicknameLength
validationMode: StatusInput.ValidationMode.IgnoreInvalidInput validationMode: StatusInput.ValidationMode.IgnoreInvalidInput
validators: [ validators: [
StatusRegularExpressionValidator { StatusValidator {
validatorObj: RXValidator { regularExpression: /^[\w\d_ -]*$/u } validatorObj: RXValidator { regularExpression: /^[\w\d_ -]*$/u }
validate: function (value) { validate: (value) => validatorObj.test(value)
return validatorObj.test(value)
}
} }
] ]
Keys.onReleased: { Keys.onReleased: {

View File

@ -129,12 +129,10 @@ Rectangle {
StatusBaseText { StatusBaseText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
text: { text: {
if (Global.appMain) { return LocaleUtils.currencyAmountToLocaleString({
return "%1%2".arg(SharedStore.RootStore.currencyStore.currentCurrencySymbol) amount: parseFloat(model.account.balance.amount),
.arg(Utils.toLocaleString(parseFloat(model.account.balance.amount).toFixed(2), appSettings.locale, {"model.account.currency": true})) symbol: SharedStore.RootStore.currencyStore.currentCurrencySymbol,
} displayDecimals: 2})
// without language/model refactor no way to read currency symbol or `appSettings.locale` before user logs in
return "$%1".arg(Utils.toLocaleString(parseFloat(model.account.balance.amount).toFixed(2), localAppSettings.language, {"model.account.currency": true}))
} }
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
font.pixelSize: Constants.keycard.general.fontSize2 font.pixelSize: Constants.keycard.general.fontSize2

View File

@ -161,12 +161,10 @@ Item {
ColumnLayout { ColumnLayout {
StatusBaseText { StatusBaseText {
text: { text: {
if (Global.appMain) { return qsTr("Balance: %1").arg(LocaleUtils.currencyAmountToLocaleString({
return "Balance: %1%2".arg(SharedStore.RootStore.currencyStore.currentCurrencySymbol) amount: parseFloat(root.sharedKeycardModule.keyPairHelper.observedAccount.balance),
.arg(Utils.toLocaleString(root.sharedKeycardModule.keyPairHelper.observedAccount.balance.toFixed(2), appSettings.locale, {"model.account.currency": true})) symbol: SharedStore.RootStore.currencyStore.currentCurrencySymbol,
} displayDecimals: 2}))
// without language/model refactor no way to read currency symbol or `appSettings.locale` before user logs in
return "Balance: $%1".arg(Utils.toLocaleString(root.sharedKeycardModule.keyPairHelper.observedAccount.balance.toFixed(2), localAppSettings.language, {"model.account.currency": true}))
} }
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
font.pixelSize: Constants.keycard.general.fontSize2 font.pixelSize: Constants.keycard.general.fontSize2

View File

@ -4,3 +4,4 @@ TransactionStore 1.0 TransactionStore.qml
BIP39_en 1.0 BIP39_en.qml BIP39_en 1.0 BIP39_en.qml
TokenBalanceHistoryStore 1.0 TokenBalanceHistoryStore.qml TokenBalanceHistoryStore 1.0 TokenBalanceHistoryStore.qml
TokenMarketValuesStore 1.0 TokenMarketValuesStore.qml TokenMarketValuesStore 1.0 TokenMarketValuesStore.qml
ChartStoreBase 1.0 ChartStoreBase.qml

View File

@ -104,6 +104,15 @@ Pane {
} }
} }
readonly property var conns3: Connections {
target: root.contactsStore.sentContactRequestsModel
function onItemChanged(pubKey) {
if (pubKey === root.publicKey)
d.reload()
}
}
readonly property var timer: Timer { readonly property var timer: Timer {
id: timer id: timer
} }
@ -171,8 +180,7 @@ Pane {
size: StatusButton.Size.Small size: StatusButton.Size.Small
text: qsTr("Send Contact Request") text: qsTr("Send Contact Request")
onClicked: { onClicked: {
Global.openContactRequestPopup(root.publicKey, Global.openContactRequestPopup(root.publicKey, null)
popup => popup.accepted.connect(d.reload))
} }
} }
} }
@ -395,8 +403,7 @@ Pane {
d.contactDetails.trustStatus === Constants.trustStatus.untrustworthy // we have an action button otherwise d.contactDetails.trustStatus === Constants.trustStatus.untrustworthy // we have an action button otherwise
onTriggered: { onTriggered: {
moreMenu.close() moreMenu.close()
Global.openContactRequestPopup(root.publicKey, Global.openContactRequestPopup(root.publicKey, null)
popup => popup.closed.connect(d.reload))
} }
} }
StatusAction { StatusAction {

View File

@ -365,14 +365,14 @@ StatusMenu {
root.store.copyToClipboard(root.unparsedText) root.store.copyToClipboard(root.unparsedText)
close() close()
} }
enabled: root.messageContentType === Constants.messageContentType.messageType && !root.isProfile && !emojiContainer.visible enabled: root.messageContentType === Constants.messageContentType.messageType && replyToMenuItem.enabled
} }
StatusAction { StatusAction {
id: copyMessageIdAction id: copyMessageIdAction
text: qsTr("Copy Message Id") text: qsTr("Copy Message Id")
icon.name: "copy" icon.name: "copy"
enabled: root.isDebugEnabled && !root.isProfile && !root.pinnedPopup && !emojiContainer.visible enabled: root.isDebugEnabled && replyToMenuItem.enabled
onTriggered: { onTriggered: {
root.store.copyToClipboard(root.messageId) root.store.copyToClipboard(root.messageId)
close() close()

View File

@ -467,7 +467,6 @@ Loader {
disableHover: root.disableHover || disableHover: root.disableHover ||
(root.chatLogView && root.chatLogView.moving) || (root.chatLogView && root.chatLogView.moving) ||
(root.messageContextMenu && root.messageContextMenu.opened) || (root.messageContextMenu && root.messageContextMenu.opened) ||
Global.profilePopupOpened ||
Global.popupOpened Global.popupOpened
hideQuickActions: root.isChatBlocked || hideQuickActions: root.isChatBlocked ||
@ -816,12 +815,7 @@ Loader {
return; return;
} }
Global.openPopup(Global.pinnedMessagesPopup, { Global.openPinnedMessagesPopupRequested(root.rootStore, messageStore, chatContentModule.pinnedMessagesModel, root.messageId)
store: root.rootStore,
messageStore: messageStore,
pinnedMessagesModel: chatContentModule.pinnedMessagesModel,
messageToPin: root.messageId
});
} }
} }
}, },

View File

@ -6,19 +6,14 @@ QtObject {
id: root id: root
property var dragArea property var dragArea
property var appMain
property var applicationWindow property var applicationWindow
property bool popupOpened: false property bool popupOpened: false
property int settingsSubsection: Constants.settingsSubsection.profile property int settingsSubsection: Constants.settingsSubsection.profile
property var userProfile property var userProfile
property var pinnedMessagesPopup
property var communityProfilePopup
property bool profilePopupOpened: false
property var sendMessageSound signal openPinnedMessagesPopupRequested(var store, var messageStore, var pinnedMessagesModel, string messageToPin)
property var notificationSound signal openCommunityProfilePopupRequested(var store, var community, var chatCommunitySectionModule)
property var errorSound
signal openLinkInBrowser(string link) signal openLinkInBrowser(string link)
signal openChooseBrowserPopup(string link) signal openChooseBrowserPopup(string link)
@ -31,7 +26,7 @@ QtObject {
signal displayToastMessage(string title, string subTitle, string icon, bool loading, int ephNotifType, string url) signal displayToastMessage(string title, string subTitle, string icon, bool loading, int ephNotifType, string url)
signal openPopupRequested(var popupComponent, var params); signal openPopupRequested(var popupComponent, var params)
signal openNicknamePopupRequested(string publicKey, string nickname, string subtitle) signal openNicknamePopupRequested(string publicKey, string nickname, string subtitle)
signal openDownloadModalRequested(bool available, string version, string url) signal openDownloadModalRequested(bool available, string version, string url)
signal openChangeProfilePicPopup(var cb) signal openChangeProfilePicPopup(var cb)
@ -51,6 +46,10 @@ QtObject {
signal setNthEnabledSectionActive(int nthSection) signal setNthEnabledSectionActive(int nthSection)
signal appSectionBySectionTypeChanged(int sectionType, int subsection) signal appSectionBySectionTypeChanged(int sectionType, int subsection)
signal playSendMessageSound()
signal playNotificationSound()
signal playErrorSound()
function openProfilePopup(publicKey, parentPopup) { function openProfilePopup(publicKey, parentPopup) {
root.openProfilePopupRequested(publicKey, parentPopup) root.openProfilePopupRequested(publicKey, parentPopup)
} }
@ -70,9 +69,4 @@ QtObject {
function changeAppSectionBySectionType(sectionType, subsection = 0) { function changeAppSectionBySectionType(sectionType, subsection = 0) {
root.appSectionBySectionTypeChanged(sectionType, subsection); root.appSectionBySectionTypeChanged(sectionType, subsection);
} }
function playErrorSound() {
if (root.errorSound)
root.errorSound.play();
}
} }