Merge pull request #130 from Nheko-Reborn/0.7.0-dev
0.7.0 dev merge to master
|
@ -11,5 +11,7 @@ FILES=$(find src -type f -type f \( -iname "*.cpp" -o -iname "*.h" \))
|
|||
|
||||
for f in $FILES
|
||||
do
|
||||
clang-format -i "$f" && git diff --exit-code
|
||||
clang-format -i "$f"
|
||||
done;
|
||||
|
||||
git diff --exit-code
|
||||
|
|
|
@ -2,14 +2,13 @@
|
|||
|
||||
set -ex
|
||||
|
||||
if [ "$FLATPAK" ]; then
|
||||
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
flatpak --noninteractive install --user flathub org.kde.Platform//5.14
|
||||
flatpak --noninteractive install --user flathub org.kde.Sdk//5.14
|
||||
exit
|
||||
fi
|
||||
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
brew update
|
||||
brew install qt5 lmdb clang-format ninja libsodium cmark
|
||||
brew upgrade boost cmake icu4c || true
|
||||
|
||||
brew tap nlohmann/json
|
||||
brew install --with-cmake nlohmann_json
|
||||
|
||||
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
|
||||
sudo python get-pip.py
|
||||
|
||||
|
@ -21,34 +20,12 @@ fi
|
|||
|
||||
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc "/usr/bin/${CC}" 10
|
||||
sudo update-alternatives --install /usr/bin/g++ g++ "/usr/bin/${CXX}" 10
|
||||
|
||||
if [ -z "$QT_VERSION" ]; then
|
||||
QT_VERSION="592"
|
||||
QT_PKG="59"
|
||||
fi
|
||||
|
||||
wget https://cmake.org/files/v3.12/cmake-3.12.2-Linux-x86_64.sh
|
||||
sudo sh cmake-3.12.2-Linux-x86_64.sh --skip-license --prefix=/usr/local
|
||||
|
||||
mkdir -p build-libsodium
|
||||
( cd build-libsodium
|
||||
curl -L https://download.libsodium.org/libsodium/releases/libsodium-1.0.16.tar.gz -o libsodium-1.0.16.tar.gz
|
||||
tar xfz libsodium-1.0.16.tar.gz
|
||||
cd libsodium-1.0.16/
|
||||
./configure && make && make check && sudo make install )
|
||||
|
||||
sudo add-apt-repository -y ppa:beineri/opt-qt${QT_VERSION}-trusty
|
||||
# needed for git-lfs, otherwise the follow apt update fails.
|
||||
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 6B05F25D762E3157
|
||||
|
||||
# needed for mongodb repository: https://github.com/travis-ci/travis-ci/issues/9037
|
||||
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6
|
||||
|
||||
sudo apt update -qq
|
||||
sudo apt install -qq -y \
|
||||
qt${QT_PKG}base \
|
||||
qt${QT_PKG}tools \
|
||||
qt${QT_PKG}svg \
|
||||
qt${QT_PKG}multimedia \
|
||||
liblmdb-dev
|
||||
sudo update-alternatives --set gcc "/usr/bin/${CC}"
|
||||
sudo update-alternatives --set g++ "/usr/bin/${CXX}"
|
||||
|
||||
wget https://cmake.org/files/v3.15/cmake-3.15.5-Linux-x86_64.sh
|
||||
sudo sh cmake-3.15.5-Linux-x86_64.sh --skip-license --prefix=/usr/local
|
||||
fi
|
||||
|
|
|
@ -25,8 +25,8 @@ for iconSize in 16 32 48 64 128 256 512; do
|
|||
done
|
||||
|
||||
# Only download the file when not already present
|
||||
if ! [ -f linuxdeployqt-continuous-x86_64.AppImage ] ; then
|
||||
wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
|
||||
if ! [ -f linuxdeployqt-6-x86_64.AppImage ] ; then
|
||||
wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/6/linuxdeployqt-6-x86_64.AppImage"
|
||||
fi
|
||||
chmod a+x linuxdeployqt*.AppImage
|
||||
|
||||
|
@ -36,7 +36,7 @@ unset LD_LIBRARY_PATH
|
|||
|
||||
ARCH=$(uname -m)
|
||||
export ARCH
|
||||
LD_LIBRARY_PATH=$(pwd)/.deps/usr/lib/:$LD_LIBRARY_PATH
|
||||
LD_LIBRARY_PATH=$(pwd)/.deps/usr/lib/:/usr/local/lib/:$LD_LIBRARY_PATH
|
||||
export LD_LIBRARY_PATH
|
||||
|
||||
for res in ./linuxdeployqt*.AppImage
|
||||
|
@ -44,12 +44,14 @@ do
|
|||
linuxdeployqt=$res
|
||||
done
|
||||
|
||||
./"$linuxdeployqt" ${DIR}/usr/share/applications/*.desktop -unsupported-allow-new-glibc -bundle-non-qt-libs
|
||||
./"$linuxdeployqt" ${DIR}/usr/share/applications/*.desktop -unsupported-allow-new-glibc -appimage
|
||||
./"$linuxdeployqt" ${DIR}/usr/share/applications/*.desktop -unsupported-allow-new-glibc -bundle-non-qt-libs -qmldir=./resources/qml -appimage
|
||||
|
||||
chmod +x nheko-*x86_64.AppImage
|
||||
|
||||
if [ ! -z "$VERSION" ]; then
|
||||
mkdir artifacts
|
||||
cp nheko-*x86_64.AppImage artifacts/
|
||||
|
||||
if [ -n "$VERSION" ]; then
|
||||
# commented out for now, as AppImage file appears to already contain the version.
|
||||
#mv nheko-*x86_64.AppImage nheko-${VERSION}-x86_64.AppImage
|
||||
echo "nheko-${VERSION}-x86_64.AppImage"
|
||||
|
|
|
@ -16,7 +16,7 @@ PATH=/usr/local/opt/qt/bin/:${PATH}
|
|||
mkdir -p nheko.app/Contents/Frameworks
|
||||
find "${ICU_LIB}" -type l -name "*.dylib" -exec cp -a -n {} nheko.app/Contents/Frameworks/ \; || true
|
||||
|
||||
sudo macdeployqt nheko.app -dmg -always-overwrite
|
||||
sudo macdeployqt nheko.app -dmg -always-overwrite -qmldir=../resources/qml/
|
||||
|
||||
user=$(id -nu)
|
||||
sudo chown "${user}" nheko.dmg
|
||||
|
@ -25,6 +25,8 @@ PATH=/usr/local/opt/qt/bin/:${PATH}
|
|||
|
||||
dmgbuild -s ./.ci/macos/settings.json "Nheko" nheko.dmg
|
||||
|
||||
if [ ! -z "$VERSION" ]; then
|
||||
if [ -n "$VERSION" ]; then
|
||||
mv nheko.dmg "nheko-${VERSION}.dmg"
|
||||
mkdir artifacts
|
||||
cp "nheko-${VERSION}.dmg" artifacts/
|
||||
fi
|
||||
|
|
|
@ -2,17 +2,25 @@
|
|||
|
||||
set -ex
|
||||
|
||||
if [ "$FLATPAK" ]; then
|
||||
mkdir -p build-flatpak
|
||||
cd build-flatpak
|
||||
|
||||
flatpak-builder --ccache --repo=repo --subject="Build of Nheko ${VERSION} `date`" app ../io.github.NhekoReborn.Nheko.json
|
||||
flatpak build-bundle repo nheko-${VERSION}-${ARCH}.flatpak io.github.NhekoReborn.Nheko 0.7.0-dev
|
||||
|
||||
mkdir ../artifacts
|
||||
mv nheko-*.flatpak ../artifacts
|
||||
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
export CC=${C_COMPILER}
|
||||
export CXX=${CXX_COMPILER}
|
||||
# make build use all available cores
|
||||
export CMAKE_BUILD_PARALLEL_LEVEL=$(cat /proc/cpuinfo | awk '/^processor/{print $3}' | wc -l)
|
||||
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc "/usr/bin/${C_COMPILER}" 10
|
||||
sudo update-alternatives --install /usr/bin/g++ g++ "/usr/bin/${CXX_COMPILER}" 10
|
||||
|
||||
sudo update-alternatives --set gcc "/usr/bin/${C_COMPILER}"
|
||||
sudo update-alternatives --set g++ "/usr/bin/${CXX_COMPILER}"
|
||||
export PATH="/usr/local/bin/:${PATH}"
|
||||
cmake --version
|
||||
fi
|
||||
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
|
@ -24,27 +32,39 @@ if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
|||
export CMAKE_PREFIX_PATH=/usr/local/opt/qt5
|
||||
fi
|
||||
|
||||
# Build & install dependencies
|
||||
cmake -GNinja -Hdeps -B.deps \
|
||||
-DUSE_BUNDLED_BOOST="${USE_BUNDLED_BOOST}" \
|
||||
-DUSE_BUNDLED_CMARK="${USE_BUNDLED_CMARK}" \
|
||||
-DUSE_BUNDLED_JSON="${USE_BUNDLED_JSON}"
|
||||
cmake --build .deps
|
||||
mkdir -p .deps/usr .hunter
|
||||
|
||||
# Build nheko
|
||||
|
||||
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
cmake -GNinja -H. -Bbuild \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_INSTALL_PREFIX=.deps/usr
|
||||
-DCMAKE_INSTALL_PREFIX=.deps/usr \
|
||||
-DHUNTER_ROOT=".hunter" \
|
||||
-DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DHUNTER_CONFIGURATION_TYPES=RelWithDebInfo \
|
||||
-DCMAKE_PREFIX_PATH=/usr/local/opt/qt5 \
|
||||
-DCI_BUILD=ON
|
||||
else
|
||||
cmake -GNinja -H. -Bbuild \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_INSTALL_PREFIX=.deps/usr \
|
||||
-DHUNTER_ROOT=".hunter" \
|
||||
-DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DHUNTER_CONFIGURATION_TYPES=RelWithDebInfo \
|
||||
-DUSE_BUNDLED_OPENSSL=OFF \
|
||||
-DCI_BUILD=ON
|
||||
fi
|
||||
cmake --build build
|
||||
|
||||
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
make lint;
|
||||
|
||||
if [ "$DEPLOYMENT" = 1 ] && [ ! -z "$VERSION" ] ; then
|
||||
if [ "$DEPLOYMENT" = 1 ] && [ -n "$VERSION" ] ; then
|
||||
make macos-deploy;
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$DEPLOYMENT" = 1 ] && [ ! -z "$VERSION" ]; then
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$DEPLOYMENT" = 1 ] && [ -n "$VERSION" ]; then
|
||||
make linux-deploy;
|
||||
fi
|
||||
|
|
4
.github/CONTRIBUTING.md
vendored
|
@ -20,9 +20,9 @@ If you're planning to work on a new feature leave a message on the Matrix room
|
|||
|
||||
Example for a Japanese translation.
|
||||
- Create a new translation file using the prototype in English
|
||||
- e.g `cp resources/langs/nheko_en.ts resources/langs/nheko_jp.ts`
|
||||
- e.g `cp resources/langs/nheko_en.ts resources/langs/nheko_ja.ts`
|
||||
- Open the new translation file and change the line regarding the locale to reflect the current language.
|
||||
- e.g `<TS version="2.1" language="en">` => `<TS version="2.1" language="jp">`
|
||||
- e.g `<TS version="2.1" language="en">` => `<TS version="2.1" language="ja">`
|
||||
- Run `make update-translations` to update the translation files with any missing text.
|
||||
- Fill out the translation file (Qt Linguist can make things easier).
|
||||
- Submit a PR!
|
||||
|
|
16
.gitignore
vendored
|
@ -1,7 +1,16 @@
|
|||
build
|
||||
/build*
|
||||
tags
|
||||
cscope*
|
||||
.clang_complete
|
||||
*wintoastlib*
|
||||
/.ccls-cache
|
||||
/.exrc
|
||||
.gdb_history
|
||||
|
||||
# GTAGS
|
||||
GTAGS
|
||||
GRTAGS
|
||||
GPATH
|
||||
|
||||
# C++ objects and libs
|
||||
|
||||
|
@ -31,6 +40,7 @@ moc_*.cpp
|
|||
qrc_*.cpp
|
||||
ui_*.h
|
||||
*-build-*
|
||||
/.clangd/
|
||||
|
||||
# QtCreator
|
||||
|
||||
|
@ -43,6 +53,10 @@ ui_*.h
|
|||
*.qmlproject.user
|
||||
*.qmlproject.user.*
|
||||
|
||||
# Vim
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
#####=== CMake ===#####
|
||||
|
||||
CMakeCache.txt
|
||||
|
|
162
.travis.yml
|
@ -1,73 +1,138 @@
|
|||
language: cpp
|
||||
sudo: required
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://scalar.vector.im/api/neb/services/hooks/dHJhdmlzLWNpLyU0MHJlZF9za3klM0FvY2Vhbi5qb2Vkb25vZnJ5LmNvbS8lMjFldkFxa1BIWnVQSElHZWVuaGklM0FvY2Vhbi5qb2Vkb25vZnJ5LmNvbQ
|
||||
- https://scalar.vector.im/api/neb/services/hooks/dHJhdmlzLWNpLyU0MHJlZF9za3klM0FuaGVrby5pbS8lMjFVYkNtSWxHVEhOSWdJUlpjcHQlM0FuaGVrby5pbQ
|
||||
on_success: always
|
||||
on_failure: always
|
||||
on_start: never
|
||||
email: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- .hunter
|
||||
- build-flatpak/.flatpak-builder
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: osx
|
||||
compiler: clang
|
||||
osx_image: xcode9
|
||||
# C++17 support
|
||||
osx_image: xcode10.2
|
||||
env:
|
||||
- DEPLOYMENT=1
|
||||
- USE_BUNDLED_BOOST=0
|
||||
- USE_BUNDLED_CMARK=0
|
||||
- USE_BUNDLED_JSON=0
|
||||
addons:
|
||||
homebrew:
|
||||
taps: nlohmann/json
|
||||
packages:
|
||||
- clang-format
|
||||
- cmake
|
||||
- ninja
|
||||
- openssl
|
||||
- qt5
|
||||
update: true # workaround for broken travis homebrew
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
compiler: gcc-7
|
||||
env:
|
||||
- CXX_COMPILER=g++-5
|
||||
- C_COMPILER=gcc-5
|
||||
- QT_VERSION="-5.10.1"
|
||||
- QT_PKG=510
|
||||
- CXX=g++-7
|
||||
- CC=gcc-7
|
||||
- QT_PKG=512
|
||||
- DEPLOYMENT=1
|
||||
- USE_BUNDLED_BOOST=1
|
||||
- USE_BUNDLED_CMARK=1
|
||||
- USE_BUNDLED_JSON=1
|
||||
addons:
|
||||
apt:
|
||||
sources: ["ubuntu-toolchain-r-test"]
|
||||
packages: ["g++-5", "ninja-build"]
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:beineri/opt-qt-5.12.6-xenial'
|
||||
packages:
|
||||
- g++-7
|
||||
- ninja-build
|
||||
- qt512base
|
||||
- qt512tools
|
||||
- qt512svg
|
||||
- qt512multimedia
|
||||
- qt512quickcontrols2
|
||||
- qt512graphicaleffects
|
||||
- liblmdb-dev
|
||||
- libgl1-mesa-dev # needed for missing gl.h
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
compiler: gcc-8
|
||||
env:
|
||||
- CXX_COMPILER=g++-8
|
||||
- C_COMPILER=gcc-8
|
||||
- QT_VERSION=571
|
||||
- QT_PKG=57
|
||||
- USE_BUNDLED_BOOST=1
|
||||
- USE_BUNDLED_CMARK=1
|
||||
- USE_BUNDLED_JSON=1
|
||||
addons:
|
||||
apt:
|
||||
sources: ["ubuntu-toolchain-r-test"]
|
||||
packages: ["g++-8", "ninja-build"]
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env:
|
||||
- CXX_COMPILER=clang++-5.0
|
||||
- C_COMPILER=clang-5.0
|
||||
- QT_VERSION=592
|
||||
- CXX=g++-8
|
||||
- CC=gcc-8
|
||||
- QT_PKG=59
|
||||
- USE_BUNDLED_BOOST=1
|
||||
- USE_BUNDLED_CMARK=1
|
||||
- USE_BUNDLED_JSON=1
|
||||
addons:
|
||||
apt:
|
||||
sources: ["ubuntu-toolchain-r-test", "llvm-toolchain-trusty-5.0"]
|
||||
packages: ["clang-5.0", "g++-7", "ninja-build"]
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:beineri/opt-qt597-xenial'
|
||||
packages:
|
||||
- g++-8
|
||||
- ninja-build
|
||||
- qt59base
|
||||
- qt59tools
|
||||
- qt59svg
|
||||
- qt59multimedia
|
||||
- qt59quickcontrols2
|
||||
- qt59graphicaleffects
|
||||
- liblmdb-dev
|
||||
- libgl1-mesa-dev # needed for missing gl.h
|
||||
- os: linux
|
||||
compiler: clang-6
|
||||
env:
|
||||
- CXX=clang++-6.0
|
||||
- CC=clang-6.0
|
||||
- QT_PKG=59
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-xenial-6.0
|
||||
- sourceline: 'ppa:beineri/opt-qt597-xenial'
|
||||
packages:
|
||||
- clang++-6.0
|
||||
- g++-7
|
||||
- ninja-build
|
||||
- qt59base
|
||||
- qt59tools
|
||||
- qt59svg
|
||||
- qt59multimedia
|
||||
- qt59quickcontrols2
|
||||
- qt59graphicaleffects
|
||||
- liblmdb-dev
|
||||
- libgl1-mesa-dev # needed for missing gl.h
|
||||
- os: linux
|
||||
env:
|
||||
- DEPLOYMENT=1
|
||||
- FLATPAK=1
|
||||
- ARCH=amd64
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:alexlarsson/flatpak'
|
||||
packages:
|
||||
- flatpak
|
||||
- flatpak-builder
|
||||
- elfutils
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- DEPLOYMENT=1
|
||||
- FLATPAK=1
|
||||
- ARCH=arm64
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:alexlarsson/flatpak'
|
||||
packages:
|
||||
- flatpak
|
||||
- flatpak-builder
|
||||
- elfutils
|
||||
- librsvg2-bin
|
||||
|
||||
before_install:
|
||||
- export CXX=${CXX_COMPILER}
|
||||
- export CC=${C_COMPILER}
|
||||
# Use TRAVIS_TAG if defined, or the short commit SHA otherwise
|
||||
- export VERSION=${TRAVIS_TAG:-$(git rev-parse --short HEAD)}
|
||||
install:
|
||||
|
@ -79,6 +144,21 @@ script:
|
|||
- sed -i -e "s/VERSION_NAME_VALUE/${VERSION}/g" ./.ci/bintray-release.json || true
|
||||
- cp ./.ci/bintray-release.json .
|
||||
deploy:
|
||||
- provider: s3
|
||||
access_key_id: $ARTIFACTS_KEY
|
||||
secret_access_key: $ARTIFACTS_SECRET
|
||||
bucket: $ARTIFACTS_BUCKET
|
||||
region: $AWS_DEFAULT_REGION
|
||||
detect_encoding: true
|
||||
cache_control: "max-age=31536000"
|
||||
skip_cleanup: true
|
||||
acl: public_read
|
||||
local_dir: artifacts
|
||||
on:
|
||||
condition: "$DEPLOYMENT == 1"
|
||||
repo: Nheko-Reborn/nheko
|
||||
tags: false
|
||||
all_branches: true
|
||||
- provider: bintray
|
||||
user: "redsky17"
|
||||
key:
|
||||
|
|
19
CHANGELOG.md
|
@ -2,6 +2,25 @@
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### [0.7.0] -- Unreleased
|
||||
|
||||
0.7.0 *requires* mtxclient 0.3.0. Make sure you compile against 0.3.0
|
||||
if you do not use the mtxclient bundled with nheko.
|
||||
|
||||
#### Features
|
||||
- Make nheko session import / export format match riot. Fixes #48 (WIP)
|
||||
- Implement proper replies (WIP)
|
||||
- Add .well-known support for auto-completing homeserver information
|
||||
- Add mentions viewer so you can see all the messages you have been mentioned in (WIP)
|
||||
- Add emoji font selection preference
|
||||
|
||||
#### Improvements
|
||||
- Add dedicated reply button to Timeline items. Add button for other options so
|
||||
that right click isn't always required.
|
||||
- Fix various things with regards to emoji rendering and the emoji picker (WIP)
|
||||
- Lots and lots and lots of localization updates.
|
||||
- Additional tweaks to the system theme
|
||||
|
||||
## [0.6.4] - 2019-05-22
|
||||
|
||||
*Most* of the below fixes are due to updates in mtxclient. Make sure you compile against 0.2.1
|
||||
|
|
348
CMakeLists.txt
|
@ -1,22 +1,86 @@
|
|||
cmake_minimum_required(VERSION 3.11)
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
option(APPVEYOR_BUILD "Build on appveyor" OFF)
|
||||
option(CI_BUILD "Set when building in CI. Enables -Werror where possible" OFF)
|
||||
option(ASAN "Compile with address sanitizers" OFF)
|
||||
option(QML_DEBUGGING "Enable qml debugging" OFF)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
set(
|
||||
CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/toolchain.cmake"
|
||||
CACHE
|
||||
FILEPATH "Default toolchain"
|
||||
)
|
||||
|
||||
add_definitions(-DBOOST_MPL_LIMIT_LIST_SIZE=30)
|
||||
add_definitions(-DBOOST_MPL_CFG_NO_PREPROCESSED_HEADERS)
|
||||
|
||||
option(HUNTER_ENABLED "Enable Hunter package manager" OFF)
|
||||
include("cmake/HunterGate.cmake")
|
||||
HunterGate(
|
||||
URL "https://github.com/cpp-pm/hunter/archive/v0.23.244.tar.gz"
|
||||
SHA1 "2c0f491fd0b80f7b09e3d21adb97237161ef9835"
|
||||
LOCAL
|
||||
)
|
||||
|
||||
option(USE_BUNDLED_BOOST "Use the bundled version of Boost." ${HUNTER_ENABLED})
|
||||
option(USE_BUNDLED_SPDLOG "Use the bundled version of spdlog."
|
||||
${HUNTER_ENABLED})
|
||||
option(USE_BUNDLED_OLM "Use the bundled version of libolm." ${HUNTER_ENABLED})
|
||||
option(USE_BUNDLED_GTEST "Use the bundled version of Google Test."
|
||||
${HUNTER_ENABLED})
|
||||
option(USE_BUNDLED_CMARK "Use the bundled version of cmark."
|
||||
${HUNTER_ENABLED})
|
||||
option(USE_BUNDLED_JSON "Use the bundled version of nlohmann json."
|
||||
${HUNTER_ENABLED})
|
||||
option(USE_BUNDLED_OPENSSL "Use the bundled version of OpenSSL."
|
||||
${HUNTER_ENABLED})
|
||||
option(USE_BUNDLED_MTXCLIENT "Use the bundled version of the Matrix Client library." ${HUNTER_ENABLED})
|
||||
option(USE_BUNDLED_SODIUM "Use the bundled version of libsodium."
|
||||
${HUNTER_ENABLED})
|
||||
option(USE_BUNDLED_ZLIB "Use the bundled version of zlib."
|
||||
${HUNTER_ENABLED})
|
||||
option(USE_BUNDLED_LMDB "Use the bundled version of lmdb."
|
||||
${HUNTER_ENABLED})
|
||||
option(USE_BUNDLED_LMDBXX "Use the bundled version of lmdb++."
|
||||
${HUNTER_ENABLED})
|
||||
option(USE_BUNDLED_TWEENY "Use the bundled version of tweeny."
|
||||
${HUNTER_ENABLED})
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.14.0")
|
||||
message("Adding FetchContent_MakeAvailable")
|
||||
# from cmakes sources
|
||||
macro(FetchContent_MakeAvailable)
|
||||
|
||||
foreach(contentName IN ITEMS ${ARGV})
|
||||
string(TOLOWER ${contentName} contentNameLower)
|
||||
FetchContent_GetProperties(${contentName})
|
||||
if(NOT ${contentNameLower}_POPULATED)
|
||||
FetchContent_Populate(${contentName})
|
||||
|
||||
# Only try to call add_subdirectory() if the populated content
|
||||
# can be treated that way. Protecting the call with the check
|
||||
# allows this function to be used for projects that just want
|
||||
# to ensure the content exists, such as to provide content at
|
||||
# a known location.
|
||||
if(EXISTS ${${contentNameLower}_SOURCE_DIR}/CMakeLists.txt)
|
||||
add_subdirectory(${${contentNameLower}_SOURCE_DIR}
|
||||
${${contentNameLower}_BINARY_DIR})
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
endmacro()
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Include Qt basic functions
|
||||
include(QtCommon)
|
||||
|
||||
project(nheko LANGUAGES C CXX)
|
||||
project(nheko LANGUAGES CXX C)
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "0")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "6")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "4")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "7")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "0")
|
||||
set(PROJECT_VERSION_MAJOR ${CPACK_PACKAGE_VERSION_MAJOR})
|
||||
set(PROJECT_VERSION_MINOR ${CPACK_PACKAGE_VERSION_MINOR})
|
||||
set(PROJECT_VERSION_PATCH ${CPACK_PACKAGE_VERSION_PATCH})
|
||||
|
@ -27,15 +91,11 @@ fix_project_version()
|
|||
|
||||
# Set additional project information
|
||||
set(COMPANY "Nheko")
|
||||
set(COPYRIGHT "Copyright (c) 2018 Nheko Contributors")
|
||||
set(COPYRIGHT "Copyright (c) 2019 Nheko Contributors")
|
||||
set(IDENTIFIER "com.github.mujx.nheko")
|
||||
|
||||
add_project_meta(META_FILES_TO_INCLUDE)
|
||||
|
||||
if(APPLE)
|
||||
set(OPENSSL_ROOT_DIR /usr/local/opt/openssl)
|
||||
endif()
|
||||
|
||||
if(NOT MSVC AND NOT APPLE)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
@ -64,12 +124,19 @@ endif()
|
|||
#
|
||||
# LMDB
|
||||
#
|
||||
include(LMDB)
|
||||
#include(LMDB)
|
||||
if(USE_BUNDLED_LMDB)
|
||||
hunter_add_package(lmdb)
|
||||
find_package(liblmdb CONFIG REQUIRED)
|
||||
else()
|
||||
find_package(LMDB)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Discover Qt dependencies.
|
||||
#
|
||||
find_package(Qt5 COMPONENTS Core Widgets LinguistTools Concurrent Svg Multimedia REQUIRED)
|
||||
find_package(Qt5 COMPONENTS Core Widgets LinguistTools Concurrent Svg Multimedia Qml QuickControls2 QuickWidgets REQUIRED)
|
||||
find_package(Qt5QuickCompiler)
|
||||
find_package(Qt5DBus)
|
||||
|
||||
if (APPLE)
|
||||
|
@ -77,21 +144,12 @@ if (APPLE)
|
|||
endif(APPLE)
|
||||
|
||||
if (Qt5Widgets_FOUND)
|
||||
if (Qt5Widgets_VERSION VERSION_LESS 5.7.0)
|
||||
if (Qt5Widgets_VERSION VERSION_LESS 5.9.0)
|
||||
message(STATUS "Qt version ${Qt5Widgets_VERSION}")
|
||||
message(WARNING "Minimum supported Qt5 version is 5.7!")
|
||||
message(WARNING "Minimum supported Qt5 version is 5.9!")
|
||||
endif()
|
||||
endif(Qt5Widgets_FOUND)
|
||||
|
||||
#
|
||||
# Set up compiler flags.
|
||||
#
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_C_COMPILER gcc)
|
||||
endif(NOT MSVC)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
if(NOT MSVC)
|
||||
set(
|
||||
|
@ -99,13 +157,12 @@ if(NOT MSVC)
|
|||
"${CMAKE_CXX_FLAGS} \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Werror \
|
||||
-pipe \
|
||||
-pedantic \
|
||||
-fsized-deallocation \
|
||||
-fdiagnostics-color=always \
|
||||
-Wunreachable-code \
|
||||
-std=c++14"
|
||||
-std=c++17"
|
||||
)
|
||||
if (NOT CMAKE_COMPILER_IS_GNUCXX)
|
||||
# -Wshadow is buggy and broken in GCC, so do not enable it.
|
||||
|
@ -171,17 +228,18 @@ configure_file(cmake/nheko.h config/nheko.h)
|
|||
set(SRC_FILES
|
||||
# Dialogs
|
||||
src/dialogs/CreateRoom.cpp
|
||||
src/dialogs/FallbackAuth.cpp
|
||||
src/dialogs/ImageOverlay.cpp
|
||||
src/dialogs/PreviewUploadOverlay.cpp
|
||||
src/dialogs/InviteUsers.cpp
|
||||
src/dialogs/JoinRoom.cpp
|
||||
src/dialogs/MemberList.cpp
|
||||
src/dialogs/LeaveRoom.cpp
|
||||
src/dialogs/Logout.cpp
|
||||
src/dialogs/UserProfile.cpp
|
||||
src/dialogs/ReadReceipts.cpp
|
||||
src/dialogs/MemberList.cpp
|
||||
src/dialogs/PreviewUploadOverlay.cpp
|
||||
src/dialogs/ReCaptcha.cpp
|
||||
src/dialogs/ReadReceipts.cpp
|
||||
src/dialogs/RoomSettings.cpp
|
||||
src/dialogs/UserProfile.cpp
|
||||
|
||||
# Emoji
|
||||
src/emoji/Category.cpp
|
||||
|
@ -192,16 +250,13 @@ set(SRC_FILES
|
|||
|
||||
# Timeline
|
||||
src/timeline/TimelineViewManager.cpp
|
||||
src/timeline/TimelineItem.cpp
|
||||
src/timeline/TimelineView.cpp
|
||||
src/timeline/widgets/AudioItem.cpp
|
||||
src/timeline/widgets/FileItem.cpp
|
||||
src/timeline/widgets/ImageItem.cpp
|
||||
src/timeline/widgets/VideoItem.cpp
|
||||
src/timeline/TimelineModel.cpp
|
||||
src/timeline/DelegateChooser.cpp
|
||||
|
||||
# UI components
|
||||
src/ui/Avatar.cpp
|
||||
src/ui/Badge.cpp
|
||||
src/ui/DropShadow.cpp
|
||||
src/ui/LoadingIndicator.cpp
|
||||
src/ui/InfoMessage.cpp
|
||||
src/ui/FlatButton.cpp
|
||||
|
@ -224,24 +279,28 @@ set(SRC_FILES
|
|||
src/ChatPage.cpp
|
||||
src/CommunitiesListItem.cpp
|
||||
src/CommunitiesList.cpp
|
||||
src/EventAccessors.cpp
|
||||
src/InviteeItem.cpp
|
||||
src/LoginPage.cpp
|
||||
src/Logging.cpp
|
||||
src/MainWindow.cpp
|
||||
src/MatrixClient.cpp
|
||||
src/MxcImageProvider.cpp
|
||||
src/ColorImageProvider.cpp
|
||||
src/QuickSwitcher.cpp
|
||||
src/Olm.cpp
|
||||
src/RegisterPage.cpp
|
||||
src/RoomInfoListItem.cpp
|
||||
src/RoomList.cpp
|
||||
src/RunGuard.cpp
|
||||
src/SideBarActions.cpp
|
||||
src/Splitter.cpp
|
||||
src/SuggestionsPopup.cpp
|
||||
src/popups/SuggestionsPopup.cpp
|
||||
src/popups/PopupItem.cpp
|
||||
src/popups/ReplyPopup.cpp
|
||||
src/popups/UserMentions.cpp
|
||||
src/TextInputWidget.cpp
|
||||
src/TopRoomBar.cpp
|
||||
src/TrayIcon.cpp
|
||||
src/TypingDisplay.cpp
|
||||
src/Utils.cpp
|
||||
src/UserInfoWidget.cpp
|
||||
src/UserSettingsPage.cpp
|
||||
|
@ -249,29 +308,80 @@ set(SRC_FILES
|
|||
src/main.cpp
|
||||
)
|
||||
|
||||
# ExternalProject dependencies
|
||||
set(EXTERNAL_PROJECT_DEPS "")
|
||||
|
||||
include(FeatureSummary)
|
||||
|
||||
set(Boost_USE_STATIC_LIBS OFF)
|
||||
set(Boost_USE_STATIC_RUNTIME OFF)
|
||||
set(Boost_USE_MULTITHREADED ON)
|
||||
find_package(Boost 1.66 REQUIRED
|
||||
COMPONENTS atomic
|
||||
chrono
|
||||
date_time
|
||||
iostreams
|
||||
random
|
||||
regex
|
||||
if(USE_BUNDLED_BOOST)
|
||||
hunter_add_package(Boost COMPONENTS iostreams system thread)
|
||||
endif()
|
||||
find_package(Boost 1.70 REQUIRED
|
||||
COMPONENTS iostreams
|
||||
system
|
||||
thread)
|
||||
if(USE_BUNDLED_ZLIB)
|
||||
hunter_add_package(ZLIB)
|
||||
endif()
|
||||
find_package(ZLIB REQUIRED)
|
||||
if(USE_BUNDLED_OPENSSL)
|
||||
hunter_add_package(OpenSSL)
|
||||
endif()
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(MatrixClient 0.1.0 REQUIRED)
|
||||
find_package(Olm 2 REQUIRED)
|
||||
if(USE_BUNDLED_MTXCLIENT)
|
||||
include(FetchContent)
|
||||
set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "")
|
||||
set(BUILD_LIB_TESTS OFF CACHE INTERNAL "")
|
||||
FetchContent_Declare(
|
||||
MatrixClient
|
||||
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
|
||||
GIT_TAG 7fc1d357afaabb134cb6d9c593f94915973d31fa
|
||||
)
|
||||
FetchContent_MakeAvailable(MatrixClient)
|
||||
else()
|
||||
find_package(MatrixClient 0.3.0 REQUIRED)
|
||||
endif()
|
||||
if(USE_BUNDLED_OLM)
|
||||
include(FetchContent)
|
||||
set(OLM_TESTS OFF CACHE INTERNAL "")
|
||||
FetchContent_Declare(
|
||||
Olm
|
||||
GIT_REPOSITORY https://gitlab.matrix.org/matrix-org/olm.git
|
||||
GIT_TAG 3.1.4
|
||||
)
|
||||
FetchContent_MakeAvailable(Olm)
|
||||
else()
|
||||
find_package(Olm 3)
|
||||
set_package_properties(Olm PROPERTIES
|
||||
DESCRIPTION "An implementation of the Double Ratchet cryptographic ratchet"
|
||||
URL "https://git.matrix.org/git/olm/about/"
|
||||
TYPE REQUIRED
|
||||
)
|
||||
endif()
|
||||
if(USE_BUNDLED_SPDLOG)
|
||||
hunter_add_package(spdlog)
|
||||
endif()
|
||||
find_package(spdlog 1.0.0 CONFIG REQUIRED)
|
||||
|
||||
if(USE_BUNDLED_CMARK)
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
cmark
|
||||
GIT_REPOSITORY https://github.com/commonmark/cmark.git
|
||||
GIT_TAG 242e277a661ec7e51f34dcaf86c1925d550b1498 #0.29.0 << doesn't work with fetch content yet
|
||||
CMAKE_ARGS "CMARK_STATIC=ON CMARK_SHARED=OFF CMARK_TESTS=OFF CMARK_TESTS=OFF"
|
||||
)
|
||||
FetchContent_MakeAvailable(cmark)
|
||||
if (MSVC)
|
||||
add_library(cmark::cmark ALIAS libcmark)
|
||||
else()
|
||||
add_library(cmark::cmark ALIAS libcmark_static)
|
||||
endif()
|
||||
else()
|
||||
find_package(cmark REQUIRED)
|
||||
endif()
|
||||
|
||||
if(USE_BUNDLED_JSON)
|
||||
hunter_add_package(nlohmann_json)
|
||||
endif()
|
||||
find_package(nlohmann_json 3.2.0)
|
||||
set_package_properties(nlohmann_json PROPERTIES
|
||||
DESCRIPTION "JSON for Modern C++, a C++11 header-only JSON class"
|
||||
|
@ -279,6 +389,10 @@ set_package_properties(nlohmann_json PROPERTIES
|
|||
TYPE REQUIRED
|
||||
)
|
||||
|
||||
if(USE_BUNDLED_LMDBXX)
|
||||
hunter_add_package(lmdbxx)
|
||||
find_package(lmdbxx CONFIG REQUIRED)
|
||||
else()
|
||||
if(NOT LMDBXX_INCLUDE_DIR)
|
||||
find_path(LMDBXX_INCLUDE_DIR
|
||||
NAMES lmdb++.h
|
||||
|
@ -286,42 +400,47 @@ if(NOT LMDBXX_INCLUDE_DIR)
|
|||
/usr/local/include
|
||||
$ENV{LIB_DIR}/include
|
||||
$ENV{LIB_DIR}/include/lmdbxx)
|
||||
|
||||
endif()
|
||||
include_directories(SYSTEM ${LMDBXX_INCLUDE_DIR})
|
||||
|
||||
if(NOT TWEENY_INCLUDE_DIR)
|
||||
find_path(TWEENY_INCLUDE_DIR
|
||||
NAMES tweeny/tweeny.h
|
||||
PATHS /usr/include/
|
||||
/usr/local/include/
|
||||
$ENV{LIB_DIR}/include/
|
||||
$ENV{LIB_DIR}/include/tweeny)
|
||||
add_library(lmdbxx INTERFACE)
|
||||
target_include_directories(lmdbxx INTERFACE ${LMDBXX_INCLUDE_DIR})
|
||||
add_library(lmdbxx::lmdbxx ALIAS lmdbxx)
|
||||
endif()
|
||||
include_directories(SYSTEM ${TWEENY_INCLUDE_DIR})
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/src)
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
if(USE_BUNDLED_TWEENY)
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
Tweeny
|
||||
GIT_REPOSITORY https://github.com/mobius3/tweeny.git
|
||||
GIT_TAG 6a5033372fe53c4c731c66c8a2d56261746cd85c #v3 <- v3 has unfixed warnings
|
||||
)
|
||||
FetchContent_MakeAvailable(Tweeny)
|
||||
else()
|
||||
find_package(Tweeny REQUIRED)
|
||||
endif()
|
||||
|
||||
# local inclue directory
|
||||
include_directories(includes)
|
||||
# single instance functionality
|
||||
set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication")
|
||||
add_subdirectory(third_party/SingleApplication-3.0.19/)
|
||||
|
||||
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
qt5_wrap_cpp(MOC_HEADERS
|
||||
# Dialogs
|
||||
src/dialogs/CreateRoom.h
|
||||
src/dialogs/FallbackAuth.h
|
||||
src/dialogs/ImageOverlay.h
|
||||
src/dialogs/PreviewUploadOverlay.h
|
||||
src/dialogs/InviteUsers.h
|
||||
src/dialogs/JoinRoom.h
|
||||
src/dialogs/MemberList.h
|
||||
src/dialogs/LeaveRoom.h
|
||||
src/dialogs/Logout.h
|
||||
src/dialogs/UserProfile.h
|
||||
src/dialogs/MemberList.h
|
||||
src/dialogs/PreviewUploadOverlay.h
|
||||
src/dialogs/RawMessage.h
|
||||
src/dialogs/ReadReceipts.h
|
||||
src/dialogs/ReCaptcha.h
|
||||
src/dialogs/ReadReceipts.h
|
||||
src/dialogs/RoomSettings.h
|
||||
src/dialogs/UserProfile.h
|
||||
|
||||
# Emoji
|
||||
src/emoji/Category.h
|
||||
|
@ -330,13 +449,9 @@ qt5_wrap_cpp(MOC_HEADERS
|
|||
src/emoji/PickButton.h
|
||||
|
||||
# Timeline
|
||||
src/timeline/TimelineItem.h
|
||||
src/timeline/TimelineView.h
|
||||
src/timeline/TimelineViewManager.h
|
||||
src/timeline/widgets/AudioItem.h
|
||||
src/timeline/widgets/FileItem.h
|
||||
src/timeline/widgets/ImageItem.h
|
||||
src/timeline/widgets/VideoItem.h
|
||||
src/timeline/TimelineModel.h
|
||||
src/timeline/DelegateChooser.h
|
||||
|
||||
# UI components
|
||||
src/ui/Avatar.h
|
||||
|
@ -361,13 +476,13 @@ qt5_wrap_cpp(MOC_HEADERS
|
|||
src/notifications/Manager.h
|
||||
|
||||
src/AvatarProvider.h
|
||||
src/Cache.h
|
||||
src/Cache_p.h
|
||||
src/ChatPage.h
|
||||
src/CommunitiesListItem.h
|
||||
src/CommunitiesList.h
|
||||
src/LoginPage.h
|
||||
src/MainWindow.h
|
||||
src/MatrixClient.h
|
||||
src/MxcImageProvider.h
|
||||
src/InviteeItem.h
|
||||
src/QuickSwitcher.h
|
||||
src/RegisterPage.h
|
||||
|
@ -375,11 +490,13 @@ qt5_wrap_cpp(MOC_HEADERS
|
|||
src/RoomList.h
|
||||
src/SideBarActions.h
|
||||
src/Splitter.h
|
||||
src/SuggestionsPopup.h
|
||||
src/popups/SuggestionsPopup.h
|
||||
src/popups/ReplyPopup.h
|
||||
src/popups/PopupItem.h
|
||||
src/popups/UserMentions.h
|
||||
src/TextInputWidget.h
|
||||
src/TopRoomBar.h
|
||||
src/TrayIcon.h
|
||||
src/TypingDisplay.h
|
||||
src/UserInfoWidget.h
|
||||
src/UserSettingsPage.h
|
||||
src/WelcomePage.h
|
||||
|
@ -391,22 +508,6 @@ qt5_wrap_cpp(MOC_HEADERS
|
|||
include(Translations)
|
||||
set(TRANSLATION_DEPS ${LANG_QRC} ${QRC} ${QM_SRC})
|
||||
|
||||
set(COMMON_LIBS
|
||||
MatrixClient::MatrixClient
|
||||
${Boost_LIBRARIES}
|
||||
cmark::cmark
|
||||
Qt5::Widgets
|
||||
Qt5::Svg
|
||||
Qt5::Concurrent
|
||||
Qt5::Multimedia
|
||||
nlohmann_json::nlohmann_json)
|
||||
|
||||
if(APPVEYOR_BUILD)
|
||||
set(NHEKO_LIBS ${COMMON_LIBS} lmdb)
|
||||
else()
|
||||
set(NHEKO_LIBS ${COMMON_LIBS} ${LMDB_LIBRARY})
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Foundation -framework Cocoa")
|
||||
set(SRC_FILES ${SRC_FILES} src/notifications/ManagerMac.mm src/emoji/MacHelper.mm)
|
||||
|
@ -437,21 +538,55 @@ if(ASAN)
|
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,undefined")
|
||||
endif()
|
||||
|
||||
add_executable (nheko ${OS_BUNDLE} ${NHEKO_DEPS})
|
||||
if(APPLE)
|
||||
add_executable (nheko ${OS_BUNDLE} ${NHEKO_DEPS})
|
||||
target_link_libraries (nheko ${NHEKO_LIBS} Qt5::MacExtras)
|
||||
target_link_libraries (nheko PRIVATE Qt5::MacExtras)
|
||||
elseif(WIN32)
|
||||
add_executable (nheko ${OS_BUNDLE} ${ICON_FILE} ${NHEKO_DEPS})
|
||||
target_link_libraries (nheko ${NTDLIB} ${NHEKO_LIBS} Qt5::WinMain)
|
||||
target_compile_definitions(nheko PRIVATE WIN32_LEAN_AND_MEAN)
|
||||
target_link_libraries (nheko PRIVATE ${NTDLIB} Qt5::WinMain)
|
||||
else()
|
||||
add_executable (nheko ${OS_BUNDLE} ${NHEKO_DEPS})
|
||||
target_link_libraries (nheko ${NHEKO_LIBS} Qt5::DBus)
|
||||
target_link_libraries (nheko PRIVATE Qt5::DBus)
|
||||
endif()
|
||||
target_include_directories(nheko PRIVATE src includes)
|
||||
|
||||
target_link_libraries(nheko PRIVATE
|
||||
MatrixClient::MatrixClient
|
||||
Boost::iostreams
|
||||
Boost::system
|
||||
Boost::thread
|
||||
cmark::cmark
|
||||
spdlog::spdlog
|
||||
Qt5::Widgets
|
||||
Qt5::Svg
|
||||
Qt5::Concurrent
|
||||
Qt5::Multimedia
|
||||
Qt5::Qml
|
||||
Qt5::QuickControls2
|
||||
Qt5::QuickWidgets
|
||||
nlohmann_json::nlohmann_json
|
||||
lmdbxx::lmdbxx
|
||||
liblmdb::lmdb
|
||||
tweeny
|
||||
SingleApplication::SingleApplication)
|
||||
|
||||
if(MSVC)
|
||||
target_link_libraries(nheko PRIVATE ntdll)
|
||||
endif()
|
||||
|
||||
if(EXTERNAL_PROJECT_DEPS)
|
||||
add_dependencies(nheko ${EXTERNAL_PROJECT_DEPS})
|
||||
|
||||
if(QML_DEBUGGING)
|
||||
target_compile_definitions(nheko PRIVATE QML_DEBUGGING)
|
||||
endif()
|
||||
|
||||
|
||||
if(NOT MSVC)
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug" OR CI_BUILD)
|
||||
target_compile_options(nheko PRIVATE "-Werror")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set_target_properties(nheko PROPERTIES SKIP_BUILD_RPATH TRUE)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
install (TARGETS nheko RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
install (FILES "resources/nheko-16.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME "nheko.png")
|
||||
|
@ -461,6 +596,7 @@ if(UNIX AND NOT APPLE)
|
|||
install (FILES "resources/nheko-128.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps" RENAME "nheko.png")
|
||||
install (FILES "resources/nheko-256.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" RENAME "nheko.png")
|
||||
install (FILES "resources/nheko-512.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" RENAME "nheko.png")
|
||||
install (FILES "resources/nheko.svg" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps" RENAME "nheko.svg")
|
||||
install (FILES "resources/nheko.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications")
|
||||
install (FILES "resources/nheko.appdata.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo")
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ RUN \
|
|||
add-apt-repository -y ppa:ubuntu-toolchain-r/test && \
|
||||
apt-get update -qq && \
|
||||
apt-get install -y \
|
||||
qt510base qt510tools qt510svg qt510multimedia \
|
||||
qt510base qt510tools qt510svg qt510multimedia qt510quickcontrols2 qt510graphicaleffects \
|
||||
gcc-5 g++-5
|
||||
|
||||
RUN \
|
||||
|
|
2
Makefile
|
@ -68,7 +68,7 @@ update-translations:
|
|||
-locations relative \
|
||||
-Iinclude/dialogs \
|
||||
-Iinclude \
|
||||
src/ -ts resources/langs/nheko_*.ts -no-obsolete
|
||||
src/ resources/qml/ -ts resources/langs/nheko_*.ts -no-obsolete
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
|
153
README.md
|
@ -3,7 +3,7 @@ nheko
|
|||
[![Build Status](https://travis-ci.org/Nheko-Reborn/nheko.svg?branch=master)](https://travis-ci.org/Nheko-Reborn/nheko)
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/07qrqbfylsg4hw2h/branch/master?svg=true)](https://ci.appveyor.com/project/redsky17/nheko/branch/master)
|
||||
[![Stable Version](https://img.shields.io/badge/download-stable-green.svg)](https://github.com/Nheko-Reborn/nheko/releases/v0.6.4)
|
||||
[![Nightly](https://img.shields.io/badge/download-nightly-green.svg)](https://bintray.com/nheko-reborn/nheko/nheko)
|
||||
[![Nightly](https://img.shields.io/badge/download-nightly-green.svg)](https://nheko-reborn-artifacts.s3.us-east-2.amazonaws.com/list.html)
|
||||
[![#nheko-reborn:matrix.org](https://img.shields.io/matrix/nheko-reborn:matrix.org.svg?label=%23nheko-reborn:matrix.org)](https://matrix.to/#/#nheko-reborn:matrix.org)
|
||||
[![AUR: nheko](https://img.shields.io/badge/AUR-nheko-blue.svg)](https://aur.archlinux.org/packages/nheko)
|
||||
<a href='https://flathub.org/apps/details/io.github.NhekoReborn.Nheko'><img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/></a>
|
||||
|
@ -11,21 +11,28 @@ nheko
|
|||
The motivation behind the project is to provide a native desktop app for [Matrix] that
|
||||
feels more like a mainstream chat app ([Riot], Telegram etc) and less like an IRC client.
|
||||
|
||||
### Translations ###
|
||||
[![Translation status](http://weblate.nheko.im/widgets/nheko/-/nheko-master/svg-badge.svg)](http://weblate.nheko.im/engage/nheko/?utm_source=widget)
|
||||
|
||||
Help us with translations so as many people as possible will be able to use nheko!
|
||||
|
||||
### Note regarding End-to-End encryption
|
||||
|
||||
Currently the implementation is at best a **proof of concept** and it should only be used for
|
||||
testing purposes.
|
||||
testing purposes. Most importantly, it is missing device verification, so while your messages
|
||||
and media are encrypted, nheko doesn't verify who gets the messages.
|
||||
|
||||
## Features
|
||||
|
||||
Most of the features you would expect from a chat application are missing right now
|
||||
but we are getting close to a more feature complete client.
|
||||
Specifically there is support for:
|
||||
- E2E encryption (text messages only: attachments are currently sent unencrypted).
|
||||
- E2E encryption.
|
||||
- User registration.
|
||||
- Creating, joining & leaving rooms.
|
||||
- Sending & receiving invites.
|
||||
- Sending & receiving files and emoji (inline widgets for images, audio and file messages).
|
||||
- Replies with text, images and other media (and actually render them as inline widgets).
|
||||
- Typing notifications.
|
||||
- Username auto-completion.
|
||||
- Message & mention notifications.
|
||||
|
@ -62,7 +69,7 @@ sudo dnf install nheko
|
|||
|
||||
#### Gentoo Linux
|
||||
```bash
|
||||
sudo layman -a matrix
|
||||
sudo eselect repository enable matrix
|
||||
sudo emerge -a nheko
|
||||
```
|
||||
|
||||
|
@ -86,7 +93,8 @@ flatpak install flathub io.github.NhekoReborn.Nheko
|
|||
guix install nheko
|
||||
```
|
||||
|
||||
#### macOS (10.12 and above)
|
||||
#### macOS (10.14 and above)
|
||||
|
||||
|
||||
with [macports](https://www.macports.org/) :
|
||||
|
||||
|
@ -96,21 +104,43 @@ sudo port install nheko
|
|||
|
||||
### Build Requirements
|
||||
|
||||
- Qt5 (5.7 or greater). Qt 5.7 adds support for color font rendering with
|
||||
Freetype, which is essential to properly support emoji.
|
||||
- CMake 3.1 or greater.
|
||||
- Qt5 (5.10 or greater). Qt 5.7 adds support for color font rendering with
|
||||
Freetype, which is essential to properly support emoji, 5.8 adds some features
|
||||
to make interopability with Qml easier, 5.10 makes sliders actually visible with different palettes.
|
||||
- CMake 3.15 or greater. (Lower version may work, but may break boost linking)
|
||||
- [mtxclient](https://github.com/Nheko-Reborn/mtxclient)
|
||||
- [LMDB](https://symas.com/lightning-memory-mapped-database/)
|
||||
- [cmark](https://github.com/commonmark/cmark)
|
||||
- Boost 1.66 or greater.
|
||||
- [cmark](https://github.com/commonmark/cmark) 0.29 or greater.
|
||||
- Boost 1.70 or greater.
|
||||
- [libolm](https://git.matrix.org/git/olm)
|
||||
- [libsodium](https://github.com/jedisct1/libsodium)
|
||||
- [spdlog](https://github.com/gabime/spdlog)
|
||||
- A compiler that supports C++ 14:
|
||||
- Clang 5 (tested on Travis CI)
|
||||
- A compiler that supports C++ 17:
|
||||
- Clang 6 (tested on Travis CI)
|
||||
- GCC 7 (tested on Travis CI)
|
||||
- MSVC 19.13 (tested on AppVeyor)
|
||||
|
||||
Nheko can use bundled version for most of those libraries automatically, if the versions in your distro are too old.
|
||||
To use them, you can enable the hunter integration by passing `-DHUNTER_ENABLED=ON`.
|
||||
It is probably wise to link those dependencies statically by passing `-DBUILD_SHARED_LIBS=OFF`
|
||||
You can select which bundled dependencies you want to use py passing various `-DUSE_BUNDLED_*` flags. By default all dependencies are bundled *if* you enable hunter.
|
||||
|
||||
The bundle flags are currently:
|
||||
|
||||
- USE_BUNDLED_BOOST
|
||||
- USE_BUNDLED_SPDLOG
|
||||
- USE_BUNDLED_OLM
|
||||
- USE_BUNDLED_GTEST
|
||||
- USE_BUNDLED_CMARK
|
||||
- USE_BUNDLED_JSON
|
||||
- USE_BUNDLED_OPENSSL
|
||||
- USE_BUNDLED_MTXCLIENT
|
||||
- USE_BUNDLED_SODIUM
|
||||
- USE_BUNDLED_ZLIB
|
||||
- USE_BUNDLED_LMDB
|
||||
- USE_BUNDLED_LMDBXX
|
||||
- USE_BUNDLED_TWEENY
|
||||
|
||||
#### Linux
|
||||
|
||||
If you don't want to install any external dependencies, you can generate an AppImage locally using docker.
|
||||
|
@ -138,26 +168,45 @@ sudo pacman -S qt5-base \
|
|||
##### Gentoo Linux
|
||||
|
||||
```bash
|
||||
sudo emerge -a ">=dev-qt/qtgui-5.7.1" media-libs/fontconfig
|
||||
sudo emerge -a ">=dev-qt/qtgui-5.9.0" media-libs/fontconfig
|
||||
```
|
||||
|
||||
##### Ubuntu (e.g 14.04)
|
||||
##### Ubuntu 16.04
|
||||
|
||||
```bash
|
||||
sudo add-apt-repository ppa:beineri/opt-qt592-trusty
|
||||
sudo add-apt-repository ppa:beineri/opt-qt592-xenial
|
||||
sudo add-apt-repository ppa:george-edison55/cmake-3.x
|
||||
sudo add-apt-repository ppa:ubuntu-toolchain-r-test
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y g++-7 qt59base qt59svg qt59tools qt59multimedia cmake liblmdb-dev libsodium-dev
|
||||
```
|
||||
|
||||
##### Ubuntu 19.10
|
||||
|
||||
```bash
|
||||
# Build requirements + qml modules needed at runtime (you may not need all of them, but the following seem to work according to reports):
|
||||
sudo apt install g++-7 cmake liblmdb-dev libsodium-dev qt{base,tools,multimedia}5-dev qml-module-qt{gstreamer,multimedia,quick-extras} libqt5svg5-dev qt{script,quickcontrols2-}5-dev
|
||||
```
|
||||
|
||||
##### Debian Buster (or higher probably)
|
||||
|
||||
(User report, not sure if all of those are needed)
|
||||
|
||||
```bash
|
||||
sudo apt install cmake gcc make automake liblmdb-dev libsodium-dev \
|
||||
qt5-default libssl-dev libqt5multimedia5-plugins libqt5multimediagsttools5 libqt5multimediaquick5 libqt5svg5-dev \
|
||||
qml-module-qtgstreamer qtmultimedia5-dev qtquickcontrols2-5-dev qttools5-dev qttools5-dev-tools \
|
||||
qml-module-qtgraphicaleffects qml-module-qtmultimedia qml-module-qtquick-controls2 qml-module-qtquick-layouts
|
||||
```
|
||||
|
||||
##### Guix
|
||||
|
||||
```bash
|
||||
guix environment nheko
|
||||
```
|
||||
|
||||
##### macOS (Xcode 8 or later)
|
||||
##### macOS (Xcode 10.2 or later)
|
||||
|
||||
|
||||
```bash
|
||||
brew update
|
||||
|
@ -170,61 +219,29 @@ brew install qt5 lmdb cmake llvm libsodium spdlog boost cmark
|
|||
(for the CMake integration) workloads.
|
||||
|
||||
2. Download the latest Qt for windows installer and install it somewhere.
|
||||
Make sure to install the `MSVC 2017 64-bit` toolset for at least Qt 5.9
|
||||
Make sure to install the `MSVC 2017 64-bit` toolset for at least Qt 5.10
|
||||
(lower versions does not support VS2017).
|
||||
|
||||
3. Install dependencies with `vcpkg`. You can simply clone it into a subfolder
|
||||
of the root nheko source directory.
|
||||
|
||||
```powershell
|
||||
git clone http:\\github.com\Microsoft\vcpkg
|
||||
cd vcpkg
|
||||
.\bootstrap-vcpkg.bat
|
||||
.\vcpkg install --triplet x64-windows \
|
||||
boost-asio \
|
||||
boost-beast \
|
||||
boost-iostreams \
|
||||
boost-random \
|
||||
boost-signals2 \
|
||||
boost-system \
|
||||
boost-thread \
|
||||
cmark \
|
||||
libsodium \
|
||||
lmdb \
|
||||
openssl \
|
||||
zlib
|
||||
```
|
||||
|
||||
4. Install dependencies not managed by vcpkg. (libolm, libmtxclient, libmatrix_structs)
|
||||
|
||||
Inside the project root run the following (replacing the path to vcpkg as necessary).
|
||||
|
||||
```bash
|
||||
cmake -G "Visual Studio 15 2017 Win64" -Hdeps -B.deps
|
||||
-DCMAKE_TOOLCHAIN_FILE=C:/Users/<your-path>/vcpkg/scripts/buildsystems/vcpkg.cmake
|
||||
-DUSE_BUNDLED_BOOST=OFF
|
||||
cmake --build .deps --config Release
|
||||
cmake --build .deps --config Debug
|
||||
```
|
||||
3. If you don't have openssl installed, you will need to install perl to build it (i.e. Strawberry Perl).
|
||||
|
||||
### Building
|
||||
|
||||
First we need to install the rest of the dependencies that are not available in our system
|
||||
We can now build nheko:
|
||||
|
||||
```bash
|
||||
cmake -Hdeps -B.deps \
|
||||
-DUSE_BUNDLED_BOOST=OFF # if we already have boost & spdlog installed.
|
||||
-DUSE_BUNDLED_SPDLOG=OFF
|
||||
cmake --build .deps
|
||||
```
|
||||
|
||||
We can now build nheko by pointing it to the path that we installed the dependencies.
|
||||
|
||||
```bash
|
||||
cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=.deps/usr
|
||||
cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
To use bundled dependencies you can use hunter, i.e.:
|
||||
|
||||
```bash
|
||||
cmake -H. -Bbuild -DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF -DUSE_BUNDLED_OPENSSL=OFF
|
||||
cmake --build build --config Release
|
||||
```
|
||||
|
||||
Adapt the USE_BUNDLED_* as needed.
|
||||
|
||||
If the build fails with the following error
|
||||
```
|
||||
Could not find a package configuration file provided by "Qt5Widgets" with
|
||||
|
@ -249,13 +266,14 @@ The `nheko` binary will be located in the `build` directory.
|
|||
After installing all dependencies, you need to edit the `CMakeSettings.json` to
|
||||
be able to load and compile nheko within Visual Studio.
|
||||
|
||||
You need to fill out the paths for the `CMAKE_TOOLCHAIN_FILE` and the `Qt5_DIR`.
|
||||
The toolchain file should point to the `vcpkg.cmake` and the Qt5 dir to the `lib\cmake\Qt5` dir.
|
||||
You need to fill out the paths for the `Qt5_DIR`.
|
||||
The Qt5 dir should point to the `lib\cmake\Qt5` dir.
|
||||
|
||||
Examples for the paths are:
|
||||
- `C:\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake`
|
||||
- `C:\\Qt\\5.10.1\\msvc2017_64\\lib\\cmake\\Qt5`
|
||||
|
||||
You should also enable hunter by setting `HUNTER_ENABLED` to `ON` and `BUILD_SHARED_LIBS` to `OFF`.
|
||||
|
||||
Now right click into the root nheko source directory and choose `Open in Visual Studio`.
|
||||
You can choose the build type Release and Debug in the top toolbar.
|
||||
After a successful CMake generation you can select the `nheko.exe` as the run target.
|
||||
|
@ -273,6 +291,9 @@ windeployqt nheko.exe
|
|||
The final binary will be located inside `build-vc\Release\Release` for the Release build
|
||||
and `build-vc\Debug\Debug` for the Debug build.
|
||||
|
||||
Also copy the respective cmark.dll to the binary dir from `build/cmark-build/src/Release` (or Debug).
|
||||
|
||||
|
||||
### Contributing
|
||||
|
||||
See [CONTRIBUTING](.github/CONTRIBUTING.md)
|
||||
|
@ -288,9 +309,7 @@ Here are some screen shots to get a feel for the UI, but things will probably ch
|
|||
|
||||
### Third party
|
||||
|
||||
- [Emoji One](http://emojione.com)
|
||||
- [Font Awesome](http://fontawesome.io/)
|
||||
- [Open Sans](https://fonts.google.com/specimen/Open+Sans)
|
||||
[Single Application for Qt](https://github.com/itay-grudev/SingleApplication)
|
||||
|
||||
[Matrix]:https://matrix.org
|
||||
[Riot]:https://riot.im
|
||||
|
|
61
appveyor.yml
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
|
||||
version: 0.6.4-{build}
|
||||
version: 0.7.0-{build}
|
||||
|
||||
configuration: Release
|
||||
image: Visual Studio 2017
|
||||
|
@ -10,30 +10,17 @@ environment:
|
|||
BINTRAY_APIKEY:
|
||||
secure: "iGl5mzE9/ta9kFELUxDw9XtlYMSCMai9xowXIkYzU8WKHz7NfW0mLwMJZvblZFXJ"
|
||||
|
||||
cache: c:\tools\vcpkg\installed\
|
||||
cache:
|
||||
- c:\hunter\ -> appveyor.yml
|
||||
- build\_deps -> appveyor.yml,deps\CMakeLists.txt
|
||||
|
||||
build:
|
||||
verbosity: minimal
|
||||
|
||||
install:
|
||||
- set QT_DIR=C:\Qt\5.10.1\msvc2017_64
|
||||
- set PATH=%PATH%;%QT_DIR%\bin;C:\MinGW\bin
|
||||
- set PATH=%PATH%;C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin
|
||||
- set QT_DIR=C:\Qt\5.13\msvc2017_64
|
||||
- set PATH=%PATH%;%QT_DIR%\bin
|
||||
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
|
||||
- cd "C:\Tools\vcpkg"&& git pull && .\bootstrap-vcpkg.bat && cd %APPVEYOR_BUILD_FOLDER%
|
||||
- vcpkg install
|
||||
nlohmann-json:%PLATFORM%-windows
|
||||
boost-asio:%PLATFORM%-windows
|
||||
boost-beast:%PLATFORM%-windows
|
||||
boost-iostreams:%PLATFORM%-windows
|
||||
boost-random:%PLATFORM%-windows
|
||||
boost-signals2:%PLATFORM%-windows
|
||||
boost-system:%PLATFORM%-windows
|
||||
boost-thread:%PLATFORM%-windows
|
||||
libsodium:%PLATFORM%-windows
|
||||
lmdb:%PLATFORM%-windows
|
||||
openssl:%PLATFORM%-windows
|
||||
zlib:%PLATFORM%-windows
|
||||
|
||||
build_script:
|
||||
# VERSION format: branch-master/branch-1.2
|
||||
|
@ -54,23 +41,13 @@ build_script:
|
|||
- echo %INSTVERSION%
|
||||
- echo %DATE%
|
||||
|
||||
# Build & install the dependencies
|
||||
- cmake -G "Visual Studio 15 2017 Win64" -Hdeps -B.deps
|
||||
-DCMAKE_TOOLCHAIN_FILE=C:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake
|
||||
-DUSE_BUNDLED_BOOST=OFF
|
||||
-DUSE_BUNDLED_JSON=OFF
|
||||
-DMTX_STATIC=ON
|
||||
- cmake --build .deps --config Release
|
||||
|
||||
# Build nheko
|
||||
- rm -f cmake/FindOlm.cmake
|
||||
#- cmake -G "Visual Studio 16 2019" -A x64 -H. -Bbuild
|
||||
- cmake -G "Visual Studio 15 2017 Win64" -H. -Bbuild
|
||||
-DCMAKE_TOOLCHAIN_FILE=C:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake
|
||||
-DLMDBXX_INCLUDE_DIR=.deps/usr/include
|
||||
-DTWEENY_INCLUDE_DIR=.deps/usr/include
|
||||
-DCMARK_INCLUDE_DIR=C:/projects/nheko/.deps/usr/include
|
||||
-DCMARK_LIBRARY=C:/projects/nheko/.deps/usr/lib/cmark.lib
|
||||
-DJSON_INCLUDE_DIR=.deps/usr/include
|
||||
-DHUNTER_ROOT="C:\hunter"
|
||||
-DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF
|
||||
-DCMAKE_BUILD_TYPE=Release -DHUNTER_CONFIGURATION_TYPES=Release
|
||||
|
||||
- cmake --build build --config Release
|
||||
|
||||
after_build:
|
||||
|
@ -79,14 +56,9 @@ after_build:
|
|||
- echo %BUILD%
|
||||
- mkdir NhekoRelease
|
||||
- copy build\Release\nheko.exe NhekoRelease\nheko.exe
|
||||
- copy build\_deps\cmark-build\src\Release\cmark.dll NhekoRelease\cmark.dll
|
||||
- windeployqt --qmldir %QT_DIR%\qml\ --release NhekoRelease\nheko.exe
|
||||
|
||||
- copy C:\Tools\vcpkg\installed\x64-windows\lib\*.lib .\NhekoRelease\
|
||||
- copy C:\Tools\vcpkg\installed\x64-windows\bin\*.dll .\NhekoRelease\
|
||||
|
||||
- copy C:\projects\nheko\.deps\usr\lib\cmark.lib .\NhekoRelease\
|
||||
- copy C:\projects\nheko\.deps\usr\bin\cmark.dll .\NhekoRelease\
|
||||
|
||||
- 7z a nheko_win_64.zip .\NhekoRelease\*
|
||||
- ls -lh build\Release\
|
||||
- ls -lh NhekoRelease\
|
||||
|
@ -135,7 +107,7 @@ on_success:
|
|||
- if "%APPVEYOR_REPO_TAG%" == "true" (curl -T nheko-%APPVEYOR_REPO_TAG_NAME%-installer.exe -uredsky17:%BINTRAY_APIKEY% https://api.bintray.com/content/nheko-reborn/nheko/%APPVEYOR_REPO_TAG_NAME%/nheko/%APPVEYOR_REPO_TAG_NAME%/)
|
||||
|
||||
deploy:
|
||||
description: "Development builds"
|
||||
- description: "Development builds"
|
||||
provider: GitHub
|
||||
auth_token:
|
||||
secure: "ShStWeqp+TkYqJPQr7uFZb+B8ZTgC7Iwth+IkhjfRDCTLhy8gtWvlPzlQilder3E"
|
||||
|
@ -144,6 +116,13 @@ deploy:
|
|||
prerelease: true
|
||||
on:
|
||||
appveyor_repo_tag: true
|
||||
- provider: S3
|
||||
access_key_id: ${AWS_ACCESS_KEY}
|
||||
secret_access_key: ${AWS_SECRET_KEY}
|
||||
bucket: ${AWS_BUCKET_NAME}
|
||||
region: ${AWS_DEFAULT_REGION}
|
||||
set_public: true
|
||||
artifact: nheko-$(APPVEYOR_REPO_TAG_NAME)-installer.exe, nheko_win_64.zip
|
||||
|
||||
artifacts:
|
||||
- path: nheko_win_64.zip
|
||||
|
|
15
cmake/FindLMDB.cmake
Normal file
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
# Find the lmdb library & include dir.
|
||||
#
|
||||
|
||||
find_path (LMDB_INCLUDE_DIR NAMES lmdb.h PATHS "$ENV{LMDB_DIR}/include")
|
||||
find_library (LMDB_LIBRARY NAMES lmdb PATHS "$ENV{LMDB_DIR}/lib" )
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(LMDB DEFAULT_MSG LMDB_INCLUDE_DIR LMDB_LIBRARY)
|
||||
|
||||
|
||||
add_library(lmdb INTERFACE IMPORTED GLOBAL)
|
||||
target_include_directories(lmdb INTERFACE ${LMDB_INCLUDE_DIR})
|
||||
target_link_libraries(lmdb INTERFACE ${LMDB_LIBRARY})
|
||||
|
||||
add_library(liblmdb::lmdb ALIAS lmdb)
|
|
@ -1,44 +0,0 @@
|
|||
#
|
||||
# CMake module to search for the olm library
|
||||
#
|
||||
# On success, the macro sets the following variables:
|
||||
# OLM_FOUND = if the library found
|
||||
# OLM_LIBRARY = full path to the library
|
||||
# OLM_INCLUDE_DIR = where to find the library headers
|
||||
#
|
||||
if(WIN32)
|
||||
message(STATUS "FindOlm is not supported in Windows")
|
||||
return()
|
||||
endif()
|
||||
|
||||
find_path(OLM_INCLUDE_DIR
|
||||
NAMES olm/olm.h
|
||||
PATHS /usr/include
|
||||
/usr/local/include
|
||||
$ENV{LIB_DIR}/include
|
||||
$ENV{LIB_DIR}/include/olm)
|
||||
|
||||
find_library(OLM_LIBRARY
|
||||
NAMES olm
|
||||
PATHS /usr/lib /usr/local/lib $ENV{LIB_DIR}/lib)
|
||||
|
||||
if(OLM_FOUND)
|
||||
set(OLM_INCLUDE_DIRS ${OLM_INCLUDE_DIR})
|
||||
|
||||
if(NOT OLM_LIBRARIES)
|
||||
set(OLM_LIBRARIES ${OLM_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT TARGET Olm::Olm)
|
||||
add_library(Olm::Olm UNKNOWN IMPORTED)
|
||||
set_target_properties(Olm::Olm
|
||||
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
|
||||
${OLM_INCLUDE_DIR})
|
||||
set_property(TARGET Olm::Olm APPEND PROPERTY IMPORTED_LOCATION ${OLM_LIBRARY})
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(OLM DEFAULT_MSG OLM_INCLUDE_DIR OLM_LIBRARY)
|
||||
|
||||
mark_as_advanced(OLM_LIBRARY OLM_INCLUDE_DIR)
|
5
cmake/Hunter/config.cmake
Normal file
|
@ -0,0 +1,5 @@
|
|||
hunter_config(
|
||||
Boost
|
||||
VERSION "1.70.0-p0"
|
||||
CMAKE_ARGS IOSTREAMS_NO_BZIP2=1
|
||||
)
|
528
cmake/HunterGate.cmake
Normal file
|
@ -0,0 +1,528 @@
|
|||
# Copyright (c) 2013-2019, Ruslan Baratov
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# This is a gate file to Hunter package manager.
|
||||
# Include this file using `include` command and add package you need, example:
|
||||
#
|
||||
# cmake_minimum_required(VERSION 3.2)
|
||||
#
|
||||
# include("cmake/HunterGate.cmake")
|
||||
# HunterGate(
|
||||
# URL "https://github.com/path/to/hunter/archive.tar.gz"
|
||||
# SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d"
|
||||
# )
|
||||
#
|
||||
# project(MyProject)
|
||||
#
|
||||
# hunter_add_package(Foo)
|
||||
# hunter_add_package(Boo COMPONENTS Bar Baz)
|
||||
#
|
||||
# Projects:
|
||||
# * https://github.com/hunter-packages/gate/
|
||||
# * https://github.com/ruslo/hunter
|
||||
|
||||
option(HUNTER_ENABLED "Enable Hunter package manager support" ON)
|
||||
|
||||
if(HUNTER_ENABLED)
|
||||
if(CMAKE_VERSION VERSION_LESS "3.2")
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"At least CMake version 3.2 required for Hunter dependency management."
|
||||
" Update CMake or set HUNTER_ENABLED to OFF."
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CMakeParseArguments) # cmake_parse_arguments
|
||||
|
||||
option(HUNTER_STATUS_PRINT "Print working status" ON)
|
||||
option(HUNTER_STATUS_DEBUG "Print a lot info" OFF)
|
||||
option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON)
|
||||
|
||||
set(HUNTER_ERROR_PAGE "https://docs.hunter.sh/en/latest/reference/errors")
|
||||
|
||||
function(hunter_gate_status_print)
|
||||
if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG)
|
||||
foreach(print_message ${ARGV})
|
||||
message(STATUS "[hunter] ${print_message}")
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_status_debug)
|
||||
if(HUNTER_STATUS_DEBUG)
|
||||
foreach(print_message ${ARGV})
|
||||
string(TIMESTAMP timestamp)
|
||||
message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}")
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_error_page error_page)
|
||||
message("------------------------------ ERROR ------------------------------")
|
||||
message(" ${HUNTER_ERROR_PAGE}/${error_page}.html")
|
||||
message("-------------------------------------------------------------------")
|
||||
message("")
|
||||
message(FATAL_ERROR "")
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_internal_error)
|
||||
message("")
|
||||
foreach(print_message ${ARGV})
|
||||
message("[hunter ** INTERNAL **] ${print_message}")
|
||||
endforeach()
|
||||
message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
|
||||
message("")
|
||||
hunter_gate_error_page("error.internal")
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_fatal_error)
|
||||
cmake_parse_arguments(hunter "" "ERROR_PAGE" "" "${ARGV}")
|
||||
if("${hunter_ERROR_PAGE}" STREQUAL "")
|
||||
hunter_gate_internal_error("Expected ERROR_PAGE")
|
||||
endif()
|
||||
message("")
|
||||
foreach(x ${hunter_UNPARSED_ARGUMENTS})
|
||||
message("[hunter ** FATAL ERROR **] ${x}")
|
||||
endforeach()
|
||||
message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
|
||||
message("")
|
||||
hunter_gate_error_page("${hunter_ERROR_PAGE}")
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_user_error)
|
||||
hunter_gate_fatal_error(${ARGV} ERROR_PAGE "error.incorrect.input.data")
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_self root version sha1 result)
|
||||
string(COMPARE EQUAL "${root}" "" is_bad)
|
||||
if(is_bad)
|
||||
hunter_gate_internal_error("root is empty")
|
||||
endif()
|
||||
|
||||
string(COMPARE EQUAL "${version}" "" is_bad)
|
||||
if(is_bad)
|
||||
hunter_gate_internal_error("version is empty")
|
||||
endif()
|
||||
|
||||
string(COMPARE EQUAL "${sha1}" "" is_bad)
|
||||
if(is_bad)
|
||||
hunter_gate_internal_error("sha1 is empty")
|
||||
endif()
|
||||
|
||||
string(SUBSTRING "${sha1}" 0 7 archive_id)
|
||||
|
||||
set(
|
||||
hunter_self
|
||||
"${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked"
|
||||
)
|
||||
|
||||
set("${result}" "${hunter_self}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Set HUNTER_GATE_ROOT cmake variable to suitable value.
|
||||
function(hunter_gate_detect_root)
|
||||
# Check CMake variable
|
||||
string(COMPARE NOTEQUAL "${HUNTER_ROOT}" "" not_empty)
|
||||
if(not_empty)
|
||||
set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE)
|
||||
hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Check environment variable
|
||||
string(COMPARE NOTEQUAL "$ENV{HUNTER_ROOT}" "" not_empty)
|
||||
if(not_empty)
|
||||
set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE)
|
||||
hunter_gate_status_debug("HUNTER_ROOT detected by environment variable")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Check HOME environment variable
|
||||
string(COMPARE NOTEQUAL "$ENV{HOME}" "" result)
|
||||
if(result)
|
||||
set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE)
|
||||
hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Check SYSTEMDRIVE and USERPROFILE environment variable (windows only)
|
||||
if(WIN32)
|
||||
string(COMPARE NOTEQUAL "$ENV{SYSTEMDRIVE}" "" result)
|
||||
if(result)
|
||||
set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE)
|
||||
hunter_gate_status_debug(
|
||||
"HUNTER_ROOT set using SYSTEMDRIVE environment variable"
|
||||
)
|
||||
return()
|
||||
endif()
|
||||
|
||||
string(COMPARE NOTEQUAL "$ENV{USERPROFILE}" "" result)
|
||||
if(result)
|
||||
set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE)
|
||||
hunter_gate_status_debug(
|
||||
"HUNTER_ROOT set using USERPROFILE environment variable"
|
||||
)
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
hunter_gate_fatal_error(
|
||||
"Can't detect HUNTER_ROOT"
|
||||
ERROR_PAGE "error.detect.hunter.root"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_download dir)
|
||||
string(
|
||||
COMPARE
|
||||
NOTEQUAL
|
||||
"$ENV{HUNTER_DISABLE_AUTOINSTALL}"
|
||||
""
|
||||
disable_autoinstall
|
||||
)
|
||||
if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL)
|
||||
hunter_gate_fatal_error(
|
||||
"Hunter not found in '${dir}'"
|
||||
"Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'"
|
||||
"Settings:"
|
||||
" HUNTER_ROOT: ${HUNTER_GATE_ROOT}"
|
||||
" HUNTER_SHA1: ${HUNTER_GATE_SHA1}"
|
||||
ERROR_PAGE "error.run.install"
|
||||
)
|
||||
endif()
|
||||
string(COMPARE EQUAL "${dir}" "" is_bad)
|
||||
if(is_bad)
|
||||
hunter_gate_internal_error("Empty 'dir' argument")
|
||||
endif()
|
||||
|
||||
string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad)
|
||||
if(is_bad)
|
||||
hunter_gate_internal_error("HUNTER_GATE_SHA1 empty")
|
||||
endif()
|
||||
|
||||
string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad)
|
||||
if(is_bad)
|
||||
hunter_gate_internal_error("HUNTER_GATE_URL empty")
|
||||
endif()
|
||||
|
||||
set(done_location "${dir}/DONE")
|
||||
set(sha1_location "${dir}/SHA1")
|
||||
|
||||
set(build_dir "${dir}/Build")
|
||||
set(cmakelists "${dir}/CMakeLists.txt")
|
||||
|
||||
hunter_gate_status_debug("Locking directory: ${dir}")
|
||||
file(LOCK "${dir}" DIRECTORY GUARD FUNCTION)
|
||||
hunter_gate_status_debug("Lock done")
|
||||
|
||||
if(EXISTS "${done_location}")
|
||||
# while waiting for lock other instance can do all the job
|
||||
hunter_gate_status_debug("File '${done_location}' found, skip install")
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(REMOVE_RECURSE "${build_dir}")
|
||||
file(REMOVE_RECURSE "${cmakelists}")
|
||||
|
||||
file(MAKE_DIRECTORY "${build_dir}") # check directory permissions
|
||||
|
||||
# Disabling languages speeds up a little bit, reduces noise in the output
|
||||
# and avoids path too long windows error
|
||||
file(
|
||||
WRITE
|
||||
"${cmakelists}"
|
||||
"cmake_minimum_required(VERSION 3.2)\n"
|
||||
"project(HunterDownload LANGUAGES NONE)\n"
|
||||
"include(ExternalProject)\n"
|
||||
"ExternalProject_Add(\n"
|
||||
" Hunter\n"
|
||||
" URL\n"
|
||||
" \"${HUNTER_GATE_URL}\"\n"
|
||||
" URL_HASH\n"
|
||||
" SHA1=${HUNTER_GATE_SHA1}\n"
|
||||
" DOWNLOAD_DIR\n"
|
||||
" \"${dir}\"\n"
|
||||
" TLS_VERIFY\n"
|
||||
" ${HUNTER_TLS_VERIFY}\n"
|
||||
" SOURCE_DIR\n"
|
||||
" \"${dir}/Unpacked\"\n"
|
||||
" CONFIGURE_COMMAND\n"
|
||||
" \"\"\n"
|
||||
" BUILD_COMMAND\n"
|
||||
" \"\"\n"
|
||||
" INSTALL_COMMAND\n"
|
||||
" \"\"\n"
|
||||
")\n"
|
||||
)
|
||||
|
||||
if(HUNTER_STATUS_DEBUG)
|
||||
set(logging_params "")
|
||||
else()
|
||||
set(logging_params OUTPUT_QUIET)
|
||||
endif()
|
||||
|
||||
hunter_gate_status_debug("Run generate")
|
||||
|
||||
# Need to add toolchain file too.
|
||||
# Otherwise on Visual Studio + MDD this will fail with error:
|
||||
# "Could not find an appropriate version of the Windows 10 SDK installed on this machine"
|
||||
if(EXISTS "${CMAKE_TOOLCHAIN_FILE}")
|
||||
get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE)
|
||||
set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}")
|
||||
else()
|
||||
# 'toolchain_arg' can't be empty
|
||||
set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=")
|
||||
endif()
|
||||
|
||||
string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make)
|
||||
if(no_make)
|
||||
set(make_arg "")
|
||||
else()
|
||||
# Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM
|
||||
set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}"
|
||||
"-H${dir}"
|
||||
"-B${build_dir}"
|
||||
"-G${CMAKE_GENERATOR}"
|
||||
"${toolchain_arg}"
|
||||
${make_arg}
|
||||
WORKING_DIRECTORY "${dir}"
|
||||
RESULT_VARIABLE download_result
|
||||
${logging_params}
|
||||
)
|
||||
|
||||
if(NOT download_result EQUAL 0)
|
||||
hunter_gate_internal_error(
|
||||
"Configure project failed."
|
||||
"To reproduce the error run: ${CMAKE_COMMAND} -H${dir} -B${build_dir} -G${CMAKE_GENERATOR} ${toolchain_arg} ${make_arg}"
|
||||
"In directory ${dir}"
|
||||
)
|
||||
endif()
|
||||
|
||||
hunter_gate_status_print(
|
||||
"Initializing Hunter workspace (${HUNTER_GATE_SHA1})"
|
||||
" ${HUNTER_GATE_URL}"
|
||||
" -> ${dir}"
|
||||
)
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" --build "${build_dir}"
|
||||
WORKING_DIRECTORY "${dir}"
|
||||
RESULT_VARIABLE download_result
|
||||
${logging_params}
|
||||
)
|
||||
|
||||
if(NOT download_result EQUAL 0)
|
||||
hunter_gate_internal_error("Build project failed")
|
||||
endif()
|
||||
|
||||
file(REMOVE_RECURSE "${build_dir}")
|
||||
file(REMOVE_RECURSE "${cmakelists}")
|
||||
|
||||
file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}")
|
||||
file(WRITE "${done_location}" "DONE")
|
||||
|
||||
hunter_gate_status_debug("Finished")
|
||||
endfunction()
|
||||
|
||||
# Must be a macro so master file 'cmake/Hunter' can
|
||||
# apply all variables easily just by 'include' command
|
||||
# (otherwise PARENT_SCOPE magic needed)
|
||||
macro(HunterGate)
|
||||
if(HUNTER_GATE_DONE)
|
||||
# variable HUNTER_GATE_DONE set explicitly for external project
|
||||
# (see `hunter_download`)
|
||||
set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)
|
||||
endif()
|
||||
|
||||
# First HunterGate command will init Hunter, others will be ignored
|
||||
get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET)
|
||||
|
||||
if(NOT HUNTER_ENABLED)
|
||||
# Empty function to avoid error "unknown function"
|
||||
function(hunter_add_package)
|
||||
endfunction()
|
||||
|
||||
set(
|
||||
_hunter_gate_disabled_mode_dir
|
||||
"${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode"
|
||||
)
|
||||
if(EXISTS "${_hunter_gate_disabled_mode_dir}")
|
||||
hunter_gate_status_debug(
|
||||
"Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}"
|
||||
)
|
||||
list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}")
|
||||
endif()
|
||||
elseif(_hunter_gate_done)
|
||||
hunter_gate_status_debug("Secondary HunterGate (use old settings)")
|
||||
hunter_gate_self(
|
||||
"${HUNTER_CACHED_ROOT}"
|
||||
"${HUNTER_VERSION}"
|
||||
"${HUNTER_SHA1}"
|
||||
_hunter_self
|
||||
)
|
||||
include("${_hunter_self}/cmake/Hunter")
|
||||
else()
|
||||
set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name)
|
||||
if(_have_project_name)
|
||||
hunter_gate_fatal_error(
|
||||
"Please set HunterGate *before* 'project' command. "
|
||||
"Detected project: ${PROJECT_NAME}"
|
||||
ERROR_PAGE "error.huntergate.before.project"
|
||||
)
|
||||
endif()
|
||||
|
||||
cmake_parse_arguments(
|
||||
HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV}
|
||||
)
|
||||
|
||||
string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1)
|
||||
string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url)
|
||||
string(
|
||||
COMPARE
|
||||
NOTEQUAL
|
||||
"${HUNTER_GATE_UNPARSED_ARGUMENTS}"
|
||||
""
|
||||
_have_unparsed
|
||||
)
|
||||
string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global)
|
||||
string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath)
|
||||
|
||||
if(_have_unparsed)
|
||||
hunter_gate_user_error(
|
||||
"HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}"
|
||||
)
|
||||
endif()
|
||||
if(_empty_sha1)
|
||||
hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory")
|
||||
endif()
|
||||
if(_empty_url)
|
||||
hunter_gate_user_error("URL suboption of HunterGate is mandatory")
|
||||
endif()
|
||||
if(_have_global)
|
||||
if(HUNTER_GATE_LOCAL)
|
||||
hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)")
|
||||
endif()
|
||||
if(_have_filepath)
|
||||
hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)")
|
||||
endif()
|
||||
endif()
|
||||
if(HUNTER_GATE_LOCAL)
|
||||
if(_have_global)
|
||||
hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)")
|
||||
endif()
|
||||
if(_have_filepath)
|
||||
hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)")
|
||||
endif()
|
||||
endif()
|
||||
if(_have_filepath)
|
||||
if(_have_global)
|
||||
hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)")
|
||||
endif()
|
||||
if(HUNTER_GATE_LOCAL)
|
||||
hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
hunter_gate_detect_root() # set HUNTER_GATE_ROOT
|
||||
|
||||
# Beautify path, fix probable problems with windows path slashes
|
||||
get_filename_component(
|
||||
HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE
|
||||
)
|
||||
hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}")
|
||||
if(NOT HUNTER_ALLOW_SPACES_IN_PATH)
|
||||
string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces)
|
||||
if(NOT _contain_spaces EQUAL -1)
|
||||
hunter_gate_fatal_error(
|
||||
"HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces."
|
||||
"Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error"
|
||||
"(Use at your own risk!)"
|
||||
ERROR_PAGE "error.spaces.in.hunter.root"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
string(
|
||||
REGEX
|
||||
MATCH
|
||||
"[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*"
|
||||
HUNTER_GATE_VERSION
|
||||
"${HUNTER_GATE_URL}"
|
||||
)
|
||||
string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty)
|
||||
if(_is_empty)
|
||||
set(HUNTER_GATE_VERSION "unknown")
|
||||
endif()
|
||||
|
||||
hunter_gate_self(
|
||||
"${HUNTER_GATE_ROOT}"
|
||||
"${HUNTER_GATE_VERSION}"
|
||||
"${HUNTER_GATE_SHA1}"
|
||||
_hunter_self
|
||||
)
|
||||
|
||||
set(_master_location "${_hunter_self}/cmake/Hunter")
|
||||
get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE)
|
||||
set(_done_location "${_archive_id_location}/DONE")
|
||||
set(_sha1_location "${_archive_id_location}/SHA1")
|
||||
|
||||
# Check Hunter already downloaded by HunterGate
|
||||
if(NOT EXISTS "${_done_location}")
|
||||
hunter_gate_download("${_archive_id_location}")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${_done_location}")
|
||||
hunter_gate_internal_error("hunter_gate_download failed")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${_sha1_location}")
|
||||
hunter_gate_internal_error("${_sha1_location} not found")
|
||||
endif()
|
||||
file(READ "${_sha1_location}" _sha1_value)
|
||||
string(COMPARE EQUAL "${_sha1_value}" "${HUNTER_GATE_SHA1}" _is_equal)
|
||||
if(NOT _is_equal)
|
||||
hunter_gate_internal_error(
|
||||
"Short SHA1 collision:"
|
||||
" ${_sha1_value} (from ${_sha1_location})"
|
||||
" ${HUNTER_GATE_SHA1} (HunterGate)"
|
||||
)
|
||||
endif()
|
||||
if(NOT EXISTS "${_master_location}")
|
||||
hunter_gate_user_error(
|
||||
"Master file not found:"
|
||||
" ${_master_location}"
|
||||
"try to update Hunter/HunterGate"
|
||||
)
|
||||
endif()
|
||||
include("${_master_location}")
|
||||
set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)
|
||||
endif()
|
||||
endmacro()
|
|
@ -1,29 +0,0 @@
|
|||
#
|
||||
# Find the lmdb library & include dir.
|
||||
# Build lmdb on Appveyor.
|
||||
#
|
||||
|
||||
if(APPVEYOR_BUILD)
|
||||
set(LMDB_VERSION "LMDB_0.9.21")
|
||||
set(NTDLIB "C:/WINDDK/7600.16385.1/lib/win7/amd64/ntdll.lib")
|
||||
|
||||
execute_process(
|
||||
COMMAND git clone --depth=1 --branch ${LMDB_VERSION} https://github.com/LMDB/lmdb)
|
||||
|
||||
set(LMDB_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/lmdb/libraries/liblmdb)
|
||||
|
||||
add_library(lmdb
|
||||
${CMAKE_SOURCE_DIR}/lmdb/libraries/liblmdb/lmdb.h
|
||||
${CMAKE_SOURCE_DIR}/lmdb/libraries/liblmdb/mdb.c
|
||||
${CMAKE_SOURCE_DIR}/lmdb/libraries/liblmdb/midl.h
|
||||
${CMAKE_SOURCE_DIR}/lmdb/libraries/liblmdb/midl.c)
|
||||
|
||||
set(LMDB_LIBRARY lmdb)
|
||||
else()
|
||||
find_path (LMDB_INCLUDE_DIR NAMES lmdb.h PATHS "$ENV{LMDB_DIR}/include")
|
||||
find_library (LMDB_LIBRARY NAMES lmdb PATHS "$ENV{LMDB_DIR}/lib" )
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(LMDB DEFAULT_MSG LMDB_INCLUDE_DIR LMDB_LIBRARY)
|
||||
endif()
|
||||
|
||||
include_directories(${LMDB_INCLUDE_DIR})
|
|
@ -21,4 +21,8 @@ if(NOT EXISTS ${_qrc})
|
|||
endif()
|
||||
|
||||
qt5_add_resources(LANG_QRC ${_qrc})
|
||||
if(Qt5QuickCompiler_FOUND)
|
||||
qtquick_compiler_add_resources(QRC resources/res.qrc)
|
||||
else()
|
||||
qt5_add_resources(QRC resources/res.qrc)
|
||||
endif()
|
||||
|
|
125
deps/CMakeLists.txt
vendored
|
@ -1,125 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.11)
|
||||
project(NHEKO_DEPS)
|
||||
|
||||
# Point CMake at any custom modules we may ship
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
|
||||
set(DEPS_INSTALL_DIR "${CMAKE_BINARY_DIR}/usr"
|
||||
CACHE PATH "Dependencies install directory.")
|
||||
set(DEPS_BIN_DIR "${DEPS_INSTALL_DIR}/bin"
|
||||
CACHE PATH "Dependencies binary install directory.")
|
||||
set(DEPS_LIB_DIR "${DEPS_INSTALL_DIR}/lib"
|
||||
CACHE PATH "Dependencies library install directory.")
|
||||
set(DEPS_BUILD_DIR "${CMAKE_BINARY_DIR}/build"
|
||||
CACHE PATH "Dependencies build directory.")
|
||||
set(DEPS_DOWNLOAD_DIR "${DEPS_BUILD_DIR}/downloads"
|
||||
CACHE PATH "Dependencies download directory.")
|
||||
|
||||
option(USE_BUNDLED "Use bundled dependencies." ON)
|
||||
|
||||
option(USE_BUNDLED_BOOST "Use the bundled version of Boost." ${USE_BUNDLED})
|
||||
option(USE_BUNDLED_CMARK "Use the bundled version of cmark." ${USE_BUNDLED})
|
||||
option(USE_BUNDLED_SPDLOG "Use the bundled version of spdlog." ${USE_BUNDLED})
|
||||
option(USE_BUNDLED_OLM "Use the bundled version of libolm." ${USE_BUNDLED})
|
||||
option(USE_BUNDLED_TWEENY "Use the bundled version of Tweeny." ${USE_BUNDLED})
|
||||
option(USE_BUNDLED_LMDBXX "Use the bundled version of lmdbxx." ${USE_BUNDLED})
|
||||
option(USE_BUNDLED_MATRIX_CLIENT "Use the bundled version of mtxclient."
|
||||
${USE_BUNDLED})
|
||||
option(USE_BUNDLED_JSON "Use the bundled version of nlohmann json." ${USE_BUNDLED})
|
||||
option(MTX_STATIC "Compile / link bundled mtx client statically" OFF)
|
||||
|
||||
if(USE_BUNDLED_BOOST)
|
||||
# bundled boost is 1.68, which requires CMake 3.12 or greater.
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
endif()
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
set(BOOST_URL
|
||||
https://dl.bintray.com/boostorg/release/1.69.0/source/boost_1_69_0.tar.bz2)
|
||||
set(BOOST_SHA256
|
||||
8f32d4617390d1c2d16f26a27ab60d97807b35440d45891fa340fc2648b04406)
|
||||
|
||||
set(
|
||||
MTXCLIENT_URL
|
||||
https://github.com/Nheko-Reborn/mtxclient/archive/975ce8906c42742dbb698fcf9fa15663c530df20.tar.gz)
|
||||
set(MTXCLIENT_HASH
|
||||
5e3169ef19b6e585069ceced42489574ce18380480628339bac015759fa1893e)
|
||||
set(
|
||||
TWEENY_URL
|
||||
https://github.com/mobius3/tweeny/archive/b94ce07cfb02a0eb8ac8aaf66137dabdaea857cf.tar.gz
|
||||
)
|
||||
set(TWEENY_HASH
|
||||
9a632b9da84823fae002ad5d9ba02c8d77c0a3810479974c6b637c5504165475)
|
||||
|
||||
set(
|
||||
LMDBXX_HEADER_URL
|
||||
https://raw.githubusercontent.com/bendiken/lmdbxx/0b43ca87d8cfabba392dfe884eb1edb83874de02/lmdb%2B%2B.h
|
||||
)
|
||||
set(LMDBXX_HASH
|
||||
c57b501a4e8fa1187fa7fd348da415c7685a50a7cb25b17b3f257b9e9426f73d)
|
||||
|
||||
set(OLM_URL https://gitlab.matrix.org/matrix-org/olm.git)
|
||||
set(OLM_TAG 4065c8e11a33ba41133a086ed3de4da94dcb6bae)
|
||||
|
||||
set(CMARK_URL https://github.com/commonmark/cmark/archive/0.28.3.tar.gz)
|
||||
set(CMARK_HASH acc98685d3c1b515ff787ac7c994188dadaf28a2d700c10c1221da4199bae1fc)
|
||||
|
||||
set(SPDLOG_URL https://github.com/gabime/spdlog/archive/v1.1.0.tar.gz)
|
||||
set(SPDLOG_HASH
|
||||
3dbcbfd8c07e25f5e0d662b194d3a7772ef214358c49ada23c044c4747ce8b19)
|
||||
|
||||
set(JSON_URL
|
||||
https://github.com/nlohmann/json.git)
|
||||
set(JSON_TAG
|
||||
v3.2.0)
|
||||
|
||||
if(USE_BUNDLED_JSON)
|
||||
include(Json)
|
||||
endif()
|
||||
|
||||
if(USE_BUNDLED_BOOST)
|
||||
include(Boost)
|
||||
endif()
|
||||
|
||||
if(USE_BUNDLED_SPDLOG)
|
||||
include(SpdLog)
|
||||
endif()
|
||||
|
||||
if(USE_BUNDLED_OLM)
|
||||
include(Olm)
|
||||
endif()
|
||||
|
||||
if(USE_BUNDLED_CMARK)
|
||||
include(cmark)
|
||||
endif()
|
||||
|
||||
if(USE_BUNDLED_TWEENY)
|
||||
include(Tweeny)
|
||||
endif()
|
||||
|
||||
if(USE_BUNDLED_LMDBXX)
|
||||
file(DOWNLOAD ${LMDBXX_HEADER_URL} ${DEPS_INSTALL_DIR}/include/lmdb++.h
|
||||
EXPECTED_HASH SHA256=${LMDBXX_HASH})
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if("${TARGET_ARCH}" STREQUAL "X86_64")
|
||||
set(TARGET_ARCH x64)
|
||||
elseif(TARGET_ARCH STREQUAL "X86")
|
||||
set(TARGET_ARCH ia32)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_custom_target(third-party ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E touch .third-party
|
||||
DEPENDS ${THIRD_PARTY_DEPS})
|
||||
|
||||
if(USE_BUNDLED_MATRIX_CLIENT)
|
||||
include(MatrixClient)
|
||||
add_dependencies(MatrixClient third-party)
|
||||
endif()
|
23
deps/cmake/Boost.cmake
vendored
|
@ -1,23 +0,0 @@
|
|||
if(WIN32)
|
||||
message(STATUS "Building Boost in Windows is not supported (skipping)")
|
||||
return()
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
Boost
|
||||
|
||||
URL ${BOOST_URL}
|
||||
URL_HASH SHA256=${BOOST_SHA256}
|
||||
DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/boost
|
||||
DOWNLOAD_NO_PROGRESS 0
|
||||
|
||||
BUILD_IN_SOURCE 1
|
||||
SOURCE_DIR ${DEPS_BUILD_DIR}/boost
|
||||
CONFIGURE_COMMAND ${DEPS_BUILD_DIR}/boost/bootstrap.sh
|
||||
--with-libraries=random,thread,system,iostreams,atomic,chrono,date_time,regex
|
||||
--prefix=${DEPS_INSTALL_DIR}
|
||||
BUILD_COMMAND ${DEPS_BUILD_DIR}/boost/b2 -d0 cxxstd=14 variant=release link=shared runtime-link=shared threading=multi --layout=system
|
||||
INSTALL_COMMAND ${DEPS_BUILD_DIR}/boost/b2 -d0 install
|
||||
)
|
||||
|
||||
list(APPEND THIRD_PARTY_DEPS Boost)
|
19
deps/cmake/Json.cmake
vendored
|
@ -1,19 +0,0 @@
|
|||
ExternalProject_Add(
|
||||
Json
|
||||
|
||||
GIT_REPOSITORY ${JSON_URL}
|
||||
GIT_TAG ${JSON_TAG}
|
||||
|
||||
BUILD_IN_SOURCE 1
|
||||
SOURCE_DIR ${DEPS_BUILD_DIR}/json
|
||||
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND}
|
||||
-DJSON_BuildTests=OFF
|
||||
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
|
||||
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
|
||||
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} --build ${DEPS_BUILD_DIR}/json
|
||||
INSTALL_COMMAND make install
|
||||
)
|
||||
|
||||
list(APPEND THIRD_PARTY_DEPS Json)
|
43
deps/cmake/MatrixClient.cmake
vendored
|
@ -1,43 +0,0 @@
|
|||
set(PLATFORM_FLAGS "")
|
||||
|
||||
if(MSVC)
|
||||
set(PLATFORM_FLAGS "-DCMAKE_GENERATOR_PLATFORM=x64")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(PLATFORM_FLAGS "-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl")
|
||||
endif()
|
||||
|
||||
# Force to build with the bundled version of Boost. This is necessary because
|
||||
# if an outdated version of Boost is installed, then CMake will grab that
|
||||
# instead of the bundled version of Boost, like we wanted.
|
||||
set(BOOST_BUNDLE_ROOT "-DBOOST_ROOT=${DEPS_BUILD_DIR}/boost")
|
||||
|
||||
set (MTX_SHARED ON)
|
||||
|
||||
if (MTX_STATIC)
|
||||
set (MTX_SHARED OFF)
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
MatrixClient
|
||||
|
||||
URL ${MTXCLIENT_URL}
|
||||
URL_HASH SHA256=${MTXCLIENT_HASH}
|
||||
|
||||
BUILD_IN_SOURCE 1
|
||||
SOURCE_DIR ${DEPS_BUILD_DIR}/mtxclient
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND}
|
||||
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DBUILD_LIB_TESTS=OFF
|
||||
-DBUILD_LIB_EXAMPLES=OFF
|
||||
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
|
||||
${BOOST_BUNDLE_ROOT}
|
||||
-DBUILD_SHARED_LIBS=${MTX_SHARED}
|
||||
${PLATFORM_FLAGS}
|
||||
${DEPS_BUILD_DIR}/mtxclient
|
||||
BUILD_COMMAND
|
||||
${CMAKE_COMMAND} --build ${DEPS_BUILD_DIR}/mtxclient --config Release)
|
||||
|
||||
list(APPEND THIRD_PARTY_DEPS MatrixClient)
|
34
deps/cmake/Olm.cmake
vendored
|
@ -1,34 +0,0 @@
|
|||
set(WINDOWS_FLAGS "")
|
||||
|
||||
if(MSVC)
|
||||
set(WINDOWS_FLAGS "-DCMAKE_GENERATOR_PLATFORM=x64")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
Olm
|
||||
|
||||
GIT_REPOSITORY ${OLM_URL}
|
||||
GIT_TAG ${OLM_TAG}
|
||||
|
||||
BUILD_IN_SOURCE 1
|
||||
SOURCE_DIR ${DEPS_BUILD_DIR}/olm
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/OlmCMakeLists.txt
|
||||
${DEPS_BUILD_DIR}/olm/CMakeLists.txt
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/OlmConfig.cmake.in
|
||||
${DEPS_BUILD_DIR}/olm/cmake/OlmConfig.cmake.in
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
${DEPS_BUILD_DIR}/olm
|
||||
${WINDOWS_FLAGS}
|
||||
BUILD_COMMAND ${CMAKE_COMMAND}
|
||||
--build ${DEPS_BUILD_DIR}/olm
|
||||
--config Release
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND}
|
||||
--build ${DEPS_BUILD_DIR}/olm
|
||||
--config Release
|
||||
--target install)
|
||||
|
||||
list(APPEND THIRD_PARTY_DEPS Olm)
|
107
deps/cmake/OlmCMakeLists.txt
vendored
|
@ -1,107 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
project(olm VERSION 2.2.2 LANGUAGES CXX C)
|
||||
|
||||
add_definitions(-DOLMLIB_VERSION_MAJOR=${PROJECT_VERSION_MAJOR})
|
||||
add_definitions(-DOLMLIB_VERSION_MINOR=${PROJECT_VERSION_MINOR})
|
||||
add_definitions(-DOLMLIB_VERSION_PATCH=${PROJECT_VERSION_PATCH})
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
|
||||
add_library(olm
|
||||
src/account.cpp
|
||||
src/base64.cpp
|
||||
src/cipher.cpp
|
||||
src/crypto.cpp
|
||||
src/memory.cpp
|
||||
src/message.cpp
|
||||
src/pickle.cpp
|
||||
src/ratchet.cpp
|
||||
src/session.cpp
|
||||
src/utility.cpp
|
||||
|
||||
src/ed25519.c
|
||||
src/error.c
|
||||
src/inbound_group_session.c
|
||||
src/megolm.c
|
||||
src/olm.cpp
|
||||
src/outbound_group_session.c
|
||||
src/pickle_encoding.c
|
||||
|
||||
lib/crypto-algorithms/aes.c
|
||||
lib/crypto-algorithms/sha256.c
|
||||
lib/curve25519-donna/curve25519-donna.c)
|
||||
add_library(Olm::Olm ALIAS olm)
|
||||
|
||||
target_include_directories(olm
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lib)
|
||||
|
||||
set_target_properties(olm PROPERTIES
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
VERSION ${PROJECT_VERSION})
|
||||
|
||||
set_target_properties(olm PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
#
|
||||
# Installation
|
||||
#
|
||||
include(GNUInstallDirs)
|
||||
set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/Olm)
|
||||
install(TARGETS olm
|
||||
EXPORT olm-targets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
# The exported target will be named Olm.
|
||||
set_target_properties(olm PROPERTIES EXPORT_NAME Olm)
|
||||
install(FILES
|
||||
${CMAKE_SOURCE_DIR}/include/olm/olm.h
|
||||
${CMAKE_SOURCE_DIR}/include/olm/outbound_group_session.h
|
||||
${CMAKE_SOURCE_DIR}/include/olm/inbound_group_session.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/olm)
|
||||
|
||||
# Export the targets to a script.
|
||||
install(EXPORT olm-targets
|
||||
FILE OlmTargets.cmake
|
||||
NAMESPACE Olm::
|
||||
DESTINATION ${INSTALL_CONFIGDIR})
|
||||
|
||||
# Create a ConfigVersion.cmake file.
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/OlmConfigVersion.cmake
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
|
||||
configure_package_config_file(
|
||||
${CMAKE_CURRENT_LIST_DIR}/cmake/OlmConfig.cmake.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/OlmConfig.cmake
|
||||
INSTALL_DESTINATION ${INSTALL_CONFIGDIR})
|
||||
|
||||
#Install the config & configversion.
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/OlmConfig.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/OlmConfigVersion.cmake
|
||||
DESTINATION ${INSTALL_CONFIGDIR})
|
||||
|
||||
# Register package in user's package registry
|
||||
export(EXPORT olm-targets
|
||||
FILE ${CMAKE_CURRENT_BINARY_DIR}/OlmTargets.cmake
|
||||
NAMESPACE Olm::)
|
||||
export(PACKAGE Olm)
|
11
deps/cmake/OlmConfig.cmake.in
vendored
|
@ -1,11 +0,0 @@
|
|||
get_filename_component(Olm_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${Olm_CMAKE_DIR})
|
||||
list(REMOVE_AT CMAKE_MODULE_PATH -1)
|
||||
|
||||
if(NOT TARGET Olm::Olm)
|
||||
include("${Olm_CMAKE_DIR}/OlmTargets.cmake")
|
||||
endif()
|
||||
|
||||
set(Olm_LIBRARIES Olm::Olm)
|
23
deps/cmake/SpdLog.cmake
vendored
|
@ -1,23 +0,0 @@
|
|||
set(WINDOWS_FLAGS "")
|
||||
|
||||
if(MSVC)
|
||||
set(WINDOWS_FLAGS "-DCMAKE_GENERATOR_PLATFORM=x64")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
SpdLog
|
||||
|
||||
URL ${SPDLOG_URL}
|
||||
URL_HASH SHA256=${SPDLOG_HASH}
|
||||
|
||||
BUILD_IN_SOURCE 1
|
||||
SOURCE_DIR ${DEPS_BUILD_DIR}/spdlog
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND}
|
||||
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
|
||||
-DSPDLOG_BUILD_EXAMPLES=0
|
||||
-DSPDLOG_BUILD_BENCH=0
|
||||
-DSPDLOG_BUILD_TESTING=0
|
||||
${DEPS_BUILD_DIR}/spdlog
|
||||
${WINDOWS_FLAGS})
|
||||
|
||||
list(APPEND THIRD_PARTY_DEPS SpdLog)
|
22
deps/cmake/Tweeny.cmake
vendored
|
@ -1,22 +0,0 @@
|
|||
set(WINDOWS_FLAGS "")
|
||||
|
||||
if(MSVC)
|
||||
set(WINDOWS_FLAGS "-DCMAKE_GENERATOR_PLATFORM=x64")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
Tweeny
|
||||
|
||||
URL ${TWEENY_URL}
|
||||
URL_HASH SHA256=${TWEENY_HASH}
|
||||
|
||||
BUILD_IN_SOURCE 1
|
||||
SOURCE_DIR ${DEPS_BUILD_DIR}/tweeny
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND}
|
||||
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
|
||||
-DTWEENY_BUILD_EXAMPLES=OFF
|
||||
-DTWEENY_BUILD_DOCUMENTATION=OFF
|
||||
${DEPS_BUILD_DIR}/tweeny
|
||||
${WINDOWS_FLAGS})
|
||||
|
||||
list(APPEND THIRD_PARTY_DEPS Tweeny)
|
21
deps/cmake/cmark.cmake
vendored
|
@ -1,21 +0,0 @@
|
|||
set(WINDOWS_FLAGS "")
|
||||
|
||||
if(MSVC)
|
||||
set(WINDOWS_FLAGS "-DCMAKE_GENERATOR_PLATFORM=x64")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
cmark
|
||||
|
||||
URL ${CMARK_URL}
|
||||
URL_HASH SHA256=${CMARK_HASH}
|
||||
|
||||
BUILD_IN_SOURCE 0
|
||||
SOURCE_DIR ${DEPS_BUILD_DIR}/cmark
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND}
|
||||
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
|
||||
-DCMARK_TESTS=OFF
|
||||
${DEPS_BUILD_DIR}/cmark
|
||||
${WINDOWS_FLAGS})
|
||||
|
||||
list(APPEND THIRD_PARTY_DEPS cmark)
|
194
io.github.NhekoReborn.Nheko.json
Normal file
|
@ -0,0 +1,194 @@
|
|||
{
|
||||
"id": "io.github.NhekoReborn.Nheko",
|
||||
"command": "nheko",
|
||||
"branch": "0.7.0-dev",
|
||||
"runtime": "org.kde.Platform",
|
||||
"runtime-version": "5.14",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"rename-icon": "nheko",
|
||||
"rename-desktop-file": "nheko.desktop",
|
||||
"rename-appdata-file": "nheko.appdata.xml",
|
||||
"finish-args": [
|
||||
"--device=dri",
|
||||
"--filesystem=home",
|
||||
"--share=ipc",
|
||||
"--share=network",
|
||||
"--socket=pulseaudio",
|
||||
"--socket=wayland",
|
||||
"--socket=x11",
|
||||
"--talk-name=org.freedesktop.Notifications",
|
||||
"--talk-name=org.kde.StatusNotifierWatcher"
|
||||
],
|
||||
"cleanup": [
|
||||
"/include",
|
||||
"/bin/mdb*",
|
||||
"*.a"
|
||||
],
|
||||
"build-options" : {
|
||||
"arch": {
|
||||
"aarch64": {
|
||||
"cxxflags": "-DBOOST_ASIO_DISABLE_EPOLL"
|
||||
}
|
||||
}
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"name": "lmdb",
|
||||
"sources": [
|
||||
{
|
||||
"sha256": "f3927859882eb608868c8c31586bb7eb84562a40a6bf5cc3e13b6b564641ea28",
|
||||
"type": "archive",
|
||||
"url": "https://github.com/LMDB/lmdb/archive/LMDB_0.9.22.tar.gz"
|
||||
}
|
||||
],
|
||||
"make-install-args": [
|
||||
"prefix=/app"
|
||||
],
|
||||
"no-autogen": true,
|
||||
"subdir": "libraries/liblmdb"
|
||||
},
|
||||
{
|
||||
"name": "cmark",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"builddir": true,
|
||||
"config-opts": [
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DCMARK_TESTS=OFF"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"sha256": "2558ace3cbeff85610de3bda32858f722b359acdadf0c4691851865bb84924a6",
|
||||
"type": "archive",
|
||||
"url": "https://github.com/commonmark/cmark/archive/0.29.0.tar.gz"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "spdlog",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"config-opts": [
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DSPDLOG_BUILD_EXAMPLES=0",
|
||||
"-DSPDLOG_BUILD_BENCH=0",
|
||||
"-DSPDLOG_BUILD_TESTING=0"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"sha256": "3dbcbfd8c07e25f5e0d662b194d3a7772ef214358c49ada23c044c4747ce8b19",
|
||||
"type": "archive",
|
||||
"url": "https://github.com/gabime/spdlog/archive/v1.1.0.tar.gz"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"config-opts": [
|
||||
"-DCMAKE_BUILD_TYPE=Release"
|
||||
],
|
||||
"buildsystem": "cmake-ninja",
|
||||
"name": "olm",
|
||||
"sources": [
|
||||
{
|
||||
"commit": "6753595300767dd70150831dbbe6f92d64e75038",
|
||||
"disable-shallow-clone": true,
|
||||
"tag": "3.1.4",
|
||||
"type": "git",
|
||||
"url": "https://gitlab.matrix.org/matrix-org/olm.git"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"config-opts":[
|
||||
"-DJSON_BuildTests=OFF"
|
||||
],
|
||||
"buildsystem":"cmake",
|
||||
"name": "nlohmann",
|
||||
"sources":[
|
||||
{
|
||||
"sha256": "d51a3a8d3efbb1139d7608e28782ea9efea7e7933157e8ff8184901efd8ee760",
|
||||
"type": "archive",
|
||||
"url": "https://github.com/nlohmann/json/archive/v3.7.0.tar.gz"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "sodium",
|
||||
"sources": [
|
||||
{
|
||||
"sha256": "6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e46636c1",
|
||||
"type": "archive",
|
||||
"url": "https://github.com/jedisct1/libsodium/releases/download/1.0.18-RELEASE/libsodium-1.0.18.tar.gz"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"build-commands": [
|
||||
"./bootstrap.sh --with-libraries=thread,system,iostreams --prefix=/app",
|
||||
"./b2 -d0 variant=release link=static threading=multi --layout=system",
|
||||
"./b2 -d0 install"
|
||||
],
|
||||
"buildsystem": "simple",
|
||||
"name": "boost",
|
||||
"sources": [
|
||||
{
|
||||
"sha256": "59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722",
|
||||
"type": "archive",
|
||||
"url": "https://dl.bintray.com/boostorg/release/1.72.0/source/boost_1_72_0.tar.bz2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"config-opts": [
|
||||
"-DBUILD_LIB_TESTS=OFF",
|
||||
"-DBUILD_LIB_EXAMPLES=OFF",
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DBUILD_SHARED_LIBS=OFF"
|
||||
],
|
||||
"buildsystem": "cmake-ninja",
|
||||
"name": "mtxclient",
|
||||
"sources": [
|
||||
{
|
||||
"sha256": "df3fe7e3d59b5fc52ee3ca9a132a55fc325aa799c676e9e420073c56daeb1848",
|
||||
"type": "archive",
|
||||
"url": "https://github.com/Nheko-Reborn/mtxclient/archive/5838f607d0e4c7595439249e8b9c213aec0667e9.tar.gz"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"config-opts": [
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DTWEENY_BUILD_DOCUMENTATION=OFF",
|
||||
"-DTWEENY_BUILD_EXAMPLES=OFF"
|
||||
],
|
||||
"buildsystem": "cmake-ninja",
|
||||
"name": "tweeny",
|
||||
"sources": [
|
||||
{
|
||||
"sha256": "482857256a7235646004682912badb6521d361ed6987c8ebdae7986bf64ce694",
|
||||
"type": "archive",
|
||||
"url": "https://github.com/mobius3/tweeny/archive/43f4130f7e4a67c19d870b60864bc2862c19b81f.tar.gz"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"config-opts": [
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DLMDBXX_INCLUDE_DIR=.deps/lmdbxx"
|
||||
],
|
||||
"buildsystem": "cmake-ninja",
|
||||
"name": "nheko",
|
||||
"sources": [
|
||||
{
|
||||
"path": ".",
|
||||
"type": "dir",
|
||||
"skip": ["build-flatpak"]
|
||||
},
|
||||
{
|
||||
"dest": ".deps/lmdbxx",
|
||||
"sha256": "93721132bbf5045d38ad62de2997655e9984c48ea5c9886746d42128f4b26fbd",
|
||||
"type": "archive",
|
||||
"url": "https://github.com/bendiken/lmdbxx/archive/0b43ca87d8cfabba392dfe884eb1edb83874de02.tar.gz"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
4446
resources/emoji-test.txt
Normal file
|
@ -1,202 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
1
resources/icons/ui/at-solid.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="at" class="svg-inline--fa fa-at fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C118.941 8 8 118.919 8 256c0 137.059 110.919 248 248 248 48.154 0 95.342-14.14 135.408-40.223 12.005-7.815 14.625-24.288 5.552-35.372l-10.177-12.433c-7.671-9.371-21.179-11.667-31.373-5.129C325.92 429.757 291.314 440 256 440c-101.458 0-184-82.542-184-184S154.542 72 256 72c100.139 0 184 57.619 184 160 0 38.786-21.093 79.742-58.17 83.693-17.349-.454-16.91-12.857-13.476-30.024l23.433-121.11C394.653 149.75 383.308 136 368.225 136h-44.981a13.518 13.518 0 0 0-13.432 11.993l-.01.092c-14.697-17.901-40.448-21.775-59.971-21.775-74.58 0-137.831 62.234-137.831 151.46 0 65.303 36.785 105.87 96 105.87 26.984 0 57.369-15.637 74.991-38.333 9.522 34.104 40.613 34.103 70.71 34.103C462.609 379.41 504 307.798 504 232 504 95.653 394.023 8 256 8zm-21.68 304.43c-22.249 0-36.07-15.623-36.07-40.771 0-44.993 30.779-72.729 58.63-72.729 22.292 0 35.601 15.241 35.601 40.77 0 45.061-33.875 72.73-58.161 72.73z"></path></svg>
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/icons/ui/mail-reply.png
Normal file
After Width: | Height: | Size: 373 B |
1369
resources/langs/nheko_fi.ts
Normal file
1368
resources/langs/nheko_ja.ts
Normal file
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 728 B After Width: | Height: | Size: 696 B |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
155
resources/nheko.svg
Normal file
|
@ -0,0 +1,155 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="1024"
|
||||
height="1024"
|
||||
viewBox="0 0 270.93333 270.93333"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"
|
||||
sodipodi:docname="nheko.svg"
|
||||
inkscape:export-filename="/home/nicolas/Dokumente/devel/open-source/nheko/resources/nheko-rebuild-round-corners.svg.png"
|
||||
inkscape:export-xdpi="130.048"
|
||||
inkscape:export-ydpi="130.048">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.35355339"
|
||||
inkscape:cx="852.07808"
|
||||
inkscape:cy="-60.410565"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer2"
|
||||
showgrid="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1019"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
showguides="true"
|
||||
inkscape:snap-grids="true"
|
||||
gridtolerance="10"
|
||||
inkscape:snap-bbox="false"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:lockguides="false"
|
||||
units="px">
|
||||
<sodipodi:guide
|
||||
position="0,0"
|
||||
orientation="0,793.70079"
|
||||
id="guide4797"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="0,297"
|
||||
orientation="1122.5197,0"
|
||||
id="guide4803"
|
||||
inkscape:locked="false" />
|
||||
<inkscape:grid
|
||||
type="axonomgrid"
|
||||
id="grid4805"
|
||||
units="px"
|
||||
empspacing="2"
|
||||
snapvisiblegridlinesonly="true"
|
||||
spacingy="1.0583333" />
|
||||
<sodipodi:guide
|
||||
position="0,0"
|
||||
orientation="0,755.90551"
|
||||
id="guide4807"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="200,0"
|
||||
orientation="-755.90551,0"
|
||||
id="guide4809"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="200,200"
|
||||
orientation="0,-755.90551"
|
||||
id="guide4811"
|
||||
inkscape:locked="false" />
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid871"
|
||||
empspacing="2"
|
||||
color="#d43fff"
|
||||
opacity="0.1254902"
|
||||
empcolor="#cf3fff"
|
||||
empopacity="0.25098039"
|
||||
units="px"
|
||||
spacingx="1.0583333"
|
||||
spacingy="1.0583333"
|
||||
enabled="false" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Logo"
|
||||
style="display:inline"
|
||||
transform="translate(0,-26.066668)">
|
||||
<circle
|
||||
id="path3792"
|
||||
cx="135.46666"
|
||||
cy="161.53333"
|
||||
style="display:inline;fill:#333333;fill-opacity:1;stroke:none;stroke-width:0.3584221"
|
||||
inkscape:transform-center-x="-57.929751"
|
||||
inkscape:transform-center-y="532.03976"
|
||||
inkscape:export-xdpi="96.000008"
|
||||
inkscape:export-ydpi="96.000008"
|
||||
r="135.46666" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.32663074px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 48.965212,110.73276 H 239.52342 c 4.88824,0 4.88824,0 0,8.46688 L 180.59519,221.2662 c -4.6188,8.00001 -4.6188,8.00001 -9.50702,8.00001 h -19.55294 c -4.88824,0 -4.88824,0 -0.26944,-8.00001 l 44.2635,-76.66608 h -29.41224 l -43.91123,76.19952 c -4.88823,8.46657 -4.88823,8.46657 -9.77646,8.46657 H 29.329398 l 49.299816,-84.66609 h -49.29982 z"
|
||||
id="path4834"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccccccccc"
|
||||
inkscape:export-xdpi="96.000008"
|
||||
inkscape:export-ydpi="96.000008" />
|
||||
<path
|
||||
style="fill:#c0def5;fill-opacity:1;stroke:none;stroke-width:0.3584221px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 97.764652,110.73276 H 127.09406 L 58.658797,229.26621 H 29.329398 Z"
|
||||
id="path4836"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:export-xdpi="96.000008"
|
||||
inkscape:export-ydpi="96.000008" />
|
||||
<path
|
||||
style="fill:#87aade;fill-opacity:1;stroke:none;stroke-width:0.3584221px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 58.658797,229.26621 127.09406,110.73276 h 29.3294 L 87.988193,229.26621 Z"
|
||||
id="path4838"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:export-xdpi="96.000008"
|
||||
inkscape:export-ydpi="96.000008" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
style="display:inline"
|
||||
transform="translate(0,-26.066668)" />
|
||||
</svg>
|
After Width: | Height: | Size: 5.2 KiB |
53
resources/qml/Avatar.qml
Normal file
|
@ -0,0 +1,53 @@
|
|||
import QtQuick 2.6
|
||||
import QtGraphicalEffects 1.0
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
Rectangle {
|
||||
id: avatar
|
||||
width: 48
|
||||
height: 48
|
||||
radius: settings.avatar_circles ? height/2 : 3
|
||||
|
||||
Settings {
|
||||
id: settings
|
||||
category: "user"
|
||||
property bool avatar_circles: true
|
||||
}
|
||||
|
||||
property alias url: img.source
|
||||
property string displayName
|
||||
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
text: chat.model.escapeEmoji(String.fromCodePoint(displayName.codePointAt(0)))
|
||||
textFormat: Text.RichText
|
||||
color: colors.text
|
||||
font.pixelSize: avatar.height/2
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: img.status != Image.Ready
|
||||
}
|
||||
|
||||
Image {
|
||||
id: img
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
mipmap: true
|
||||
smooth: false
|
||||
|
||||
sourceSize.width: avatar.width
|
||||
sourceSize.height: avatar.height
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
anchors.fill: parent
|
||||
width: avatar.width
|
||||
height: avatar.height
|
||||
radius: settings.avatar_circles ? height/2 : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
color: colors.base
|
||||
}
|
26
resources/qml/EncryptionIndicator.qml
Normal file
|
@ -0,0 +1,26 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.1
|
||||
import im.nheko 1.0
|
||||
|
||||
Rectangle {
|
||||
id: indicator
|
||||
color: "transparent"
|
||||
width: 16
|
||||
height: 16
|
||||
|
||||
ToolTip.visible: ma.containsMouse && indicator.visible
|
||||
ToolTip.text: qsTr("Encrypted")
|
||||
|
||||
MouseArea{
|
||||
id: ma
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
}
|
||||
|
||||
Image {
|
||||
id: stateImg
|
||||
anchors.fill: parent
|
||||
source: "image://colorimage/:/icons/icons/ui/lock.png?"+colors.buttonText
|
||||
}
|
||||
}
|
||||
|
29
resources/qml/ImageButton.qml
Normal file
|
@ -0,0 +1,29 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 2.3
|
||||
|
||||
Button {
|
||||
property string image: undefined
|
||||
|
||||
id: button
|
||||
|
||||
flat: true
|
||||
|
||||
// disable background, because we don't want a border on hover
|
||||
background: Item {
|
||||
}
|
||||
|
||||
Image {
|
||||
id: buttonImg
|
||||
// Workaround, can't get icon.source working for now...
|
||||
anchors.fill: parent
|
||||
source: "image://colorimage/" + image + "?" + (button.hovered ? colors.highlight : colors.buttonText)
|
||||
}
|
||||
|
||||
MouseArea
|
||||
{
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
onPressed: mouse.accepted = false
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
32
resources/qml/MatrixText.qml
Normal file
|
@ -0,0 +1,32 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.3
|
||||
|
||||
TextEdit {
|
||||
textFormat: TextEdit.RichText
|
||||
readOnly: true
|
||||
wrapMode: Text.Wrap
|
||||
selectByMouse: true
|
||||
color: colors.text
|
||||
|
||||
onLinkActivated: {
|
||||
if (/^https:\/\/matrix.to\/#\/(@.*)$/.test(link)) chat.model.openUserProfile(/^https:\/\/matrix.to\/#\/(@.*)$/.exec(link)[1])
|
||||
else if (/^https:\/\/matrix.to\/#\/(![^\/]*)$/.test(link)) timelineManager.setHistoryView(/^https:\/\/matrix.to\/#\/(!.*)$/.exec(link)[1])
|
||||
else if (/^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.test(link)) {
|
||||
var match = /^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.exec(link)
|
||||
timelineManager.setHistoryView(match[1])
|
||||
chat.positionViewAtIndex(chat.model.idToIndex(match[2]), ListView.Contain)
|
||||
}
|
||||
else Qt.openUrlExternally(link)
|
||||
}
|
||||
MouseArea
|
||||
{
|
||||
id: ma
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
|
||||
ToolTip.visible: hoveredLink
|
||||
ToolTip.text: hoveredLink
|
||||
}
|
39
resources/qml/StatusIndicator.qml
Normal file
|
@ -0,0 +1,39 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.1
|
||||
import im.nheko 1.0
|
||||
|
||||
Rectangle {
|
||||
id: indicator
|
||||
property int state: 0
|
||||
color: "transparent"
|
||||
width: 16
|
||||
height: 16
|
||||
|
||||
ToolTip.visible: ma.containsMouse && state != MtxEvent.Empty
|
||||
ToolTip.text: switch (state) {
|
||||
case MtxEvent.Failed: return qsTr("Failed")
|
||||
case MtxEvent.Sent: return qsTr("Sent")
|
||||
case MtxEvent.Received: return qsTr("Received")
|
||||
case MtxEvent.Read: return qsTr("Read")
|
||||
default: return ""
|
||||
}
|
||||
MouseArea{
|
||||
id: ma
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
}
|
||||
|
||||
Image {
|
||||
id: stateImg
|
||||
// Workaround, can't get icon.source working for now...
|
||||
anchors.fill: parent
|
||||
source: switch (indicator.state) {
|
||||
case MtxEvent.Failed: return "image://colorimage/:/icons/icons/ui/remove-symbol.png?" + colors.buttonText
|
||||
case MtxEvent.Sent: return "image://colorimage/:/icons/icons/ui/clock.png?" + colors.buttonText
|
||||
case MtxEvent.Received: return "image://colorimage/:/icons/icons/ui/checkmark.png?" + colors.buttonText
|
||||
case MtxEvent.Read: return "image://colorimage/:/icons/icons/ui/double-tick-indicator.png?" + colors.buttonText
|
||||
default: return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
134
resources/qml/TimelineRow.qml
Normal file
|
@ -0,0 +1,134 @@
|
|||
import QtQuick 2.6
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQuick.Window 2.2
|
||||
|
||||
import im.nheko 1.0
|
||||
|
||||
import "./delegates"
|
||||
|
||||
MouseArea {
|
||||
id: rowArea
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: row.height
|
||||
|
||||
hoverEnabled: true
|
||||
preventStealing: true
|
||||
propagateComposedEvents: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
|
||||
property bool showButtons: false
|
||||
|
||||
Timer {
|
||||
running: rowArea.containsMouse
|
||||
interval: 150
|
||||
onTriggered: rowArea.state = "showButtons"
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "hideButtons"
|
||||
when: !rowArea.containsMouse
|
||||
PropertyChanges { target: rowArea; showButtons: false; }
|
||||
},
|
||||
State {
|
||||
name: "showButtons"
|
||||
PropertyChanges { target: rowArea; showButtons: true; }
|
||||
}
|
||||
]
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.leftMargin: avatarSize + 4
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
|
||||
Column {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
spacing: 4
|
||||
|
||||
// fancy reply, if this is a reply
|
||||
Reply {
|
||||
visible: model.replyTo
|
||||
modelData: chat.model.getDump(model.replyTo)
|
||||
userColor: timelineManager.userColor(modelData.userId, colors.window)
|
||||
}
|
||||
|
||||
// actual message content
|
||||
MessageDelegate {
|
||||
id: contentItem
|
||||
|
||||
width: parent.width
|
||||
|
||||
modelData: model
|
||||
}
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
visible: rowArea.showButtons
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.preferredHeight: 16
|
||||
width: 16
|
||||
id: replyButton
|
||||
hoverEnabled: true
|
||||
|
||||
|
||||
image: ":/icons/icons/ui/mail-reply.png"
|
||||
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Reply")
|
||||
|
||||
onClicked: chat.model.replyAction(model.id)
|
||||
}
|
||||
ImageButton {
|
||||
visible: rowArea.showButtons
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.preferredHeight: 16
|
||||
width: 16
|
||||
id: optionsButton
|
||||
hoverEnabled: true
|
||||
|
||||
image: ":/icons/icons/ui/vertical-ellipsis.png"
|
||||
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Options")
|
||||
|
||||
onClicked: messageContextMenu.show(model.id, model.type, optionsButton)
|
||||
|
||||
}
|
||||
|
||||
StatusIndicator {
|
||||
state: model.state
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.preferredHeight: 16
|
||||
width: 16
|
||||
}
|
||||
|
||||
EncryptionIndicator {
|
||||
visible: model.isEncrypted
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.preferredHeight: 16
|
||||
width: 16
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
text: model.timestamp.toLocaleTimeString("HH:mm")
|
||||
color: inactiveColors.text
|
||||
|
||||
MouseArea{
|
||||
id: ma
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
}
|
||||
|
||||
ToolTip.visible: ma.containsMouse
|
||||
ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate)
|
||||
}
|
||||
}
|
||||
}
|
309
resources/qml/TimelineView.qml
Normal file
|
@ -0,0 +1,309 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.2
|
||||
|
||||
import im.nheko 1.0
|
||||
|
||||
import "./delegates"
|
||||
|
||||
Item {
|
||||
property var colors: currentActivePalette
|
||||
property var systemInactive: SystemPalette { colorGroup: SystemPalette.Disabled }
|
||||
property var inactiveColors: currentInactivePalette ? currentInactivePalette : systemInactive
|
||||
property int avatarSize: 40
|
||||
|
||||
Menu {
|
||||
id: messageContextMenu
|
||||
palette: colors
|
||||
modal: true
|
||||
|
||||
function show(eventId_, eventType_, showAt) {
|
||||
eventId = eventId_
|
||||
eventType = eventType_
|
||||
popup(showAt)
|
||||
}
|
||||
|
||||
property string eventId
|
||||
property int eventType
|
||||
|
||||
MenuItem {
|
||||
text: qsTr("Read receipts")
|
||||
onTriggered: chat.model.readReceiptsAction(messageContextMenu.eventId)
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Mark as read")
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("View raw message")
|
||||
onTriggered: chat.model.viewRawMessage(messageContextMenu.eventId)
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Redact message")
|
||||
onTriggered: chat.model.redactEvent(messageContextMenu.eventId)
|
||||
}
|
||||
MenuItem {
|
||||
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
|
||||
text: qsTr("Save as")
|
||||
onTriggered: timelineManager.timeline.saveMedia(messageContextMenu.eventId)
|
||||
}
|
||||
}
|
||||
|
||||
id: timelineRoot
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: colors.window
|
||||
|
||||
Text {
|
||||
visible: !timelineManager.timeline && !timelineManager.isInitialSync
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("No room open")
|
||||
font.pointSize: 24
|
||||
color: colors.windowText
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
running: timelineManager.isInitialSync
|
||||
height: 200
|
||||
width: 200
|
||||
z: 3
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: chat
|
||||
|
||||
visible: timelineManager.timeline != null
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: chatFooter.top
|
||||
|
||||
anchors.leftMargin: 4
|
||||
anchors.rightMargin: scrollbar.width
|
||||
|
||||
model: timelineManager.timeline
|
||||
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
pixelAligned: true
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
z: -1
|
||||
onWheel: {
|
||||
if (wheel.angleDelta != 0) {
|
||||
chat.contentY = chat.contentY - wheel.angleDelta.y
|
||||
wheel.accepted = true
|
||||
chat.returnToBounds()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.MoveToPreviousPage
|
||||
onActivated: { chat.contentY = chat.contentY - chat.height / 2; chat.returnToBounds(); }
|
||||
}
|
||||
Shortcut {
|
||||
sequence: StandardKey.MoveToNextPage
|
||||
onActivated: { chat.contentY = chat.contentY + chat.height / 2; chat.returnToBounds(); }
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
id: scrollbar
|
||||
parent: chat.parent
|
||||
anchors.top: chat.top
|
||||
anchors.left: chat.right
|
||||
anchors.bottom: chat.bottom
|
||||
}
|
||||
|
||||
spacing: 4
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
|
||||
onCountChanged: if (atYEnd) model.currentIndex = 0 // Mark last event as read, since we are at the bottom
|
||||
|
||||
delegate: Rectangle {
|
||||
// This would normally be previousSection, but our model's order is inverted.
|
||||
property bool sectionBoundary: (ListView.nextSection != "" && ListView.nextSection !== ListView.section) || model.index === chat.count - 1
|
||||
|
||||
id: wrapper
|
||||
property Item section
|
||||
width: chat.width
|
||||
height: section ? section.height + timelinerow.height : timelinerow.height
|
||||
color: "transparent"
|
||||
|
||||
TimelineRow {
|
||||
id: timelinerow
|
||||
y: section ? section.y + section.height : 0
|
||||
}
|
||||
|
||||
onSectionBoundaryChanged: {
|
||||
if (sectionBoundary) {
|
||||
var properties = {
|
||||
'modelData': model.dump,
|
||||
'section': ListView.section,
|
||||
'nextSection': ListView.nextSection
|
||||
}
|
||||
section = sectionHeader.createObject(wrapper, properties)
|
||||
} else {
|
||||
section.destroy()
|
||||
section = null
|
||||
}
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: chat.model
|
||||
property: "currentIndex"
|
||||
when: y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height
|
||||
value: index
|
||||
delayed: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
section {
|
||||
property: "section"
|
||||
}
|
||||
Component {
|
||||
id: sectionHeader
|
||||
Column {
|
||||
property var modelData
|
||||
property string section
|
||||
property string nextSection
|
||||
|
||||
topPadding: 4
|
||||
bottomPadding: 4
|
||||
spacing: 8
|
||||
|
||||
visible: !!modelData
|
||||
|
||||
width: parent.width
|
||||
height: (section.includes(" ") ? dateBubble.height + 8 + userName.height : userName.height) + 8
|
||||
|
||||
Label {
|
||||
id: dateBubble
|
||||
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
|
||||
visible: section.includes(" ")
|
||||
text: chat.model.formatDateSeparator(modelData.timestamp)
|
||||
color: colors.windowText
|
||||
|
||||
height: contentHeight * 1.2
|
||||
width: contentWidth * 1.2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
background: Rectangle {
|
||||
radius: parent.height / 2
|
||||
color: colors.base
|
||||
}
|
||||
}
|
||||
Row {
|
||||
height: userName.height
|
||||
spacing: 4
|
||||
Avatar {
|
||||
width: avatarSize
|
||||
height: avatarSize
|
||||
url: chat.model.avatarUrl(modelData.userId).replace("mxc://", "image://MxcImage/")
|
||||
displayName: modelData.userName
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: chat.model.openUserProfile(modelData.userId)
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: userName
|
||||
text: chat.model.escapeEmoji(modelData.userName)
|
||||
color: timelineManager.userColor(modelData.userId, colors.window)
|
||||
textFormat: Text.RichText
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: chat.model.openUserProfile(section.split(" ")[0])
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: chatFooter
|
||||
|
||||
height: Math.max(16, footerContent.height)
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
z: 3
|
||||
|
||||
color: "transparent"
|
||||
|
||||
Column {
|
||||
id: footerContent
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Text {
|
||||
id: typingDisplay
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
|
||||
text: chat.model ? chat.model.formatTypingUsers(chat.model.typingUsers, colors.window) : ""
|
||||
textFormat: Text.RichText
|
||||
color: colors.windowText
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
id: replyPopup
|
||||
|
||||
visible: timelineManager.replyingEvent && chat.model
|
||||
// Height of child, plus margins, plus border
|
||||
height: replyPreview.height + 10
|
||||
color: colors.base
|
||||
|
||||
|
||||
Reply {
|
||||
id: replyPreview
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 10
|
||||
anchors.right: closeReplyButton.left
|
||||
anchors.rightMargin: 20
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
modelData: chat.model ? chat.model.getDump(timelineManager.replyingEvent) : {}
|
||||
userColor: timelineManager.userColor(modelData.userId, colors.window)
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
id: closeReplyButton
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
anchors.top: replyPreview.top
|
||||
hoverEnabled: true
|
||||
width: 16
|
||||
height: 16
|
||||
|
||||
image: ":/icons/icons/ui/remove-symbol.png"
|
||||
ToolTip.visible: closeReplyButton.hovered
|
||||
ToolTip.text: qsTr("Close")
|
||||
|
||||
onClicked: timelineManager.closeReply()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
57
resources/qml/delegates/FileMessage.qml
Normal file
|
@ -0,0 +1,57 @@
|
|||
import QtQuick 2.6
|
||||
import QtQuick.Layouts 1.2
|
||||
|
||||
Rectangle {
|
||||
radius: 10
|
||||
color: colors.base
|
||||
height: row.height + 24
|
||||
width: parent ? parent.width : undefined
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - 24
|
||||
|
||||
spacing: 15
|
||||
|
||||
Rectangle {
|
||||
id: button
|
||||
color: colors.light
|
||||
radius: 22
|
||||
height: 44
|
||||
width: 44
|
||||
Image {
|
||||
id: img
|
||||
anchors.centerIn: parent
|
||||
|
||||
source: "qrc:/icons/icons/ui/arrow-pointing-down.png"
|
||||
fillMode: Image.Pad
|
||||
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: timelineManager.timeline.saveMedia(model.data.id)
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
id: col
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: model.data.body
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
color: colors.text
|
||||
}
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: model.data.filesize
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
color: colors.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
28
resources/qml/delegates/ImageMessage.qml
Normal file
|
@ -0,0 +1,28 @@
|
|||
import QtQuick 2.6
|
||||
|
||||
import im.nheko 1.0
|
||||
|
||||
Item {
|
||||
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width)
|
||||
property double tempHeight: tempWidth * model.data.proportionalHeight
|
||||
|
||||
property bool tooHigh: tempHeight > timelineRoot.height / 2
|
||||
|
||||
height: tooHigh ? timelineRoot.height / 2 : tempHeight
|
||||
width: tooHigh ? (timelineRoot.height / 2) / model.data.proportionalHeight : tempWidth
|
||||
|
||||
Image {
|
||||
id: img
|
||||
anchors.fill: parent
|
||||
|
||||
source: model.data.url.replace("mxc://", "image://MxcImage/")
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
|
||||
MouseArea {
|
||||
enabled: model.data.type == MtxEvent.ImageMessage
|
||||
anchors.fill: parent
|
||||
onClicked: timelineManager.openImageOverlay(model.data.url, model.data.id)
|
||||
}
|
||||
}
|
||||
}
|
94
resources/qml/delegates/MessageDelegate.qml
Normal file
|
@ -0,0 +1,94 @@
|
|||
import QtQuick 2.6
|
||||
import im.nheko 1.0
|
||||
|
||||
Item {
|
||||
// Workaround to have an assignable global property
|
||||
Item {
|
||||
id: model
|
||||
property var data;
|
||||
}
|
||||
|
||||
property alias modelData: model.data
|
||||
|
||||
height: chooser.childrenRect.height
|
||||
|
||||
DelegateChooser {
|
||||
id: chooser
|
||||
//role: "type" //< not supported in our custom implementation, have to use roleValue
|
||||
roleValue: model.data.type
|
||||
anchors.fill: parent
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.UnknownMessage
|
||||
Placeholder { text: "Unretrieved event" }
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.TextMessage
|
||||
TextMessage {}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.NoticeMessage
|
||||
NoticeMessage {}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.EmoteMessage
|
||||
NoticeMessage {
|
||||
formatted: chat.model.escapeEmoji(modelData.userName) + " " + model.data.formattedBody
|
||||
color: timelineManager.userColor(modelData.userId, colors.window)
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.ImageMessage
|
||||
ImageMessage {}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Sticker
|
||||
ImageMessage {}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.FileMessage
|
||||
FileMessage {}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.VideoMessage
|
||||
PlayableMediaMessage {}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.AudioMessage
|
||||
PlayableMediaMessage {}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Redacted
|
||||
Pill {
|
||||
text: qsTr("redacted")
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Encryption
|
||||
Pill {
|
||||
text: qsTr("Encryption enabled")
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Name
|
||||
NoticeMessage {
|
||||
text: model.data.roomName ? qsTr("room name changed to: %1").arg(model.data.roomName) : qsTr("removed room name")
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Topic
|
||||
NoticeMessage {
|
||||
text: model.data.roomTopic ? qsTr("topic changed to: %1").arg(model.data.roomTopic) : qsTr("removed topic")
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Member
|
||||
NoticeMessage {
|
||||
text: timelineManager.timeline.formatMemberEvent(model.data.id);
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
Placeholder {}
|
||||
}
|
||||
}
|
||||
}
|
4
resources/qml/delegates/NoticeMessage.qml
Normal file
|
@ -0,0 +1,4 @@
|
|||
TextMessage {
|
||||
font.italic: true
|
||||
color: inactiveColors.text
|
||||
}
|
14
resources/qml/delegates/Pill.qml
Normal file
|
@ -0,0 +1,14 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.1
|
||||
|
||||
Label {
|
||||
color: inactiveColors.text
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
height: contentHeight * 1.2
|
||||
width: contentWidth * 1.2
|
||||
background: Rectangle {
|
||||
radius: parent.height / 2
|
||||
color: colors.base
|
||||
}
|
||||
}
|
7
resources/qml/delegates/Placeholder.qml
Normal file
|
@ -0,0 +1,7 @@
|
|||
import ".."
|
||||
|
||||
MatrixText {
|
||||
text: qsTr("unimplemented event: ") + model.data.typeString
|
||||
width: parent ? parent.width : undefined
|
||||
color: inactiveColors.text
|
||||
}
|
167
resources/qml/delegates/PlayableMediaMessage.qml
Normal file
|
@ -0,0 +1,167 @@
|
|||
import QtQuick 2.6
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQuick.Controls 2.1
|
||||
import QtMultimedia 5.6
|
||||
|
||||
import im.nheko 1.0
|
||||
|
||||
Rectangle {
|
||||
id: bg
|
||||
radius: 10
|
||||
color: colors.base
|
||||
height: content.height + 24
|
||||
width: parent ? parent.width : undefined
|
||||
|
||||
Column {
|
||||
id: content
|
||||
width: parent.width - 24
|
||||
anchors.centerIn: parent
|
||||
|
||||
Rectangle {
|
||||
id: videoContainer
|
||||
visible: model.data.type == MtxEvent.VideoMessage
|
||||
width: Math.min(parent.width, model.data.width ? model.data.width : 400) // some media has 0 as size...
|
||||
height: width*model.data.proportionalHeight
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: model.data.thumbnailUrl.replace("mxc://", "image://MxcImage/")
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
|
||||
VideoOutput {
|
||||
anchors.fill: parent
|
||||
fillMode: VideoOutput.PreserveAspectFit
|
||||
source: media
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
Text {
|
||||
id: positionText
|
||||
text: "--:--:--"
|
||||
color: colors.text
|
||||
}
|
||||
Slider {
|
||||
Layout.fillWidth: true
|
||||
id: progress
|
||||
value: media.position
|
||||
from: 0
|
||||
to: media.duration
|
||||
|
||||
onMoved: media.seek(value)
|
||||
//indeterminate: true
|
||||
function updatePositionTexts() {
|
||||
function formatTime(date) {
|
||||
var hh = date.getUTCHours();
|
||||
var mm = date.getUTCMinutes();
|
||||
var ss = date.getSeconds();
|
||||
if (hh < 10) {hh = "0"+hh;}
|
||||
if (mm < 10) {mm = "0"+mm;}
|
||||
if (ss < 10) {ss = "0"+ss;}
|
||||
return hh+":"+mm+":"+ss;
|
||||
}
|
||||
positionText.text = formatTime(new Date(media.position))
|
||||
durationText.text = formatTime(new Date(media.duration))
|
||||
}
|
||||
onValueChanged: updatePositionTexts()
|
||||
|
||||
palette: colors
|
||||
}
|
||||
Text {
|
||||
id: durationText
|
||||
text: "--:--:--"
|
||||
color: colors.text
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
|
||||
spacing: 15
|
||||
|
||||
Rectangle {
|
||||
id: button
|
||||
color: colors.window
|
||||
radius: 22
|
||||
height: 44
|
||||
width: 44
|
||||
Image {
|
||||
id: img
|
||||
anchors.centerIn: parent
|
||||
z: 3
|
||||
|
||||
source: "image://colorimage/:/icons/icons/ui/arrow-pointing-down.png?"+colors.text
|
||||
fillMode: Image.Pad
|
||||
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
switch (button.state) {
|
||||
case "": timelineManager.timeline.cacheMedia(model.data.id); break;
|
||||
case "stopped":
|
||||
media.play(); console.log("play");
|
||||
button.state = "playing"
|
||||
break
|
||||
case "playing":
|
||||
media.pause(); console.log("pause");
|
||||
button.state = "stopped"
|
||||
break
|
||||
}
|
||||
}
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
MediaPlayer {
|
||||
id: media
|
||||
onError: console.log(errorString)
|
||||
onStatusChanged: if(status == MediaPlayer.Loaded) progress.updatePositionTexts()
|
||||
onStopped: button.state = "stopped"
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: timelineManager.timeline
|
||||
onMediaCached: {
|
||||
if (mxcUrl == model.data.url) {
|
||||
media.source = "file://" + cacheUrl
|
||||
button.state = "stopped"
|
||||
console.log("media loaded: " + mxcUrl + " at " + cacheUrl)
|
||||
}
|
||||
console.log("media cached: " + mxcUrl + " at " + cacheUrl)
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "stopped"
|
||||
PropertyChanges { target: img; source: "image://colorimage/:/icons/icons/ui/play-sign.png?"+colors.text }
|
||||
},
|
||||
State {
|
||||
name: "playing"
|
||||
PropertyChanges { target: img; source: "image://colorimage/:/icons/icons/ui/pause-symbol.png?"+colors.text }
|
||||
}
|
||||
]
|
||||
}
|
||||
ColumnLayout {
|
||||
id: col
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: model.data.body
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
color: colors.text
|
||||
}
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: model.data.filesize
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
color: colors.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
58
resources/qml/delegates/Reply.qml
Normal file
|
@ -0,0 +1,58 @@
|
|||
import QtQuick 2.6
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQuick.Window 2.2
|
||||
|
||||
Rectangle {
|
||||
id: replyComponent
|
||||
|
||||
property alias modelData: reply.modelData
|
||||
property color userColor: "red"
|
||||
|
||||
width: parent.width
|
||||
height: replyContainer.height
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
preventStealing: true
|
||||
onClicked: chat.positionViewAtIndex(chat.model.idToIndex(timelineManager.replyingEvent), ListView.Contain)
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: colorLine
|
||||
|
||||
anchors.top: replyContainer.top
|
||||
anchors.bottom: replyContainer.bottom
|
||||
width: 4
|
||||
|
||||
color: timelineManager.userColor(reply.modelData.userId, colors.window)
|
||||
}
|
||||
|
||||
Column {
|
||||
id: replyContainer
|
||||
anchors.left: colorLine.right
|
||||
anchors.leftMargin: 4
|
||||
width: parent.width - 8
|
||||
|
||||
Text {
|
||||
id: userName
|
||||
text: chat.model ? chat.model.escapeEmoji(reply.modelData.userName) : ""
|
||||
color: replyComponent.userColor
|
||||
textFormat: Text.RichText
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: chat.model.openUserProfile(reply.modelData.userId)
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
|
||||
MessageDelegate {
|
||||
id: reply
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
color: Qt.rgba(userColor.r, userColor.g, userColor.b, 0.2)
|
||||
}
|
7
resources/qml/delegates/TextMessage.qml
Normal file
|
@ -0,0 +1,7 @@
|
|||
import ".."
|
||||
|
||||
MatrixText {
|
||||
property string formatted: model.data.formattedBody
|
||||
text: "<style type=\"text/css\">a { color:"+colors.link+";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap'>")
|
||||
width: parent ? parent.width : undefined
|
||||
}
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
@ -1,5 +1,6 @@
|
|||
<RCC>
|
||||
<qresource prefix="/icons">
|
||||
<file>icons/ui/at-solid.svg</file>
|
||||
<file>icons/ui/volume-off-indicator.png</file>
|
||||
<file>icons/ui/volume-off-indicator@2x.png</file>
|
||||
<file>icons/ui/black-bubble-speech.png</file>
|
||||
|
@ -63,6 +64,8 @@
|
|||
<file>icons/ui/edit.png</file>
|
||||
<file>icons/ui/edit@2x.png</file>
|
||||
|
||||
<file>icons/ui/mail-reply.png</file>
|
||||
|
||||
<file>icons/emoji-categories/people.png</file>
|
||||
<file>icons/emoji-categories/people@2x.png</file>
|
||||
<file>icons/emoji-categories/nature.png</file>
|
||||
|
@ -82,6 +85,7 @@
|
|||
</qresource>
|
||||
<qresource prefix="/logos">
|
||||
<file>nheko.png</file>
|
||||
<file>nheko.svg</file>
|
||||
|
||||
<file>splash.png</file>
|
||||
<file>splash@2x.png</file>
|
||||
|
@ -99,16 +103,27 @@
|
|||
<file>nheko-32.png</file>
|
||||
<file>nheko-16.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/fonts">
|
||||
<file>fonts/OpenSans/OpenSans-Regular.ttf</file>
|
||||
<file>fonts/OpenSans/OpenSans-Italic.ttf</file>
|
||||
<file>fonts/OpenSans/OpenSans-Bold.ttf</file>
|
||||
<file>fonts/OpenSans/OpenSans-Semibold.ttf</file>
|
||||
<file>fonts/EmojiOne/emojione-android.ttf</file>
|
||||
</qresource>
|
||||
<qresource prefix="/styles">
|
||||
<file>styles/system.qss</file>
|
||||
<file>styles/nheko.qss</file>
|
||||
<file>styles/nheko-dark.qss</file>
|
||||
</qresource>
|
||||
<qresource prefix="/">
|
||||
<file>qml/TimelineView.qml</file>
|
||||
<file>qml/Avatar.qml</file>
|
||||
<file>qml/ImageButton.qml</file>
|
||||
<file>qml/MatrixText.qml</file>
|
||||
<file>qml/StatusIndicator.qml</file>
|
||||
<file>qml/EncryptionIndicator.qml</file>
|
||||
<file>qml/TimelineRow.qml</file>
|
||||
<file>qml/delegates/MessageDelegate.qml</file>
|
||||
<file>qml/delegates/TextMessage.qml</file>
|
||||
<file>qml/delegates/NoticeMessage.qml</file>
|
||||
<file>qml/delegates/ImageMessage.qml</file>
|
||||
<file>qml/delegates/PlayableMediaMessage.qml</file>
|
||||
<file>qml/delegates/FileMessage.qml</file>
|
||||
<file>qml/delegates/Pill.qml</file>
|
||||
<file>qml/delegates/Placeholder.qml</file>
|
||||
<file>qml/delegates/Reply.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
@ -3,13 +3,75 @@ QLabel {
|
|||
color: #caccd1;
|
||||
}
|
||||
|
||||
TimelineItem {
|
||||
qproperty-backgroundColor: #202228;
|
||||
TextLabel::a {
|
||||
color: #38a3d8;
|
||||
}
|
||||
|
||||
QuickSwitcher,
|
||||
ReplyPopup,
|
||||
SuggestionsPopup,
|
||||
UserSettingsPage,
|
||||
#scroll_widget,
|
||||
#UserSettingScrollWidget {
|
||||
background-color: #202228;
|
||||
}
|
||||
|
||||
#chatPage,
|
||||
#chatPage > * {
|
||||
#chatPage > *,
|
||||
CommunitiesList,
|
||||
CommunitiesList > *,
|
||||
RoomList,
|
||||
RoomList > *,
|
||||
TimelineView,
|
||||
TimelineView > *,
|
||||
UserMentionsWidget,
|
||||
UserMentionsWidget > * {
|
||||
background-color: #2d3139;
|
||||
border: none;
|
||||
}
|
||||
|
||||
QLineEdit,
|
||||
QListWidget,
|
||||
WelcomePage,
|
||||
LoginPage,
|
||||
RegisterPage,
|
||||
EditModal,
|
||||
emoji--Panel,
|
||||
emoji--Panel > *,
|
||||
dialogs--Logout,
|
||||
dialogs--ReCaptcha,
|
||||
dialogs--LeaveRoom,
|
||||
dialogs--CreateRoom,
|
||||
dialogs--RoomSettings,
|
||||
dialogs--InviteUsers,
|
||||
dialogs--ReadReceipts,
|
||||
dialogs--JoinRoom,
|
||||
dialogs--MemberList,
|
||||
dialogs--PreviewUploadOverlay,
|
||||
dialogs--UserProfile,
|
||||
dialogs--CreateRoom > QLineEdit,
|
||||
dialogs--InviteUsers > QLineEdit,
|
||||
dialogs--JoinRoom > QLineEdit {
|
||||
background-color: #202228;
|
||||
color: #caccd1;
|
||||
}
|
||||
|
||||
emoji--Panel QWidget { border: none; }
|
||||
emoji--Panel QScrollBar:vertical { width: 0px; margin: 0px; }
|
||||
emoji--Panel QScrollBar::handle:vertical { min-height: 30px; }
|
||||
|
||||
emoji--Category,
|
||||
emoji--Category > * {
|
||||
background-color: #2d3139;
|
||||
color: #727274;
|
||||
}
|
||||
|
||||
emoji--Category QLabel {
|
||||
margin: 20px 0 20px 8px;
|
||||
}
|
||||
|
||||
TimelineItem {
|
||||
qproperty-backgroundColor: #202228;
|
||||
}
|
||||
|
||||
#sideBar {
|
||||
|
@ -18,18 +80,9 @@ TimelineItem {
|
|||
border-left: 1px solid #202228;
|
||||
}
|
||||
|
||||
TimelineView,
|
||||
TimelineView > * {
|
||||
background-color: #202228;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#scroll_widget {
|
||||
background-color: #202228;
|
||||
}
|
||||
|
||||
QuickSwitcher {
|
||||
background-color: #202228;
|
||||
UserMentionsWidget > TimelineItem {
|
||||
qproperty-backgroundColor: #202228;
|
||||
qproperty-hoverColor: rgba(45, 49, 57, 120);
|
||||
}
|
||||
|
||||
InfoMessage {
|
||||
|
@ -37,21 +90,11 @@ InfoMessage {
|
|||
qproperty-boxColor: rgba(45, 49, 57, 120);
|
||||
}
|
||||
|
||||
SuggestionsPopup {
|
||||
background-color: #202228;
|
||||
}
|
||||
|
||||
PopupItem {
|
||||
background-color: #202228;
|
||||
qproperty-hoverColor: rgba(45, 49, 57, 120);
|
||||
}
|
||||
|
||||
RoomList,
|
||||
RoomList > * {
|
||||
background-color: #2d3139;
|
||||
border: none;
|
||||
}
|
||||
|
||||
TypingDisplay {
|
||||
qproperty-textColor: #caccd1;
|
||||
qproperty-backgroundColor: #202228;
|
||||
|
@ -61,35 +104,26 @@ TypingDisplay {
|
|||
background-color: #2d3139;
|
||||
}
|
||||
|
||||
CommunitiesList,
|
||||
CommunitiesList > * {
|
||||
background-color: #2d3139;
|
||||
}
|
||||
|
||||
FlatButton {
|
||||
qproperty-foregroundColor: #727274;
|
||||
qproperty-backgroundColor: #333;
|
||||
qproperty-disabledForegroundColor: #222;
|
||||
}
|
||||
|
||||
AudioItem,
|
||||
FileItem {
|
||||
qproperty-textColor: #caccd1;
|
||||
qproperty-backgroundColor: #2d3139;
|
||||
qproperty-iconColor: #caccd1;
|
||||
}
|
||||
|
||||
AudioItem {
|
||||
qproperty-textColor: #caccd1;
|
||||
qproperty-backgroundColor: #2d3139;
|
||||
qproperty-iconColor: #caccd1;
|
||||
}
|
||||
|
||||
RaisedButton {
|
||||
qproperty-foregroundColor: #caccd1;
|
||||
qproperty-backgroundColor: #333;
|
||||
}
|
||||
|
||||
RoomInfoListItem {
|
||||
RoomInfoListItem,
|
||||
UserMentionsWidget {
|
||||
qproperty-mentionedColor: #a82353;
|
||||
qproperty-highlightedBackgroundColor: #4d84c7;
|
||||
qproperty-hoverBackgroundColor: rgba(230, 230, 230, 30);
|
||||
|
@ -111,13 +145,15 @@ RoomInfoListItem {
|
|||
qproperty-highlightedTimestampColor: #e7e7e9;
|
||||
qproperty-hoverTimestampColor: #f4f5f8;
|
||||
|
||||
qproperty-avatarBgColor: #202228;
|
||||
qproperty-avatarFgColor: white;
|
||||
|
||||
qproperty-bubbleFgColor: white;
|
||||
qproperty-bubbleBgColor: #4d84c7;
|
||||
}
|
||||
|
||||
RoomInfoListItem > Avatar {
|
||||
qproperty-backgroundColor: #202228;
|
||||
qproperty-textColor: white;
|
||||
}
|
||||
|
||||
CommunitiesListItem {
|
||||
qproperty-highlightedBackgroundColor: #4d84c7;
|
||||
qproperty-hoverBackgroundColor: rgba(230, 230, 230, 30);
|
||||
|
@ -141,12 +177,12 @@ UserInfoWidget {
|
|||
border-bottom: 1px solid #202228;
|
||||
}
|
||||
|
||||
UserSettingsPage {
|
||||
background-color: #202228;
|
||||
#UserSettingScrollWidget > QComboBox {
|
||||
color: #202228;
|
||||
}
|
||||
|
||||
#UserSettingScrollWidget {
|
||||
background-color: #202228;
|
||||
#UserSettingScrollWidget > QComboBox {
|
||||
color: #202228;
|
||||
}
|
||||
|
||||
Avatar {
|
||||
|
@ -154,55 +190,17 @@ Avatar {
|
|||
qproperty-backgroundColor: #2d3139;
|
||||
}
|
||||
|
||||
#displayNameLabel {
|
||||
color: #f2f2f2;
|
||||
}
|
||||
|
||||
#displayNameLabel,
|
||||
#userIdLabel {
|
||||
color: #f2f2f2;
|
||||
}
|
||||
|
||||
dialogs--Logout,
|
||||
dialogs--ReCaptcha,
|
||||
dialogs--LeaveRoom,
|
||||
dialogs--CreateRoom,
|
||||
dialogs--RoomSettings,
|
||||
dialogs--InviteUsers,
|
||||
dialogs--ReadReceipts,
|
||||
dialogs--JoinRoom,
|
||||
dialogs--MemberList,
|
||||
dialogs--PreviewUploadOverlay,
|
||||
dialogs--UserProfile,
|
||||
dialogs--CreateRoom > QLineEdit,
|
||||
dialogs--InviteUsers > QLineEdit,
|
||||
EditModal,
|
||||
dialogs--JoinRoom > QLineEdit {
|
||||
background-color: #202228;
|
||||
color: #caccd1;
|
||||
}
|
||||
|
||||
TopSection {
|
||||
qproperty-textColor: #caccd1;
|
||||
}
|
||||
|
||||
QListWidget,
|
||||
WelcomePage,
|
||||
LoginPage,
|
||||
RegisterPage {
|
||||
background-color: #202228;
|
||||
color: #caccd1;
|
||||
}
|
||||
|
||||
emoji--Panel,
|
||||
emoji--Panel > * {
|
||||
background-color: #202228;
|
||||
color: #caccd1;
|
||||
}
|
||||
|
||||
emoji--Category,
|
||||
emoji--Category > * {
|
||||
background-color: #2d3139;
|
||||
color: #caccd1;
|
||||
emoji--Category {
|
||||
qproperty-hoverBackgroundColor: rgba(230, 230, 230, 30);
|
||||
}
|
||||
|
||||
FloatingButton {
|
||||
|
@ -221,23 +219,14 @@ ScrollBar {
|
|||
qproperty-backgroundColor: #202228;
|
||||
}
|
||||
|
||||
SideBarActions {
|
||||
SideBarActions,
|
||||
TopRoomBar
|
||||
{
|
||||
border: none;
|
||||
border-top: 1px solid #202228;
|
||||
background-color: #2d3139;
|
||||
}
|
||||
|
||||
TopRoomBar {
|
||||
border: none;
|
||||
border-bottom: 1px solid #202228;
|
||||
background-color: #2d3139;
|
||||
}
|
||||
|
||||
QLineEdit {
|
||||
background-color: #202228;
|
||||
color: #caccd1;
|
||||
}
|
||||
|
||||
TextInputWidget {
|
||||
border: none;
|
||||
border-top: 1px solid #2d3139;
|
||||
|
@ -261,3 +250,5 @@ SnackBar {
|
|||
qproperty-textColor: #caccd1;
|
||||
qproperty-bgColor: #202228;
|
||||
}
|
||||
|
||||
QSplitter::handle { image: none; }
|
||||
|
|
|
@ -3,13 +3,38 @@ QLabel {
|
|||
color: #333;
|
||||
}
|
||||
|
||||
TimelineItem {
|
||||
qproperty-backgroundColor: white;
|
||||
TextLabel::a {
|
||||
color: #0077b5;
|
||||
}
|
||||
|
||||
|
||||
QuickSwitcher,
|
||||
ReplyPopup,
|
||||
SuggestionsPopup,
|
||||
UserSettingsPage,
|
||||
#scroll_widget,
|
||||
#UserSettingScrollWidget {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#chatPage,
|
||||
#chatPage > * {
|
||||
#chatPage > *,
|
||||
CommunitiesList,
|
||||
CommunitiesList > *,
|
||||
RoomList,
|
||||
RoomList > *,
|
||||
TimelineView,
|
||||
TimelineView > *,
|
||||
UserMentionsWidget,
|
||||
UserMentionsWidget > *,
|
||||
TimelineView,
|
||||
TimelineView > * {
|
||||
background-color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
TimelineItem {
|
||||
qproperty-backgroundColor: white;
|
||||
}
|
||||
|
||||
#sideBar {
|
||||
|
@ -18,18 +43,9 @@ TimelineItem {
|
|||
border-left: 1px solid #dee1f3;
|
||||
}
|
||||
|
||||
TimelineView,
|
||||
TimelineView > * {
|
||||
background-color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#scroll_widget {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
QuickSwitcher {
|
||||
background-color: white;
|
||||
UserMentionsWidget > TimelineItem {
|
||||
qproperty-backgroundColor: white;
|
||||
qproperty-hoverColor: rgba(192, 193, 195, 120);
|
||||
}
|
||||
|
||||
InfoMessage {
|
||||
|
@ -42,17 +58,15 @@ TypingDisplay {
|
|||
qproperty-backgroundColor: white;
|
||||
}
|
||||
|
||||
SuggestionsPopup {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
PopupItem {
|
||||
background-color: white;
|
||||
qproperty-hoverColor: rgba(192, 193, 195, 120);
|
||||
}
|
||||
|
||||
RoomList,
|
||||
RoomList > * {
|
||||
RoomList > *,
|
||||
CommunitiesList,
|
||||
CommunitiesList > * {
|
||||
background-color: #2e3649;
|
||||
border: none;
|
||||
}
|
||||
|
@ -61,27 +75,17 @@ RoomList > * {
|
|||
background-color: #2e3649;
|
||||
}
|
||||
|
||||
CommunitiesList,
|
||||
CommunitiesList > * {
|
||||
background-color: #2e3649;
|
||||
}
|
||||
|
||||
FlatButton {
|
||||
qproperty-foregroundColor: #495057;
|
||||
}
|
||||
|
||||
AudioItem,
|
||||
FileItem {
|
||||
qproperty-textColor: #333;
|
||||
qproperty-backgroundColor: #f2f2f2;
|
||||
qproperty-iconColor: white;
|
||||
}
|
||||
|
||||
AudioItem {
|
||||
qproperty-textColor: #333;
|
||||
qproperty-backgroundColor: #f2f2f2;
|
||||
qproperty-iconColor: white;
|
||||
}
|
||||
|
||||
RaisedButton {
|
||||
qproperty-foregroundColor: white;
|
||||
}
|
||||
|
@ -89,7 +93,7 @@ RaisedButton {
|
|||
RoomInfoListItem {
|
||||
qproperty-mentionedColor: #a82353;
|
||||
qproperty-highlightedBackgroundColor: #38A3D8;
|
||||
qproperty-hoverBackgroundColor: rgba(200, 200, 200, 70);
|
||||
qproperty-hoverBackgroundColor: rgba(200, 200, 200, 40);
|
||||
qproperty-hoverTitleColor: #f2f5f8;
|
||||
qproperty-hoverSubtitleColor: white;
|
||||
qproperty-backgroundColor: #f2f5f8;
|
||||
|
@ -107,16 +111,18 @@ RoomInfoListItem {
|
|||
qproperty-highlightedTimestampColor: #f4f4f5;
|
||||
qproperty-hoverTimestampColor: white;
|
||||
|
||||
qproperty-avatarBgColor: #eee;
|
||||
qproperty-avatarFgColor: black;
|
||||
|
||||
qproperty-bubbleFgColor: white;
|
||||
qproperty-bubbleBgColor: #38A3D8;
|
||||
}
|
||||
|
||||
RoomInfoListItem > Avatar {
|
||||
qproperty-backgroundColor: #eee;
|
||||
qproperty-textColor: black;
|
||||
}
|
||||
|
||||
CommunitiesListItem {
|
||||
qproperty-highlightedBackgroundColor: #38A3D8;
|
||||
qproperty-hoverBackgroundColor: rgba(200, 200, 200, 70);
|
||||
qproperty-hoverBackgroundColor: rgba(200, 200, 200, 40);
|
||||
qproperty-backgroundColor: #f2f5f8;
|
||||
|
||||
qproperty-avatarBgColor: #eee;
|
||||
|
@ -141,14 +147,6 @@ UserInfoWidget {
|
|||
border-bottom: 2px solid #ccc;
|
||||
}
|
||||
|
||||
UserSettingsPage {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#UserSettingScrollWidget {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
Avatar {
|
||||
qproperty-textColor: black;
|
||||
qproperty-backgroundColor: #eee;
|
||||
|
@ -196,12 +194,22 @@ emoji--Panel > * {
|
|||
color: #333;
|
||||
}
|
||||
|
||||
emoji--Panel QWidget { border: none; }
|
||||
emoji--Panel QScrollBar:vertical { width: 0px; margin: 0px; }
|
||||
emoji--Panel QScrollBar::handle:vertical { min-height: 30px; }
|
||||
|
||||
emoji--Category {
|
||||
qproperty-hoverBackgroundColor: rgba(200, 200, 200, 70);
|
||||
}
|
||||
|
||||
emoji--Category,
|
||||
emoji--Category > * {
|
||||
background-color: white;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
emoji--Category QLabel { margin: 20px 0 20px 8px; }
|
||||
|
||||
FloatingButton {
|
||||
qproperty-backgroundColor: #efefef;
|
||||
qproperty-foregroundColor: black;
|
||||
|
@ -244,3 +252,5 @@ SnackBar {
|
|||
qproperty-textColor: white;
|
||||
qproperty-bgColor: #495057;
|
||||
}
|
||||
|
||||
QSplitter::handle { image: none; }
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
#chatPage,
|
||||
#chatPage > *,
|
||||
CommunitiesList,
|
||||
CommunitiesList > *,
|
||||
RoomList,
|
||||
RoomList > *,
|
||||
TimelineView,
|
||||
TimelineView > *,
|
||||
UserMentionsWidget,
|
||||
UserMentionsWidget > * {
|
||||
border: none;
|
||||
}
|
||||
|
||||
TypingDisplay {
|
||||
qproperty-textColor: palette(text);
|
||||
qproperty-backgroundColor: palette(window);
|
||||
|
@ -7,21 +20,18 @@ TimelineItem {
|
|||
qproperty-backgroundColor: palette(window);
|
||||
}
|
||||
|
||||
TimelineView,
|
||||
TimelineView > * {
|
||||
border: none;
|
||||
UserMentionsWidget > TimelineItem {
|
||||
qproperty-backgroundColor: palette(window);
|
||||
qproperty-hoverColor: palette(base);
|
||||
}
|
||||
|
||||
SideBarActions,
|
||||
TextInputWidget {
|
||||
border: none;
|
||||
border-top: 1px solid palette(mid);
|
||||
}
|
||||
|
||||
SideBarActions {
|
||||
border: none;
|
||||
border-top: 1px solid palette(mid);
|
||||
}
|
||||
|
||||
UserInfoWidget,
|
||||
TopRoomBar {
|
||||
border: none;
|
||||
border-bottom: 1px solid palette(mid);
|
||||
|
@ -33,11 +43,6 @@ RoomList > * {
|
|||
border: none;
|
||||
}
|
||||
|
||||
UserInfoWidget {
|
||||
border: none;
|
||||
border-bottom: 1px solid palette(mid);
|
||||
}
|
||||
|
||||
#sideBar {
|
||||
border: none;
|
||||
border-right: 1px solid palette(mid);
|
||||
|
@ -57,18 +62,13 @@ FlatButton {
|
|||
qproperty-foregroundColor: palette(text);
|
||||
}
|
||||
|
||||
AudioItem,
|
||||
FileItem {
|
||||
qproperty-textColor: palette(text);
|
||||
qproperty-backgroundColor: palette(base);
|
||||
qproperty-iconColor: palette(window);
|
||||
}
|
||||
|
||||
AudioItem {
|
||||
qproperty-textColor: palette(text);
|
||||
qproperty-backgroundColor: palette(base);
|
||||
qproperty-iconColor: palette(window);
|
||||
}
|
||||
|
||||
RaisedButton {
|
||||
qproperty-foregroundColor: palette(buttonText);
|
||||
}
|
||||
|
@ -85,10 +85,11 @@ QListWidget {
|
|||
background-color: palette(window);
|
||||
}
|
||||
|
||||
RoomInfoListItem {
|
||||
RoomInfoListItem,
|
||||
UserMentionsWidget {
|
||||
qproperty-mentionedColor: palette(alternate-base);
|
||||
qproperty-highlightedBackgroundColor: palette(highlight);
|
||||
qproperty-hoverBackgroundColor: palette(base);
|
||||
qproperty-hoverBackgroundColor: palette(light);
|
||||
qproperty-backgroundColor: palette(window);
|
||||
|
||||
qproperty-titleColor: palette(text);
|
||||
|
@ -107,16 +108,19 @@ RoomInfoListItem {
|
|||
qproperty-highlightedTimestampColor: palette(highlightedtext);
|
||||
qproperty-hoverTimestampColor: palette(highlightedtext);
|
||||
|
||||
qproperty-avatarBgColor: palette(base);
|
||||
qproperty-avatarFgColor: palette(text);
|
||||
|
||||
qproperty-bubbleBgColor: palette(base);
|
||||
qproperty-bubbleFgColor: palette(text);
|
||||
}
|
||||
|
||||
RoomInfoListItem > Avatar {
|
||||
qproperty-backgroundColor: palette(base);
|
||||
qproperty-textColor: palette(text);
|
||||
}
|
||||
|
||||
|
||||
CommunitiesListItem {
|
||||
qproperty-highlightedBackgroundColor: palette(highlight);
|
||||
qproperty-hoverBackgroundColor: palette(base);
|
||||
qproperty-hoverBackgroundColor: palette(light);
|
||||
qproperty-backgroundColor: palette(window);
|
||||
|
||||
qproperty-avatarBgColor: palette(base);
|
||||
|
@ -131,6 +135,30 @@ LoadingIndicator {
|
|||
qproperty-color: palette(light);
|
||||
}
|
||||
|
||||
emoji--Panel,
|
||||
emoji--Panel > * {
|
||||
background-color: palette(base);
|
||||
color: palette(text);
|
||||
}
|
||||
|
||||
emoji--Panel QWidget { border: none; }
|
||||
emoji--Panel QScrollBar:vertical { width: 0px; margin: 0px; }
|
||||
emoji--Panel QScrollBar::handle:vertical { min-height: 30px; }
|
||||
|
||||
emoji--Category {
|
||||
qproperty-hoverBackgroundColor: palette(highlight);
|
||||
}
|
||||
|
||||
emoji--Category,
|
||||
emoji--Category > * {
|
||||
background-color: palette(window);
|
||||
color: palette(text);
|
||||
}
|
||||
|
||||
emoji--Category QLabel {
|
||||
margin: 20px 0 20px 8px;
|
||||
}
|
||||
|
||||
FloatingButton {
|
||||
qproperty-backgroundColor: palette(base);
|
||||
qproperty-foregroundColor: palette(text);
|
||||
|
@ -141,13 +169,11 @@ SnackBar {
|
|||
qproperty-bgColor: palette(base);
|
||||
}
|
||||
|
||||
MemberItem {
|
||||
background-color: palette(window);
|
||||
}
|
||||
|
||||
Toggle {
|
||||
qproperty-activeColor: palette(highlight);
|
||||
qproperty-disabledColor: palette(dark);
|
||||
qproperty-inactiveColor: palette(mid);
|
||||
qproperty-trackColor: palette(base);
|
||||
}
|
||||
|
||||
QSplitter::handle { image: none; }
|
||||
|
|
|
@ -1,31 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
|
||||
class Emoji(object):
|
||||
def __init__(self, code, shortname, category, order):
|
||||
self.code = ''.join(list(map(code_to_bytes, code.split('-'))))
|
||||
def __init__(self, code, shortname):
|
||||
self.code = repr(code.encode('utf-8'))[1:].strip("'")
|
||||
self.shortname = shortname
|
||||
self.category = category
|
||||
self.order = int(order)
|
||||
|
||||
|
||||
def code_to_bytes(codepoint):
|
||||
'''
|
||||
Convert hex unicode codepoint to hex byte array.
|
||||
'''
|
||||
bytes = chr(int(codepoint, 16)).encode('utf-8')
|
||||
|
||||
return str(bytes)[1:].strip("'")
|
||||
|
||||
|
||||
def generate_code(emojis, category):
|
||||
tmpl = Template('''
|
||||
const QList<Emoji> EmojiProvider::{{ category }} = {
|
||||
const std::vector<Emoji> emoji::Provider::{{ category }} = {
|
||||
{%- for e in emoji %}
|
||||
Emoji{QString::fromUtf8("{{ e.code }}"), "{{ e.shortname }}"},
|
||||
{%- endfor %}
|
||||
|
@ -38,44 +26,56 @@ const QList<Emoji> EmojiProvider::{{ category }} = {
|
|||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 2:
|
||||
print('usage: emoji_codegen.py /path/to/emoji.json')
|
||||
print('usage: emoji_codegen.py /path/to/emoji-test.txt')
|
||||
sys.exit(1)
|
||||
|
||||
filename = sys.argv[1]
|
||||
data = {}
|
||||
|
||||
with open(filename, 'r') as filename:
|
||||
data = json.loads(filename.read())
|
||||
people = []
|
||||
nature = []
|
||||
food = []
|
||||
activity = []
|
||||
travel = []
|
||||
objects = []
|
||||
symbols = []
|
||||
flags = []
|
||||
|
||||
emojis = []
|
||||
categories = {
|
||||
'Smileys & Emotion': people,
|
||||
'People & Body': people,
|
||||
'Animals & Nature': nature,
|
||||
'Food & Drink': food,
|
||||
'Travel & Places': travel,
|
||||
'Activities': activity,
|
||||
'Objects': objects,
|
||||
'Symbols': symbols,
|
||||
'Flags': flags
|
||||
}
|
||||
|
||||
for emoji_name in data:
|
||||
tmp = data[emoji_name]
|
||||
current_category = ''
|
||||
for line in open(filename, 'r'):
|
||||
if line.startswith('# group:'):
|
||||
current_category = line.split(':', 1)[1].strip()
|
||||
|
||||
l = len(tmp['unicode'].split('-'))
|
||||
|
||||
if l > 1 and tmp['category'] == 'people':
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
|
||||
emojis.append(
|
||||
Emoji(
|
||||
tmp['unicode'],
|
||||
tmp['shortname'],
|
||||
tmp['category'],
|
||||
tmp['emoji_order']
|
||||
)
|
||||
)
|
||||
segments = re.split(r'\s+[#;] ', line.strip())
|
||||
if len(segments) != 3:
|
||||
continue
|
||||
|
||||
emojis.sort(key=lambda x: x.order)
|
||||
code, qualification, charAndName = segments
|
||||
|
||||
people = list(filter(lambda x: x.category == "people", emojis))
|
||||
nature = list(filter(lambda x: x.category == "nature", emojis))
|
||||
food = list(filter(lambda x: x.category == "food", emojis))
|
||||
activity = list(filter(lambda x: x.category == "activity", emojis))
|
||||
travel = list(filter(lambda x: x.category == "travel", emojis))
|
||||
objects = list(filter(lambda x: x.category == "objects", emojis))
|
||||
symbols = list(filter(lambda x: x.category == "symbols", emojis))
|
||||
flags = list(filter(lambda x: x.category == "flags", emojis))
|
||||
# skip fully qualified versions of same unicode
|
||||
if code.endswith('FE0F'):
|
||||
continue
|
||||
|
||||
if qualification == 'component':
|
||||
continue
|
||||
|
||||
char, name = re.match(r'^(\S+) E\d+\.\d+ (.*)$', charAndName).groups()
|
||||
|
||||
categories[current_category].append(Emoji(char, name))
|
||||
|
||||
# Use xclip to pipe the output to clipboard.
|
||||
# e.g ./codegen.py emoji.json | xclip -sel clip
|
||||
|
|
7
scripts/update_emoji.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Updating emoji
|
||||
|
||||
1. Get the latest emoji-test.txt from here: https://unicode.org/Public/emoji/
|
||||
2. Overwrite the existing resources/emoji-test.txt with the new one
|
||||
3. Run `./scripts/emoji_codegen.py resources/emoji-test.txt` and replace the current tail of src/emoji/Provider.cpp with the new output
|
||||
4. `make lint`
|
||||
5. Compile and test
|
|
@ -16,30 +16,37 @@
|
|||
*/
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QPixmapCache>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "AvatarProvider.h"
|
||||
#include "Cache.h"
|
||||
#include "Logging.h"
|
||||
#include "MatrixClient.h"
|
||||
|
||||
static QPixmapCache avatar_cache;
|
||||
|
||||
namespace AvatarProvider {
|
||||
|
||||
void
|
||||
resolve(const QString &room_id, const QString &user_id, QObject *receiver, AvatarCallback callback)
|
||||
resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback callback)
|
||||
{
|
||||
const auto key = QString("%1 %2").arg(room_id).arg(user_id);
|
||||
const auto avatarUrl = Cache::avatarUrl(room_id, user_id);
|
||||
|
||||
if (!Cache::AvatarUrls.contains(key) || !cache::client())
|
||||
return;
|
||||
const auto cacheKey = QString("%1_size_%2").arg(avatarUrl).arg(size);
|
||||
|
||||
if (avatarUrl.isEmpty())
|
||||
return;
|
||||
|
||||
auto data = cache::client()->image(avatarUrl);
|
||||
QPixmap pixmap;
|
||||
if (avatar_cache.find(cacheKey, &pixmap)) {
|
||||
callback(pixmap);
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = cache::image(cacheKey);
|
||||
if (!data.isNull()) {
|
||||
callback(QImage::fromData(data));
|
||||
pixmap.loadFromData(data);
|
||||
avatar_cache.insert(cacheKey, pixmap);
|
||||
callback(pixmap);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -47,16 +54,22 @@ resolve(const QString &room_id, const QString &user_id, QObject *receiver, Avata
|
|||
QObject::connect(proxy.get(),
|
||||
&AvatarProxy::avatarDownloaded,
|
||||
receiver,
|
||||
[callback](const QByteArray &data) { callback(QImage::fromData(data)); });
|
||||
[callback, cacheKey](const QByteArray &data) {
|
||||
QPixmap pm;
|
||||
pm.loadFromData(data);
|
||||
avatar_cache.insert(cacheKey, pm);
|
||||
callback(pm);
|
||||
});
|
||||
|
||||
mtx::http::ThumbOpts opts;
|
||||
opts.width = 256;
|
||||
opts.height = 256;
|
||||
opts.width = size;
|
||||
opts.height = size;
|
||||
opts.mxc_url = avatarUrl.toStdString();
|
||||
|
||||
http::client()->get_thumbnail(
|
||||
opts,
|
||||
[opts, proxy = std::move(proxy)](const std::string &res, mtx::http::RequestErr err) {
|
||||
[opts, cacheKey, proxy = std::move(proxy)](const std::string &res,
|
||||
mtx::http::RequestErr err) {
|
||||
if (err) {
|
||||
nhlog::net()->warn("failed to download avatar: {} - ({} {})",
|
||||
opts.mxc_url,
|
||||
|
@ -65,10 +78,21 @@ resolve(const QString &room_id, const QString &user_id, QObject *receiver, Avata
|
|||
return;
|
||||
}
|
||||
|
||||
cache::client()->saveImage(opts.mxc_url, res);
|
||||
cache::saveImage(cacheKey.toStdString(), res);
|
||||
|
||||
auto data = QByteArray(res.data(), res.size());
|
||||
emit proxy->avatarDownloaded(data);
|
||||
emit proxy->avatarDownloaded(QByteArray(res.data(), res.size()));
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
resolve(const QString &room_id,
|
||||
const QString &user_id,
|
||||
int size,
|
||||
QObject *receiver,
|
||||
AvatarCallback callback)
|
||||
{
|
||||
const auto avatarUrl = cache::avatarUrl(room_id, user_id);
|
||||
|
||||
resolve(avatarUrl, size, receiver, callback);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
#include <functional>
|
||||
|
||||
class AvatarProxy : public QObject
|
||||
|
@ -28,9 +28,15 @@ signals:
|
|||
void avatarDownloaded(const QByteArray &data);
|
||||
};
|
||||
|
||||
using AvatarCallback = std::function<void(QImage)>;
|
||||
using AvatarCallback = std::function<void(QPixmap)>;
|
||||
|
||||
namespace AvatarProvider {
|
||||
void
|
||||
resolve(const QString &room_id, const QString &user_id, QObject *receiver, AvatarCallback cb);
|
||||
resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback cb);
|
||||
void
|
||||
resolve(const QString &room_id,
|
||||
const QString &user_id,
|
||||
int size,
|
||||
QObject *receiver,
|
||||
AvatarCallback cb);
|
||||
}
|
||||
|
|
878
src/Cache.cpp
796
src/Cache.h
|
@ -17,716 +17,280 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QImage>
|
||||
#include <QString>
|
||||
|
||||
#if __has_include(<lmdbxx/lmdb++.h>)
|
||||
#include <lmdbxx/lmdb++.h>
|
||||
#else
|
||||
#include <lmdb++.h>
|
||||
#include <mtx/events/join_rules.hpp>
|
||||
#endif
|
||||
|
||||
#include <mtx/responses.hpp>
|
||||
#include <mtxclient/crypto/client.hpp>
|
||||
#include <mutex>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "Logging.h"
|
||||
#include "CacheCryptoStructs.h"
|
||||
#include "CacheStructs.h"
|
||||
|
||||
using mtx::events::state::JoinRule;
|
||||
namespace cache {
|
||||
void
|
||||
init(const QString &user_id);
|
||||
|
||||
struct RoomMember
|
||||
{
|
||||
QString user_id;
|
||||
QString display_name;
|
||||
QImage avatar;
|
||||
};
|
||||
std::string
|
||||
displayName(const std::string &room_id, const std::string &user_id);
|
||||
QString
|
||||
displayName(const QString &room_id, const QString &user_id);
|
||||
QString
|
||||
avatarUrl(const QString &room_id, const QString &user_id);
|
||||
|
||||
struct SearchResult
|
||||
{
|
||||
QString user_id;
|
||||
QString display_name;
|
||||
};
|
||||
void
|
||||
removeDisplayName(const QString &room_id, const QString &user_id);
|
||||
void
|
||||
removeAvatarUrl(const QString &room_id, const QString &user_id);
|
||||
|
||||
static int
|
||||
numeric_key_comparison(const MDB_val *a, const MDB_val *b)
|
||||
{
|
||||
auto lhs = std::stoull(std::string((char *)a->mv_data, a->mv_size));
|
||||
auto rhs = std::stoull(std::string((char *)b->mv_data, b->mv_size));
|
||||
|
||||
if (lhs < rhs)
|
||||
return 1;
|
||||
else if (lhs == rhs)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(SearchResult)
|
||||
Q_DECLARE_METATYPE(QVector<SearchResult>)
|
||||
Q_DECLARE_METATYPE(RoomMember)
|
||||
Q_DECLARE_METATYPE(mtx::responses::Timeline)
|
||||
|
||||
//! Used to uniquely identify a list of read receipts.
|
||||
struct ReadReceiptKey
|
||||
{
|
||||
std::string event_id;
|
||||
std::string room_id;
|
||||
};
|
||||
|
||||
inline void
|
||||
to_json(json &j, const ReadReceiptKey &key)
|
||||
{
|
||||
j = json{{"event_id", key.event_id}, {"room_id", key.room_id}};
|
||||
}
|
||||
|
||||
inline void
|
||||
from_json(const json &j, ReadReceiptKey &key)
|
||||
{
|
||||
key.event_id = j.at("event_id").get<std::string>();
|
||||
key.room_id = j.at("room_id").get<std::string>();
|
||||
}
|
||||
|
||||
struct DescInfo
|
||||
{
|
||||
QString event_id;
|
||||
QString username;
|
||||
QString userid;
|
||||
QString body;
|
||||
QString timestamp;
|
||||
QDateTime datetime;
|
||||
};
|
||||
|
||||
//! UI info associated with a room.
|
||||
struct RoomInfo
|
||||
{
|
||||
//! The calculated name of the room.
|
||||
std::string name;
|
||||
//! The topic of the room.
|
||||
std::string topic;
|
||||
//! The calculated avatar url of the room.
|
||||
std::string avatar_url;
|
||||
//! Whether or not the room is an invite.
|
||||
bool is_invite = false;
|
||||
//! Total number of members in the room.
|
||||
int16_t member_count = 0;
|
||||
//! Who can access to the room.
|
||||
JoinRule join_rule = JoinRule::Public;
|
||||
bool guest_access = false;
|
||||
//! Metadata describing the last message in the timeline.
|
||||
DescInfo msgInfo;
|
||||
//! The list of tags associated with this room
|
||||
std::vector<std::string> tags;
|
||||
};
|
||||
|
||||
inline void
|
||||
to_json(json &j, const RoomInfo &info)
|
||||
{
|
||||
j["name"] = info.name;
|
||||
j["topic"] = info.topic;
|
||||
j["avatar_url"] = info.avatar_url;
|
||||
j["is_invite"] = info.is_invite;
|
||||
j["join_rule"] = info.join_rule;
|
||||
j["guest_access"] = info.guest_access;
|
||||
|
||||
if (info.member_count != 0)
|
||||
j["member_count"] = info.member_count;
|
||||
|
||||
if (info.tags.size() != 0)
|
||||
j["tags"] = info.tags;
|
||||
}
|
||||
|
||||
inline void
|
||||
from_json(const json &j, RoomInfo &info)
|
||||
{
|
||||
info.name = j.at("name");
|
||||
info.topic = j.at("topic");
|
||||
info.avatar_url = j.at("avatar_url");
|
||||
info.is_invite = j.at("is_invite");
|
||||
info.join_rule = j.at("join_rule");
|
||||
info.guest_access = j.at("guest_access");
|
||||
|
||||
if (j.count("member_count"))
|
||||
info.member_count = j.at("member_count");
|
||||
|
||||
if (j.count("tags"))
|
||||
info.tags = j.at("tags").get<std::vector<std::string>>();
|
||||
}
|
||||
|
||||
//! Basic information per member;
|
||||
struct MemberInfo
|
||||
{
|
||||
std::string name;
|
||||
std::string avatar_url;
|
||||
};
|
||||
|
||||
inline void
|
||||
to_json(json &j, const MemberInfo &info)
|
||||
{
|
||||
j["name"] = info.name;
|
||||
j["avatar_url"] = info.avatar_url;
|
||||
}
|
||||
|
||||
inline void
|
||||
from_json(const json &j, MemberInfo &info)
|
||||
{
|
||||
info.name = j.at("name");
|
||||
info.avatar_url = j.at("avatar_url");
|
||||
}
|
||||
|
||||
struct RoomSearchResult
|
||||
{
|
||||
std::string room_id;
|
||||
RoomInfo info;
|
||||
QImage img;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(RoomSearchResult)
|
||||
Q_DECLARE_METATYPE(RoomInfo)
|
||||
|
||||
// Extra information associated with an outbound megolm session.
|
||||
struct OutboundGroupSessionData
|
||||
{
|
||||
std::string session_id;
|
||||
std::string session_key;
|
||||
uint64_t message_index = 0;
|
||||
};
|
||||
|
||||
inline void
|
||||
to_json(nlohmann::json &obj, const OutboundGroupSessionData &msg)
|
||||
{
|
||||
obj["session_id"] = msg.session_id;
|
||||
obj["session_key"] = msg.session_key;
|
||||
obj["message_index"] = msg.message_index;
|
||||
}
|
||||
|
||||
inline void
|
||||
from_json(const nlohmann::json &obj, OutboundGroupSessionData &msg)
|
||||
{
|
||||
msg.session_id = obj.at("session_id");
|
||||
msg.session_key = obj.at("session_key");
|
||||
msg.message_index = obj.at("message_index");
|
||||
}
|
||||
|
||||
struct OutboundGroupSessionDataRef
|
||||
{
|
||||
OlmOutboundGroupSession *session;
|
||||
OutboundGroupSessionData data;
|
||||
};
|
||||
|
||||
struct DevicePublicKeys
|
||||
{
|
||||
std::string ed25519;
|
||||
std::string curve25519;
|
||||
};
|
||||
|
||||
inline void
|
||||
to_json(nlohmann::json &obj, const DevicePublicKeys &msg)
|
||||
{
|
||||
obj["ed25519"] = msg.ed25519;
|
||||
obj["curve25519"] = msg.curve25519;
|
||||
}
|
||||
|
||||
inline void
|
||||
from_json(const nlohmann::json &obj, DevicePublicKeys &msg)
|
||||
{
|
||||
msg.ed25519 = obj.at("ed25519");
|
||||
msg.curve25519 = obj.at("curve25519");
|
||||
}
|
||||
|
||||
//! Represents a unique megolm session identifier.
|
||||
struct MegolmSessionIndex
|
||||
{
|
||||
//! The room in which this session exists.
|
||||
std::string room_id;
|
||||
//! The session_id of the megolm session.
|
||||
std::string session_id;
|
||||
//! The curve25519 public key of the sender.
|
||||
std::string sender_key;
|
||||
};
|
||||
|
||||
inline void
|
||||
to_json(nlohmann::json &obj, const MegolmSessionIndex &msg)
|
||||
{
|
||||
obj["room_id"] = msg.room_id;
|
||||
obj["session_id"] = msg.session_id;
|
||||
obj["sender_key"] = msg.sender_key;
|
||||
}
|
||||
|
||||
inline void
|
||||
from_json(const nlohmann::json &obj, MegolmSessionIndex &msg)
|
||||
{
|
||||
msg.room_id = obj.at("room_id");
|
||||
msg.session_id = obj.at("session_id");
|
||||
msg.sender_key = obj.at("sender_key");
|
||||
}
|
||||
|
||||
struct OlmSessionStorage
|
||||
{
|
||||
// Megolm sessions
|
||||
std::map<std::string, mtx::crypto::InboundGroupSessionPtr> group_inbound_sessions;
|
||||
std::map<std::string, mtx::crypto::OutboundGroupSessionPtr> group_outbound_sessions;
|
||||
std::map<std::string, OutboundGroupSessionData> group_outbound_session_data;
|
||||
|
||||
// Guards for accessing megolm sessions.
|
||||
std::mutex group_outbound_mtx;
|
||||
std::mutex group_inbound_mtx;
|
||||
};
|
||||
|
||||
class Cache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Cache(const QString &userId, QObject *parent = nullptr);
|
||||
|
||||
static QHash<QString, QString> DisplayNames;
|
||||
static QHash<QString, QString> AvatarUrls;
|
||||
static QHash<QString, QString> UserColors;
|
||||
|
||||
static std::string displayName(const std::string &room_id, const std::string &user_id);
|
||||
static QString displayName(const QString &room_id, const QString &user_id);
|
||||
static QString avatarUrl(const QString &room_id, const QString &user_id);
|
||||
static QString userColor(const QString &user_id);
|
||||
|
||||
static void removeDisplayName(const QString &room_id, const QString &user_id);
|
||||
static void removeAvatarUrl(const QString &room_id, const QString &user_id);
|
||||
static void removeUserColor(const QString &user_id);
|
||||
|
||||
static void insertDisplayName(const QString &room_id,
|
||||
const QString &user_id,
|
||||
const QString &display_name);
|
||||
static void insertAvatarUrl(const QString &room_id,
|
||||
const QString &user_id,
|
||||
const QString &avatar_url);
|
||||
static void insertUserColor(const QString &user_id, const QString &color_name);
|
||||
|
||||
static void clearUserColors();
|
||||
void
|
||||
insertDisplayName(const QString &room_id, const QString &user_id, const QString &display_name);
|
||||
void
|
||||
insertAvatarUrl(const QString &room_id, const QString &user_id, const QString &avatar_url);
|
||||
|
||||
//! Load saved data for the display names & avatars.
|
||||
void populateMembers();
|
||||
std::vector<std::string> joinedRooms();
|
||||
void
|
||||
populateMembers();
|
||||
std::vector<std::string>
|
||||
joinedRooms();
|
||||
|
||||
QMap<QString, RoomInfo> roomInfo(bool withInvites = true);
|
||||
std::map<QString, bool> invites();
|
||||
QMap<QString, RoomInfo>
|
||||
roomInfo(bool withInvites = true);
|
||||
std::map<QString, bool>
|
||||
invites();
|
||||
|
||||
//! Calculate & return the name of the room.
|
||||
QString getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
|
||||
QString
|
||||
getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
|
||||
//! Get room join rules
|
||||
JoinRule getRoomJoinRule(lmdb::txn &txn, lmdb::dbi &statesdb);
|
||||
bool getRoomGuestAccess(lmdb::txn &txn, lmdb::dbi &statesdb);
|
||||
mtx::events::state::JoinRule
|
||||
getRoomJoinRule(lmdb::txn &txn, lmdb::dbi &statesdb);
|
||||
bool
|
||||
getRoomGuestAccess(lmdb::txn &txn, lmdb::dbi &statesdb);
|
||||
//! Retrieve the topic of the room if any.
|
||||
QString getRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb);
|
||||
QString
|
||||
getRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb);
|
||||
//! Retrieve the room avatar's url if any.
|
||||
QString getRoomAvatarUrl(lmdb::txn &txn,
|
||||
lmdb::dbi &statesdb,
|
||||
lmdb::dbi &membersdb,
|
||||
const QString &room_id);
|
||||
QString
|
||||
getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb, const QString &room_id);
|
||||
//! Retrieve the version of the room if any.
|
||||
QString
|
||||
getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb);
|
||||
|
||||
//! Retrieve member info from a room.
|
||||
std::vector<RoomMember> getMembers(const std::string &room_id,
|
||||
std::size_t startIndex = 0,
|
||||
std::size_t len = 30);
|
||||
std::vector<RoomMember>
|
||||
getMembers(const std::string &room_id, std::size_t startIndex = 0, std::size_t len = 30);
|
||||
|
||||
void saveState(const mtx::responses::Sync &res);
|
||||
bool isInitialized() const;
|
||||
void
|
||||
saveState(const mtx::responses::Sync &res);
|
||||
bool
|
||||
isInitialized();
|
||||
|
||||
std::string nextBatchToken() const;
|
||||
std::string
|
||||
nextBatchToken();
|
||||
|
||||
void deleteData();
|
||||
void
|
||||
deleteData();
|
||||
|
||||
void removeInvite(lmdb::txn &txn, const std::string &room_id);
|
||||
void removeInvite(const std::string &room_id);
|
||||
void removeRoom(lmdb::txn &txn, const std::string &roomid);
|
||||
void removeRoom(const std::string &roomid);
|
||||
void removeRoom(const QString &roomid) { removeRoom(roomid.toStdString()); };
|
||||
void setup();
|
||||
void
|
||||
removeInvite(lmdb::txn &txn, const std::string &room_id);
|
||||
void
|
||||
removeInvite(const std::string &room_id);
|
||||
void
|
||||
removeRoom(lmdb::txn &txn, const std::string &roomid);
|
||||
void
|
||||
removeRoom(const std::string &roomid);
|
||||
void
|
||||
removeRoom(const QString &roomid);
|
||||
void
|
||||
setup();
|
||||
|
||||
bool isFormatValid();
|
||||
void setCurrentFormat();
|
||||
bool
|
||||
isFormatValid();
|
||||
void
|
||||
setCurrentFormat();
|
||||
|
||||
std::map<QString, mtx::responses::Timeline> roomMessages();
|
||||
std::map<QString, mtx::responses::Timeline>
|
||||
roomMessages();
|
||||
|
||||
QMap<QString, mtx::responses::Notifications>
|
||||
getTimelineMentions();
|
||||
|
||||
//! Retrieve all the user ids from a room.
|
||||
std::vector<std::string> roomMembers(const std::string &room_id);
|
||||
std::vector<std::string>
|
||||
roomMembers(const std::string &room_id);
|
||||
|
||||
//! Check if the given user has power leve greater than than
|
||||
//! lowest power level of the given events.
|
||||
bool hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes,
|
||||
bool
|
||||
hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes,
|
||||
const std::string &room_id,
|
||||
const std::string &user_id);
|
||||
|
||||
//! Retrieves the saved room avatar.
|
||||
QImage getRoomAvatar(const QString &id);
|
||||
QImage getRoomAvatar(const std::string &id);
|
||||
QImage
|
||||
getRoomAvatar(const QString &id);
|
||||
QImage
|
||||
getRoomAvatar(const std::string &id);
|
||||
|
||||
//! Adds a user to the read list for the given event.
|
||||
//!
|
||||
//! There should be only one user id present in a receipt list per room.
|
||||
//! The user id should be removed from any other lists.
|
||||
using Receipts = std::map<std::string, std::map<std::string, uint64_t>>;
|
||||
void updateReadReceipt(lmdb::txn &txn,
|
||||
const std::string &room_id,
|
||||
const Receipts &receipts);
|
||||
void
|
||||
updateReadReceipt(lmdb::txn &txn, const std::string &room_id, const Receipts &receipts);
|
||||
|
||||
//! Retrieve all the read receipts for the given event id and room.
|
||||
//!
|
||||
//! Returns a map of user ids and the time of the read receipt in milliseconds.
|
||||
using UserReceipts = std::multimap<uint64_t, std::string, std::greater<uint64_t>>;
|
||||
UserReceipts readReceipts(const QString &event_id, const QString &room_id);
|
||||
UserReceipts
|
||||
readReceipts(const QString &event_id, const QString &room_id);
|
||||
|
||||
//! Filter the events that have at least one read receipt.
|
||||
std::vector<QString> filterReadEvents(const QString &room_id,
|
||||
std::vector<QString>
|
||||
filterReadEvents(const QString &room_id,
|
||||
const std::vector<QString> &event_ids,
|
||||
const std::string &excluded_user);
|
||||
//! Add event for which we are expecting some read receipts.
|
||||
void addPendingReceipt(const QString &room_id, const QString &event_id);
|
||||
void removePendingReceipt(lmdb::txn &txn,
|
||||
const std::string &room_id,
|
||||
const std::string &event_id);
|
||||
void notifyForReadReceipts(const std::string &room_id);
|
||||
std::vector<QString> pendingReceiptsEvents(lmdb::txn &txn, const std::string &room_id);
|
||||
void
|
||||
addPendingReceipt(const QString &room_id, const QString &event_id);
|
||||
void
|
||||
removePendingReceipt(lmdb::txn &txn, const std::string &room_id, const std::string &event_id);
|
||||
void
|
||||
notifyForReadReceipts(const std::string &room_id);
|
||||
std::vector<QString>
|
||||
pendingReceiptsEvents(lmdb::txn &txn, const std::string &room_id);
|
||||
|
||||
QByteArray image(const QString &url) const;
|
||||
QByteArray image(lmdb::txn &txn, const std::string &url) const;
|
||||
QByteArray image(const std::string &url) const
|
||||
QByteArray
|
||||
image(const QString &url);
|
||||
QByteArray
|
||||
image(lmdb::txn &txn, const std::string &url);
|
||||
inline QByteArray
|
||||
image(const std::string &url)
|
||||
{
|
||||
return image(QString::fromStdString(url));
|
||||
}
|
||||
void saveImage(const std::string &url, const std::string &data);
|
||||
void saveImage(const QString &url, const QByteArray &data);
|
||||
void
|
||||
saveImage(const std::string &url, const std::string &data);
|
||||
void
|
||||
saveImage(const QString &url, const QByteArray &data);
|
||||
|
||||
RoomInfo singleRoomInfo(const std::string &room_id);
|
||||
std::vector<std::string> roomsWithStateUpdates(const mtx::responses::Sync &res);
|
||||
std::vector<std::string> roomsWithTagUpdates(const mtx::responses::Sync &res);
|
||||
std::map<QString, RoomInfo> getRoomInfo(const std::vector<std::string> &rooms);
|
||||
std::map<QString, RoomInfo> roomUpdates(const mtx::responses::Sync &sync)
|
||||
RoomInfo
|
||||
singleRoomInfo(const std::string &room_id);
|
||||
std::vector<std::string>
|
||||
roomsWithStateUpdates(const mtx::responses::Sync &res);
|
||||
std::vector<std::string>
|
||||
roomsWithTagUpdates(const mtx::responses::Sync &res);
|
||||
std::map<QString, RoomInfo>
|
||||
getRoomInfo(const std::vector<std::string> &rooms);
|
||||
inline std::map<QString, RoomInfo>
|
||||
roomUpdates(const mtx::responses::Sync &sync)
|
||||
{
|
||||
return getRoomInfo(roomsWithStateUpdates(sync));
|
||||
}
|
||||
std::map<QString, RoomInfo> roomTagUpdates(const mtx::responses::Sync &sync)
|
||||
inline std::map<QString, RoomInfo>
|
||||
roomTagUpdates(const mtx::responses::Sync &sync)
|
||||
{
|
||||
return getRoomInfo(roomsWithTagUpdates(sync));
|
||||
}
|
||||
|
||||
//! Calculates which the read status of a room.
|
||||
//! Whether all the events in the timeline have been read.
|
||||
bool calculateRoomReadStatus(const std::string &room_id);
|
||||
void calculateRoomReadStatus();
|
||||
bool
|
||||
calculateRoomReadStatus(const std::string &room_id);
|
||||
void
|
||||
calculateRoomReadStatus();
|
||||
|
||||
QVector<SearchResult> searchUsers(const std::string &room_id,
|
||||
const std::string &query,
|
||||
std::uint8_t max_items = 5);
|
||||
std::vector<RoomSearchResult> searchRooms(const std::string &query,
|
||||
std::uint8_t max_items = 5);
|
||||
std::vector<SearchResult>
|
||||
searchUsers(const std::string &room_id, const std::string &query, std::uint8_t max_items = 5);
|
||||
std::vector<RoomSearchResult>
|
||||
searchRooms(const std::string &query, std::uint8_t max_items = 5);
|
||||
|
||||
void markSentNotification(const std::string &event_id);
|
||||
void
|
||||
markSentNotification(const std::string &event_id);
|
||||
//! Removes an event from the sent notifications.
|
||||
void removeReadNotification(const std::string &event_id);
|
||||
void
|
||||
removeReadNotification(const std::string &event_id);
|
||||
//! Check if we have sent a desktop notification for the given event id.
|
||||
bool isNotificationSent(const std::string &event_id);
|
||||
bool
|
||||
isNotificationSent(const std::string &event_id);
|
||||
|
||||
//! Add all notifications containing a user mention to the db.
|
||||
void
|
||||
saveTimelineMentions(const mtx::responses::Notifications &res);
|
||||
|
||||
//! Remove old unused data.
|
||||
void deleteOldMessages();
|
||||
void deleteOldData() noexcept;
|
||||
void
|
||||
deleteOldMessages();
|
||||
void
|
||||
deleteOldData() noexcept;
|
||||
//! Retrieve all saved room ids.
|
||||
std::vector<std::string> getRoomIds(lmdb::txn &txn);
|
||||
std::vector<std::string>
|
||||
getRoomIds(lmdb::txn &txn);
|
||||
|
||||
//! Mark a room that uses e2e encryption.
|
||||
void setEncryptedRoom(lmdb::txn &txn, const std::string &room_id);
|
||||
bool isRoomEncrypted(const std::string &room_id);
|
||||
|
||||
//! Save the public keys for a device.
|
||||
void saveDeviceKeys(const std::string &device_id);
|
||||
void getDeviceKeys(const std::string &device_id);
|
||||
|
||||
//! Save the device list for a user.
|
||||
void setDeviceList(const std::string &user_id, const std::vector<std::string> &devices);
|
||||
std::vector<std::string> getDeviceList(const std::string &user_id);
|
||||
void
|
||||
setEncryptedRoom(lmdb::txn &txn, const std::string &room_id);
|
||||
bool
|
||||
isRoomEncrypted(const std::string &room_id);
|
||||
|
||||
//! Check if a user is a member of the room.
|
||||
bool isRoomMember(const std::string &user_id, const std::string &room_id);
|
||||
bool
|
||||
isRoomMember(const std::string &user_id, const std::string &room_id);
|
||||
|
||||
//
|
||||
// Outbound Megolm Sessions
|
||||
//
|
||||
void saveOutboundMegolmSession(const std::string &room_id,
|
||||
void
|
||||
saveOutboundMegolmSession(const std::string &room_id,
|
||||
const OutboundGroupSessionData &data,
|
||||
mtx::crypto::OutboundGroupSessionPtr session);
|
||||
OutboundGroupSessionDataRef getOutboundMegolmSession(const std::string &room_id);
|
||||
bool outboundMegolmSessionExists(const std::string &room_id) noexcept;
|
||||
void updateOutboundMegolmSession(const std::string &room_id, int message_index);
|
||||
OutboundGroupSessionDataRef
|
||||
getOutboundMegolmSession(const std::string &room_id);
|
||||
bool
|
||||
outboundMegolmSessionExists(const std::string &room_id) noexcept;
|
||||
void
|
||||
updateOutboundMegolmSession(const std::string &room_id, int message_index);
|
||||
|
||||
void importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys);
|
||||
mtx::crypto::ExportedSessionKeys exportSessionKeys();
|
||||
void
|
||||
importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys);
|
||||
mtx::crypto::ExportedSessionKeys
|
||||
exportSessionKeys();
|
||||
|
||||
//
|
||||
// Inbound Megolm Sessions
|
||||
//
|
||||
void saveInboundMegolmSession(const MegolmSessionIndex &index,
|
||||
void
|
||||
saveInboundMegolmSession(const MegolmSessionIndex &index,
|
||||
mtx::crypto::InboundGroupSessionPtr session);
|
||||
OlmInboundGroupSession *getInboundMegolmSession(const MegolmSessionIndex &index);
|
||||
bool inboundMegolmSessionExists(const MegolmSessionIndex &index);
|
||||
OlmInboundGroupSession *
|
||||
getInboundMegolmSession(const MegolmSessionIndex &index);
|
||||
bool
|
||||
inboundMegolmSessionExists(const MegolmSessionIndex &index);
|
||||
|
||||
//
|
||||
// Olm Sessions
|
||||
//
|
||||
void saveOlmSession(const std::string &curve25519, mtx::crypto::OlmSessionPtr session);
|
||||
std::vector<std::string> getOlmSessions(const std::string &curve25519);
|
||||
boost::optional<mtx::crypto::OlmSessionPtr> getOlmSession(const std::string &curve25519,
|
||||
const std::string &session_id);
|
||||
|
||||
void saveOlmAccount(const std::string &pickled);
|
||||
std::string restoreOlmAccount();
|
||||
|
||||
void restoreSessions();
|
||||
|
||||
OlmSessionStorage session_storage;
|
||||
|
||||
signals:
|
||||
void newReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
|
||||
void roomReadStatus(const std::map<QString, bool> &status);
|
||||
|
||||
private:
|
||||
//! Save an invited room.
|
||||
void saveInvite(lmdb::txn &txn,
|
||||
lmdb::dbi &statesdb,
|
||||
lmdb::dbi &membersdb,
|
||||
const mtx::responses::InvitedRoom &room);
|
||||
|
||||
QString getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
|
||||
QString getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb);
|
||||
QString getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
|
||||
|
||||
DescInfo getLastMessageInfo(lmdb::txn &txn, const std::string &room_id);
|
||||
void saveTimelineMessages(lmdb::txn &txn,
|
||||
const std::string &room_id,
|
||||
const mtx::responses::Timeline &res);
|
||||
|
||||
mtx::responses::Timeline getTimelineMessages(lmdb::txn &txn, const std::string &room_id);
|
||||
|
||||
//! Remove a room from the cache.
|
||||
// void removeLeftRoom(lmdb::txn &txn, const std::string &room_id);
|
||||
template<class T>
|
||||
void saveStateEvents(lmdb::txn &txn,
|
||||
const lmdb::dbi &statesdb,
|
||||
const lmdb::dbi &membersdb,
|
||||
const std::string &room_id,
|
||||
const std::vector<T> &events)
|
||||
{
|
||||
for (const auto &e : events)
|
||||
saveStateEvent(txn, statesdb, membersdb, room_id, e);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void saveStateEvent(lmdb::txn &txn,
|
||||
const lmdb::dbi &statesdb,
|
||||
const lmdb::dbi &membersdb,
|
||||
const std::string &room_id,
|
||||
const T &event)
|
||||
{
|
||||
using namespace mtx::events;
|
||||
using namespace mtx::events::state;
|
||||
|
||||
if (boost::get<StateEvent<Member>>(&event) != nullptr) {
|
||||
const auto e = boost::get<StateEvent<Member>>(event);
|
||||
|
||||
switch (e.content.membership) {
|
||||
//
|
||||
// We only keep users with invite or join membership.
|
||||
//
|
||||
case Membership::Invite:
|
||||
case Membership::Join: {
|
||||
auto display_name = e.content.display_name.empty()
|
||||
? e.state_key
|
||||
: e.content.display_name;
|
||||
|
||||
// Lightweight representation of a member.
|
||||
MemberInfo tmp{display_name, e.content.avatar_url};
|
||||
|
||||
lmdb::dbi_put(txn,
|
||||
membersdb,
|
||||
lmdb::val(e.state_key),
|
||||
lmdb::val(json(tmp).dump()));
|
||||
|
||||
insertDisplayName(QString::fromStdString(room_id),
|
||||
QString::fromStdString(e.state_key),
|
||||
QString::fromStdString(display_name));
|
||||
|
||||
insertAvatarUrl(QString::fromStdString(room_id),
|
||||
QString::fromStdString(e.state_key),
|
||||
QString::fromStdString(e.content.avatar_url));
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
lmdb::dbi_del(
|
||||
txn, membersdb, lmdb::val(e.state_key), lmdb::val(""));
|
||||
|
||||
removeDisplayName(QString::fromStdString(room_id),
|
||||
QString::fromStdString(e.state_key));
|
||||
removeAvatarUrl(QString::fromStdString(room_id),
|
||||
QString::fromStdString(e.state_key));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (boost::get<StateEvent<Encryption>>(&event) != nullptr) {
|
||||
setEncryptedRoom(txn, room_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isStateEvent(event))
|
||||
return;
|
||||
|
||||
boost::apply_visitor(
|
||||
[&txn, &statesdb](auto e) {
|
||||
lmdb::dbi_put(
|
||||
txn, statesdb, lmdb::val(to_string(e.type)), lmdb::val(json(e).dump()));
|
||||
},
|
||||
event);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool isStateEvent(const T &e)
|
||||
{
|
||||
using namespace mtx::events;
|
||||
using namespace mtx::events::state;
|
||||
|
||||
return boost::get<StateEvent<Aliases>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<state::Avatar>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<CanonicalAlias>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<Create>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<GuestAccess>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<HistoryVisibility>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<JoinRules>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<Name>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<Member>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<PowerLevels>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<Topic>>(&e) != nullptr;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool containsStateUpdates(const T &e)
|
||||
{
|
||||
using namespace mtx::events;
|
||||
using namespace mtx::events::state;
|
||||
|
||||
return boost::get<StateEvent<state::Avatar>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<CanonicalAlias>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<Name>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<Member>>(&e) != nullptr ||
|
||||
boost::get<StateEvent<Topic>>(&e) != nullptr;
|
||||
}
|
||||
|
||||
bool containsStateUpdates(const mtx::events::collections::StrippedEvents &e)
|
||||
{
|
||||
using namespace mtx::events;
|
||||
using namespace mtx::events::state;
|
||||
|
||||
return boost::get<StrippedEvent<state::Avatar>>(&e) != nullptr ||
|
||||
boost::get<StrippedEvent<CanonicalAlias>>(&e) != nullptr ||
|
||||
boost::get<StrippedEvent<Name>>(&e) != nullptr ||
|
||||
boost::get<StrippedEvent<Member>>(&e) != nullptr ||
|
||||
boost::get<StrippedEvent<Topic>>(&e) != nullptr;
|
||||
}
|
||||
|
||||
void saveInvites(lmdb::txn &txn,
|
||||
const std::map<std::string, mtx::responses::InvitedRoom> &rooms);
|
||||
|
||||
//! Sends signals for the rooms that are removed.
|
||||
void 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);
|
||||
}
|
||||
}
|
||||
|
||||
lmdb::dbi getPendingReceiptsDb(lmdb::txn &txn)
|
||||
{
|
||||
return lmdb::dbi::open(txn, "pending_receipts", MDB_CREATE);
|
||||
}
|
||||
|
||||
lmdb::dbi getMessagesDb(lmdb::txn &txn, const std::string &room_id)
|
||||
{
|
||||
auto db =
|
||||
lmdb::dbi::open(txn, std::string(room_id + "/messages").c_str(), MDB_CREATE);
|
||||
lmdb::dbi_set_compare(txn, db, numeric_key_comparison);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return lmdb::dbi::open(txn, std::string(room_id + "/state").c_str(), MDB_CREATE);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
//! Retrieves or creates the database that stores the open OLM sessions between our device
|
||||
//! and the given curve25519 key which represents another device.
|
||||
//!
|
||||
//! Each entry is a map from the session_id to the pickled representation of the session.
|
||||
lmdb::dbi getOlmSessionsDb(lmdb::txn &txn, const std::string &curve25519_key)
|
||||
{
|
||||
return lmdb::dbi::open(
|
||||
txn, std::string("olm_sessions/" + curve25519_key).c_str(), MDB_CREATE);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void setNextBatchToken(lmdb::txn &txn, const std::string &token);
|
||||
void setNextBatchToken(lmdb::txn &txn, const QString &token);
|
||||
|
||||
lmdb::env env_;
|
||||
lmdb::dbi syncStateDb_;
|
||||
lmdb::dbi roomsDb_;
|
||||
lmdb::dbi invitesDb_;
|
||||
lmdb::dbi mediaDb_;
|
||||
lmdb::dbi readReceiptsDb_;
|
||||
lmdb::dbi notificationsDb_;
|
||||
|
||||
lmdb::dbi devicesDb_;
|
||||
lmdb::dbi deviceKeysDb_;
|
||||
|
||||
lmdb::dbi inboundMegolmSessionDb_;
|
||||
lmdb::dbi outboundMegolmSessionDb_;
|
||||
|
||||
QString localUserId_;
|
||||
QString cacheDirectory_;
|
||||
};
|
||||
|
||||
namespace cache {
|
||||
void
|
||||
init(const QString &user_id);
|
||||
saveOlmSession(const std::string &curve25519, mtx::crypto::OlmSessionPtr session);
|
||||
std::vector<std::string>
|
||||
getOlmSessions(const std::string &curve25519);
|
||||
std::optional<mtx::crypto::OlmSessionPtr>
|
||||
getOlmSession(const std::string &curve25519, const std::string &session_id);
|
||||
|
||||
Cache *
|
||||
client();
|
||||
void
|
||||
saveOlmAccount(const std::string &pickled);
|
||||
std::string
|
||||
restoreOlmAccount();
|
||||
|
||||
void
|
||||
restoreSessions();
|
||||
}
|
||||
|
|
67
src/CacheCryptoStructs.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
//#include <nlohmann/json.hpp>
|
||||
|
||||
#include <mtx/responses.hpp>
|
||||
#include <mtxclient/crypto/client.hpp>
|
||||
|
||||
// Extra information associated with an outbound megolm session.
|
||||
struct OutboundGroupSessionData
|
||||
{
|
||||
std::string session_id;
|
||||
std::string session_key;
|
||||
uint64_t message_index = 0;
|
||||
};
|
||||
|
||||
void
|
||||
to_json(nlohmann::json &obj, const OutboundGroupSessionData &msg);
|
||||
void
|
||||
from_json(const nlohmann::json &obj, OutboundGroupSessionData &msg);
|
||||
|
||||
struct OutboundGroupSessionDataRef
|
||||
{
|
||||
OlmOutboundGroupSession *session;
|
||||
OutboundGroupSessionData data;
|
||||
};
|
||||
|
||||
struct DevicePublicKeys
|
||||
{
|
||||
std::string ed25519;
|
||||
std::string curve25519;
|
||||
};
|
||||
|
||||
void
|
||||
to_json(nlohmann::json &obj, const DevicePublicKeys &msg);
|
||||
void
|
||||
from_json(const nlohmann::json &obj, DevicePublicKeys &msg);
|
||||
|
||||
//! Represents a unique megolm session identifier.
|
||||
struct MegolmSessionIndex
|
||||
{
|
||||
//! The room in which this session exists.
|
||||
std::string room_id;
|
||||
//! The session_id of the megolm session.
|
||||
std::string session_id;
|
||||
//! The curve25519 public key of the sender.
|
||||
std::string sender_key;
|
||||
};
|
||||
|
||||
void
|
||||
to_json(nlohmann::json &obj, const MegolmSessionIndex &msg);
|
||||
void
|
||||
from_json(const nlohmann::json &obj, MegolmSessionIndex &msg);
|
||||
|
||||
struct OlmSessionStorage
|
||||
{
|
||||
// Megolm sessions
|
||||
std::map<std::string, mtx::crypto::InboundGroupSessionPtr> group_inbound_sessions;
|
||||
std::map<std::string, mtx::crypto::OutboundGroupSessionPtr> group_outbound_sessions;
|
||||
std::map<std::string, OutboundGroupSessionData> group_outbound_session_data;
|
||||
|
||||
// Guards for accessing megolm sessions.
|
||||
std::mutex group_outbound_mtx;
|
||||
std::mutex group_inbound_mtx;
|
||||
};
|