diff --git a/.ci/macos/build.sh b/.ci/macos/build.sh index 34dee564..ea6da16d 100755 --- a/.ci/macos/build.sh +++ b/.ci/macos/build.sh @@ -20,7 +20,7 @@ cmake -GNinja -S. -Bbuild \ -DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF \ -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHUNTER_CONFIGURATION_TYPES=RelWithDebInfo \ -DUSE_BUNDLED_OPENSSL=ON \ - -DUSE_BUNDLED_KDSINGLEAPPLICATION=ON \ + -DUSE_BUNDLED_KDSINGLEAPPLICATION=ON -DKDSingleApplication_STATIC=ON \ -DQt6_DIR=${QT_BASEPATH}/lib/cmake \ -DCI_BUILD=ON cmake --build build diff --git a/.ci/windows/build.bat b/.ci/windows/build.bat index a79e2360..d74a2ea6 100644 --- a/.ci/windows/build.bat +++ b/.ci/windows/build.bat @@ -15,7 +15,7 @@ echo %DATE% call "C:/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/VC/Auxiliary/Build/vcvarsall.bat" x64 -cmake -G "Visual Studio 17 2022" -A x64 -S. -Bbuild -DHUNTER_ROOT="C:\hunter" -DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF -DUSE_BUNDLED_OPENSSL=ON -DUSE_BUNDLED_KDSINGLEAPPLICATION=ON -DCMAKE_BUILD_TYPE=Release -DHUNTER_CONFIGURATION_TYPES=Release +cmake -G "Visual Studio 17 2022" -A x64 -S. -Bbuild -DHUNTER_ROOT="C:\hunter" -DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF -DUSE_BUNDLED_OPENSSL=ON -DUSE_BUNDLED_KDSINGLEAPPLICATION=ON -DKDSingleApplication_STATIC=ON -DCMAKE_BUILD_TYPE=Release -DHUNTER_CONFIGURATION_TYPES=Release cmake --build build --config Release diff --git a/.ci/windows/sign.bat b/.ci/windows/sign.bat new file mode 100644 index 00000000..18cd4931 --- /dev/null +++ b/.ci/windows/sign.bat @@ -0,0 +1,5 @@ +@echo off + +call "C:/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/VC/Auxiliary/Build/vcvarsall.bat" x64 + +@C:\smartcardtools\x64\scsigntool -pin %WINDOWS_SIGNING_KEY_PIN% sign /fd SHA256 /t http://timestamp.digicert.com /a /sha1 %WINDOWS_SIGNING_KEY_THUMBPRINT% nheko.msix >nul 2>&1 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c28efe23..19ce069f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,16 +8,21 @@ variables: CCACHE_DIR: "${CI_PROJECT_DIR}/.ccache" # prevent configure tzdata hanging apt install commands DEBIAN_FRONTEND: noninteractive + FF_SCRIPT_SECTIONS: 1 + FF_USE_FASTZIP: 1 build-windows: stage: build image: win10-base tags: [libvirt,powershell] + rules: + - if : '$CI_PIPELINE_TRIGGERED == null' variables: APPVEYOR_REPO_BRANCH: "${CI_COMMIT_REF_NAME}" APPVEYOR_REPO_COMMIT: "${CI_COMMIT_SHORT_SHA}" + CMAKE_BUILD_PARALLEL_LEVEL: 8 before_script: - - mkdir -p hunter + - mkdir -p hunter -f - Move-Item -Path hunter -Destination C:/hunter script: - ./.ci/windows/build.bat @@ -33,6 +38,28 @@ build-windows: paths: - nheko.msix - nheko_win_64.zip + name: nheko-${CI_COMMIT_SHORT_SHA}-windows-unsigned + expose_as: 'windows-app-unsigned' + +codesign-windows: + stage: sign + image: win10-base + tags: [libvirt,powershell] + resource_group: windows_signingA + environment: + name: windows_signing + #variables: + # GIT_STRATEGY: none + script: + - Get-ChildItem -Path Cert:CurrentUser\My + - ./.ci/windows/sign.bat + needs: + - job: build-windows + rules: + - if : '$CI_COMMIT_REF_PROTECTED == "true"' + artifacts: + paths: + - nheko.msix name: nheko-${CI_COMMIT_SHORT_SHA}-windows expose_as: 'windows-app' @@ -44,9 +71,7 @@ build-clazy: CLAZY_CHECKS: level0,level1,no-non-pod-global-static TRAVIS_OS_NAME: linux before_script: - - echo -e "\e[0Ksection_start:`date +%s`:install_deps[collapsed=true]\r\e[0K\e[1m\e[95mInstalling apk dependencies" - - apk add asciidoctor cmake cmark-dev gst-plugins-bad-dev gst-plugins-base-dev gstreamer-dev lmdb-dev lmdbxx nlohmann-json olm-dev openssl-dev qt6-qtbase-dev qt6-qtdeclarative-dev qt6-qtmultimedia-dev qt6-qtsvg-dev qt6-qttools-dev samurai spdlog-dev xcb-util-wm-dev zlib-dev ccache curl-dev libevent-dev meson clazy clang16 gcc musl-dev git re2-dev libsecret-dev clang16 - - echo -e "\e[0Ksection_end:`date +%s`:install_deps\r\e[0K" + - apk add asciidoctor cmake cmark-dev gst-plugins-bad-dev gst-plugins-base-dev gstreamer-dev lmdb-dev lmdbxx nlohmann-json olm-dev openssl-dev qt6-qtbase-dev qt6-qtdeclarative-dev qt6-qtmultimedia-dev qt6-qtsvg-dev qt6-qttools-dev samurai spdlog-dev xcb-util-wm-dev zlib-dev ccache curl-dev libevent-dev meson clazy clang16 gcc musl-dev git re2-dev libsecret-dev script: - export PATH="/usr/lib/ccache:${PATH}" - export CMAKE_BUILD_PARALLEL_LEVEL=$(cat /proc/cpuinfo | awk '/^processor/{print $3}' | wc -l) @@ -73,7 +98,6 @@ build-clazy: variables: TRAVIS_OS_NAME: linux before_script: - - echo -e "\e[0Ksection_start:`date +%s`:install_deps[collapsed=true]\r\e[0K\e[1m\e[95mInstalling apt dependencies" - apt-get update - 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 @@ -84,7 +108,6 @@ build-clazy: - apt-get -y install wget - /usr/sbin/update-ccache-symlinks - rm -rf ../.hunter && mv .hunter ../.hunter || true - - echo -e "\e[0Ksection_end:`date +%s`:install_deps\r\e[0K" script: - export PATH="/usr/lib/ccache:${PATH}" - cmake -GNinja -H. -Bbuild @@ -113,7 +136,6 @@ build-tw: variables: TRAVIS_OS_NAME: linux before_script: - - echo -e "\e[0Ksection_start:`date +%s`:install_deps[collapsed=true]\r\e[0K\e[1m\e[95mInstalling apt dependencies" - > zypper --non-interactive install "appstream-glib" @@ -152,7 +174,6 @@ build-tw: "pkgconfig(xcb)" "pkgconfig(xcb-ewmh)" "time" - - echo -e "\e[0Ksection_end:`date +%s`:install_deps\r\e[0K" script: - export PATH="/usr/lib64/ccache:${PATH}" - cmake -GNinja -H. -Bbuild @@ -235,9 +256,7 @@ build-flatpak: - ARCH: arm64 JOBS: 3 before_script: - - echo -e "\e[0Ksection_start:`date +%s`:install_deps[collapsed=true]\r\e[0K\e[1m\e[95mInstalling apt dependencies" - apt-get update && apt-get -y install flatpak-builder git python3 curl python3-aiohttp python3-tenacity gir1.2-ostree-1.0 - - echo -e "\e[0Ksection_end:`date +%s`:install_deps\r\e[0K" - flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo # see https://github.com/flatpak/flatpak-builder/issues/495 - git config --global protocol.file.allow always @@ -245,15 +264,11 @@ build-flatpak: - export VERSION=$(git describe) - mkdir -p build-flatpak - cd build-flatpak - - echo -e "\e[0Ksection_start:`date +%s`:build_flatpak[collapsed=true]\r\e[0K\e[1m\e[95mBuilding flatpak" - flatpak-builder --install-deps-from=flathub --user --disable-rofiles-fuse --ccache --repo=repo --default-branch=${CI_COMMIT_REF_NAME//\//_} --subject="Build of Nheko ${VERSION} `date` for ${ARCH}" app ../im.nheko.Nheko.yaml --jobs=$JOBS - - echo -e "\e[0Ksection_end:`date +%s`:build_flatpak\r\e[0K" - flatpak build-bundle repo nheko-${ARCH}.flatpak im.nheko.Nheko ${CI_COMMIT_REF_NAME//\//_} after_script: - - echo -e "\e[0Ksection_start:`date +%s`:upload_flatpak[collapsed=true]\r\e[0K\e[1m\e[95mUploading flatpak" - bash ./.ci/upload-nightly-gitlab.sh build-flatpak/nheko-${ARCH}.flatpak - (cd ./scripts && ./upload-to-flatpak-repo.sh ../build-flatpak/repo) || exit_code=$? - - echo -e "\e[0Ksection_end:`date +%s`:upload_flatpak\r\e[0K" rules: - if : '$CI_PIPELINE_TRIGGERED == null' cache: @@ -273,7 +288,6 @@ build-flatpak: tags: [docker] allow_failure: true before_script: - - echo -e "\e[0Ksection_start:`date +%s`:install_deps[collapsed=true]\r\e[0K\e[1m\e[95mInstalling apt dependencies" # Installing the packages needed to download and install third-party tools - apt-get update && apt-get install -y software-properties-common git wget curl python3 python3-pip python3-setuptools @@ -305,7 +319,6 @@ build-flatpak: - /usr/sbin/update-ccache-symlinks - rm -rf ../.hunter && mv .hunter ../.hunter || true - - echo -e "\e[0Ksection_end:`date +%s`:install_deps\r\e[0K" script: - export PATH="/usr/local/bin/:/usr/lib/ccache:${PATH}" diff --git a/CMakeLists.txt b/CMakeLists.txt index 525a3592..af50aff8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -647,14 +647,14 @@ if(APPLE) endif() elseif(WIN32) file(DOWNLOAD - "https://raw.githubusercontent.com/mohabouje/WinToast/41ed1c58d5dce0ee9c01dbdeac05be45358d4f57/src/wintoastlib.cpp" + "https://raw.githubusercontent.com/mohabouje/WinToast/v1.3.0/src/wintoastlib.cpp" ${PROJECT_SOURCE_DIR}/src/wintoastlib.cpp - EXPECTED_HASH SHA256=1A1A7CE41C1052B12946798F4A6C67CE1FAD209C967F5ED4D720B173527E2073) + EXPECTED_HASH SHA256=1ddf8ee2604e1757d383a2f60d4d597c4f5d9496369e6a22586cedf4af77ca8f) file(DOWNLOAD - "https://raw.githubusercontent.com/mohabouje/WinToast/41ed1c58d5dce0ee9c01dbdeac05be45358d4f57/src/wintoastlib.h" + "https://raw.githubusercontent.com/mohabouje/WinToast/v1.3.0/include/wintoastlib.h" ${PROJECT_SOURCE_DIR}/src/wintoastlib.h - EXPECTED_HASH SHA256=b4481023c5782733795838be22bf1a75f45d87458cd4d9a5a75f664a146eea11) + EXPECTED_HASH SHA256=08cb30d28db9c851d3d4259ae6108ce7dafa966957df6735a06cd55e0716fded) set(SRC_FILES ${SRC_FILES} src/notifications/ManagerWin.cpp src/wintoastlib.cpp src/wintoastlib.h) else() diff --git a/resources/AppxManifest.xml b/resources/AppxManifest.xml index 57c7188e..417d0229 100644 --- a/resources/AppxManifest.xml +++ b/resources/AppxManifest.xml @@ -4,7 +4,7 @@ xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"> - + Nheko Nheko-Reborn @@ -26,7 +26,7 @@ uap10:RuntimeBehavior="packagedClassicApp" uap10:TrustLevel="mediumIL"> + Square44x44Logo="nheko.png" BackgroundColor="transparent" /> diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index b7834cba..a4a0eff0 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -201,9 +201,14 @@ NhekoFixupPaletteEventFilter::eventFilter(QObject *obj, QEvent *event) // reason?!? if (event->type() == QEvent::ChildAdded && obj->metaObject()->className() == QStringLiteral("QQuickRootItem")) { + QSet newWindows; for (const auto window : QGuiApplication::topLevelWindows()) { + newWindows.insert(window); + if (m_postedWindows.contains(window)) + continue; QGuiApplication::postEvent(window, new QEvent(QEvent::ApplicationPaletteChange)); } + m_postedWindows.swap(newWindows); } return false; } diff --git a/src/MainWindow.h b/src/MainWindow.h index c493b5b2..928446aa 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -45,6 +45,9 @@ public: } bool eventFilter(QObject *obj, QEvent *event) override; + +private: + QSet m_postedWindows; }; class MainWindow : public QQuickView diff --git a/src/encryption/SelfVerificationStatus.cpp b/src/encryption/SelfVerificationStatus.cpp index 32e75c4f..b5549ca1 100644 --- a/src/encryption/SelfVerificationStatus.cpp +++ b/src/encryption/SelfVerificationStatus.cpp @@ -24,11 +24,12 @@ SelfVerificationStatus::SelfVerificationStatus(QObject *o) : QObject(o) { connect(ChatPage::instance(), &ChatPage::contentLoaded, this, [this] { + // We connect INSIDE a lambda, not A lambda... connect(cache::client(), &Cache::selfVerificationStatusChanged, this, &SelfVerificationStatus::invalidate, - Qt::UniqueConnection); + Qt::UniqueConnection); // clazy:exclude=lambda-unique-connection cache::client()->markUserKeysOutOfDate({http::client()->user_id().to_string()}); }); diff --git a/src/notifications/Manager.h b/src/notifications/Manager.h index fbebfcba..707a4fb3 100644 --- a/src/notifications/Manager.h +++ b/src/notifications/Manager.h @@ -93,8 +93,12 @@ public: #if defined(Q_OS_WINDOWS) private: - void - systemPostNotification(const QString &line1, const QString &line2, const QString &iconPath); + void systemPostNotification(const QString &roomid, + const QString &eventid, + const QString &line1, + const QString &line2, + const QString &iconPath, + const QString &bodyImagePath); #endif // these slots are platform specific (D-Bus only) diff --git a/src/notifications/ManagerLinux.cpp b/src/notifications/ManagerLinux.cpp index b181fdc3..11a7c1a1 100644 --- a/src/notifications/ManagerLinux.cpp +++ b/src/notifications/ManagerLinux.cpp @@ -114,7 +114,8 @@ NotificationsManager::postNotification(const mtx::responses::Notification ¬if if (hasMarkup_) { if (hasImages_ && - mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Image) { + (mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Image || + mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Image)) { MxcImageProvider::download( QString::fromStdString(mtx::accessors::url(notification.event)) .remove(QStringLiteral("mxc://")), diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp index 8d200abc..f1d3797b 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp @@ -13,6 +13,7 @@ #include "Cache.h" #include "EventAccessors.h" +#include "MxcImageProvider.h" #include "Utils.h" using namespace WinToastLib; @@ -20,10 +21,21 @@ using namespace WinToastLib; class CustomHandler : public IWinToastHandler { public: - void toastActivated() const {} + CustomHandler(NotificationsManager *manager_, const QString &roomid_, const QString &eventid_) + : manager(manager_) + , roomid(roomid_) + , eventid(eventid_) + { + } + + void toastActivated() const { manager->notificationClicked(roomid, eventid); } void toastActivated(int) const {} void toastFailed() const { std::wcout << L"Error showing current toast" << std::endl; } void toastDismissed(WinToastDismissalReason) const {} + + NotificationsManager *manager; + QString roomid; + QString eventid; }; namespace { @@ -34,9 +46,9 @@ init() { isInitialized = true; - WinToast::instance()->setAppName(L"Nheko"); WinToast::instance()->setAppUserModelId( WinToast::configureAUMI(L"NhekoReborn", L"in.nheko.Nheko")); + WinToast::instance()->setAppName(L"Nheko"); if (!WinToast::instance()->initialize()) std::wcout << "Your system is not compatible with toast notifications\n"; } @@ -52,6 +64,8 @@ NotificationsManager::postNotification(const mtx::responses::Notification ¬if const QImage &icon) { const auto room_name = QString::fromStdString(cache::singleRoomInfo(notification.room_id).name); + auto roomid = QString::fromStdString(notification.room_id); + auto eventid = QString::fromStdString(mtx::accessors::event_id(notification.event)); auto formatNotification = [this, notification] { const auto template_ = getMessageTemplate(notification); @@ -61,20 +75,40 @@ NotificationsManager::postNotification(const mtx::responses::Notification ¬if } return template_.arg(utils::stripReplyFallbacks(notification.event, {}, {}).quoted_body); - }; + }(); auto iconPath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + room_name + "-room-avatar.png"; if (!icon.save(iconPath)) iconPath.clear(); - systemPostNotification(room_name, formatNotification(), iconPath); + if (mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Image || + mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Image) { + MxcImageProvider::download( + QString::fromStdString(mtx::accessors::url(notification.event)) + .remove(QStringLiteral("mxc://")), + QSize(200, 80), + [this, roomid, eventid, room_name, formatNotification, iconPath]( + QString, QSize, QImage, QString imgPath) { + if (imgPath.isEmpty()) + systemPostNotification( + roomid, eventid, room_name, formatNotification, iconPath, ""); + else + systemPostNotification( + roomid, eventid, room_name, formatNotification, iconPath, imgPath); + }); + } else { + systemPostNotification(roomid, eventid, room_name, formatNotification, iconPath, ""); + } } void -NotificationsManager::systemPostNotification(const QString &line1, +NotificationsManager::systemPostNotification(const QString &roomid, + const QString &eventid, + const QString &line1, const QString &line2, - const QString &iconPath) + const QString &iconPath, + const QString &bodyImagePath) { if (!isInitialized) init(); @@ -85,8 +119,12 @@ NotificationsManager::systemPostNotification(const QString &line1, if (!iconPath.isNull()) templ.setImagePath(iconPath.toStdWString()); + if (!bodyImagePath.isNull()) + templ.setHeroImagePath(bodyImagePath.toStdWString(), true); - WinToast::instance()->showToast(templ, new CustomHandler()); + templ.setAudioPath(WinToastTemplate::IM); + + WinToast::instance()->showToast(templ, new CustomHandler(this, roomid, eventid)); } // clang-format off diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index cdaa02ec..da639843 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -271,11 +271,13 @@ RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification) { if (!models.contains(room_id)) { // ensure we get read status updates and are only connected once + // WORKAROUND(Nico): This is not a lambda, but clazy on alpine currently doesn't + // believe us... connect(cache::client(), &Cache::roomReadStatus, this, &RoomlistModel::updateReadStatus, - Qt::UniqueConnection); + Qt::UniqueConnection); // clazy:exclude=lambda-unique-connection QSharedPointer newRoom(new TimelineModel(manager, room_id)); newRoom->setDecryptDescription(ChatPage::instance()->userSettings()->decryptSidebar()); @@ -529,11 +531,13 @@ RoomlistModel::sync(const mtx::responses::Sync &sync_) addRoom(qroomid); const auto &room_model = models.value(qroomid); + // WORKAROUND(Nico): This is not a lambda, but clazy on alpine currently doesn't + // believe us connect(room_model.data(), &TimelineModel::newCallEvent, ChatPage::instance()->callManager(), &CallManager::syncEvent, - Qt::UniqueConnection); + Qt::UniqueConnection); // clazy:exclude=lambda-unique-connection room_model->sync(room);