mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-12-04 16:18:48 +03:00
Compare commits
13 commits
4c33d10e19
...
d3051aa8ff
Author | SHA1 | Date | |
---|---|---|---|
|
d3051aa8ff | ||
|
1a00d91316 | ||
|
b7a5d714c6 | ||
|
2f967978f2 | ||
|
3b0df06629 | ||
|
27683bedc4 | ||
|
80a39cca17 | ||
|
5523460f4e | ||
|
65c6e96e24 | ||
|
3a3c3def7c | ||
|
da2d7861d7 | ||
|
db68281a28 | ||
|
d299f7af5c |
20 changed files with 714 additions and 597 deletions
|
@ -151,7 +151,7 @@ build-clazy:
|
||||||
- apt-get -y install --no-install-suggests --no-install-recommends ca-certificates build-essential ninja-build cmake gcc make automake ccache liblmdb-dev
|
- apt-get -y install --no-install-suggests --no-install-recommends ca-certificates build-essential ninja-build cmake gcc make automake ccache liblmdb-dev
|
||||||
libssl-dev libqt5multimedia5-plugins libqt5multimediagsttools5 libqt5multimediaquick5 libqt5svg5-dev
|
libssl-dev libqt5multimedia5-plugins libqt5multimediagsttools5 libqt5multimediaquick5 libqt5svg5-dev
|
||||||
qtmultimedia5-dev qtquickcontrols2-5-dev qttools5-dev qttools5-dev-tools qtdeclarative5-dev
|
qtmultimedia5-dev qtquickcontrols2-5-dev qttools5-dev qttools5-dev-tools qtdeclarative5-dev
|
||||||
qml-module-qtmultimedia qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qt-labs-platform
|
qml-module-qtmultimedia qml-module-qtquick-controls2 qml-module-qtquick-layouts
|
||||||
qt5keychain-dev ccache libcurl4-openssl-dev libevent-dev libspdlog-dev git nlohmann-json3-dev libcmark-dev asciidoc time # libolm-dev
|
qt5keychain-dev ccache libcurl4-openssl-dev libevent-dev libspdlog-dev git nlohmann-json3-dev libcmark-dev asciidoc time # libolm-dev
|
||||||
# need recommended deps for wget
|
# need recommended deps for wget
|
||||||
- apt-get -y install wget
|
- apt-get -y install wget
|
||||||
|
@ -282,7 +282,7 @@ build-macos-as:
|
||||||
- pipx ensurepath
|
- pipx ensurepath
|
||||||
- . ~/.zshrc
|
- . ~/.zshrc
|
||||||
- mkdir $HOME/Qt
|
- mkdir $HOME/Qt
|
||||||
- aqt install-qt --outputdir $HOME/qt mac desktop 6.6 clang_64 -m qtlocation qtimageformats qtmultimedia qtpositioning qtshadertools
|
- aqt install-qt --outputdir $HOME/qt mac desktop 6.8 clang_64 -m qtlocation qtimageformats qtmultimedia qtpositioning qtshadertools
|
||||||
script:
|
script:
|
||||||
- export QTPATH=($HOME/qt/6.*/macos/bin)
|
- export QTPATH=($HOME/qt/6.*/macos/bin)
|
||||||
- export PATH="$QTPATH:${PATH}"
|
- export PATH="$QTPATH:${PATH}"
|
||||||
|
@ -394,7 +394,7 @@ build-flatpak:
|
||||||
- apt-get -y install --no-install-suggests --no-install-recommends ca-certificates build-essential ninja-build cmake gcc make automake ccache liblmdb-dev
|
- apt-get -y install --no-install-suggests --no-install-recommends ca-certificates build-essential ninja-build cmake gcc make automake ccache liblmdb-dev
|
||||||
libssl-dev libqt5multimedia5-plugins libqt5multimediagsttools5 libqt5multimediaquick5 libqt5svg5-dev
|
libssl-dev libqt5multimedia5-plugins libqt5multimediagsttools5 libqt5multimediaquick5 libqt5svg5-dev
|
||||||
qtmultimedia5-dev qtquickcontrols2-5-dev qttools5-dev qttools5-dev-tools qtdeclarative5-dev
|
qtmultimedia5-dev qtquickcontrols2-5-dev qttools5-dev qttools5-dev-tools qtdeclarative5-dev
|
||||||
qml-module-qtmultimedia qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qt-labs-platform
|
qml-module-qtmultimedia qml-module-qtquick-controls2 qml-module-qtquick-layouts
|
||||||
qt5keychain-dev ccache libcurl4-openssl-dev libevent-dev libspdlog-dev nlohmann-json3-dev libcmark-dev asciidoc libre2-dev libgtest-dev libgl1-mesa-dev qml-module-qtquick-particles2
|
qt5keychain-dev ccache libcurl4-openssl-dev libevent-dev libspdlog-dev nlohmann-json3-dev libcmark-dev asciidoc libre2-dev libgtest-dev libgl1-mesa-dev qml-module-qtquick-particles2
|
||||||
|
|
||||||
# Installing the packages needed to build AppImage
|
# Installing the packages needed to build AppImage
|
||||||
|
|
|
@ -365,7 +365,7 @@ cmake --build build
|
||||||
*Build requirements + qml modules needed at runtime (you may not need all of them, but the following seem to work according to reports):*
|
*Build requirements + qml modules needed at runtime (you may not need all of them, but the following seem to work according to reports):*
|
||||||
```bash
|
```bash
|
||||||
sudo apt install --no-install-recommends g++ cmake make zlib1g-dev libssl-dev libolm-dev liblmdb-dev libcmark-dev nlohmann-json3-dev libspdlog-dev libevent-dev libcurl4-openssl-dev libre2-dev libxcb-ewmh-dev asciidoc-base \
|
sudo apt install --no-install-recommends g++ cmake make zlib1g-dev libssl-dev libolm-dev liblmdb-dev libcmark-dev nlohmann-json3-dev libspdlog-dev libevent-dev libcurl4-openssl-dev libre2-dev libxcb-ewmh-dev asciidoc-base \
|
||||||
qt{base,declarative,tools,multimedia,quickcontrols2-}5-dev libqt6svg5-dev qt6keychain-dev qml-module-qt{gstreamer,multimedia,quick-extras,-labs-settings,-labs-platform,graphicaleffects,quick-controls2,quick-particles2} \
|
qt{base,declarative,tools,multimedia,quickcontrols2-}5-dev libqt6svg5-dev qt6keychain-dev qml-module-qt{gstreamer,multimedia,quick-extras,-labs-settings,graphicaleffects,quick-controls2,quick-particles2} \
|
||||||
libgstreamer1.0-dev libgstreamer-plugins-{base,bad}1.0-dev qtgstreamer-plugins-qt6 libnice-dev ninja-build
|
libgstreamer1.0-dev libgstreamer-plugins-{base,bad}1.0-dev qtgstreamer-plugins-qt6 libnice-dev ninja-build
|
||||||
```
|
```
|
||||||
lmdb++-dev is too old so bundled lmdbxx must be used.
|
lmdb++-dev is too old so bundled lmdbxx must be used.
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import "./components"
|
import "./components"
|
||||||
import Qt.labs.platform 1.1 as Platform
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
@ -101,17 +100,18 @@ Page {
|
||||||
]
|
]
|
||||||
|
|
||||||
onClicked: Communities.setCurrentTagId(model.id)
|
onClicked: Communities.setCurrentTagId(model.id)
|
||||||
onPressAndHold: communityContextMenu.show(model.id, model.hidden, model.muted)
|
onPressAndHold: communityContextMenu.show(communityItem, model.id, model.hidden, model.muted)
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
|
id: rth
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.RightButton
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
|
|
||||||
onSingleTapped: communityContextMenu.show(model.id, model.hidden, model.muted)
|
onSingleTapped: communityContextMenu.show(rth, model.id, model.hidden, model.muted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
@ -195,28 +195,28 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.Menu {
|
Menu {
|
||||||
id: communityContextMenu
|
id: communityContextMenu
|
||||||
|
|
||||||
property bool hidden
|
property bool hidden
|
||||||
property bool muted
|
property bool muted
|
||||||
property string tagId
|
property string tagId
|
||||||
|
|
||||||
function show(id_, hidden_, muted_) {
|
function show(parent, id_, hidden_, muted_) {
|
||||||
tagId = id_;
|
tagId = id_;
|
||||||
hidden = hidden_;
|
hidden = hidden_;
|
||||||
muted = muted_;
|
muted = muted_;
|
||||||
open();
|
popup(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: communityContextMenu.muted
|
checked: communityContextMenu.muted
|
||||||
text: qsTr("Do not show notification counts for this community or tag.")
|
text: qsTr("Do not show notification counts for this community or tag.")
|
||||||
|
|
||||||
onTriggered: Communities.toggleTagMute(communityContextMenu.tagId)
|
onTriggered: Communities.toggleTagMute(communityContextMenu.tagId)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: communityContextMenu.hidden
|
checked: communityContextMenu.hidden
|
||||||
text: qsTr("Hide rooms with this tag or from this community by default.")
|
text: qsTr("Hide rooms with this tag or from this community by default.")
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
import "./ui"
|
import "./ui"
|
||||||
import "./dialogs"
|
import "./dialogs"
|
||||||
import Qt.labs.platform 1.1 as Platform
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
@ -393,7 +392,7 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.Menu {
|
Menu {
|
||||||
id: messageContextMenuC
|
id: messageContextMenuC
|
||||||
|
|
||||||
property string eventId
|
property string eventId
|
||||||
|
@ -421,9 +420,9 @@ Item {
|
||||||
else
|
else
|
||||||
link = "";
|
link = "";
|
||||||
if (showAt_)
|
if (showAt_)
|
||||||
open(showAt_);
|
popup(showAt_);
|
||||||
else
|
else
|
||||||
open();
|
popup();
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
|
@ -448,7 +447,7 @@ Item {
|
||||||
ReportMessage {}
|
ReportMessage {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("Go to &message")
|
text: qsTr("Go to &message")
|
||||||
visible: filteredTimeline.filterByContent
|
visible: filteredTimeline.filterByContent
|
||||||
|
@ -458,21 +457,21 @@ Item {
|
||||||
room.showEvent(messageContextMenuC.eventId);
|
room.showEvent(messageContextMenuC.eventId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("&Copy")
|
text: qsTr("&Copy")
|
||||||
visible: messageContextMenuC.text
|
visible: messageContextMenuC.text
|
||||||
|
|
||||||
onTriggered: Clipboard.text = messageContextMenuC.text
|
onTriggered: Clipboard.text = messageContextMenuC.text
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("Copy &link location")
|
text: qsTr("Copy &link location")
|
||||||
visible: messageContextMenuC.link
|
visible: messageContextMenuC.link
|
||||||
|
|
||||||
onTriggered: Clipboard.text = messageContextMenuC.link
|
onTriggered: Clipboard.text = messageContextMenuC.link
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
id: reactionOption
|
id: reactionOption
|
||||||
|
|
||||||
text: qsTr("Re&act")
|
text: qsTr("Re&act")
|
||||||
|
@ -483,39 +482,39 @@ Item {
|
||||||
TimelineManager.focusMessageInput();
|
TimelineManager.focusMessageInput();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Repl&y")
|
text: qsTr("Repl&y")
|
||||||
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
||||||
|
|
||||||
onTriggered: room.reply = (messageContextMenuC.eventId)
|
onTriggered: room.reply = (messageContextMenuC.eventId)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("&Edit")
|
text: qsTr("&Edit")
|
||||||
visible: messageContextMenuC.isEditable && (room ? room.permissions.canSend(MtxEvent.TextMessage) : false)
|
visible: messageContextMenuC.isEditable && (room ? room.permissions.canSend(MtxEvent.TextMessage) : false)
|
||||||
|
|
||||||
onTriggered: room.edit = (messageContextMenuC.eventId)
|
onTriggered: room.edit = (messageContextMenuC.eventId)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("&Thread")
|
text: qsTr("&Thread")
|
||||||
visible: (room ? room.permissions.canSend(MtxEvent.TextMessage) : false)
|
visible: (room ? room.permissions.canSend(MtxEvent.TextMessage) : false)
|
||||||
|
|
||||||
onTriggered: room.thread = (messageContextMenuC.threadId || messageContextMenuC.eventId)
|
onTriggered: room.thread = (messageContextMenuC.threadId || messageContextMenuC.eventId)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: visible && room.pinnedMessages.includes(messageContextMenuC.eventId) ? qsTr("Un&pin") : qsTr("&Pin")
|
text: visible && room.pinnedMessages.includes(messageContextMenuC.eventId) ? qsTr("Un&pin") : qsTr("&Pin")
|
||||||
visible: (room ? room.permissions.canChange(MtxEvent.PinnedEvents) : false)
|
visible: (room ? room.permissions.canChange(MtxEvent.PinnedEvents) : false)
|
||||||
|
|
||||||
onTriggered: visible && room.pinnedMessages.includes(messageContextMenuC.eventId) ? room.unpin(messageContextMenuC.eventId) : room.pin(messageContextMenuC.eventId)
|
onTriggered: visible && room.pinnedMessages.includes(messageContextMenuC.eventId) ? room.unpin(messageContextMenuC.eventId) : room.pin(messageContextMenuC.eventId)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("&Read receipts")
|
text: qsTr("&Read receipts")
|
||||||
|
|
||||||
onTriggered: room.showReadReceipts(messageContextMenuC.eventId)
|
onTriggered: room.showReadReceipts(messageContextMenuC.eventId)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("&Forward")
|
text: qsTr("&Forward")
|
||||||
visible: messageContextMenuC.eventType == MtxEvent.ImageMessage || messageContextMenuC.eventType == MtxEvent.VideoMessage || messageContextMenuC.eventType == MtxEvent.AudioMessage || messageContextMenuC.eventType == MtxEvent.FileMessage || messageContextMenuC.eventType == MtxEvent.Sticker || messageContextMenuC.eventType == MtxEvent.TextMessage || messageContextMenuC.eventType == MtxEvent.LocationMessage || messageContextMenuC.eventType == MtxEvent.EmoteMessage || messageContextMenuC.eventType == MtxEvent.NoticeMessage
|
visible: messageContextMenuC.eventType == MtxEvent.ImageMessage || messageContextMenuC.eventType == MtxEvent.VideoMessage || messageContextMenuC.eventType == MtxEvent.AudioMessage || messageContextMenuC.eventType == MtxEvent.FileMessage || messageContextMenuC.eventType == MtxEvent.Sticker || messageContextMenuC.eventType == MtxEvent.TextMessage || messageContextMenuC.eventType == MtxEvent.LocationMessage || messageContextMenuC.eventType == MtxEvent.EmoteMessage || messageContextMenuC.eventType == MtxEvent.NoticeMessage
|
||||||
|
|
||||||
|
@ -526,15 +525,15 @@ Item {
|
||||||
timelineRoot.destroyOnClose(forwardMess);
|
timelineRoot.destroyOnClose(forwardMess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("&Mark as read")
|
text: qsTr("&Mark as read")
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("View raw message")
|
text: qsTr("View raw message")
|
||||||
|
|
||||||
onTriggered: room.viewRawMessage(messageContextMenuC.eventId)
|
onTriggered: room.viewRawMessage(messageContextMenuC.eventId)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("View decrypted raw message")
|
text: qsTr("View decrypted raw message")
|
||||||
// TODO(Nico): Fix this still being iterated over, when using keyboard to select options
|
// TODO(Nico): Fix this still being iterated over, when using keyboard to select options
|
||||||
|
@ -542,7 +541,7 @@ Item {
|
||||||
|
|
||||||
onTriggered: room.viewDecryptedRawMessage(messageContextMenuC.eventId)
|
onTriggered: room.viewDecryptedRawMessage(messageContextMenuC.eventId)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Remo&ve message")
|
text: qsTr("Remo&ve message")
|
||||||
visible: (room ? room.permissions.canRedact() : false) || messageContextMenuC.isSender
|
visible: (room ? room.permissions.canRedact() : false) || messageContextMenuC.isSender
|
||||||
|
|
||||||
|
@ -554,7 +553,7 @@ Item {
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Report message")
|
text: qsTr("Report message")
|
||||||
enabled: visible
|
enabled: visible
|
||||||
onTriggered: function () {
|
onTriggered: function () {
|
||||||
|
@ -564,21 +563,21 @@ Item {
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("&Save as")
|
text: qsTr("&Save as")
|
||||||
visible: messageContextMenuC.eventType == MtxEvent.ImageMessage || messageContextMenuC.eventType == MtxEvent.VideoMessage || messageContextMenuC.eventType == MtxEvent.AudioMessage || messageContextMenuC.eventType == MtxEvent.FileMessage || messageContextMenuC.eventType == MtxEvent.Sticker
|
visible: messageContextMenuC.eventType == MtxEvent.ImageMessage || messageContextMenuC.eventType == MtxEvent.VideoMessage || messageContextMenuC.eventType == MtxEvent.AudioMessage || messageContextMenuC.eventType == MtxEvent.FileMessage || messageContextMenuC.eventType == MtxEvent.Sticker
|
||||||
|
|
||||||
onTriggered: room.saveMedia(messageContextMenuC.eventId)
|
onTriggered: room.saveMedia(messageContextMenuC.eventId)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("&Open in external program")
|
text: qsTr("&Open in external program")
|
||||||
visible: messageContextMenuC.eventType == MtxEvent.ImageMessage || messageContextMenuC.eventType == MtxEvent.VideoMessage || messageContextMenuC.eventType == MtxEvent.AudioMessage || messageContextMenuC.eventType == MtxEvent.FileMessage || messageContextMenuC.eventType == MtxEvent.Sticker
|
visible: messageContextMenuC.eventType == MtxEvent.ImageMessage || messageContextMenuC.eventType == MtxEvent.VideoMessage || messageContextMenuC.eventType == MtxEvent.AudioMessage || messageContextMenuC.eventType == MtxEvent.FileMessage || messageContextMenuC.eventType == MtxEvent.Sticker
|
||||||
|
|
||||||
onTriggered: room.openMedia(messageContextMenuC.eventId)
|
onTriggered: room.openMedia(messageContextMenuC.eventId)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("Copy link to eve&nt")
|
text: qsTr("Copy link to eve&nt")
|
||||||
visible: messageContextMenuC.eventId
|
visible: messageContextMenuC.eventId
|
||||||
|
@ -592,7 +591,7 @@ Item {
|
||||||
ForwardCompleter {
|
ForwardCompleter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.Menu {
|
Menu {
|
||||||
id: replyContextMenuC
|
id: replyContextMenuC
|
||||||
|
|
||||||
property string eventId
|
property string eventId
|
||||||
|
@ -606,21 +605,21 @@ Item {
|
||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("&Copy")
|
text: qsTr("&Copy")
|
||||||
visible: replyContextMenuC.text
|
visible: replyContextMenuC.text
|
||||||
|
|
||||||
onTriggered: Clipboard.text = replyContextMenuC.text
|
onTriggered: Clipboard.text = replyContextMenuC.text
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("Copy &link location")
|
text: qsTr("Copy &link location")
|
||||||
visible: replyContextMenuC.link
|
visible: replyContextMenuC.link
|
||||||
|
|
||||||
onTriggered: Clipboard.text = replyContextMenuC.link
|
onTriggered: Clipboard.text = replyContextMenuC.link
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("&Go to quoted message")
|
text: qsTr("&Go to quoted message")
|
||||||
visible: true
|
visible: true
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
import "./components"
|
import "./components"
|
||||||
import "./dialogs"
|
import "./dialogs"
|
||||||
import "./ui"
|
import "./ui"
|
||||||
import Qt.labs.platform 1.1 as Platform
|
|
||||||
import QtQml
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
@ -43,6 +41,8 @@ Page {
|
||||||
id: buttonRow
|
id: buttonRow
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
|
id: startChatButton
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.margins: Nheko.paddingMedium
|
Layout.margins: Nheko.paddingMedium
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
@ -53,17 +53,17 @@ Page {
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/add-square-button.svg"
|
image: ":/icons/icons/ui/add-square-button.svg"
|
||||||
|
|
||||||
onClicked: roomJoinCreateMenu.open(parent)
|
onClicked: roomJoinCreateMenu.popup(startChatButton)
|
||||||
|
|
||||||
Platform.Menu {
|
Menu {
|
||||||
id: roomJoinCreateMenu
|
id: roomJoinCreateMenu
|
||||||
|
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Join a room")
|
text: qsTr("Join a room")
|
||||||
|
|
||||||
onTriggered: Nheko.openJoinRoomDialog()
|
onTriggered: Nheko.openJoinRoomDialog()
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Create a new room")
|
text: qsTr("Create a new room")
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
@ -72,7 +72,7 @@ Page {
|
||||||
timelineRoot.destroyOnClose(createRoom);
|
timelineRoot.destroyOnClose(createRoom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Start a direct chat")
|
text: qsTr("Start a direct chat")
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
@ -81,7 +81,7 @@ Page {
|
||||||
timelineRoot.destroyOnClose(createDirect);
|
timelineRoot.destroyOnClose(createDirect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Create a new community")
|
text: qsTr("Create a new community")
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
@ -255,68 +255,72 @@ Page {
|
||||||
Nheko.setStatusMessage(text);
|
Nheko.setStatusMessage(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.Menu {
|
Menu {
|
||||||
id: userInfoMenu
|
id: userInfoMenu
|
||||||
|
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Profile settings")
|
text: qsTr("Profile settings")
|
||||||
|
|
||||||
onTriggered: userInfoPanel.openUserProfile()
|
onTriggered: userInfoPanel.openUserProfile()
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Set status message")
|
text: qsTr("Set status message")
|
||||||
|
|
||||||
onTriggered: statusDialog.show()
|
onTriggered: statusDialog.show()
|
||||||
}
|
}
|
||||||
Platform.MenuSeparator {
|
MenuSeparator {
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItemGroup {
|
ButtonGroup {
|
||||||
id: onlineStateGroup
|
id: onlineStateGroup
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Automatic online status")
|
text: qsTr("Automatic online status")
|
||||||
group: onlineStateGroup
|
ButtonGroup.group: onlineStateGroup
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: Settings.presence == Settings.AutomaticPresence
|
checked: Settings.presence == Settings.AutomaticPresence
|
||||||
onTriggered: if (checked) Settings.presence = Settings.AutomaticPresence
|
onTriggered: if (checked) Settings.presence = Settings.AutomaticPresence
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Online")
|
text: qsTr("Online")
|
||||||
group: onlineStateGroup
|
ButtonGroup.group: onlineStateGroup
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: Settings.presence == Settings.Online
|
checked: Settings.presence == Settings.Online
|
||||||
onTriggered: if (checked) Settings.presence = Settings.Online
|
onTriggered: if (checked) Settings.presence = Settings.Online
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Unavailable")
|
text: qsTr("Unavailable")
|
||||||
group: onlineStateGroup
|
ButtonGroup.group: onlineStateGroup
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: Settings.presence == Settings.Unavailable
|
checked: Settings.presence == Settings.Unavailable
|
||||||
onTriggered: if (checked) Settings.presence = Settings.Unavailable
|
onTriggered: if (checked) Settings.presence = Settings.Unavailable
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Offline")
|
text: qsTr("Offline")
|
||||||
group: onlineStateGroup
|
ButtonGroup.group: onlineStateGroup
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: Settings.presence == Settings.Offline
|
checked: Settings.presence == Settings.Offline
|
||||||
onTriggered: if (checked) Settings.presence = Settings.Offline
|
onTriggered: if (checked) Settings.presence = Settings.Offline
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TapHandler {
|
TapHandler {
|
||||||
|
id: userTapHandler
|
||||||
|
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
margin: -Nheko.paddingSmall
|
margin: -Nheko.paddingSmall
|
||||||
|
|
||||||
onLongPressed: userInfoMenu.open()
|
onLongPressed: userInfoMenu.popup(userTapHandler)
|
||||||
onSingleTapped: userInfoPanel.openUserProfile()
|
onSingleTapped: userInfoPanel.openUserProfile()
|
||||||
}
|
}
|
||||||
TapHandler {
|
TapHandler {
|
||||||
|
id: userTapHandler2
|
||||||
|
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.RightButton
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
margin: -Nheko.paddingSmall
|
margin: -Nheko.paddingSmall
|
||||||
|
|
||||||
onSingleTapped: userInfoMenu.open()
|
onSingleTapped: userInfoMenu.popup(userTapHandler2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
@ -525,7 +529,7 @@ Page {
|
||||||
}
|
}
|
||||||
onPressAndHold: {
|
onPressAndHold: {
|
||||||
if (!isInvite)
|
if (!isInvite)
|
||||||
roomContextMenu.show(roomId, tags);
|
roomContextMenu.show(roomItem, roomId, tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ripple {
|
Ripple {
|
||||||
|
@ -538,13 +542,15 @@ Page {
|
||||||
anchors.margins: 1
|
anchors.margins: 1
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
|
id: roomItemTh
|
||||||
|
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.RightButton
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
|
|
||||||
onSingleTapped: {
|
onSingleTapped: {
|
||||||
if (!TimelineManager.isInvite)
|
if (!TimelineManager.isInvite)
|
||||||
roomContextMenu.show(roomId, tags);
|
roomContextMenu.show(roomItemTh, roomId, tags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -734,16 +740,16 @@ Page {
|
||||||
roomid: roomContextMenu.roomid
|
roomid: roomContextMenu.roomid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.Menu {
|
Menu {
|
||||||
id: roomContextMenu
|
id: roomContextMenu
|
||||||
|
|
||||||
property string roomid
|
property string roomid
|
||||||
property var tags
|
property var tags
|
||||||
|
|
||||||
function show(roomid_, tags_) {
|
function show(parent, roomid_, tags_) {
|
||||||
roomid = roomid_;
|
roomid = roomid_;
|
||||||
tags = tags_;
|
tags = tags_;
|
||||||
open();
|
popup(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDialog {
|
InputDialog {
|
||||||
|
@ -756,7 +762,7 @@ Page {
|
||||||
Rooms.toggleTag(roomContextMenu.roomid, "u." + text, true);
|
Rooms.toggleTag(roomContextMenu.roomid, "u." + text, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Open separately")
|
text: qsTr("Open separately")
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
@ -768,27 +774,27 @@ Page {
|
||||||
destroyOnClose(roomWindow);
|
destroyOnClose(roomWindow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Mark as read")
|
text: qsTr("Mark as read")
|
||||||
|
|
||||||
onTriggered: Rooms.getRoomById(roomContextMenu.roomid).markRoomAsRead()
|
onTriggered: Rooms.getRoomById(roomContextMenu.roomid).markRoomAsRead()
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Room settings")
|
text: qsTr("Room settings")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openRoomSettings(roomContextMenu.roomid)
|
onTriggered: TimelineManager.openRoomSettings(roomContextMenu.roomid)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Leave room")
|
text: qsTr("Leave room")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openLeaveRoomDialog(roomContextMenu.roomid)
|
onTriggered: TimelineManager.openLeaveRoomDialog(roomContextMenu.roomid)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Copy room link")
|
text: qsTr("Copy room link")
|
||||||
|
|
||||||
onTriggered: Rooms.copyLink(roomContextMenu.roomid)
|
onTriggered: Rooms.copyLink(roomContextMenu.roomid)
|
||||||
}
|
}
|
||||||
Platform.Menu {
|
Menu {
|
||||||
id: tagsMenu
|
id: tagsMenu
|
||||||
|
|
||||||
title: qsTr("Tag room as:")
|
title: qsTr("Tag room as:")
|
||||||
|
@ -796,7 +802,7 @@ Page {
|
||||||
Instantiator {
|
Instantiator {
|
||||||
model: Communities.tagsWithDefault
|
model: Communities.tagsWithDefault
|
||||||
|
|
||||||
delegate: Platform.MenuItem {
|
delegate: MenuItem {
|
||||||
property string t: modelData
|
property string t: modelData
|
||||||
|
|
||||||
checkable: true
|
checkable: true
|
||||||
|
@ -820,7 +826,7 @@ Page {
|
||||||
onObjectAdded: (index, object) => tagsMenu.insertItem(index, object)
|
onObjectAdded: (index, object) => tagsMenu.insertItem(index, object)
|
||||||
onObjectRemoved: (index, object) => tagsMenu.removeItem(object)
|
onObjectRemoved: (index, object) => tagsMenu.removeItem(object)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Create new tag...")
|
text: qsTr("Create new tag...")
|
||||||
|
|
||||||
onTriggered: newTag.show()
|
onTriggered: newTag.show()
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
import "./dialogs"
|
import "./dialogs"
|
||||||
import "./pages"
|
import "./pages"
|
||||||
import "./ui"
|
import "./ui"
|
||||||
import Qt.labs.platform 1.1 as Platform
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Window
|
import QtQuick.Window
|
||||||
|
import QtQuick.Dialogs
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
|
@ -342,15 +342,13 @@ Pane {
|
||||||
return UIA.submit3pidToken(t);
|
return UIA.submit3pidToken(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.MessageDialog {
|
MessageDialog {
|
||||||
id: uiaConfirmationLinkDialog
|
id: uiaConfirmationLinkDialog
|
||||||
|
|
||||||
buttons: Platform.MessageDialog.Ok
|
buttons: MessageDialog.Ok
|
||||||
text: qsTr("Wait for the confirmation link to arrive, then continue.")
|
text: qsTr("Wait for the confirmation link to arrive, then continue.")
|
||||||
|
|
||||||
// Broken on macos, see https://bugreports.qt.io/browse/QTBUG-102078
|
onAccepted: UIA.continue3pidReceived()
|
||||||
//onAccepted: UIA.continue3pidReceived()
|
|
||||||
onOkClicked: UIA.continue3pidReceived()
|
|
||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
function onConfirm3pidToken() {
|
function onConfirm3pidToken() {
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import Qt.labs.platform 1.1 as Platform
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
@ -235,28 +234,28 @@ Pane {
|
||||||
image: ":/icons/icons/ui/options.svg"
|
image: ":/icons/icons/ui/options.svg"
|
||||||
visible: !!room
|
visible: !!room
|
||||||
|
|
||||||
onClicked: roomOptionsMenu.open(roomOptionsButton)
|
onClicked: roomOptionsMenu.popup(roomOptionsButton)
|
||||||
|
|
||||||
Platform.Menu {
|
Menu {
|
||||||
id: roomOptionsMenu
|
id: roomOptionsMenu
|
||||||
|
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Invite users")
|
text: qsTr("Invite users")
|
||||||
visible: room ? room.permissions.canInvite() : false
|
visible: room ? room.permissions.canInvite() : false
|
||||||
|
|
||||||
onTriggered: TimelineManager.openInviteUsers(roomId)
|
onTriggered: TimelineManager.openInviteUsers(roomId)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Members")
|
text: qsTr("Members")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openRoomMembers(room)
|
onTriggered: TimelineManager.openRoomMembers(room)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Leave room")
|
text: qsTr("Leave room")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openLeaveRoomDialog(roomId)
|
onTriggered: TimelineManager.openLeaveRoomDialog(roomId)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Settings")
|
text: qsTr("Settings")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openRoomSettings(roomId)
|
onTriggered: TimelineManager.openRoomSettings(roomId)
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick
|
||||||
import Qt.labs.platform 1.1 as Platform
|
import QtQuick.Controls
|
||||||
import im.nheko 1.0
|
import im.nheko
|
||||||
|
|
||||||
Platform.Menu {
|
Menu {
|
||||||
id: spacesMenu
|
id: spacesMenu
|
||||||
|
|
||||||
property string roomid
|
property string roomid
|
||||||
|
@ -19,56 +19,61 @@ Platform.Menu {
|
||||||
onAboutToShow: loadChildren = true
|
onAboutToShow: loadChildren = true
|
||||||
//onAboutToHide: loadChildren = false
|
//onAboutToHide: loadChildren = false
|
||||||
|
|
||||||
Platform.MenuItemGroup {
|
ButtonGroup {
|
||||||
id: modificationGroup
|
id: modificationGroup
|
||||||
visible: position != -1
|
//visible: position != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Official community for this room")
|
text: qsTr("Official community for this room")
|
||||||
group: modificationGroup
|
ButtonGroup.group: modificationGroup
|
||||||
|
visible: position != -1
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: spacesMenu.position >= 0 && (modelData.childValid && modelData.parentValid && modelData.canonical)
|
checked: spacesMenu.position >= 0 && (modelData.childValid && modelData.parentValid && modelData.canonical)
|
||||||
enabled: spacesMenu.position >= 0 && (modelData.canEditChild && modelData.canEditParent)
|
enabled: spacesMenu.position >= 0 && (modelData.canEditChild && modelData.canEditParent)
|
||||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, true, true)
|
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, true, true)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Affiliated community for this room")
|
text: qsTr("Affiliated community for this room")
|
||||||
group: modificationGroup
|
ButtonGroup.group: modificationGroup
|
||||||
|
visible: position != -1
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: spacesMenu.position >= 0 && (modelData.childValid && modelData.parentValid && !modelData.canonical)
|
checked: spacesMenu.position >= 0 && (modelData.childValid && modelData.parentValid && !modelData.canonical)
|
||||||
enabled: spacesMenu.position >= 0 && (modelData.canEditChild && modelData.canEditParent)
|
enabled: spacesMenu.position >= 0 && (modelData.canEditChild && modelData.canEditParent)
|
||||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, true, false)
|
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, true, false)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Listed only for community members")
|
text: qsTr("Listed only for community members")
|
||||||
group: modificationGroup
|
ButtonGroup.group: modificationGroup
|
||||||
|
visible: position != -1
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: spacesMenu.position >= 0 && (modelData.childValid && !modelData.parentValid)
|
checked: spacesMenu.position >= 0 && (modelData.childValid && !modelData.parentValid)
|
||||||
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild || modelData.childValid) && (!modelData.parentValid || modelData.canEditParent))
|
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild || modelData.childValid) && (!modelData.parentValid || modelData.canEditParent))
|
||||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, false, true, false)
|
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, false, true, false)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Listed only for room members")
|
text: qsTr("Listed only for room members")
|
||||||
group: modificationGroup
|
ButtonGroup.group: modificationGroup
|
||||||
|
visible: position != -1
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: spacesMenu.position >= 0 && (!modelData.childValid && modelData.parentValid)
|
checked: spacesMenu.position >= 0 && (!modelData.childValid && modelData.parentValid)
|
||||||
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild) && (modelData.parentValid || modelData.canEditParent))
|
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild) && (modelData.parentValid || modelData.canEditParent))
|
||||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, false, false)
|
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, false, false)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Not related")
|
text: qsTr("Not related")
|
||||||
group: modificationGroup
|
ButtonGroup.group: modificationGroup
|
||||||
|
visible: position != -1
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: spacesMenu.position >= 0 && (!modelData.childValid && !modelData.parentValid)
|
checked: spacesMenu.position >= 0 && (!modelData.childValid && !modelData.parentValid)
|
||||||
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild || !modelData.childValid) && (!modelData.parentValid || modelData.canEditParent))
|
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild || !modelData.childValid) && (!modelData.parentValid || modelData.canEditParent))
|
||||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, false, false, false)
|
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, false, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuSeparator {
|
MenuSeparator {
|
||||||
text: qsTr("Subcommunities")
|
//text: qsTr("Subcommunities")
|
||||||
group: modificationGroup
|
ButtonGroup.group: modificationGroup
|
||||||
visible: modificationGroup.visible && inst.model != undefined
|
visible: position != -1 && inst.model != undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
Instantiator {
|
Instantiator {
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
|
|
||||||
import ".."
|
import ".."
|
||||||
import "../ui"
|
import "../ui"
|
||||||
import Qt.labs.platform 1.1 as Platform
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Window 2.13
|
import QtQuick.Window 2.13
|
||||||
|
import QtQuick.Dialogs
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
|
@ -580,26 +580,23 @@ ApplicationWindow {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MessageDialog {
|
MessageDialog {
|
||||||
id: confirmEncryptionDialog
|
id: confirmEncryptionDialog
|
||||||
|
|
||||||
title: qsTr("End-to-End Encryption")
|
title: qsTr("End-to-End Encryption")
|
||||||
text: qsTr(`Encryption is currently experimental and things might break unexpectedly. <br>
|
text: qsTr(`Encryption is currently experimental and things might break unexpectedly. <br>
|
||||||
Please take note that it can't be disabled afterwards.`)
|
Please take note that it can't be disabled afterwards.`)
|
||||||
modality: Qt.NonModal
|
modality: Qt.NonModal
|
||||||
// Broken on macos, see https://bugreports.qt.io/browse/QTBUG-102078
|
onAccepted: {
|
||||||
//onAccepted: {
|
|
||||||
onOkClicked: {
|
|
||||||
if (roomSettings.isEncryptionEnabled)
|
if (roomSettings.isEncryptionEnabled)
|
||||||
return ;
|
return ;
|
||||||
|
|
||||||
roomSettings.enableEncryption();
|
roomSettings.enableEncryption();
|
||||||
}
|
}
|
||||||
//onRejected: {
|
onRejected: {
|
||||||
onCancelClicked: {
|
|
||||||
encryptionToggle.checked = false;
|
encryptionToggle.checked = false;
|
||||||
}
|
}
|
||||||
buttons: Platform.MessageDialog.Ok | Platform.MessageDialog.Cancel
|
buttons: MessageDialog.Ok | MessageDialog.Cancel
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
|
787
src/Cache.cpp
787
src/Cache.cpp
File diff suppressed because it is too large
Load diff
169
src/Cache_p.h
169
src/Cache_p.h
|
@ -9,12 +9,6 @@
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#if __has_include(<lmdbxx/lmdb++.h>)
|
|
||||||
#include <lmdbxx/lmdb++.h>
|
|
||||||
#else
|
|
||||||
#include <lmdb++.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <mtx/events/collections.hpp>
|
#include <mtx/events/collections.hpp>
|
||||||
#include <mtx/responses/notifications.hpp>
|
#include <mtx/responses/notifications.hpp>
|
||||||
#include <mtx/responses/sync.hpp>
|
#include <mtx/responses/sync.hpp>
|
||||||
|
@ -29,12 +23,20 @@ struct Messages;
|
||||||
struct StateEvents;
|
struct StateEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace lmdb {
|
||||||
|
class txn;
|
||||||
|
class dbi;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CacheDb;
|
||||||
|
|
||||||
class Cache final : public QObject
|
class Cache final : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Cache(const QString &userId, QObject *parent = nullptr);
|
Cache(const QString &userId, QObject *parent = nullptr);
|
||||||
|
~Cache() noexcept;
|
||||||
|
|
||||||
std::string displayName(const std::string &room_id, const std::string &user_id);
|
std::string displayName(const std::string &room_id, const std::string &user_id);
|
||||||
QString displayName(const QString &room_id, const QString &user_id);
|
QString displayName(const QString &room_id, const QString &user_id);
|
||||||
|
@ -97,19 +99,11 @@ public:
|
||||||
//! Get a specific state event
|
//! Get a specific state event
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::optional<mtx::events::StateEvent<T>>
|
std::optional<mtx::events::StateEvent<T>>
|
||||||
getStateEvent(const std::string &room_id, std::string_view state_key = "")
|
getStateEvent(const std::string &room_id, std::string_view state_key = "");
|
||||||
{
|
|
||||||
auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY);
|
|
||||||
return getStateEvent<T>(txn, room_id, state_key);
|
|
||||||
}
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::vector<mtx::events::StateEvent<T>>
|
std::vector<mtx::events::StateEvent<T>>
|
||||||
getStateEventsWithType(const std::string &room_id,
|
getStateEventsWithType(const std::string &room_id,
|
||||||
mtx::events::EventType type = mtx::events::state_content_to_type<T>)
|
mtx::events::EventType type = mtx::events::state_content_to_type<T>);
|
||||||
{
|
|
||||||
auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY);
|
|
||||||
return getStateEventsWithType<T>(txn, room_id, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! retrieve a specific event from account data
|
//! retrieve a specific event from account data
|
||||||
//! pass empty room_id for global account data
|
//! pass empty room_id for global account data
|
||||||
|
@ -304,20 +298,6 @@ public:
|
||||||
std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>,
|
std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>,
|
||||||
mtx::events::StateEvent<decltype(std::declval<T>().content)>>;
|
mtx::events::StateEvent<decltype(std::declval<T>().content)>>;
|
||||||
|
|
||||||
static int compare_state_key(const MDB_val *a, const MDB_val *b)
|
|
||||||
{
|
|
||||||
auto get_skey = [](const MDB_val *v) {
|
|
||||||
auto temp = std::string_view(static_cast<const char *>(v->mv_data), v->mv_size);
|
|
||||||
// allow only passing the state key, in which case no null char will be in it and we
|
|
||||||
// return the whole string because rfind returns npos.
|
|
||||||
// We search from the back, because state keys could include nullbytes, event ids can
|
|
||||||
// not.
|
|
||||||
return temp.substr(0, temp.rfind('\0'));
|
|
||||||
};
|
|
||||||
|
|
||||||
return get_skey(a).compare(get_skey(b));
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void newReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
|
void newReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
|
||||||
void roomReadStatus(const std::map<QString, bool> &status);
|
void roomReadStatus(const std::map<QString, bool> &status);
|
||||||
|
@ -401,107 +381,44 @@ private:
|
||||||
|
|
||||||
//! Sends signals for the rooms that are removed.
|
//! Sends signals for the rooms that are removed.
|
||||||
void
|
void
|
||||||
removeLeftRooms(lmdb::txn &txn, const std::map<std::string, mtx::responses::LeftRoom> &rooms)
|
removeLeftRooms(lmdb::txn &txn, const std::map<std::string, mtx::responses::LeftRoom> &rooms);
|
||||||
{
|
|
||||||
for (const auto &room : rooms) {
|
|
||||||
removeRoom(txn, room.first);
|
|
||||||
|
|
||||||
// Clean up leftover invites.
|
|
||||||
removeInvite(txn, room.first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateSpaces(lmdb::txn &txn,
|
void updateSpaces(lmdb::txn &txn,
|
||||||
const std::set<std::string> &spaces_with_updates,
|
const std::set<std::string> &spaces_with_updates,
|
||||||
std::set<std::string> rooms_with_updates);
|
std::set<std::string> rooms_with_updates);
|
||||||
|
|
||||||
lmdb::dbi getEventsDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getEventsDb(lmdb::txn &txn, const std::string &room_id);
|
||||||
{
|
|
||||||
return lmdb::dbi::open(txn, std::string(room_id + "/events").c_str(), MDB_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
lmdb::dbi getEventOrderDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getEventOrderDb(lmdb::txn &txn, const std::string &room_id);
|
||||||
{
|
|
||||||
return lmdb::dbi::open(
|
|
||||||
txn, std::string(room_id + "/event_order").c_str(), MDB_CREATE | MDB_INTEGERKEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// inverse of EventOrderDb
|
// inverse of EventOrderDb
|
||||||
lmdb::dbi getEventToOrderDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getEventToOrderDb(lmdb::txn &txn, const std::string &room_id);
|
||||||
{
|
|
||||||
return lmdb::dbi::open(txn, std::string(room_id + "/event2order").c_str(), MDB_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
lmdb::dbi getMessageToOrderDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getMessageToOrderDb(lmdb::txn &txn, const std::string &room_id);
|
||||||
{
|
|
||||||
return lmdb::dbi::open(txn, std::string(room_id + "/msg2order").c_str(), MDB_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
lmdb::dbi getOrderToMessageDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getOrderToMessageDb(lmdb::txn &txn, const std::string &room_id);
|
||||||
{
|
|
||||||
return lmdb::dbi::open(
|
|
||||||
txn, std::string(room_id + "/order2msg").c_str(), MDB_CREATE | MDB_INTEGERKEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
lmdb::dbi getPendingMessagesDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getPendingMessagesDb(lmdb::txn &txn, const std::string &room_id);
|
||||||
{
|
|
||||||
return lmdb::dbi::open(
|
|
||||||
txn, std::string(room_id + "/pending").c_str(), MDB_CREATE | MDB_INTEGERKEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
lmdb::dbi getRelationsDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getRelationsDb(lmdb::txn &txn, const std::string &room_id);
|
||||||
{
|
|
||||||
return lmdb::dbi::open(
|
|
||||||
txn, std::string(room_id + "/related").c_str(), MDB_CREATE | MDB_DUPSORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
lmdb::dbi getInviteStatesDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getInviteStatesDb(lmdb::txn &txn, const std::string &room_id);
|
||||||
{
|
|
||||||
return lmdb::dbi::open(txn, std::string(room_id + "/invite_state").c_str(), MDB_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
lmdb::dbi getInviteMembersDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getInviteMembersDb(lmdb::txn &txn, const std::string &room_id);
|
||||||
{
|
|
||||||
return lmdb::dbi::open(txn, std::string(room_id + "/invite_members").c_str(), MDB_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
lmdb::dbi getStatesDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getStatesDb(lmdb::txn &txn, const std::string &room_id);
|
||||||
{
|
|
||||||
return lmdb::dbi::open(txn, std::string(room_id + "/state").c_str(), MDB_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
lmdb::dbi getStatesKeyDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getStatesKeyDb(lmdb::txn &txn, const std::string &room_id);
|
||||||
{
|
|
||||||
auto db = lmdb::dbi::open(
|
|
||||||
txn, std::string(room_id + "/states_key").c_str(), MDB_CREATE | MDB_DUPSORT);
|
|
||||||
lmdb::dbi_set_dupsort(txn, db, compare_state_key);
|
|
||||||
return db;
|
|
||||||
}
|
|
||||||
|
|
||||||
lmdb::dbi getAccountDataDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getAccountDataDb(lmdb::txn &txn, const std::string &room_id);
|
||||||
{
|
|
||||||
return lmdb::dbi::open(txn, std::string(room_id + "/account_data").c_str(), MDB_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
lmdb::dbi getMembersDb(lmdb::txn &txn, const std::string &room_id)
|
lmdb::dbi getMembersDb(lmdb::txn &txn, const std::string &room_id);
|
||||||
{
|
|
||||||
return lmdb::dbi::open(txn, std::string(room_id + "/members").c_str(), MDB_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
lmdb::dbi getUserKeysDb(lmdb::txn &txn) { return lmdb::dbi::open(txn, "user_key", MDB_CREATE); }
|
lmdb::dbi getUserKeysDb(lmdb::txn &txn);
|
||||||
|
|
||||||
lmdb::dbi getVerificationDb(lmdb::txn &txn)
|
lmdb::dbi getVerificationDb(lmdb::txn &txn);
|
||||||
{
|
|
||||||
return lmdb::dbi::open(txn, "verified", MDB_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString getDisplayName(const mtx::events::StateEvent<mtx::events::state::Member> &event)
|
QString getDisplayName(const mtx::events::StateEvent<mtx::events::state::Member> &event);
|
||||||
{
|
|
||||||
if (!event.content.display_name.empty())
|
|
||||||
return QString::fromStdString(event.content.display_name);
|
|
||||||
|
|
||||||
return QString::fromStdString(event.state_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<VerificationCache> verificationCache(const std::string &user_id, lmdb::txn &txn);
|
std::optional<VerificationCache> verificationCache(const std::string &user_id, lmdb::txn &txn);
|
||||||
VerificationStatus verificationStatus_(const std::string &user_id, lmdb::txn &txn);
|
VerificationStatus verificationStatus_(const std::string &user_id, lmdb::txn &txn);
|
||||||
|
@ -509,27 +426,6 @@ private:
|
||||||
|
|
||||||
void setNextBatchToken(lmdb::txn &txn, const std::string &token);
|
void setNextBatchToken(lmdb::txn &txn, const std::string &token);
|
||||||
|
|
||||||
lmdb::env env_;
|
|
||||||
lmdb::dbi syncStateDb_;
|
|
||||||
lmdb::dbi roomsDb_;
|
|
||||||
lmdb::dbi spacesChildrenDb_, spacesParentsDb_;
|
|
||||||
lmdb::dbi invitesDb_;
|
|
||||||
lmdb::dbi readReceiptsDb_;
|
|
||||||
lmdb::dbi notificationsDb_;
|
|
||||||
lmdb::dbi presenceDb_;
|
|
||||||
|
|
||||||
lmdb::dbi devicesDb_;
|
|
||||||
lmdb::dbi deviceKeysDb_;
|
|
||||||
|
|
||||||
lmdb::dbi inboundMegolmSessionDb_;
|
|
||||||
lmdb::dbi outboundMegolmSessionDb_;
|
|
||||||
lmdb::dbi megolmSessionDataDb_;
|
|
||||||
lmdb::dbi olmSessionDb_;
|
|
||||||
|
|
||||||
lmdb::dbi encryptedRooms_;
|
|
||||||
|
|
||||||
lmdb::dbi eventExpiryBgJob_;
|
|
||||||
|
|
||||||
QString localUserId_;
|
QString localUserId_;
|
||||||
QString cacheDirectory_;
|
QString cacheDirectory_;
|
||||||
|
|
||||||
|
@ -538,6 +434,8 @@ private:
|
||||||
VerificationStorage verification_storage;
|
VerificationStorage verification_storage;
|
||||||
|
|
||||||
bool databaseReady_ = false;
|
bool databaseReady_ = false;
|
||||||
|
|
||||||
|
std::unique_ptr<CacheDb> db;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace cache {
|
namespace cache {
|
||||||
|
@ -546,11 +444,12 @@ client();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NHEKO_CACHE_GET_STATE_EVENT_FORWARD(Content) \
|
#define NHEKO_CACHE_GET_STATE_EVENT_FORWARD(Content) \
|
||||||
extern template std::optional<mtx::events::StateEvent<Content>> Cache::getStateEvent( \
|
extern template std::optional<mtx::events::StateEvent<Content>> Cache::getStateEvent<Content>( \
|
||||||
lmdb::txn &txn, const std::string &room_id, std::string_view state_key); \
|
const std::string &room_id, std::string_view state_key); \
|
||||||
\
|
\
|
||||||
extern template std::vector<mtx::events::StateEvent<Content>> Cache::getStateEventsWithType( \
|
extern template std::vector<mtx::events::StateEvent<Content>> \
|
||||||
lmdb::txn &txn, const std::string &room_id, mtx::events::EventType type);
|
Cache::getStateEventsWithType<Content>(const std::string &room_id, \
|
||||||
|
mtx::events::EventType type);
|
||||||
|
|
||||||
NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::Aliases)
|
NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::Aliases)
|
||||||
NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::Avatar)
|
NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::Avatar)
|
||||||
|
|
|
@ -13,6 +13,9 @@
|
||||||
|
|
||||||
#include <mtx/responses.hpp>
|
#include <mtx/responses.hpp>
|
||||||
|
|
||||||
|
#include "Logging.h"
|
||||||
|
#include "UserSettingsPage.h"
|
||||||
|
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
mtx::http::Client *
|
mtx::http::Client *
|
||||||
|
@ -20,9 +23,15 @@ client()
|
||||||
{
|
{
|
||||||
static auto client_ = [] {
|
static auto client_ = [] {
|
||||||
auto c = std::make_shared<mtx::http::Client>();
|
auto c = std::make_shared<mtx::http::Client>();
|
||||||
c->alt_svc_cache_path((QStandardPaths::writableLocation(QStandardPaths::CacheLocation) +
|
|
||||||
"/curl_alt_svc_cache.txt")
|
// Disabled by default until CPU usage and reliability improves
|
||||||
.toStdString());
|
if (UserSettings::instance()->qsettings()->value("enable_http3").toBool()) {
|
||||||
|
nhlog::net()->warn("Enabling http3 support. This is currently usually a worse "
|
||||||
|
"experience, so you are on your own.");
|
||||||
|
c->alt_svc_cache_path((QStandardPaths::writableLocation(QStandardPaths::CacheLocation) +
|
||||||
|
"/curl_alt_svc_cache.txt")
|
||||||
|
.toStdString());
|
||||||
|
}
|
||||||
return c;
|
return c;
|
||||||
}();
|
}();
|
||||||
return client_.get();
|
return client_.get();
|
||||||
|
|
|
@ -32,16 +32,17 @@ MxcImageProvider::MxcImageProvider()
|
||||||
timer->setInterval(std::chrono::hours(1));
|
timer->setInterval(std::chrono::hours(1));
|
||||||
connect(timer, &QTimer::timeout, this, [] {
|
connect(timer, &QTimer::timeout, this, [] {
|
||||||
QThreadPool::globalInstance()->start([] {
|
QThreadPool::globalInstance()->start([] {
|
||||||
|
nhlog::net()->debug("Running media purge");
|
||||||
QDir dir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) +
|
QDir dir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) +
|
||||||
"/media_cache",
|
"/media_cache",
|
||||||
"",
|
"",
|
||||||
QDir::SortFlags(QDir::Name | QDir::IgnoreCase),
|
QDir::SortFlags(QDir::Name | QDir::IgnoreCase),
|
||||||
QDir::Filter::Writable | QDir::Filter::NoDotAndDotDot | QDir::Filter::Files);
|
QDir::Filter::Writable | QDir::Filter::NoDotAndDotDot | QDir::Filter::Files |
|
||||||
|
QDir::Filter::Dirs);
|
||||||
|
|
||||||
auto files = dir.entryInfoList();
|
auto handleFile = [](const QFileInfo &fileInfo) {
|
||||||
for (const auto &fileInfo : std::as_const(files)) {
|
|
||||||
if (fileInfo.fileTime(QFile::FileTime::FileAccessTime)
|
if (fileInfo.fileTime(QFile::FileTime::FileAccessTime)
|
||||||
.daysTo(QDateTime::currentDateTime()) > 30) {
|
.daysTo(QDateTime::currentDateTime()) > 14) {
|
||||||
if (QFile::remove(fileInfo.absoluteFilePath()))
|
if (QFile::remove(fileInfo.absoluteFilePath()))
|
||||||
nhlog::net()->debug("Deleted stale media '{}'",
|
nhlog::net()->debug("Deleted stale media '{}'",
|
||||||
fileInfo.absoluteFilePath().toStdString());
|
fileInfo.absoluteFilePath().toStdString());
|
||||||
|
@ -49,6 +50,24 @@ MxcImageProvider::MxcImageProvider()
|
||||||
nhlog::net()->warn("Failed to delete stale media '{}'",
|
nhlog::net()->warn("Failed to delete stale media '{}'",
|
||||||
fileInfo.absoluteFilePath().toStdString());
|
fileInfo.absoluteFilePath().toStdString());
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto files = dir.entryInfoList();
|
||||||
|
for (const auto &fileInfo : std::as_const(files)) {
|
||||||
|
if (fileInfo.isDir()) {
|
||||||
|
// handle one level of legacy directories
|
||||||
|
auto nestedDir = QDir(fileInfo.absoluteFilePath(),
|
||||||
|
"",
|
||||||
|
QDir::SortFlags(QDir::Name | QDir::IgnoreCase),
|
||||||
|
QDir::Filter::Writable | QDir::Filter::NoDotAndDotDot |
|
||||||
|
QDir::Filter::Files)
|
||||||
|
.entryInfoList();
|
||||||
|
for (const auto &nestedFile : std::as_const(nestedDir)) {
|
||||||
|
handleFile(nestedFile);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleFile(fileInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
|
|
||||||
#include "TrayIcon.h"
|
#include "TrayIcon.h"
|
||||||
|
#include "UserSettingsPage.h"
|
||||||
|
|
||||||
MsgCountComposedIcon::MsgCountComposedIcon(const QIcon &icon)
|
MsgCountComposedIcon::MsgCountComposedIcon(const QIcon &icon)
|
||||||
: QIconEngine()
|
: QIconEngine()
|
||||||
|
@ -114,12 +115,30 @@ TrayIcon::TrayIcon(const QString &filename, QWindow *parent)
|
||||||
|
|
||||||
menu->addAction(viewAction_);
|
menu->addAction(viewAction_);
|
||||||
menu->addAction(quitAction_);
|
menu->addAction(quitAction_);
|
||||||
|
|
||||||
|
QString toolTip = QLatin1String("nheko");
|
||||||
|
QString profile = UserSettings::instance()->profile();
|
||||||
|
if (!profile.isEmpty())
|
||||||
|
toolTip.append(QStringLiteral(" | %1").arg(profile));
|
||||||
|
|
||||||
|
setToolTip(toolTip);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TrayIcon::setUnreadCount(int count)
|
TrayIcon::setUnreadCount(int count)
|
||||||
{
|
{
|
||||||
qGuiApp->setBadgeNumber(count);
|
qGuiApp->setBadgeNumber(count);
|
||||||
|
if (count != previousCount) {
|
||||||
|
QString toolTip = QLatin1String("nheko");
|
||||||
|
QString profile = UserSettings::instance()->profile();
|
||||||
|
if (!profile.isEmpty())
|
||||||
|
toolTip.append(QStringLiteral(" | %1").arg(profile));
|
||||||
|
|
||||||
|
if (count != 0)
|
||||||
|
toolTip.append(tr("\n%n unread message(s)", "", count));
|
||||||
|
|
||||||
|
setToolTip(toolTip);
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN)
|
#if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN)
|
||||||
if (count != previousCount) {
|
if (count != previousCount) {
|
||||||
|
@ -131,13 +150,6 @@ TrayIcon::setUnreadCount(int count)
|
||||||
#else
|
#else
|
||||||
(void)previousCount;
|
(void)previousCount;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QString toolTip = QLatin1String("nheko");
|
|
||||||
if (count > 0) {
|
|
||||||
toolTip.append(tr("\n%n unread message(s)", "", count));
|
|
||||||
}
|
|
||||||
|
|
||||||
setToolTip(toolTip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "moc_TrayIcon.cpp"
|
#include "moc_TrayIcon.cpp"
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#include <QVideoFrame>
|
#include <QVideoFrame>
|
||||||
#include <QVideoSink>
|
#include <QVideoSink>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
#include <mtx/responses/common.hpp>
|
#include <mtx/responses/common.hpp>
|
||||||
|
@ -578,8 +580,7 @@ InputBar::message(const QString &msg, MarkdownOverride useMarkdown, bool rainbow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
text.body =
|
text.body = fmt::format("{}\n{}", body.toStdString(), text.body);
|
||||||
QStringLiteral("%1\n%2").arg(body, QString::fromStdString(text.body)).toStdString();
|
|
||||||
|
|
||||||
// NOTE(Nico): rich replies always need a formatted_body!
|
// NOTE(Nico): rich replies always need a formatted_body!
|
||||||
text.format = "org.matrix.custom.html";
|
text.format = "org.matrix.custom.html";
|
||||||
|
|
|
@ -37,8 +37,14 @@ static CacheEntry *
|
||||||
pullPresence(const QString &id)
|
pullPresence(const QString &id)
|
||||||
{
|
{
|
||||||
auto p = cache::presence(id.toStdString());
|
auto p = cache::presence(id.toStdString());
|
||||||
auto c = new CacheEntry{
|
|
||||||
utils::replaceEmoji(QString::fromStdString(p.status_msg).toHtmlEscaped()), p.presence};
|
auto statusMsg = QString::fromStdString(p.status_msg);
|
||||||
|
if (statusMsg.size() > 255) {
|
||||||
|
statusMsg.truncate(255);
|
||||||
|
statusMsg.append(u'…');
|
||||||
|
}
|
||||||
|
|
||||||
|
auto c = new CacheEntry{utils::replaceEmoji(std::move(statusMsg).toHtmlEscaped()), p.presence};
|
||||||
presences.insert(id, c);
|
presences.insert(id, c);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
|
@ -238,9 +238,11 @@ TimelineFilter::filterAcceptsRow(int source_row, const QModelIndex &) const
|
||||||
|
|
||||||
if (auto s = sourceModel()) {
|
if (auto s = sourceModel()) {
|
||||||
auto idx = s->index(source_row, 0);
|
auto idx = s->index(source_row, 0);
|
||||||
if (!contentFilter.isEmpty() && !s->data(idx, TimelineModel::Body)
|
if (
|
||||||
.toString()
|
!contentFilter.isEmpty()
|
||||||
.contains(contentFilter, Qt::CaseInsensitive)) {
|
&& !s->data(idx, TimelineModel::Body).toString().contains(contentFilter, Qt::CaseInsensitive)
|
||||||
|
&& !s->data(idx, TimelineModel::UserName).toString().contains(contentFilter, Qt::CaseInsensitive)
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2073,10 +2073,12 @@ TimelineModel::cacheMedia(const QString &eventId,
|
||||||
|
|
||||||
const auto url = mxcUrl.toStdString();
|
const auto url = mxcUrl.toStdString();
|
||||||
const auto name = QString(mxcUrl).remove(QStringLiteral("mxc://"));
|
const auto name = QString(mxcUrl).remove(QStringLiteral("mxc://"));
|
||||||
QFileInfo filename(
|
QFileInfo filename(QStringLiteral("%1/media_cache/%2.%3")
|
||||||
QStringLiteral("%1/media_cache/%2.%3")
|
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation),
|
||||||
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation), name, suffix));
|
QString::fromUtf8(name.toUtf8().toBase64(
|
||||||
if (QDir::cleanPath(name) != name) {
|
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)),
|
||||||
|
suffix));
|
||||||
|
if (QDir::cleanPath(filename.filePath()) != filename.filePath()) {
|
||||||
nhlog::net()->warn("mxcUrl '{}' is not safe, not downloading file", url);
|
nhlog::net()->warn("mxcUrl '{}' is not safe, not downloading file", url);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,10 +53,12 @@ MxcAnimatedImage::startDownload()
|
||||||
|
|
||||||
const auto url = mxcUrl.toStdString();
|
const auto url = mxcUrl.toStdString();
|
||||||
const auto name = QString(mxcUrl).remove(QStringLiteral("mxc://"));
|
const auto name = QString(mxcUrl).remove(QStringLiteral("mxc://"));
|
||||||
QFileInfo filename(
|
QFileInfo filename(QStringLiteral("%1/media_cache/media/%2.%3")
|
||||||
QStringLiteral("%1/media_cache/media/%2.%3")
|
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation),
|
||||||
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation), name, suffix));
|
QString::fromUtf8(name.toUtf8().toBase64(
|
||||||
if (QDir::cleanPath(name) != name) {
|
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)),
|
||||||
|
suffix));
|
||||||
|
if (QDir::cleanPath(filename.filePath()) != filename.filePath()) {
|
||||||
nhlog::net()->warn("mxcUrl '{}' is not safe, not downloading file", url);
|
nhlog::net()->warn("mxcUrl '{}' is not safe, not downloading file", url);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,10 +96,12 @@ MxcMediaProxy::startDownload(bool onlyCached)
|
||||||
|
|
||||||
const auto url = mxcUrl.toStdString();
|
const auto url = mxcUrl.toStdString();
|
||||||
const auto name = QString(mxcUrl).remove(QStringLiteral("mxc://"));
|
const auto name = QString(mxcUrl).remove(QStringLiteral("mxc://"));
|
||||||
QFileInfo filename(
|
QFileInfo filename(QStringLiteral("%1/media_cache/media/%2.%3")
|
||||||
QStringLiteral("%1/media_cache/media/%2.%3")
|
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation),
|
||||||
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation), name, suffix));
|
QString::fromUtf8(name.toUtf8().toBase64(
|
||||||
if (QDir::cleanPath(name) != name) {
|
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)),
|
||||||
|
suffix));
|
||||||
|
if (QDir::cleanPath(filename.filePath()) != filename.filePath()) {
|
||||||
nhlog::net()->warn("mxcUrl '{}' is not safe, not downloading file", url);
|
nhlog::net()->warn("mxcUrl '{}' is not safe, not downloading file", url);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue