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

565 lines
20 KiB
QML

import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Dialogs 1.3
import QtQml.Models 2.15
import QtQml 2.15
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Popups 0.1
import AppLayouts.Chat.popups 1.0
import AppLayouts.Profile.popups 1.0
import AppLayouts.Communities.popups 1.0
import shared.popups 1.0
import shared.status 1.0
import utils 1.0
QtObject {
id: root
required property var popupParent
required property var rootStore
property var communitiesStore
property bool isDevBuild
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.createCommunityPopupRequested.connect(openCreateCommunityPopup)
Global.importCommunityPopupRequested.connect(openImportCommunityPopup)
Global.removeContactRequested.connect(openRemoveContactConfirmationPopup)
Global.openPopupRequested.connect(openPopup)
Global.closePopupRequested.connect(closePopup)
Global.openDeleteMessagePopup.connect(openDeleteMessagePopup)
Global.openDownloadImageDialog.connect(openDownloadImageDialog)
Global.leaveCommunityRequested.connect(openLeaveCommunityPopup)
}
property var currentPopup
function openPopup(popupComponent, params = {}, cb = null) {
if (activePopupComponents.includes(popupComponent)) {
return
}
root.currentPopup = popupComponent.createObject(popupParent, params)
root.currentPopup.open();
if (cb)
cb(root.currentPopup)
activePopupComponents.push(popupComponent)
root.currentPopup.closed.connect(() => {
const removeIndex = activePopupComponents.indexOf(popupComponent)
if (removeIndex !== -1) {
activePopupComponents.splice(removeIndex, 1)
}
})
}
function closePopup() {
if (!!root.currentPopup)
root.currentPopup.close();
}
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) {
var popup = imagePopupComponent.createObject(popupParent)
popup.openPopup(image)
}
function openProfilePopup(publicKey: string, parentPopup, cb) {
openPopup(profilePopupComponent, {publicKey: publicKey, parentPopup: parentPopup}, cb)
}
function openNicknamePopup(publicKey: string, nickname: string, subtitle: string) {
openPopup(nicknamePopupComponent, {publicKey: publicKey, nickname: nickname, "headerSettings.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) {
const contactDetails = Utils.getContactDetailsAsJson(publicKey, false)
openPopup(sendIDRequestPopupComponent, {
userPublicKey: publicKey,
userDisplayName: contactDetails.displayName,
userIcon: contactDetails.largeImage,
userIsEnsVerified: contactDetails.ensVerified,
"headerSettings.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),
buttonText: qsTr("Send verification request")
}, cb)
}
function openOutgoingIDRequestPopup(publicKey, cb) {
try {
const verificationDetails = root.rootStore.profileSectionStore.contactsStore.getSentVerificationDetailsAsJson(publicKey)
const popupProperties = {
userPublicKey: publicKey,
verificationStatus: verificationDetails.requestStatus,
verificationChallenge: verificationDetails.challenge,
verificationResponse: verificationDetails.response,
verificationResponseDisplayName: verificationDetails.displayName,
verificationResponseIcon: verificationDetails.icon,
verificationRequestedAt: verificationDetails.requestedAt,
verificationRepliedAt: verificationDetails.repliedAt
}
openPopup(contactOutgoingVerificationRequestPopupComponent, popupProperties, cb)
} catch (e) {
console.error("Error getting or parsing verification data", e)
}
}
function openIncomingIDRequestPopup(publicKey, cb) {
const popupProperties = {
contactsStore: root.rootStore.profileSectionStore.contactsStore,
publicKey: publicKey
}
openPopup(contactVerificationRequestPopupComponent, popupProperties, cb)
}
function openInviteFriendsToCommunityPopup(community, communitySectionModule, cb) {
openPopup(inviteFriendsToCommunityPopup, { community: community, communitySectionModule: communitySectionModule }, cb)
}
function openContactRequestPopup(publicKey, cb) {
const contactDetails = Utils.getContactDetailsAsJson(publicKey, false)
const popupProperties = {
userPublicKey: publicKey,
userDisplayName: contactDetails.displayName,
userIcon: contactDetails.largeImage,
userIsEnsVerified: contactDetails.ensVerified
}
openPopup(sendContactRequestPopupComponent, popupProperties, cb)
}
function openPinnedMessagesPopup(store, messageStore, pinnedMessagesModel, messageToPin, chatId) {
openPopup(pinnedMessagesPopup, {
store: store,
messageStore: messageStore,
pinnedMessagesModel: pinnedMessagesModel,
messageToPin: messageToPin,
chatId: chatId
})
}
function openCommunityPopup(store, community, chatCommunitySectionModule) {
openPopup(communityProfilePopup, {store: store, community: community, chatCommunitySectionModule: chatCommunitySectionModule})
}
function openCreateCommunityPopup(isDiscordImport) {
openPopup(createCommunitiesPopupComponent, {isDiscordImport: isDiscordImport})
}
function openImportCommunityPopup() {
openPopup(importCommunitiesPopupComponent)
}
function openDiscordImportProgressPopup() {
openPopup(discordImportProgressDialog)
}
function openRemoveContactConfirmationPopup(displayName, publicKey) {
openPopup(removeContactConfirmationDialog, {
displayName: displayName,
publicKey: publicKey
})
}
function openDeleteMessagePopup(messageId, messageStore) {
openPopup(deleteMessageConfirmationDialogComponent,
{
messageId,
messageStore
})
}
function openDownloadImageDialog(imageSource) {
// We don't use `openPopup`, because there's no `FileDialog::closed` signal.
const popup = downloadImageDialogComponent.createObject(popupParent, { imageSource })
popup.open()
}
function openLeaveCommunityPopup(community, communityId, outroMessage) {
openPopup(leaveCommunityPopupComponent, {community, communityId, outroMessage})
}
readonly property list<Component> _components: [
Component {
id: removeContactConfirmationDialog
ConfirmationDialog {
property string displayName
property string publicKey
headerSettings.title: qsTr("Remove '%1' as a contact").arg(displayName)
confirmationText: qsTr("This will mean that you and '%1' will no longer be able to send direct messages to each other. You will need to send them a new Contact Request in order to message again. All previous direct messages between you and '%1' will be retained in read-only mode.").arg(displayName)
showCancelButton: true
cancelBtnType: ""
onConfirmButtonClicked: {
rootStore.contactStore.removeContact(publicKey);
close();
}
onCancelButtonClicked: {
close();
}
onClosed: { destroy(); }
}
},
Component {
id: contactVerificationRequestPopupComponent
ContactVerificationRequestPopup {
onResponseSent: {
root.rootStore.profileSectionStore.contactsStore.acceptVerificationRequest(senderPublicKey, response)
}
onVerificationRefused: {
root.rootStore.profileSectionStore.contactsStore.declineVerificationRequest(senderPublicKey)
}
onClosed: destroy()
}
},
Component {
id: contactOutgoingVerificationRequestPopupComponent
OutgoingContactVerificationRequestPopup {
onVerificationRequestCanceled: {
root.rootStore.profileSectionStore.contactsStore.cancelVerificationRequest(userPublicKey)
}
onUntrustworthyVerified: {
root.rootStore.profileSectionStore.contactsStore.verifiedUntrustworthy(userPublicKey)
}
onTrustedVerified: {
root.rootStore.profileSectionStore.contactsStore.verifiedTrusted(userPublicKey)
}
onClosed: destroy()
}
},
Component {
id: sendIDRequestPopupComponent
SendContactRequestModal {
rootStore: root.rootStore
onAccepted: root.rootStore.profileSectionStore.contactsStore.sendVerificationRequest(userPublicKey, message)
onClosed: destroy()
}
},
Component {
id: inviteFriendsToCommunityPopup
InviteFriendsToCommunityPopup {
rootStore: root.rootStore
contactsStore: root.rootStore.contactStore
onClosed: destroy()
}
},
Component {
id: sendContactRequestPopupComponent
SendContactRequestModal {
rootStore: root.rootStore
onAccepted: root.rootStore.profileSectionStore.contactsStore.sendContactRequest(userPublicKey, message)
onClosed: destroy()
}
},
Component {
id: backupSeedModalComponent
BackupSeedModal {
privacyStore: rootStore.profileSectionStore.privacyStore
onClosed: destroy()
}
},
Component {
id: displayNamePopupComponent
DisplayNamePopup {
profileStore: rootStore.profileSectionStore.profileStore
onClosed: destroy()
}
},
Component {
id: downloadPageComponent
DownloadPage {
onClosed: destroy()
}
},
Component {
id: imagePopupComponent
StatusImageModal {
id: imagePopup
onClosed: destroy()
}
},
Component {
id: profilePopupComponent
ProfileDialog {
id: profilePopup
profileStore: rootStore.profileSectionStore.profileStore
contactsStore: rootStore.profileSectionStore.contactsStore
communitiesModel: rootStore.profileSectionStore.communitiesList
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 {
contactsStore: rootStore.contactStore
hasAddedContacts: rootStore.hasAddedContacts
onClosed: destroy()
}
},
Component {
id: pinnedMessagesPopup
PinnedMessagesPopup {
onClosed: destroy()
}
},
Component {
id: nicknamePopupComponent
NicknamePopup {
onEditDone: {
if (nickname !== newNickname) {
rootStore.contactStore.changeContactNickname(publicKey, newNickname)
Global.contactRenamed(publicKey)
}
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()
}
},
Component {
id: importCommunitiesPopupComponent
ImportCommunityPopup {
store: root.communitiesStore
onClosed: {
destroy()
}
}
},
Component {
id: createCommunitiesPopupComponent
CreateCommunityPopup {
store: root.communitiesStore
isDevBuild: root.isDevBuild
onClosed: {
destroy()
}
}
},
Component {
id: discordImportProgressDialog
DiscordImportProgressDialog {
store: root.communitiesStore
}
},
Component {
id: deleteMessageConfirmationDialogComponent
DeleteMessageConfirmationPopup {
onClosed: destroy()
}
},
Component {
id: downloadImageDialogComponent
FileDialog {
property string imageSource
title: qsTr("Please choose a directory")
selectFolder: true
selectExisting: true
selectMultiple: false
modality: Qt.NonModal
onAccepted: {
Utils.downloadImageByUrl(imageSource, fileUrl)
destroy()
}
onRejected: {
destroy()
}
}
},
Component {
id: leaveCommunityPopupComponent
StatusModal {
id: leavePopup
property string community
property string communityId
property string outroMessage
headerSettings.title: qsTr("Are you sure want to leave '%1'?").arg(community)
padding: 16
width: 640
contentItem: ColumnLayout {
spacing: 16
StatusBaseText {
id: outroMessage
Layout.fillWidth: true
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
text: leavePopup.outroMessage
visible: !!text
}
StatusMenuSeparator {
Layout.fillWidth: true
visible: outroMessage.visible
}
StatusBaseText {
Layout.fillWidth: true
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
font.pixelSize: 13
text: qsTr("You will need to request to join if you want to become a member again in the future. If you joined the Community via public key ensure you have a copy of it before you go.")
}
}
rightButtons: [
StatusFlatButton {
text: qsTr("Cancel")
onClicked: leavePopup.close()
},
StatusButton {
objectName: "CommunitiesListPanel_leaveCommunityButtonInPopup"
type: StatusBaseButton.Type.Danger
text: qsTr("Leave %1").arg(leavePopup.community)
onClicked: {
leavePopup.close()
root.rootStore.profileSectionStore.communitiesProfileModule.leaveCommunity(leavePopup.communityId)
}
}
]
onClosed: destroy()
}
}
]
}