mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-23 19:38:48 +03:00
Compare commits
1 commit
5514bb6cf7
...
2e40612e19
Author | SHA1 | Date | |
---|---|---|---|
|
2e40612e19 |
24 changed files with 644 additions and 713 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-qtmultimedia qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qt-labs-platform
|
||||||
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.8 clang_64 -m qtlocation qtimageformats qtmultimedia qtpositioning qtshadertools
|
- aqt install-qt --outputdir $HOME/qt mac desktop 6.6 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-qtmultimedia qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qt-labs-platform
|
||||||
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
|
||||||
|
|
|
@ -648,9 +648,9 @@ qt_add_resources(QRC resources/res.qrc)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Foundation -framework Cocoa -framework UserNotifications")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Foundation -framework Cocoa -framework UserNotifications")
|
||||||
set(SRC_FILES ${SRC_FILES} src/notifications/NotificationManagerProxy.h src/notifications/MacNotificationDelegate.h src/notifications/MacNotificationDelegate.mm src/notifications/ManagerMac.mm src/notifications/ManagerMac.cpp)
|
set(SRC_FILES ${SRC_FILES} src/notifications/NotificationManagerProxy.h src/notifications/MacNotificationDelegate.h src/notifications/MacNotificationDelegate.mm src/notifications/ManagerMac.mm src/notifications/ManagerMac.cpp src/emoji/MacHelper.mm src/emoji/MacHelper.h)
|
||||||
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
|
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
|
||||||
set_source_files_properties( src/notifications/NotificationManagerProxy.h src/notifications/MacNotificationDelegate.h src/notifications/MacNotificationDelegate.mm src/notifications/ManagerMac.mm PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
|
set_source_files_properties( src/notifications/NotificationManagerProxy.h src/notifications/MacNotificationDelegate.h src/notifications/MacNotificationDelegate.mm src/notifications/ManagerMac.mm src/emoji/MacHelper.mm src/emoji/MacHelper.h PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
|
||||||
endif()
|
endif()
|
||||||
elseif(WIN32)
|
elseif(WIN32)
|
||||||
file(DOWNLOAD
|
file(DOWNLOAD
|
||||||
|
|
|
@ -348,11 +348,12 @@ sudo pacman -S qt6-base \
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Debian 13 [Testing/Sid] (Nheko QT6 Version)
|
#### Debian 13 [Testing/Sid] (Nheko QT6 Version)
|
||||||
|
*As of February 2024, Nheko from git master requires QT 6.5 whereas Trixie has 6.4.2, so you must enable Debian's [Experimental Repository](https://wiki.debian.org/DebianExperimental) to install newer QT6. This may not be necessary in the future and the `-t experimental` can be removed for the second set of build requirements.*
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install build requirements
|
# Install build requirements
|
||||||
sudo apt install -y cmake asciidoc-base libevent-dev libspdlog-dev libre2-dev liblmdb++-dev libcurl4-openssl-dev libssl-dev libolm-dev libcmark-dev nlohmann-json3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev libkdsingleapplication-qt6-dev
|
sudo apt install -y cmake asciidoc-base libevent-dev libspdlog-dev libre2-dev liblmdb++-dev libcurl4-openssl-dev libssl-dev libolm-dev libcmark-dev nlohmann-json3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev libkdsingleapplication-qt6-dev
|
||||||
sudo apt install -y qt6-base-dev qt6-tools-dev qt6-svg-dev qt6-multimedia-dev qt6-declarative-dev qtkeychain-qt6-dev qt6-base-private-dev qt6-declarative-private-dev
|
sudo apt install -y -t experimental qt6-base-dev qt6-tools-dev qt6-svg-dev qt6-multimedia-dev qt6-declarative-dev qtkeychain-qt6-dev qt6-base-private-dev qt6-declarative-private-dev
|
||||||
# Clone nheko repository from github
|
# Clone nheko repository from github
|
||||||
sudo apt install -y git
|
sudo apt install -y git
|
||||||
git clone https://github.com/Nheko-Reborn/nheko && cd nheko
|
git clone https://github.com/Nheko-Reborn/nheko && cd nheko
|
||||||
|
@ -365,7 +366,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,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,-labs-platform,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,6 +3,7 @@
|
||||||
// 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
|
||||||
|
@ -100,18 +101,17 @@ Page {
|
||||||
]
|
]
|
||||||
|
|
||||||
onClicked: Communities.setCurrentTagId(model.id)
|
onClicked: Communities.setCurrentTagId(model.id)
|
||||||
onPressAndHold: communityContextMenu.show(communityItem, model.id, model.hidden, model.muted)
|
onPressAndHold: communityContextMenu.show(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(rth, model.id, model.hidden, model.muted)
|
onSingleTapped: communityContextMenu.show(model.id, model.hidden, model.muted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
@ -195,28 +195,28 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu {
|
Platform.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(parent, id_, hidden_, muted_) {
|
function show(id_, hidden_, muted_) {
|
||||||
tagId = id_;
|
tagId = id_;
|
||||||
hidden = hidden_;
|
hidden = hidden_;
|
||||||
muted = muted_;
|
muted = muted_;
|
||||||
popup(parent);
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
Platform.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)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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.")
|
||||||
|
|
|
@ -173,7 +173,7 @@ Rectangle {
|
||||||
} else if (event.matches(StandardKey.InsertLineSeparator)) {
|
} else if (event.matches(StandardKey.InsertLineSeparator)) {
|
||||||
if (popup.opened)
|
if (popup.opened)
|
||||||
popup.close();
|
popup.close();
|
||||||
if (Settings.invertEnterKey) {
|
if (Settings.invertEnterKey && (!Qt.inputMethod.visible || Qt.platform.os === "windows")) {
|
||||||
room.input.send();
|
room.input.send();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,7 @@ Rectangle {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!Settings.invertEnterKey) {
|
if (!Settings.invertEnterKey && (!Qt.inputMethod.visible || Qt.platform.os === "windows")) {
|
||||||
room.input.send();
|
room.input.send();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
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
|
||||||
|
@ -392,7 +393,7 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Menu {
|
Platform.Menu {
|
||||||
id: messageContextMenuC
|
id: messageContextMenuC
|
||||||
|
|
||||||
property string eventId
|
property string eventId
|
||||||
|
@ -420,9 +421,9 @@ Item {
|
||||||
else
|
else
|
||||||
link = "";
|
link = "";
|
||||||
if (showAt_)
|
if (showAt_)
|
||||||
popup(showAt_);
|
open(showAt_);
|
||||||
else
|
else
|
||||||
popup();
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
|
@ -447,7 +448,7 @@ Item {
|
||||||
ReportMessage {}
|
ReportMessage {}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("Go to &message")
|
text: qsTr("Go to &message")
|
||||||
visible: filteredTimeline.filterByContent
|
visible: filteredTimeline.filterByContent
|
||||||
|
@ -457,21 +458,21 @@ Item {
|
||||||
room.showEvent(messageContextMenuC.eventId);
|
room.showEvent(messageContextMenuC.eventId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
id: reactionOption
|
id: reactionOption
|
||||||
|
|
||||||
text: qsTr("Re&act")
|
text: qsTr("Re&act")
|
||||||
|
@ -482,39 +483,39 @@ Item {
|
||||||
TimelineManager.focusMessageInput();
|
TimelineManager.focusMessageInput();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("&Read receipts")
|
text: qsTr("&Read receipts")
|
||||||
|
|
||||||
onTriggered: room.showReadReceipts(messageContextMenuC.eventId)
|
onTriggered: room.showReadReceipts(messageContextMenuC.eventId)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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
|
||||||
|
|
||||||
|
@ -525,15 +526,15 @@ Item {
|
||||||
timelineRoot.destroyOnClose(forwardMess);
|
timelineRoot.destroyOnClose(forwardMess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("&Mark as read")
|
text: qsTr("&Mark as read")
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("View raw message")
|
text: qsTr("View raw message")
|
||||||
|
|
||||||
onTriggered: room.viewRawMessage(messageContextMenuC.eventId)
|
onTriggered: room.viewRawMessage(messageContextMenuC.eventId)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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
|
||||||
|
@ -541,7 +542,7 @@ Item {
|
||||||
|
|
||||||
onTriggered: room.viewDecryptedRawMessage(messageContextMenuC.eventId)
|
onTriggered: room.viewDecryptedRawMessage(messageContextMenuC.eventId)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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
|
||||||
|
|
||||||
|
@ -553,7 +554,7 @@ Item {
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Report message")
|
text: qsTr("Report message")
|
||||||
enabled: visible
|
enabled: visible
|
||||||
onTriggered: function () {
|
onTriggered: function () {
|
||||||
|
@ -563,21 +564,21 @@ Item {
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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
|
||||||
|
@ -591,7 +592,7 @@ Item {
|
||||||
ForwardCompleter {
|
ForwardCompleter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Menu {
|
Platform.Menu {
|
||||||
id: replyContextMenuC
|
id: replyContextMenuC
|
||||||
|
|
||||||
property string eventId
|
property string eventId
|
||||||
|
@ -605,21 +606,21 @@ Item {
|
||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
Platform.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
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("&Go to quoted message")
|
text: qsTr("&Go to quoted message")
|
||||||
visible: true
|
visible: true
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
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
|
||||||
|
@ -41,8 +43,6 @@ 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.popup(startChatButton)
|
onClicked: roomJoinCreateMenu.open(parent)
|
||||||
|
|
||||||
Menu {
|
Platform.Menu {
|
||||||
id: roomJoinCreateMenu
|
id: roomJoinCreateMenu
|
||||||
|
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Join a room")
|
text: qsTr("Join a room")
|
||||||
|
|
||||||
onTriggered: Nheko.openJoinRoomDialog()
|
onTriggered: Nheko.openJoinRoomDialog()
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Create a new community")
|
text: qsTr("Create a new community")
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
@ -255,72 +255,68 @@ Page {
|
||||||
Nheko.setStatusMessage(text);
|
Nheko.setStatusMessage(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Menu {
|
Platform.Menu {
|
||||||
id: userInfoMenu
|
id: userInfoMenu
|
||||||
|
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Profile settings")
|
text: qsTr("Profile settings")
|
||||||
|
|
||||||
onTriggered: userInfoPanel.openUserProfile()
|
onTriggered: userInfoPanel.openUserProfile()
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Set status message")
|
text: qsTr("Set status message")
|
||||||
|
|
||||||
onTriggered: statusDialog.show()
|
onTriggered: statusDialog.show()
|
||||||
}
|
}
|
||||||
MenuSeparator {
|
Platform.MenuSeparator {
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonGroup {
|
Platform.MenuItemGroup {
|
||||||
id: onlineStateGroup
|
id: onlineStateGroup
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Automatic online status")
|
text: qsTr("Automatic online status")
|
||||||
ButtonGroup.group: onlineStateGroup
|
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
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Online")
|
text: qsTr("Online")
|
||||||
ButtonGroup.group: onlineStateGroup
|
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
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Unavailable")
|
text: qsTr("Unavailable")
|
||||||
ButtonGroup.group: onlineStateGroup
|
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
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Offline")
|
text: qsTr("Offline")
|
||||||
ButtonGroup.group: onlineStateGroup
|
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.popup(userTapHandler)
|
onLongPressed: userInfoMenu.open()
|
||||||
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.popup(userTapHandler2)
|
onSingleTapped: userInfoMenu.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
@ -529,7 +525,7 @@ Page {
|
||||||
}
|
}
|
||||||
onPressAndHold: {
|
onPressAndHold: {
|
||||||
if (!isInvite)
|
if (!isInvite)
|
||||||
roomContextMenu.show(roomItem, roomId, tags);
|
roomContextMenu.show(roomId, tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ripple {
|
Ripple {
|
||||||
|
@ -542,15 +538,13 @@ 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(roomItemTh, roomId, tags);
|
roomContextMenu.show(roomId, tags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -740,16 +734,16 @@ Page {
|
||||||
roomid: roomContextMenu.roomid
|
roomid: roomContextMenu.roomid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Menu {
|
Platform.Menu {
|
||||||
id: roomContextMenu
|
id: roomContextMenu
|
||||||
|
|
||||||
property string roomid
|
property string roomid
|
||||||
property var tags
|
property var tags
|
||||||
|
|
||||||
function show(parent, roomid_, tags_) {
|
function show(roomid_, tags_) {
|
||||||
roomid = roomid_;
|
roomid = roomid_;
|
||||||
tags = tags_;
|
tags = tags_;
|
||||||
popup(parent);
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDialog {
|
InputDialog {
|
||||||
|
@ -762,7 +756,7 @@ Page {
|
||||||
Rooms.toggleTag(roomContextMenu.roomid, "u." + text, true);
|
Rooms.toggleTag(roomContextMenu.roomid, "u." + text, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Open separately")
|
text: qsTr("Open separately")
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
@ -774,27 +768,27 @@ Page {
|
||||||
destroyOnClose(roomWindow);
|
destroyOnClose(roomWindow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Mark as read")
|
text: qsTr("Mark as read")
|
||||||
|
|
||||||
onTriggered: Rooms.getRoomById(roomContextMenu.roomid).markRoomAsRead()
|
onTriggered: Rooms.getRoomById(roomContextMenu.roomid).markRoomAsRead()
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Room settings")
|
text: qsTr("Room settings")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openRoomSettings(roomContextMenu.roomid)
|
onTriggered: TimelineManager.openRoomSettings(roomContextMenu.roomid)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Leave room")
|
text: qsTr("Leave room")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openLeaveRoomDialog(roomContextMenu.roomid)
|
onTriggered: TimelineManager.openLeaveRoomDialog(roomContextMenu.roomid)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Copy room link")
|
text: qsTr("Copy room link")
|
||||||
|
|
||||||
onTriggered: Rooms.copyLink(roomContextMenu.roomid)
|
onTriggered: Rooms.copyLink(roomContextMenu.roomid)
|
||||||
}
|
}
|
||||||
Menu {
|
Platform.Menu {
|
||||||
id: tagsMenu
|
id: tagsMenu
|
||||||
|
|
||||||
title: qsTr("Tag room as:")
|
title: qsTr("Tag room as:")
|
||||||
|
@ -802,7 +796,7 @@ Page {
|
||||||
Instantiator {
|
Instantiator {
|
||||||
model: Communities.tagsWithDefault
|
model: Communities.tagsWithDefault
|
||||||
|
|
||||||
delegate: MenuItem {
|
delegate: Platform.MenuItem {
|
||||||
property string t: modelData
|
property string t: modelData
|
||||||
|
|
||||||
checkable: true
|
checkable: true
|
||||||
|
@ -826,7 +820,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)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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,13 +342,15 @@ Pane {
|
||||||
return UIA.submit3pidToken(t);
|
return UIA.submit3pidToken(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MessageDialog {
|
Platform.MessageDialog {
|
||||||
id: uiaConfirmationLinkDialog
|
id: uiaConfirmationLinkDialog
|
||||||
|
|
||||||
buttons: MessageDialog.Ok
|
buttons: Platform.MessageDialog.Ok
|
||||||
text: qsTr("Wait for the confirmation link to arrive, then continue.")
|
text: qsTr("Wait for the confirmation link to arrive, then continue.")
|
||||||
|
|
||||||
onAccepted: UIA.continue3pidReceived()
|
// Broken on macos, see https://bugreports.qt.io/browse/QTBUG-102078
|
||||||
|
//onAccepted: UIA.continue3pidReceived()
|
||||||
|
onOkClicked: UIA.continue3pidReceived()
|
||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
function onConfirm3pidToken() {
|
function onConfirm3pidToken() {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
//
|
//
|
||||||
// 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
|
||||||
|
@ -234,28 +235,28 @@ Pane {
|
||||||
image: ":/icons/icons/ui/options.svg"
|
image: ":/icons/icons/ui/options.svg"
|
||||||
visible: !!room
|
visible: !!room
|
||||||
|
|
||||||
onClicked: roomOptionsMenu.popup(roomOptionsButton)
|
onClicked: roomOptionsMenu.open(roomOptionsButton)
|
||||||
|
|
||||||
Menu {
|
Platform.Menu {
|
||||||
id: roomOptionsMenu
|
id: roomOptionsMenu
|
||||||
|
|
||||||
MenuItem {
|
Platform.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)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Members")
|
text: qsTr("Members")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openRoomMembers(room)
|
onTriggered: TimelineManager.openRoomMembers(room)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Leave room")
|
text: qsTr("Leave room")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openLeaveRoomDialog(roomId)
|
onTriggered: TimelineManager.openLeaveRoomDialog(roomId)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.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
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls
|
import Qt.labs.platform 1.1 as Platform
|
||||||
import im.nheko
|
import im.nheko 1.0
|
||||||
|
|
||||||
Menu {
|
Platform.Menu {
|
||||||
id: spacesMenu
|
id: spacesMenu
|
||||||
|
|
||||||
property string roomid
|
property string roomid
|
||||||
|
@ -19,61 +19,56 @@ Menu {
|
||||||
onAboutToShow: loadChildren = true
|
onAboutToShow: loadChildren = true
|
||||||
//onAboutToHide: loadChildren = false
|
//onAboutToHide: loadChildren = false
|
||||||
|
|
||||||
ButtonGroup {
|
Platform.MenuItemGroup {
|
||||||
id: modificationGroup
|
id: modificationGroup
|
||||||
//visible: position != -1
|
visible: position != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Official community for this room")
|
text: qsTr("Official community for this room")
|
||||||
ButtonGroup.group: modificationGroup
|
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)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Affiliated community for this room")
|
text: qsTr("Affiliated community for this room")
|
||||||
ButtonGroup.group: modificationGroup
|
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)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Listed only for community members")
|
text: qsTr("Listed only for community members")
|
||||||
ButtonGroup.group: modificationGroup
|
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)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Listed only for room members")
|
text: qsTr("Listed only for room members")
|
||||||
ButtonGroup.group: modificationGroup
|
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)
|
||||||
}
|
}
|
||||||
MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Not related")
|
text: qsTr("Not related")
|
||||||
ButtonGroup.group: modificationGroup
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuSeparator {
|
Platform.MenuSeparator {
|
||||||
//text: qsTr("Subcommunities")
|
text: qsTr("Subcommunities")
|
||||||
ButtonGroup.group: modificationGroup
|
group: modificationGroup
|
||||||
visible: position != -1 && inst.model != undefined
|
visible: modificationGroup.visible && 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,23 +580,26 @@ ApplicationWindow {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageDialog {
|
Platform.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
|
||||||
onAccepted: {
|
// Broken on macos, see https://bugreports.qt.io/browse/QTBUG-102078
|
||||||
|
//onAccepted: {
|
||||||
|
onOkClicked: {
|
||||||
if (roomSettings.isEncryptionEnabled)
|
if (roomSettings.isEncryptionEnabled)
|
||||||
return ;
|
return ;
|
||||||
|
|
||||||
roomSettings.enableEncryption();
|
roomSettings.enableEncryption();
|
||||||
}
|
}
|
||||||
onRejected: {
|
//onRejected: {
|
||||||
|
onCancelClicked: {
|
||||||
encryptionToggle.checked = false;
|
encryptionToggle.checked = false;
|
||||||
}
|
}
|
||||||
buttons: MessageDialog.Ok | MessageDialog.Cancel
|
buttons: Platform.MessageDialog.Ok | Platform.MessageDialog.Cancel
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
|
785
src/Cache.cpp
785
src/Cache.cpp
File diff suppressed because it is too large
Load diff
169
src/Cache_p.h
169
src/Cache_p.h
|
@ -9,6 +9,12 @@
|
||||||
#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>
|
||||||
|
@ -23,20 +29,12 @@ 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);
|
||||||
|
@ -99,11 +97,19 @@ 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
|
||||||
|
@ -298,6 +304,20 @@ 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);
|
||||||
|
@ -381,44 +401,107 @@ 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);
|
lmdb::dbi getUserKeysDb(lmdb::txn &txn) { return lmdb::dbi::open(txn, "user_key", MDB_CREATE); }
|
||||||
|
|
||||||
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);
|
||||||
|
@ -426,6 +509,27 @@ 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_;
|
||||||
|
|
||||||
|
@ -434,8 +538,6 @@ private:
|
||||||
VerificationStorage verification_storage;
|
VerificationStorage verification_storage;
|
||||||
|
|
||||||
bool databaseReady_ = false;
|
bool databaseReady_ = false;
|
||||||
|
|
||||||
std::unique_ptr<CacheDb> db;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace cache {
|
namespace cache {
|
||||||
|
@ -444,12 +546,11 @@ 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<Content>( \
|
extern template std::optional<mtx::events::StateEvent<Content>> Cache::getStateEvent( \
|
||||||
const std::string &room_id, std::string_view state_key); \
|
lmdb::txn &txn, const std::string &room_id, std::string_view state_key); \
|
||||||
\
|
\
|
||||||
extern template std::vector<mtx::events::StateEvent<Content>> \
|
extern template std::vector<mtx::events::StateEvent<Content>> Cache::getStateEventsWithType( \
|
||||||
Cache::getStateEventsWithType<Content>(const std::string &room_id, \
|
lmdb::txn &txn, const std::string &room_id, mtx::events::EventType type);
|
||||||
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,9 +13,6 @@
|
||||||
|
|
||||||
#include <mtx/responses.hpp>
|
#include <mtx/responses.hpp>
|
||||||
|
|
||||||
#include "Logging.h"
|
|
||||||
#include "UserSettingsPage.h"
|
|
||||||
|
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
mtx::http::Client *
|
mtx::http::Client *
|
||||||
|
@ -23,15 +20,9 @@ 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) +
|
||||||
// Disabled by default until CPU usage and reliability improves
|
"/curl_alt_svc_cache.txt")
|
||||||
if (UserSettings::instance()->qsettings()->value("enable_http3").toBool()) {
|
.toStdString());
|
||||||
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,17 +32,16 @@ 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 handleFile = [](const QFileInfo &fileInfo) {
|
auto files = dir.entryInfoList();
|
||||||
|
for (const auto &fileInfo : std::as_const(files)) {
|
||||||
if (fileInfo.fileTime(QFile::FileTime::FileAccessTime)
|
if (fileInfo.fileTime(QFile::FileTime::FileAccessTime)
|
||||||
.daysTo(QDateTime::currentDateTime()) > 14) {
|
.daysTo(QDateTime::currentDateTime()) > 30) {
|
||||||
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());
|
||||||
|
@ -50,24 +49,6 @@ 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,7 +11,6 @@
|
||||||
#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()
|
||||||
|
@ -115,30 +114,12 @@ 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) {
|
||||||
|
@ -150,6 +131,13 @@ 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"
|
||||||
|
|
14
src/emoji/MacHelper.h
Normal file
14
src/emoji/MacHelper.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// SPDX-FileCopyrightText: Nheko Contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QMenuBar>
|
||||||
|
|
||||||
|
class MacHelper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void showEmojiWindow();
|
||||||
|
static void initializeMenus();
|
||||||
|
};
|
26
src/emoji/MacHelper.mm
Normal file
26
src/emoji/MacHelper.mm
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#include "MacHelper.h"
|
||||||
|
|
||||||
|
#include <Cocoa/Cocoa.h>
|
||||||
|
#include <Foundation/Foundation.h>
|
||||||
|
#include <Foundation/NSString.h>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
void
|
||||||
|
MacHelper::showEmojiWindow()
|
||||||
|
{
|
||||||
|
NSApplication *theNSApplication = [NSApplication sharedApplication];
|
||||||
|
[theNSApplication orderFrontCharacterPalette:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacHelper::initializeMenus()
|
||||||
|
{
|
||||||
|
NSApplication *theNSApplication = [NSApplication sharedApplication];
|
||||||
|
|
||||||
|
NSArray<NSMenuItem *> *menus = [theNSApplication mainMenu].itemArray;
|
||||||
|
NSUInteger size = menus.count;
|
||||||
|
for (NSUInteger i = 0; i < size; i++) {
|
||||||
|
NSMenuItem *item = [menus objectAtIndex:i];
|
||||||
|
[item setTitle:@"Edit"];
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,6 +37,7 @@
|
||||||
#include "config/nheko.h"
|
#include "config/nheko.h"
|
||||||
|
|
||||||
#if defined(Q_OS_MACOS)
|
#if defined(Q_OS_MACOS)
|
||||||
|
#include "emoji/MacHelper.h"
|
||||||
#include "notifications/Manager.h"
|
#include "notifications/Manager.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -465,6 +466,10 @@ main(int argc, char *argv[])
|
||||||
QStringLiteral("matrix"), ChatPage::instance(), "handleMatrixUri");
|
QStringLiteral("matrix"), ChatPage::instance(), "handleMatrixUri");
|
||||||
|
|
||||||
#if defined(Q_OS_MACOS)
|
#if defined(Q_OS_MACOS)
|
||||||
|
// Temporary solution for the emoji picker until
|
||||||
|
// nheko has a proper menu bar with more functionality.
|
||||||
|
MacHelper::initializeMenus();
|
||||||
|
|
||||||
// Need to set up notification delegate so users can respond to messages from within the
|
// Need to set up notification delegate so users can respond to messages from within the
|
||||||
// notification itself.
|
// notification itself.
|
||||||
NotificationsManager::attachToMacNotifCenter();
|
NotificationsManager::attachToMacNotifCenter();
|
||||||
|
|
|
@ -20,8 +20,6 @@
|
||||||
#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>
|
||||||
|
@ -580,7 +578,8 @@ InputBar::message(const QString &msg, MarkdownOverride useMarkdown, bool rainbow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
text.body = fmt::format("{}\n{}", body.toStdString(), text.body);
|
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,14 +37,8 @@ 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{
|
||||||
auto statusMsg = QString::fromStdString(p.status_msg);
|
utils::replaceEmoji(QString::fromStdString(p.status_msg).toHtmlEscaped()), p.presence};
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2073,12 +2073,10 @@ 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(QStringLiteral("%1/media_cache/%2.%3")
|
QFileInfo filename(
|
||||||
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation),
|
QStringLiteral("%1/media_cache/%2.%3")
|
||||||
QString::fromUtf8(name.toUtf8().toBase64(
|
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation), name, suffix));
|
||||||
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)),
|
if (QDir::cleanPath(name) != name) {
|
||||||
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,12 +53,10 @@ 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(QStringLiteral("%1/media_cache/media/%2.%3")
|
QFileInfo filename(
|
||||||
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation),
|
QStringLiteral("%1/media_cache/media/%2.%3")
|
||||||
QString::fromUtf8(name.toUtf8().toBase64(
|
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation), name, suffix));
|
||||||
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)),
|
if (QDir::cleanPath(name) != name) {
|
||||||
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,12 +96,10 @@ 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(QStringLiteral("%1/media_cache/media/%2.%3")
|
QFileInfo filename(
|
||||||
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation),
|
QStringLiteral("%1/media_cache/media/%2.%3")
|
||||||
QString::fromUtf8(name.toUtf8().toBase64(
|
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation), name, suffix));
|
||||||
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)),
|
if (QDir::cleanPath(name) != name) {
|
||||||
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