From ff4334d59e950e9f51a924e06457f21fa1bbaf2a Mon Sep 17 00:00:00 2001
From: Nicolas Werner <nicolas.werner@hotmail.de>
Date: Mon, 21 Feb 2022 05:01:01 +0100
Subject: [PATCH] Don't leak dialogs

It seems that you need to manually destroy created objects... Great...

fixes #898
---
 resources/qml/MessageInput.qml                |  1 +
 resources/qml/MessageView.qml                 |  2 ++
 resources/qml/RoomList.qml                    |  3 +++
 resources/qml/Root.qml                        | 23 +++++++++++++++++++
 resources/qml/TimelineView.qml                |  2 ++
 .../qml/dialogs/ImagePackSettingsDialog.qml   |  3 +++
 resources/qml/voip/CallInvite.qml             |  1 +
 resources/qml/voip/CallInviteBar.qml          |  4 ++++
 resources/qml/voip/PlaceCall.qml              |  2 ++
 src/timeline/TimelineViewManager.cpp          | 16 +++++++++----
 10 files changed, 52 insertions(+), 5 deletions(-)

diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml
index 7ad4ef696..dd888ab18 100644
--- a/resources/qml/MessageInput.qml
+++ b/resources/qml/MessageInput.qml
@@ -54,6 +54,7 @@ Rectangle {
                     } else {
                         var dialog = placeCallDialog.createObject(timelineRoot);
                         dialog.open();
+                        timelineRoot.destroyOnClose(dialog);
                     }
                 }
             }
diff --git a/resources/qml/MessageView.qml b/resources/qml/MessageView.qml
index e801f2d1a..542304d87 100644
--- a/resources/qml/MessageView.qml
+++ b/resources/qml/MessageView.qml
@@ -226,6 +226,7 @@ Item {
                     forwardMess.setMessageEventId(chat.model.reply);
                     forwardMess.open();
                     chat.model.reply = null;
+                    timelineRoot.destroyOnClose(forwardMess);
                 }
             }
         }
@@ -653,6 +654,7 @@ Item {
                 var forwardMess = forwardCompleterComponent.createObject(timelineRoot);
                 forwardMess.setMessageEventId(messageContextMenu.eventId);
                 forwardMess.open();
+                timelineRoot.destroyOnClose(forwardMess);
             }
         }
 
diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml
index b1d09eb1d..1e7f0c36b 100644
--- a/resources/qml/RoomList.qml
+++ b/resources/qml/RoomList.qml
@@ -394,6 +394,7 @@ Page {
                     "profile": Nheko.currentUser
                 });
                 userProfile.show();
+                timelineRoot.destroyOnClose(userProfile);
             }
 
 
@@ -670,6 +671,7 @@ Page {
                     onClicked: {
                         var win = roomDirectoryComponent.createObject(timelineRoot);
                         win.show();
+                        timelineRoot.destroyOnClose(win);
                     }
                 }
 
@@ -688,6 +690,7 @@ Page {
                     onClicked: {
                         var quickSwitch = quickSwitcherComponent.createObject(timelineRoot);
                         quickSwitch.open();
+                        timelineRoot.destroyOnClose(quickSwitch);
                     }
                 }
 
diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml
index 88d3e7c6f..f9da6b7ba 100644
--- a/resources/qml/Root.qml
+++ b/resources/qml/Root.qml
@@ -29,6 +29,13 @@ Pane {
         id: fontMetrics
     }
 
+    //Timer {
+    //    onTriggered: gc()
+    //    interval: 1000
+    //    running: true
+    //    repeat: true
+    //}
+
     EmojiPicker {
         id: emojiPopup
 
@@ -166,6 +173,7 @@ Pane {
         onActivated: {
             var quickSwitch = quickSwitcherComponent.createObject(timelineRoot);
             quickSwitch.open();
+            destroyOnClose(quickSwitch);
         }
     }
 
@@ -189,11 +197,13 @@ Pane {
         function onOpenLogoutDialog() {
             var dialog = logoutDialog.createObject(timelineRoot);
             dialog.open();
+            destroyOnClose(dialog);
         }
 
         function onOpenJoinRoomDialog() {
             var dialog = joinRoomDialog.createObject(timelineRoot);
             dialog.show();
+            destroyOnClose(dialog);
         }
 
         target: Nheko
@@ -205,17 +215,23 @@ Pane {
                 "flow": flow
             });
             dialog.show();
+            destroyOnClose(dialog);
         }
 
         target: VerificationManager
     }
 
