status-desktop/ui/app/mainui/AppMain.qml

1266 lines
48 KiB
QML
Raw Normal View History

2020-06-17 19:18:31 +00:00
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import QtMultimedia 5.13
import Qt.labs.qmlmodels 1.0
import Qt.labs.platform 1.1
import Qt.labs.settings 1.0
import QtQml.Models 2.14
2022-03-08 18:49:33 +00:00
import AppLayouts.Wallet 1.0
import AppLayouts.Node 1.0
import AppLayouts.Browser 1.0
import AppLayouts.Chat 1.0
import AppLayouts.Chat.popups 1.0
import AppLayouts.Chat.views 1.0
2022-03-08 18:49:33 +00:00
import AppLayouts.Profile 1.0
import AppLayouts.Profile.popups 1.0
import AppLayouts.CommunitiesPortal 1.0
import utils 1.0
import shared 1.0
import shared.controls 1.0
import shared.panels 1.0
import shared.popups 1.0
import shared.popups.keycard 1.0
import shared.status 1.0
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Layout 0.1
import StatusQ.Popups 0.1
import StatusQ.Popups.Dialog 0.1
import StatusQ.Core 0.1
import AppLayouts.Browser.stores 1.0 as BrowserStores
2022-03-08 18:49:33 +00:00
import AppLayouts.stores 1.0
import "popups"
import "panels"
import "activitycenter/popups"
import "activitycenter/stores" as AC
2021-06-17 18:41:11 +00:00
Item {
id: appMain
anchors.fill: parent
property alias appLayout: appLayout
property RootStore rootStore: RootStore {}
property AC.RootStore acStore: AC.RootStore {}
// set from main.qml
property var sysPalette
Connections {
target: rootStore.mainModuleInst
onDisplayUserProfile: Global.openProfilePopup(publicKey)
onDisplayKeycardSharedModuleFlow: {
keycardPopup.active = true
}
onDestroyKeycardSharedModuleFlow: {
keycardPopup.active = false
}
}
Connections {
target: Global
onOpenLinkInBrowser: {
browserLayoutContainer.item.openUrlInNewTab(link);
}
onOpenChooseBrowserPopup: {
Global.openPopup(chooseBrowserPopupComponent, {link: link});
}
onOpenDownloadModalRequested: {
const downloadPage = downloadPageComponent.createObject(appMain,
{
newVersionAvailable: available,
downloadURL: url,
currentVersion: rootStore.profileSectionStore.getCurrentVersion(),
newVersion: version
})
return downloadPage
}
2022-09-27 21:26:26 +00:00
onOpenProfilePopupRequested: {
2022-09-27 21:26:26 +00:00
Global.openPopup(profilePopupComponent, {publicKey: publicKey, parentPopup: parentPopup})
Global.profilePopupOpened = true
}
onOpenNicknamePopupRequested: {
Global.openPopup(nicknamePopupComponent, {publicKey: publicKey, nickname: nickname, "header.subTitle": subtitle})
}
onBlockContactRequested: {
Global.openPopup(blockContactConfirmationComponent, {contactName: contactName, contactAddress: publicKey})
}
onUnblockContactRequested: {
Global.openPopup(unblockContactConfirmationComponent, {contactName: contactName, contactAddress: publicKey})
}
onOpenActivityCenterPopupRequested: {
Global.openPopup(activityCenterPopupComponent)
Global.activityCenterPopupOpened = true
}
onOpenChangeProfilePicPopup: {
var popup = changeProfilePicComponent.createObject(appMain);
2022-06-22 12:16:21 +00:00
popup.chooseImageToCrop();
}
onOpenBackUpSeedPopup: Global.openPopup(backupSeedModalComponent)
onDisplayToastMessage: {
appMain.rootStore.mainModuleInst.displayEphemeralNotification(title, subTitle, icon, loading, ephNotifType, url);
}
2022-09-27 21:26:26 +00:00
onOpenEditDisplayNamePopup: Global.openPopup(displayNamePopupComponent)
}
function changeAppSectionBySectionId(sectionId) {
mainModule.setActiveSectionById(sectionId)
}
2022-09-27 21:26:26 +00:00
Component {
id: backupSeedModalComponent
BackupSeedModal {
anchors.centerIn: parent
privacyStore: appMain.rootStore.profileSectionStore.privacyStore
onClosed: destroy()
}
}
2022-09-27 21:26:26 +00:00
Component {
id: displayNamePopupComponent
DisplayNamePopup {
anchors.centerIn: parent
profileStore: appMain.rootStore.profileSectionStore.profileStore
onClosed: {
destroy()
}
}
}
Component {
id: downloadPageComponent
DownloadPage {
onClosed: {
destroy();
}
}
}
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.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)
}
}
}
2022-09-27 21:26:26 +00:00
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()
}
2021-08-13 20:04:04 +00:00
}
}
2022-09-27 21:26:26 +00:00
Component {
id: changeProfilePicComponent
2022-06-22 12:16:21 +00:00
ImageCropWorkflow {
title: qsTr("Profile Picture")
acceptButtonText: qsTr("Make this my Profile Pic")
onImageCropped: {
appMain.rootStore.profileSectionStore.profileStore.uploadImage(image,
cropRect.x.toFixed(),
cropRect.y.toFixed(),
(cropRect.x + cropRect.width).toFixed(),
(cropRect.y + cropRect.height).toFixed());
}
}
}
Audio {
id: sendMessageSound
store: rootStore
track: Qt.resolvedUrl("../imports/assets/audio/send_message.wav")
Component.onCompleted: {
Global.sendMessageSound = this;
}
}
Audio {
id: notificationSound
store: rootStore
track: Qt.resolvedUrl("../imports/assets/audio/notification.wav")
Component.onCompleted: {
Global.notificationSound = this;
}
}
Audio {
id: errorSound
track: Qt.resolvedUrl("../imports/assets/audio/error.mp3")
store: rootStore
Component.onCompleted: {
Global.errorSound = this;
}
}
AppSearch {
id: appSearch
store: appMain.rootStore.appSearchStore
}
StatusEmojiPopup {
id: statusEmojiPopup
width: 360
height: 440
}
StatusMainLayout {
id: appLayout
anchors.fill: parent
2021-06-17 18:41:11 +00:00
leftPanel: StatusAppNavBar {
communityTypeRole: "sectionType"
communityTypeValue: Constants.appSection.community
sectionModel: mainModule.sectionsModel
Component.onCompleted: {
mainModule.sectionsModel.sectionVisibilityUpdated.connect(function(){
triggerUpdate()
})
}
property bool communityAdded: false
onAboutToUpdateFilteredRegularModel: {
communityAdded = false
}
filterRegularItem: function(item) {
if(!item.enabled)
return false
if(item.sectionType === Constants.appSection.community)
if(communityAdded)
return false
else
communityAdded = true
return true
}
filterCommunityItem: function(item) {
return item.sectionType === Constants.appSection.community
2021-06-17 18:41:11 +00:00
}
regularNavBarButton: StatusNavBarTabButton {
id: navbar
objectName: model.name + "-navbar"
anchors.horizontalCenter: parent.horizontalCenter
name: model.icon.length > 0? "" : model.name
icon.name: model.icon
icon.source: model.image
tooltip.text: model.name
checked: model.active
badge.value: model.notificationsCount
badge.visible: model.hasNotification
badge.border.color: hovered ? Theme.palette.statusBadge.hoverBorderColor : Theme.palette.statusBadge.borderColor
badge.border.width: 2
2021-06-17 18:41:11 +00:00
onClicked: {
changeAppSectionBySectionId(model.id)
2021-06-17 18:41:11 +00:00
}
}
2021-06-17 18:41:11 +00:00
communityNavBarButton: StatusNavBarTabButton {
objectName: "CommunityNavBarButton"
2021-06-17 18:41:11 +00:00
anchors.horizontalCenter: parent.horizontalCenter
name: model.icon.length > 0? "" : model.name
icon.name: model.icon
icon.source: model.image
identicon.asset.color: (hovered || identicon.highlighted || checked) ? model.color : icon.color
2021-06-17 18:41:11 +00:00
tooltip.text: model.name
checked: model.active
badge.value: model.notificationsCount
badge.visible: model.hasNotification
2021-06-17 18:41:11 +00:00
badge.border.color: hovered ? Theme.palette.statusBadge.hoverBorderColor : Theme.palette.statusBadge.borderColor
badge.border.width: 2
onClicked: {
changeAppSectionBySectionId(model.id)
}
2021-06-17 18:41:11 +00:00
popupMenu: StatusPopupMenu {
id: communityContextMenu
property var chatCommunitySectionModule
2021-06-17 18:41:11 +00:00
openHandler: function () {
// // we cannot return QVariant if we pass another parameter in a function call
// // that's why we're using it this way
mainModule.prepareCommunitySectionModuleForCommunityId(model.id)
communityContextMenu.chatCommunitySectionModule = mainModule.getCommunitySectionModule()
2021-06-17 18:41:11 +00:00
}
StatusMenuItem {
text: qsTr("Invite People")
2021-06-17 18:41:11 +00:00
icon.name: "share-ios"
enabled: model.canManageUsers
onTriggered: {
Global.openInviteFriendsToCommunityPopup(model,
communityContextMenu.chatCommunitySectionModule)
}
2021-06-17 18:41:11 +00:00
}
StatusMenuItem {
text: qsTr("View Community")
2021-06-17 18:41:11 +00:00
icon.name: "group-chat"
onTriggered: Global.openPopup(communityProfilePopup, {
store: appMain.rootStore,
community: model,
communitySectionModule: communityContextMenu.chatCommunitySectionModule
})
2021-06-17 18:41:11 +00:00
}
StatusMenuSeparator {}
StatusMenuItem {
text: qsTr("Leave Community")
icon.name: "arrow-left"
2021-06-17 18:41:11 +00:00
type: StatusMenuItem.Type.Danger
onTriggered: communityContextMenu.chatCommunitySectionModule.leaveCommunity()
2021-06-17 18:41:11 +00:00
}
}
}
navBarProfileButton: StatusNavBarTabButton {
id: profileButton
objectName: "statusProfileNavBarTabButton"
property bool opened: false
name: appMain.rootStore.userProfileInst.name
icon.source: appMain.rootStore.userProfileInst.icon
width: 32
height: 32
identicon.asset.width: width
identicon.asset.height: height
identicon.asset.charactersLen: 2
identicon.asset.color: Utils.colorForPubkey(appMain.rootStore.userProfileInst.pubKey)
identicon.ringSettings.ringSpecModel: appMain.rootStore.userProfileInst.ensName ? undefined : Utils.getColorHashAsJson(appMain.rootStore.userProfileInst.pubKey, true)
2021-06-17 18:41:11 +00:00
badge.visible: true
badge.anchors {
left: undefined
top: undefined
right: profileButton.right
bottom: profileButton.bottom
margins: 0
rightMargin: -badge.border.width
bottomMargin: -badge.border.width
}
badge.implicitHeight: 12
badge.implicitWidth: 12
badge.border.width: 2
2021-06-17 18:41:11 +00:00
badge.border.color: hovered ? Theme.palette.statusBadge.hoverBorderColor : Theme.palette.statusAppNavBar.backgroundColor
badge.color: {
switch(appMain.rootStore.userProfileInst.currentUserStatus){
case Constants.currentUserStatus.automatic:
case Constants.currentUserStatus.alwaysOnline:
return Style.current.green;
default:
return Style.current.midGrey;
}
}
onClicked: userStatusContextMenu.opened ? userStatusContextMenu.close() : userStatusContextMenu.open()
2021-06-17 18:41:11 +00:00
UserStatusContextMenu {
id: userStatusContextMenu
y: profileButton.y - userStatusContextMenu.height + profileButton.height
x: profileButton.x + profileButton.width + 5
store: appMain.rootStore
2021-06-17 18:41:11 +00:00
}
}
}
2021-06-17 18:41:11 +00:00
rightPanel: ColumnLayout {
spacing: 0
objectName: "mainRightView"
ColumnLayout {
id: bannersLayout
property var updateBanner: null
property var connectedBanner: null
readonly property bool isConnected: mainModule.isOnline
function processUpdateAvailable() {
if (!updateBanner)
updateBanner = updateBannerComponent.createObject(this)
}
function processConnected() {
if (!connectedBanner)
connectedBanner = connectedBannerComponent.createObject(this)
}
Layout.fillWidth: true
Layout.maximumHeight: implicitHeight
spacing: 1
onIsConnectedChanged: {
processConnected()
}
Connections {
target: rootStore.aboutModuleInst
onAppVersionFetched: {
rootStore.setLatestVersionInfo(available, version, url);
bannersLayout.processUpdateAvailable()
}
}
ModuleWarning {
id: testnetBanner
objectName: "testnetBanner"
Layout.fillWidth: true
text: qsTr("Testnet mode is enabled. All balances, transactions and dApp interactions will be on testnets.")
buttonText: qsTr("Turn off")
type: ModuleWarning.Danger
active: appMain.rootStore.profileSectionStore.walletStore.areTestNetworksEnabled
onClicked: {
testnetBannerDialog.open()
}
onCloseClicked: {
testnetBannerDialog.open()
}
StatusDialog {
id: testnetBannerDialog
width: 400
title: qsTr("Turn off Testnet mode")
StatusBaseText {
anchors.fill: parent
text: qsTr("Closing this banner will turn off Testnet mode.\nAll future transactions will be on mainnet or other active networks.")
font.pixelSize: 15
wrapMode: Text.WordWrap
}
footer: StatusDialogFooter {
rightButtons: ObjectModel {
StatusButton {
type: StatusButton.Danger
text: qsTr("Turn off Testnet")
onClicked: {
appMain.rootStore.profileSectionStore.walletStore.toggleTestNetworksEnabled()
testnetBannerDialog.close()
}
}
}
}
}
}
ModuleWarning {
id: secureYourSeedPhrase
objectName: "secureYourSeedPhraseBanner"
Layout.fillWidth: true
active: !appMain.rootStore.profileSectionStore.profileStore.userDeclinedBackupBanner
&& !appMain.rootStore.profileSectionStore.profileStore.privacyStore.mnemonicBackedUp
type: ModuleWarning.Danger
text: qsTr("Secure your seed phrase")
buttonText: qsTr("Back up now")
onClicked: {
Global.openBackUpSeedPopup();
}
onCloseClicked: {
appMain.rootStore.profileSectionStore.profileStore.userDeclinedBackupBanner = true
}
}
ModuleWarning {
Layout.fillWidth: true
readonly property int progress: communitiesPortalLayoutContainer.communitiesStore.discordImportProgress
readonly property bool inProgress: progress > 0 && progress < 100
readonly property bool finished: progress >= 100
readonly property bool cancelled: communitiesPortalLayoutContainer.communitiesStore.discordImportCancelled
readonly property bool stopped: communitiesPortalLayoutContainer.communitiesStore.discordImportProgressStopped
readonly property int errors: communitiesPortalLayoutContainer.communitiesStore.discordImportErrorsCount
readonly property int warnings: communitiesPortalLayoutContainer.communitiesStore.discordImportWarningsCount
readonly property string communityId: communitiesPortalLayoutContainer.communitiesStore.discordImportCommunityId
readonly property string communityName: communitiesPortalLayoutContainer.communitiesStore.discordImportCommunityName
active: !cancelled && (inProgress || finished || stopped)
type: errors ? ModuleWarning.Type.Danger : ModuleWarning.Type.Success
text: {
if (finished || stopped) {
if (errors)
return qsTr("The import of %1 from Discord to Status was stopped: <a href='#'>Critical issues found</a>").arg(communityName)
let result = qsTr("%1 was successfully imported from Discord to Status").arg(communityName) + " <a href='#'>"
if (warnings)
result += qsTr("Details (%1)").arg(qsTr("%n issue(s)", "", warnings))
else
result += qsTr("Details")
result += "</a>"
return result
}
if (inProgress) {
let result = qsTr("Importing %1 from Discord to Status").arg(communityName) + " <a href='#'>"
if (warnings)
result += qsTr("Check progress (%1)").arg(qsTr("%n issue(s)", "", warnings))
else
result += qsTr("Check progress")
result += "</a>"
return result
}
}
onLinkActivated: Global.openPopup(communitiesPortalLayoutContainer.discordImportProgressPopup)
progressValue: progress
closeBtnVisible: finished || stopped
buttonText: finished && !errors ? qsTr("Visit your Community") : ""
onClicked: function() {
communitiesPortalLayoutContainer.communitiesStore.setActiveCommunity(communityId)
}
onCloseClicked: {
hide();
}
}
Component {
id: connectedBannerComponent
ModuleWarning {
readonly property bool isConnected: bannersLayout.isConnected
objectName: "connectionInfoBanner"
Layout.fillWidth: true
text: isConnected ? qsTr("Connected") : qsTr("Disconnected")
type: isConnected ? ModuleWarning.Success : ModuleWarning.Danger
function updateState() {
if (isConnected)
showFor()
else
show();
}
Component.onCompleted: {
updateState()
}
onIsConnectedChanged: {
updateState();
}
onCloseClicked: {
hide();
}
onHideStarted: {
bannersLayout.connectedBanner = null
}
onHideFinished: {
destroy()
}
}
}
Component {
id: updateBannerComponent
ModuleWarning {
readonly property string version: appMain.rootStore.latestVersion
readonly property bool updateAvailable: appMain.rootStore.newVersionAvailable
objectName: "appVersionUpdateBanner"
Layout.fillWidth: true
type: ModuleWarning.Success
text: updateAvailable ? qsTr("A new version of Status (%1) is available").arg(version)
: qsTr("Your version is up to date")
buttonText: updateAvailable ? qsTr("Update")
: qsTr("Close")
function updateState() {
if (updateAvailable)
show()
else
showFor(5000)
}
Component.onCompleted: {
updateState()
}
onUpdateAvailableChanged: {
updateState();
}
onClicked: {
if (updateAvailable)
Global.openDownloadModal(appMain.rootStore.newVersionAvailable,
appMain.rootStore.latestVersion,
appMain.rootStore.downloadURL)
else
close()
}
onCloseClicked: {
if (updateAvailable)
appMain.rootStore.resetLastVersion();
hide()
}
onHideStarted: {
bannersLayout.updateBanner = null
}
onHideFinished: {
destroy()
}
}
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
StackLayout {
id: appView
anchors.fill: parent
currentIndex: {
if(mainModule.activeSection.sectionType === Constants.appSection.chat) {
return Constants.appViewStackIndex.chat
}
else if(mainModule.activeSection.sectionType === Constants.appSection.community) {
for(let i = this.children.length - 1; i >=0; i--)
{
var obj = this.children[i];
if(obj && obj.sectionId && obj.sectionId == mainModule.activeSection.id)
{
return i
}
}
// Should never be here, correct index must be returned from the for loop above
console.error("Wrong section type: ", mainModule.activeSection.sectionType,
" or section id: ", mainModule.activeSection.id)
return Constants.appViewStackIndex.community
}
else if(mainModule.activeSection.sectionType === Constants.appSection.communitiesPortal) {
return Constants.appViewStackIndex.communitiesPortal
}
else if(mainModule.activeSection.sectionType === Constants.appSection.wallet) {
return Constants.appViewStackIndex.wallet
}
else if(mainModule.activeSection.sectionType === Constants.appSection.browser) {
return Constants.appViewStackIndex.browser
}
else if(mainModule.activeSection.sectionType === Constants.appSection.profile) {
return Constants.appViewStackIndex.profile
}
else if(mainModule.activeSection.sectionType === Constants.appSection.node) {
return Constants.appViewStackIndex.node
}
// We should never end up here
console.error("Unknown section type")
}
onCurrentIndexChanged: {
var obj = this.children[currentIndex];
if(!obj)
return
if (obj.onActivated && typeof obj.onActivated === "function") {
this.children[currentIndex].onActivated()
}
if(obj === browserLayoutContainer && browserLayoutContainer.active == false){
browserLayoutContainer.active = true;
}
if(obj === walletLayoutContainer){
walletLayoutContainer.showSigningPhrasePopup();
}
}
// NOTE:
// If we ever change stack layout component order we need to updade
// Constants.appViewStackIndex accordingly
ChatLayout {
id: chatLayoutContainer
2021-06-17 18:41:11 +00:00
chatView.emojiPopup: statusEmojiPopup
contactsStore: appMain.rootStore.contactStore
rootStore.emojiReactionsModel: appMain.rootStore.emojiReactionsModel
rootStore.openCreateChat: createChatView.opened
chatView.onProfileButtonClicked: {
Global.changeAppSectionBySectionType(Constants.appSection.profile);
}
chatView.onOpenAppSearch: {
appSearch.openSearchPopup()
}
onImportCommunityClicked: {
Global.openPopup(communitiesPortalLayoutContainer.importCommunitiesPopup);
}
onCreateCommunityClicked: {
Global.openPopup(communitiesPortalLayoutContainer.createCommunitiesPopup);
}
Component.onCompleted: {
rootStore.chatCommunitySectionModule = mainModule.getChatSectionModule()
}
}
2021-06-17 18:41:11 +00:00
CommunitiesPortalLayout {
id: communitiesPortalLayoutContainer
Layout.fillWidth: true
Layout.fillHeight: true
contentPrefferedWidth: appView.width
}
WalletLayout {
id: walletLayoutContainer
Layout.fillWidth: true
Layout.fillHeight: true
store: appMain.rootStore
contactsStore: appMain.rootStore.profileSectionStore.contactsStore
emojiPopup: statusEmojiPopup
sendModal: sendModal
}
Component {
id: browserLayoutComponent
BrowserLayout {
globalStore: appMain.rootStore
sendTransactionModal: sendModal
}
}
Loader {
id: browserLayoutContainer
sourceComponent: browserLayoutComponent
active: false
Layout.fillWidth: true
Layout.fillHeight: true
// Loaders do not have access to the context, so props need to be set
// Adding a "_" to avoid a binding loop
// Not Refactored Yet
// property var _chatsModel: chatsModel.messageView
// Not Refactored Yet
// property var _walletModel: walletModel
// Not Refactored Yet
// property var _utilsModel: utilsModel
property var _web3Provider: BrowserStores.Web3ProviderStore.web3ProviderInst
}
ProfileLayout {
id: profileLayoutContainer
Layout.fillWidth: true
Layout.fillHeight: true
store: appMain.rootStore.profileSectionStore
globalStore: appMain.rootStore
systemPalette: appMain.sysPalette
emojiPopup: statusEmojiPopup
}
NodeLayout {
id: nodeLayoutContainer
Layout.fillWidth: true
Layout.fillHeight: true
}
2021-06-17 18:41:11 +00:00
Repeater {
model: mainModule.sectionsModel
delegate: DelegateChooser {
id: delegateChooser
role: "sectionType"
DelegateChoice {
roleValue: Constants.appSection.community
delegate: ChatLayout {
property string sectionId: model.id
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillHeight: true
chatView.emojiPopup: statusEmojiPopup
contactsStore: appMain.rootStore.contactStore
rootStore.emojiReactionsModel: appMain.rootStore.emojiReactionsModel
rootStore.openCreateChat: createChatView.opened
chatView.onProfileButtonClicked: {
Global.changeAppSectionBySectionType(Constants.appSection.profile);
}
chatView.onOpenAppSearch: {
appSearch.openSearchPopup()
}
Component.onCompleted: {
// we cannot return QVariant if we pass another parameter in a function call
// that's why we're using it this way
mainModule.prepareCommunitySectionModuleForCommunityId(model.id)
rootStore.chatCommunitySectionModule = mainModule.getCommunitySectionModule()
}
}
}
}
}
}
2021-06-17 18:41:11 +00:00
CreateChatView {
id: createChatView
property bool opened: false
rootStore: chatLayoutContainer.rootStore
emojiPopup: statusEmojiPopup
anchors.top: parent.top
anchors.topMargin: 8
anchors.rightMargin: 8
anchors.bottom: parent.bottom
anchors.right: parent.right
width: parent.width - chatLayoutContainer.chatView.leftPanel.width - anchors.rightMargin - anchors.leftMargin
visible: createChatView.opened
Connections {
target: Global
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
property var mailserverNotWorkingPopup: null
2021-06-17 18:41:11 +00:00
Component {
id: mailserverNotWorkingPopupComponent
MailserverConnectionDialog {
onClosed: {
appLayout.mailserverNotWorkingPopup = null
destroy()
}
}
}
2021-06-17 18:41:11 +00:00
Component {
id: chooseBrowserPopupComponent
ChooseBrowserPopup {
onClosed: {
destroy()
}
}
}
2021-06-17 18:41:11 +00:00
Component {
id: communityProfilePopup
2021-06-17 18:41:11 +00:00
CommunityProfilePopup {
anchors.centerIn: parent
contactsStore: appMain.rootStore.contactStore
hasAddedContacts: appMain.rootStore.hasAddedContacts
2021-06-17 18:41:11 +00:00
onClosed: {
destroy()
}
}
}
Component {
id: pinnedMessagesPopupComponent
PinnedMessagesPopup {
id: pinnedMessagesPopup
emojiReactionsModel: appMain.rootStore.emojiReactionsModel
onClosed: destroy()
}
}
2021-06-17 18:41:11 +00:00
Component {
id: genericConfirmationDialog
ConfirmationDialog {
onClosed: {
destroy()
}
}
}
Component {
id: activityCenterPopupComponent
ActivityCenterPopup {
id: activityCenter
height: appView.height - 56 * 2 // TODO get screen size // Taken from old code top bar height was fixed there to 56
y: 56
store: chatLayoutContainer.rootStore
acStore: appMain.acStore
chatSectionModule: chatLayoutContainer.rootStore.chatCommunitySectionModule
onClosed: {
Global.activityCenterPopupOpened = false
}
}
}
2022-09-27 21:26:26 +00:00
Component {
id: nicknamePopupComponent
NicknamePopup {
onEditDone: {
if (nickname !== newNickname) {
appMain.rootStore.contactStore.changeContactNickname(publicKey, newNickname)
Global.nickNameChanged(publicKey, newNickname)
}
close()
}
onClosed: destroy()
}
}
Component {
id: unblockContactConfirmationComponent
UnblockContactConfirmationDialog {
onUnblockButtonClicked: {
appMain.rootStore.contactStore.unblockContact(contactAddress)
Global.contactUnblocked(contactAddress)
close()
}
onClosed: destroy()
}
}
Component {
id: blockContactConfirmationComponent
BlockContactConfirmationDialog {
onBlockButtonClicked: {
appMain.rootStore.contactStore.blockContact(contactAddress)
Global.contactBlocked(contactAddress)
close()
}
onClosed: destroy()
}
}
// Add SendModal here as it is used by the Wallet as well as the Browser
2021-06-17 18:41:11 +00:00
Loader {
id: sendModal
active: false
function open(address = "") {
2021-06-17 18:41:11 +00:00
this.active = true
this.item.addressText = address;
2021-06-17 18:41:11 +00:00
this.item.open()
}
2021-06-17 18:41:11 +00:00
function closed() {
// this.sourceComponent = undefined // kill an opened instance
this.active = false
}
property var selectedAccount
2021-06-17 18:41:11 +00:00
sourceComponent: SendModal {
store: appMain.rootStore
contactsStore: appMain.rootStore.profileSectionStore.contactsStore
2021-06-17 18:41:11 +00:00
onClosed: {
sendModal.closed()
}
}
onLoaded: {
if (!!sendModal.selectedAccount) {
item.selectedAccount = sendModal.selectedAccount
}
}
}
2021-06-17 18:41:11 +00:00
Action {
shortcut: "Ctrl+1"
onTriggered: {
Global.setNthEnabledSectionActive(0)
}
}
2021-06-17 18:41:11 +00:00
Action {
shortcut: "Ctrl+2"
onTriggered: {
Global.setNthEnabledSectionActive(1)
}
}
2021-06-17 18:41:11 +00:00
Action {
shortcut: "Ctrl+3"
onTriggered: {
Global.setNthEnabledSectionActive(2)
}
2021-06-17 18:41:11 +00:00
}
Action {
shortcut: "Ctrl+4"
onTriggered: {
Global.setNthEnabledSectionActive(3)
}
2021-06-17 18:41:11 +00:00
}
Action {
shortcut: "Ctrl+5"
onTriggered: {
Global.setNthEnabledSectionActive(4)
}
}
Action {
shortcut: "Ctrl+6"
onTriggered: {
Global.setNthEnabledSectionActive(5)
}
}
Action {
shortcut: "Ctrl+7"
onTriggered: {
Global.setNthEnabledSectionActive(6)
}
}
Action {
shortcut: "Ctrl+8"
onTriggered: {
Global.setNthEnabledSectionActive(7)
}
}
Action {
shortcut: "Ctrl+9"
onTriggered: {
Global.setNthEnabledSectionActive(8)
}
}
2021-06-17 18:41:11 +00:00
Action {
shortcut: "Ctrl+K"
onTriggered: {
// FIXME the focus is no longer on the AppMain when the popup is opened, so this does not work to close
2021-06-17 18:41:11 +00:00
if (channelPicker.opened) {
channelPicker.close()
} else {
channelPicker.open()
}
}
}
Action {
shortcut: "Ctrl+F"
onTriggered: {
// FIXME the focus is no longer on the AppMain when the popup is opened, so this does not work to close
if (appSearch.opened) {
appSearch.closeSearchPopup()
} else {
appSearch.openSearchPopup()
}
}
}
2021-06-17 18:41:11 +00:00
StatusSearchListPopup {
2021-06-17 18:41:11 +00:00
id: channelPicker
2021-06-17 18:41:11 +00:00
x: parent.width / 2 - width / 2
y: parent.height / 2 - height / 2
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)
}
title: modelData ? modelData.name : ""
label: modelData? modelData.sectionName : ""
highlighted: isCurrentItem
sensor.hoverEnabled: false
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")
2021-06-17 18:41:11 +00:00
}
onAboutToShow: rootStore.rebuildChatSearchModel()
onSelected: {
rootStore.setActiveSectionChat(modelData.sectionId, modelData.chatId)
close()
}
}
}
StatusListView {
id: toastArea
anchors.right: parent.right
anchors.rightMargin: 8
anchors.bottom: parent.bottom
anchors.bottomMargin: 60
width: 343
height: Math.min(parent.height - 120, toastArea.contentHeight)
spacing: 8
verticalLayoutDirection: ListView.BottomToTop
model: appMain.rootStore.mainModuleInst.ephemeralNotificationModel
delegate: StatusToastMessage {
primaryText: model.title
secondaryText: model.subTitle
icon.name: model.icon
loading: model.loading
type: model.ephNotifType
linkUrl: model.url
duration: model.durationInMs
onClicked: {
appMain.rootStore.mainModuleInst.ephemeralNotificationClicked(model.timestamp)
this.open = false
}
onLinkActivated: {
Qt.openUrlExternally(link);
}
onClose: {
appMain.rootStore.mainModuleInst.removeEphemeralNotification(model.timestamp)
}
}
}
Component.onCompleted: {
Global.appMain = this;
Global.pinnedMessagesPopup = pinnedMessagesPopupComponent;
Global.communityProfilePopup = communityProfilePopup;
const whitelist = appMain.rootStore.messagingStore.getLinkPreviewWhitelist()
try {
const whiteListedSites = JSON.parse(whitelist)
let settingsUpdated = false
// Add Status links to whitelist
whiteListedSites.push({title: "Status", address: Constants.deepLinkPrefix, imageSite: false})
whiteListedSites.push({title: "Status", address: Constants.joinStatusLink, imageSite: false})
let settings = localAccountSensitiveSettings.whitelistedUnfurlingSites
if (!settings) {
settings = {}
}
// Set Status links as true. We intercept thoseURLs so it is privacy-safe
if (!settings[Constants.deepLinkPrefix] || !settings[Constants.joinStatusLink]) {
settings[Constants.deepLinkPrefix] = true
settings[Constants.joinStatusLink] = true
settingsUpdated = true
}
const whitelistedHostnames = []
// Add whitelisted sites in to app settings that are not already there
whiteListedSites.forEach(site => {
if (!settings.hasOwnProperty(site.address)) {
settings[site.address] = false
settingsUpdated = true
}
whitelistedHostnames.push(site.address)
})
// Remove any whitelisted sites from app settings that don't exist in the
// whitelist from status-go
Object.keys(settings).forEach(settingsHostname => {
if (!whitelistedHostnames.includes(settingsHostname)) {
delete settings[settingsHostname]
settingsUpdated = true
}
})
if (settingsUpdated) {
localAccountSensitiveSettings.whitelistedUnfurlingSites = settings
}
} catch (e) {
console.error('Could not parse the whitelist for sites', e)
}
2022-09-27 21:26:26 +00:00
Global.privacyModuleInst = appMain.rootStore.profileSectionStore.privacyStore.privacyModule
Global.settingsHasLoaded();
}
Loader {
id: keycardPopup
active: false
sourceComponent: KeycardPopup {
sharedKeycardModule: rootStore.mainModuleInst.keycardSharedModule
}
onLoaded: {
keycardPopup.item.open()
}
}
DropAreaPanel {
width: appMain.width
height: appMain.height
activeChatType: chatCommunitySectionModule && chatCommunitySectionModule.activeItem.type
enabled: !drag.source && (
// in chat view
(mainModule.activeSection.sectionType === Constants.appSection.chat &&
(
// in a one-to-one chat
activeChatType === Constants.chatType.oneToOne ||
// in a private group chat
activeChatType === Constants.chatType.privateGroupChat
)
) ||
// In community section
mainModule.activeSection.sectionType === Constants.appSection.community)
}
}