+    function destroyOnClose(obj) {
+        obj.closing.connect(() => obj.destroy());
+    }
+
     Connections {
         function onOpenProfile(profile) {
             var userProfile = userProfileComponent.createObject(timelineRoot, {
                 "profile": profile
             });
             userProfile.show();
+            destroyOnClose(userProfile);
         }
 
         function onShowImagePackSettings(room, packlist) {
@@ -224,6 +240,7 @@ Pane {
                 "packlist": packlist
             });
             packSet.show();
+            destroyOnClose(packSet);
         }
 
         function onOpenRoomMembersDialog(members, room) {
@@ -232,6 +249,7 @@ Pane {
                 "room": room
             });
             membersDialog.show();
+            destroyOnClose(membersDialog);
         }
 
         function onOpenRoomSettingsDialog(settings) {
@@ -239,6 +257,7 @@ Pane {
                 "roomSettings": settings
             });
             roomSettings.show();
+            destroyOnClose(roomSettings);
         }
 
         function onOpenInviteUsersDialog(invitees) {
@@ -248,6 +267,7 @@ Pane {
                 "invitees": invitees
             });
             dialog.show();
+            destroyOnClose(dialog);
         }
 
         function onOpenLeaveRoomDialog(roomid) {
@@ -255,6 +275,7 @@ Pane {
                 "roomId": roomid
             });
             dialog.open();
+            destroyOnClose(dialog);
         }
 
         function onShowImageOverlay(room, eventId, url, proportionalHeight, originalWidth) {
@@ -264,6 +285,7 @@ Pane {
                 "url": url
             });
             dialog.showFullScreen();
+            destroyOnClose(dialog);
         }
 
         target: TimelineManager
@@ -274,6 +296,7 @@ Pane {
             if (CallManager.haveCallInvite && Settings.mobileMode) {
                 var dialog = mobileCallInviteDialog.createObject(msgView);
                 dialog.open();
+                destroyOnClose(dialog);
             }
         }
 
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index b04ca8f64..1933baeb2 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -261,6 +261,7 @@ Item {
                 "room": room
             });
             dialog.show();
+            timelineRoot.destroyOnClose(dialog);
         }
 
         function onShowRawMessageDialog(rawMessage) {
@@ -268,6 +269,7 @@ Item {
                 "rawMessage": rawMessage
             });
             dialog.show();
+            timelineRoot.destroyOnClose(dialog);
         }
 
         target: room
diff --git a/resources/qml/dialogs/ImagePackSettingsDialog.qml b/resources/qml/dialogs/ImagePackSettingsDialog.qml
index 18c32c416..2ce19b80a 100644
--- a/resources/qml/dialogs/ImagePackSettingsDialog.qml
+++ b/resources/qml/dialogs/ImagePackSettingsDialog.qml
@@ -71,6 +71,7 @@ ApplicationWindow {
                                 "imagePack": packlist.newPack(false)
                             });
                             dialog.show();
+                            timelineRoot.destroyOnClose(dialog);
                         }
                         width: packlistC.width
                         visible: !packlist.containsAccountPack
@@ -84,6 +85,7 @@ ApplicationWindow {
                                 "imagePack": packlist.newPack(true)
                             });
                             dialog.show();
+                            timelineRoot.destroyOnClose(dialog);
                         }
                         width: packlistC.width
                         visible: room.permissions.canChange(MtxEvent.ImagePackInRoom)
@@ -199,6 +201,7 @@ ApplicationWindow {
                                 "imagePack": currentPack
                             });
                             dialog.show();
+                            timelineRoot.destroyOnClose(dialog);
                         }
                     }
 
diff --git a/resources/qml/voip/CallInvite.qml b/resources/qml/voip/CallInvite.qml
index a17e18d52..3bf7c4cda 100644
--- a/resources/qml/voip/CallInvite.qml
+++ b/resources/qml/voip/CallInvite.qml
@@ -136,6 +136,7 @@ Popup {
                         "image": ":/icons/icons/ui/place-call.svg"
                     });
                     dialog.open();
+                    timelineRoot.destroyOnClose(dialog);
                     return false;
                 }
                 return true;
diff --git a/resources/qml/voip/CallInviteBar.qml b/resources/qml/voip/CallInviteBar.qml
index b2c2dbad3..ab377ca86 100644
--- a/resources/qml/voip/CallInviteBar.qml
+++ b/resources/qml/voip/CallInviteBar.qml
@@ -83,6 +83,7 @@ Rectangle {
             onClicked: {
                 var dialog = devicesDialog.createObject(timelineRoot);
                 dialog.open();
+            timelineRoot.destroyOnClose(dialog);
             }
         }
 
@@ -98,6 +99,7 @@ Rectangle {
                         "image": ":/icons/icons/ui/place-call.svg"
                     });
                     dialog.open();
+            timelineRoot.destroyOnClose(dialog);
                     return ;
                 } else if (!CallManager.mics.includes(Settings.microphone)) {
                     var dialog = deviceError.createObject(timelineRoot, {
@@ -105,6 +107,7 @@ Rectangle {
                         "image": ":/icons/icons/ui/place-call.svg"
                     });
                     dialog.open();
+            timelineRoot.destroyOnClose(dialog);
                     return ;
                 }
                 if (CallManager.callType == CallType.VIDEO && CallManager.cameras.length > 0 && !CallManager.cameras.includes(Settings.camera)) {
@@ -113,6 +116,7 @@ Rectangle {
                         "image": ":/icons/icons/ui/video.svg"
                     });
                     dialog.open();
+            timelineRoot.destroyOnClose(dialog);
                     return ;
                 }
                 CallManager.acceptInvite();
diff --git a/resources/qml/voip/PlaceCall.qml b/resources/qml/voip/PlaceCall.qml
index 1404adf93..2639f5bbc 100644
--- a/resources/qml/voip/PlaceCall.qml
+++ b/resources/qml/voip/PlaceCall.qml
@@ -66,6 +66,7 @@ Popup {
                         "image": ":/icons/icons/ui/place-call.svg"
                     });
                     dialog.open();
+                    timelineRoot.destroyOnClose(dialog);
                     return false;
                 }
                 return true;
@@ -118,6 +119,7 @@ Popup {
                     var dialog = screenShareDialog.createObject(timelineRoot);
                     dialog.open();
                     close();
+                    timelineRoot.destroyOnClose(dialog);
                 }
             }
 
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 0851724a3..d36985fee 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -183,28 +183,31 @@ TimelineViewManager::openRoomMembers(TimelineModel *room)
 {
     if (!room)
         return;
-    MemberList *memberList = new MemberList(room->roomId(), this);
+    MemberList *memberList = new MemberList(room->roomId());
+    QQmlEngine::setObjectOwnership(memberList, QQmlEngine::JavaScriptOwnership);
     emit openRoomMembersDialog(memberList, room);
 }
 
 void
 TimelineViewManager::openRoomSettings(QString room_id)
 {
-    RoomSettings *settings = new RoomSettings(room_id, this);
+    RoomSettings *settings = new RoomSettings(room_id);
     connect(rooms_->getRoomById(room_id).data(),
             &TimelineModel::roomAvatarUrlChanged,
             settings,
             &RoomSettings::avatarChanged);
+    QQmlEngine::setObjectOwnership(settings, QQmlEngine::JavaScriptOwnership);
     emit openRoomSettingsDialog(settings);
 }
 
 void
 TimelineViewManager::openInviteUsers(QString roomId)
 {
-    InviteesModel *model = new InviteesModel{this};
+    InviteesModel *model = new InviteesModel{};
     connect(model, &InviteesModel::accept, this, [this, model, roomId]() {
         emit inviteUsers(roomId, model->mxids());
     });
+    QQmlEngine::setObjectOwnership(model, QQmlEngine::JavaScriptOwnership);
     emit openInviteUsersDialog(model);
 }
 
@@ -212,6 +215,7 @@ void
 TimelineViewManager::openGlobalUserProfile(QString userId)
 {
     UserProfile *profile = new UserProfile{QString{}, userId, this};
+    QQmlEngine::setObjectOwnership(profile, QQmlEngine::JavaScriptOwnership);
     emit openProfile(profile);
 }
 
@@ -269,8 +273,10 @@ TimelineViewManager::openImageOverlay(TimelineModel *room, QString mxcUrl, QStri
 void
 TimelineViewManager::openImagePackSettings(QString roomid)
 {
-    auto room = rooms_->getRoomById(roomid).get();
-    emit showImagePackSettings(room, new ImagePackListModel(roomid.toStdString(), this));
+    auto room  = rooms_->getRoomById(roomid).get();
+    auto model = new ImagePackListModel(roomid.toStdString());
+    QQmlEngine::setObjectOwnership(model, QQmlEngine::JavaScriptOwnership);
+    emit showImagePackSettings(room, model);
 }
 
 void
-- 
GitLab