Compare commits

..

34 Commits

Author SHA1 Message Date
f513358236 LayoutManager: respect WITH_WEBENGINE flag
This patch updates LayoutManager to use WebEngine only
when WITH_WEBENGINE is set to true.

Change-Id: Idcfc99af9fb4d74c68aac8fc9029cde2bc05b4eb
2025-07-18 14:10:39 -04:00
3c00829afb packaging: use new icon filename
Follow-up to commit 5c772960bc.

Change-Id: Ib5c0b4bc1c5e3f5112bbd49bf323aeb4cc87c75e
2025-07-18 13:30:23 -04:00
84a59889e3 tests/qml: fix undefined references to appWindow
GitLab: #1780
Change-Id: I166f68c7abdf0f21d8d5dcec85e377c27de798a3
2025-07-16 16:17:16 -04:00
6c35561817 Reinstate "cmake: Simplify lookup logic for libjami headers." (again).
This reinstates commit f6c8197cba, which was seemingly reverted by
mistake in 4d2c55348b ("packaging: migrate from Qt 6.2.3 to
6.4.3").

Change-Id: I603e004431be0503be0acdd7ce37fa5aaa4ecd91
2025-07-16 12:58:01 -04:00
6ffdda7b81 tests: Fix WITH_WEBENGINE check.
Use #if, not #ifdef, to test the integer value of WITH_WEBENGINE, not
just whether it's defined.  Otherwise, -DWITH_WEBENGINE=OFF would not
work.

* tests/qml/main.cpp: Replace '#ifdef WITH_WEBENGINE' with '#if
WITH_WEBENGINE'.

Change-Id: Ieda8c46fa696afa1e4118acc7d4fecd4b7f9a171
2025-07-16 12:03:56 -04:00
a51078c900 tests: Avoid takeFirst-caused segfaults on empty arrays.
Replace EXPECT_EQ with ASSERT_EQ where a failure should be
fatal (abort execution).

Relates-to: <https://git.jami.net/savoirfairelinux/jami-client-qt/-/issues/1507>.
Change-Id: I4b5e38cdc399c1d1a51f72abab23cce963578541
2025-07-16 11:59:26 -04:00
76a710e2ab Fix running tests when building out of the source tree.
CMake's enable_testing needs to be called from the root of the project
as well for CTest to discover the tests in the build tree.  This is
covered in CMake's manual in section 7.2.17 enable_testing.

Change-Id: I7c9b845c52064ff83e39b483b76137b529c1a9a4
2025-07-16 11:57:28 -04:00
bc324aa8bb build: update guix manifest for build.py
Change-Id: I878ad4b2ae4adee4ebc623e4ab40362fd9e1034a
2025-07-16 11:56:55 -04:00
5c772960bc Adhere to flatpak/flathub requirements
- Developer id must be rDns identifier of developer
- Exported icons should match rDNS of app id
- Release version and dates should be included
- Added light and dark branding colours for Flathub banner
- Contact url removed - does not pass appstream validation

Change-Id: Ic5e2a5abeab4310ea87a34cc81363d1851135bcd
2025-07-16 11:31:12 -04:00
da2acefced packaging: add AlmaLinux 10
GitLab: #2065
Change-Id: I4252e2c128f72b3f6a78204a4134374b44155b80
2025-07-14 16:58:06 -04:00
524c9b0ed4 misc: bump daemon submodule
Change-Id: I98aed9601ef2c5dbaf5430ee4abf4d3ee3741f63
2025-07-14 13:29:10 -04:00
8677349c4a accessibility: fix chatview
Add keyboard and screen-reader navigation for the chat view.

Change-Id: I11a5dc1ee3b0d6303f4598f10008ecc6979bb777
2025-07-11 15:08:43 -04:00
905b2e858e build: Guard against potential unset CMAKE_BUILD_TYPE.
This would fail the build with the error:

  CMake Error at tests/CMakeLists.txt:74 (string):
    string no output variable specified

* tests/CMakeLists.txt [CMAKE_BUILD_TYPE]: Make BUILD_TYPE variable
assignment conditional.

Change-Id: I610fe296ed18bd038b2d23d4acd530f05f9526ce
2025-07-11 17:07:56 +09:00
6fc2e75a33 CMakeLists.txt: use system <md4c, tidy>
Search for libraries in the system first, falling back to bundled
copies in case they aren't found.

Change-Id: I10e154f18c569d87dbdcbee3341316ceed57f13c
GitLab: #1506
2025-07-10 10:59:30 -04:00
c738caa3a4 accessibility: fix the messagebar
Add proper labels to the messagebar and remove the existing
keyboard trap in the showmore menu.

Change-Id: Ib70ca29979dc3909585383d612d1062259df83a8
2025-07-09 11:58:49 -04:00
cb13d4f771 accessibility: fix the tip row
Indicate correctly what tips are on the main page.

Change-Id: I142cde7ab3e553cf981a59090b363694dab08775
2025-07-09 11:58:49 -04:00
436e11a6a4 accessibility: fix buttons
Fix the frameless, conversations and invitations buttons for
accessibility.

GitLab: #2049
Change-Id: I82b6722de53c40a260e706a2517aa7cb37dd99a5
2025-07-09 11:58:46 -04:00
869aef8929 accessibility: fix call action bar
Make the call action bar usable with only a keyboard and a
screen-reader by makint it visible using tab and inplementing
proper labels.

GitLab: #2049
Change-Id: Ifa1f1463f27b30bcfffae228058223729f00e51d
2025-07-08 16:40:31 -04:00
e0f939318e accessibility: fix the account creation wizard
Add support for screen readers and keyboard navigation for the account
creation.

GitLab: #2049
Change-Id: Ie91d7222388a9fb627d5a981ba9e7d8afdebcd55
2025-07-08 15:12:42 -04:00
136dea011f accessibility: fix accountlist
Allow to naviguate with keyboard inside of the accountlist and label
the accounts properly

GitLab: #2049

Change-Id: I53e5e97f344df86d6390ab444a642dcabea61cde
2025-07-08 14:16:36 -04:00
af09269d81 conversationmodel: fix multi-device file transfers
The current logic for handling received messages assumes that files sent
by the user don't need to be downloaded. However, this assumption is
incorrect when the user's account is present on multiple devices.

GitLab: #2069
Change-Id: I4e6a53389d736aec4aa86cd39862b7905b9b09ad
2025-06-26 15:30:20 -04:00
5a5ef4711d *.desktop and *.metainfo.xml: update
https://github.com/flathub/net.jami.Jami/blob/master/files/jami-client-qt_appdata.patch
https://docs.flathub.org/docs/for-app-authors/metainfo-guidelines
https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html
https://specifications.freedesktop.org/desktop-entry-spec/latest/file-naming.html
https://specifications.freedesktop.org/desktop-entry-spec/latest/localized-keys.html
https://www.rfc-editor.org/rfc/bcp/bcp47.txt
https://review.jami.net/c/jami-client-android/+/30260
https://review.jami.net/c/jami-client-qt/+/29902

GitLab: #1955

Change-Id: Ib6a3c9770693cdae0f5d6a6a0ccee624febeabe3
2025-06-17 19:34:51 -04:00
1dd745d446 packaging: remove EOL distributions
GitLab: #2064
Change-Id: I566936c16512e7cc3db085a362fbd30f32bb693f
2025-06-17 13:30:12 -04:00
3dd2d26d86 versioning: add build version to snap packaging
GitLab: #2045
Change-Id: I173342d3b9351644a4bad4f70e033d29f06a6cd8
2025-06-16 16:45:33 -04:00
eb6b6a2b93 versioning: add build version to rpm packaging
GitLab: #2045
Change-Id: I44e1c6862305706bb177b1d8f4b1900f1c0098a5
2025-06-16 16:45:10 -04:00
b15d692a0e spellchecker: fix codec segfault
Fix the segfault happening when a codec is not properly loaded by
only activated spellcheck when a dictionnary has been successfully
loaded, initializing a default codec and adding error handling.

GitLab: #2063

Change-Id: I48339ce6d13120cfbae3c6c7eb6f40e87f16f084
2025-06-13 16:03:38 -04:00
e76bcbd555 versioning: add build version to debian packaging
GitLab: #2045
Change-Id: If0f8e2fef4ed38ae9cd4238bb725158526d24658
2025-06-13 10:21:43 -04:00
b5a979e6b1 versioning: add build version propagation for unix packaging system
The application on linux systems currently has a different build version
built into it than the build version generated on Jenkins which dictates
the filenames for release packages. This commit aims to propagate a
build version defined in the gnu-linux Jenkinsfile down to the
build-package-*.sh scripts.

Changes:
- create build version (timestamp) in Jenkinsfile
- propagate build version to Makefile, then the docker containers that
will execute the build-package-*.sh scripts

GitLab: #2045
Change-Id: Ia7cbb2f707c233741039dc7bfd3e5bd6a96c2270
2025-06-12 13:32:03 -04:00
898444dd3c versioning: add build version as windows build argument
The application currently has a different build version than the one set
in Jenkins. This causes issues in the crash reports on windows where
they contain a different build version than the one defined on Jenkins.
This leads to the names of the build artifacts on dl.jami.net not
matching the crash report's build version.

Changes:
- Add build version as parameter to the build-windows.py script.
- Propagate build version value to the application through the
BUILD_VERSION symbol.
- Deprecate version.h file and VERSION_STRING symbol.

GitLab: #2045
Change-Id: I7986679aaeebf2bcbbd63a781499f5a50e089712
2025-06-12 13:31:59 -04:00
99f246016d cmake : fix output directory for windows builds
The output directory for windows builds currently matches either Release
or Beta. This does not work as Beta is only a build option, not a build
type. Changes :

- Change build output directory to match the cmake build type (Release
or Debug)
- Change Beta output path references to Release

Change-Id: Ib513f177d93e3c9fb529e00aa160443ac2e804b5
2025-06-12 13:31:54 -04:00
e24f3d91e8 spellchecker: fix greek
greek spell correction wasn't working. this is because  modern greek
is in a special encoding : ISO8859-7. The text in Jami is in utf-8.
Therefore this patch detect the encoding and decode/encode informations
in the relevent encoding between the client and the hunspell library.

GitLab: #2062
Change-Id: Ia33f154e3bf4b84f8337f669df81152ddcd25ec6
2025-06-10 10:29:11 -04:00
3a7850b398 theme: add dark mode support in keyboard shortcuts window
GitLab: #1891
Change-Id: I1e0746b00776564cc7ddbe12e4ad5665f037a092
2025-06-10 10:28:22 -04:00
1e1750024b JamiStrings: spellChecker name
Change-Id: Id163c032c7507496254061f2d707debefe7ab1b5
2025-06-06 01:52:10 -04:00
2ba53a2e40 misc: bump daemon
Change-Id: I33da6f7a22d31b8ac5289f53ea7596b7f7925a66
2025-06-05 14:38:22 -04:00
95 changed files with 1247 additions and 978 deletions

View File

@ -84,10 +84,8 @@ if(WIN32)
if(BETA)
message(STATUS "Beta config enabled")
add_definitions(-DBETA)
set(JAMI_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/x64/Beta)
else()
set(JAMI_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/x64/Release)
endif()
set(JAMI_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/x64/${CMAKE_BUILD_TYPE})
endif()
if(WIN32)
@ -246,7 +244,7 @@ set(CMAKE_MODULE_PATH
${CMAKE_MODULE_PATH} "${EXTRAS_DIR}/build/cmake/modules")
find_package(LibJami REQUIRED)
if(LIBJAMI_FOUND)
include_directories(${LIBJAMI_INCLUDE_DIRS})
include_directories(${LIBJAMI_INCLUDE_DIR})
endif()
include(FindPython3)
@ -266,6 +264,7 @@ add_custom_target(
-DAPP_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}
-DCORE_SOURCE_DIR=${DAEMON_DIR}
-DCPP_INT_FILE=${VERSION_INFO_DIR}/version_info.cpp.in
-DBUILD_VERSION=${BUILD_VERSION}
-P ${CMAKE_SCRIPTS_DIR}/generate_version_info.cmake
)
list(APPEND CLIENT_INCLUDE_DIRS ${VERSION_INFO_DIR})
@ -379,7 +378,6 @@ set(COMMON_HEADERS
${APP_SRC_DIR}/appversionmanager.h
${APP_SRC_DIR}/utils.h
${APP_SRC_DIR}/bannedlistmodel.h
${APP_SRC_DIR}/version.h
${APP_SRC_DIR}/accountlistmodel.h
${APP_SRC_DIR}/instancemanager.h
${APP_SRC_DIR}/connectivitymonitor.h
@ -696,20 +694,34 @@ add_subdirectory(3rdparty/SortFilterProxyModel)
set(SFPM_OBJECTS $<TARGET_OBJECTS:SortFilterProxyModel>)
# md4c
set(BUILD_MD2HTML_EXECUTABLE OFF CACHE BOOL "Don't build md2html executable" FORCE)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Don't build shared md4c library" FORCE)
add_subdirectory(3rdparty/md4c EXCLUDE_FROM_ALL)
list(APPEND CLIENT_LINK_DIRS ${MD4C_BINARY_DIR}/src)
list(APPEND CLIENT_INCLUDE_DIRS ${MD4C_SOURCE_DIR}/src)
list(APPEND CLIENT_LIBS md4c-html)
find_package(md4c)
if(md4c_FOUND)
message(STATUS "Using system-provided md4c-html")
list(APPEND CLIENT_LIBS md4c::md4c-html)
else()
message("Using bundled md4c-html library")
set(BUILD_MD2HTML_EXECUTABLE OFF CACHE BOOL "Don't build md2html executable" FORCE)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Don't build shared md4c library" FORCE)
add_subdirectory(3rdparty/md4c EXCLUDE_FROM_ALL)
list(APPEND CLIENT_LINK_DIRS ${MD4C_BINARY_DIR}/src)
list(APPEND CLIENT_INCLUDE_DIRS ${MD4C_SOURCE_DIR}/src)
list(APPEND CLIENT_LIBS md4c-html)
endif()
# tidy-html5
set(BUILD_SHARED_LIB OFF CACHE BOOL "Don't build shared tidy library" FORCE)
set(SUPPORT_CONSOLE_APP OFF CACHE BOOL "Don't build tidy console app" FORCE)
add_subdirectory(3rdparty/tidy-html5 EXCLUDE_FROM_ALL)
list(APPEND CLIENT_LINK_DIRS ${tidy_BINARY_DIR}/Release)
list(APPEND CLIENT_INCLUDE_DIRS ${tidy_SOURCE_DIR}/include)
list(APPEND CLIENT_LIBS tidy-static)
pkg_check_modules(tidy IMPORTED_TARGET tidy)
if(tidy_FOUND)
message(STATUS "Using system-provided tidy")
list(APPEND CLIENT_LIBS PkgConfig::tidy)
else()
message("Using bundled tidy library")
set(BUILD_SHARED_LIB OFF CACHE BOOL "Don't build shared tidy library" FORCE)
set(SUPPORT_CONSOLE_APP OFF CACHE BOOL "Don't build tidy console app" FORCE)
add_subdirectory(3rdparty/tidy-html5 EXCLUDE_FROM_ALL)
list(APPEND CLIENT_LINK_DIRS ${tidy_BINARY_DIR}/Release)
list(APPEND CLIENT_INCLUDE_DIRS ${tidy_SOURCE_DIR}/include)
list(APPEND CLIENT_LIBS tidy-static)
endif()
# ZXing-cpp configuration
set(BUILD_EXAMPLES OFF CACHE BOOL "")
@ -835,12 +847,12 @@ elseif (NOT APPLE)
# Logos
install(
FILES resources/images/jami.svg
FILES resources/images/net.jami.Jami.svg
DESTINATION
${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps)
install(
FILES resources/images/jami-48px.png
FILES resources/images/net.jami.Jami-48px.png
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps
RENAME jami.png)
@ -1018,5 +1030,6 @@ qt_finalize_executable(${PROJECT_NAME})
# tests
if(BUILD_TESTING)
message("Add Jami tests")
enable_testing()
add_subdirectory(${TESTS_DIR})
endif()

View File

@ -4,10 +4,5 @@
<?define ExeName="Jami" ?>
<?define AppName="Jami" ?>
<?define Manufacturer="Savoir-Faire Linux"?>
<?if $(var.Configuration) = Release ?>
<?define ReleaseDir="..\x64\Release"?>
<?else?>
<?define ReleaseDir="..\x64\Beta"?>
<?endif ?>
</Include>

View File

@ -16,9 +16,9 @@
<InstallerPlatform>x64</InstallerPlatform>
<DefineSolutionProperties>false</DefineSolutionProperties>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<PropertyGroup>
<OutputPath>bin\Release\</OutputPath>
<IntermediateOutputPath>obj\Release\</IntermediateOutputPath>
<DefineConstants>AppHarvestPath=..\x64\Release;CrtHarvestPath=$(VC_CRT_Dir)</DefineConstants>
<SuppressPdbOutput>True</SuppressPdbOutput>
<CompilerAdditionalOptions>
@ -26,16 +26,6 @@
<WixVariables>
</WixVariables>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Beta|x64' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>AppHarvestPath=..\x64\Beta;CrtHarvestPath=$(VC_CRT_Dir)</DefineConstants>
<SuppressPdbOutput>True</SuppressPdbOutput>
<CompilerAdditionalOptions>
</CompilerAdditionalOptions>
<WixVariables>
</WixVariables>
</PropertyGroup>
<ItemGroup>
<Compile Include="Product.wxs" />
<Compile Include="AppComponents.wxs" />
@ -70,7 +60,7 @@
<Error Text="The WiX Toolset v3.11 (or newer) build tools must be installed to build this project. To download the WiX Toolset, see http://wixtoolset.org/releases/" />
</Target>
<Target Name="BeforeBuild">
<HeatDirectory Directory="..\x64\$(Configuration)"
<HeatDirectory Directory="..\x64\Release"
PreprocessorVariable="var.AppHarvestPath"
OutputFile="AppComponents.wxs"
ComponentGroupName="AppHeatGenerated"

2
daemon

Submodule daemon updated: 6f81476042...1969975c2c

View File

@ -24,6 +24,7 @@ set(CMAKE_CURRENT_BINARY_DIR ${APP_BINARY_DIR})
# Generate the version string for the application and core
configure_version_string(${APP_SOURCE_DIR} APP_VERSION_STRING)
configure_version_string(${CORE_SOURCE_DIR} CORE_VERSION_STRING)
set(BUILD_VERSION_STRING ${BUILD_VERSION})
# Get output file names with the .in extension removed
get_filename_component(VERSION_CPP_FILENAME ${CPP_INT_FILE} NAME_WE)

View File

@ -16,30 +16,19 @@
# Once done, this find module will set:
#
# LIBJAMI_INCLUDE_DIRS - libjami include directories
# LIBJAMI_INCLUDE_DIR - libjami include directory
# LIBJAMI_FOUND - whether it was able to find the include directories
# LIBJAMI_LIB - path to libjami or libring library
set(LIBJAMI_FOUND true)
if(WITH_DAEMON_SUBMODULE)
set(LIBJAMI_INCLUDE_DIRS ${DAEMON_DIR}/src/jami)
set(LIBJAMI_INCLUDE_DIR ${DAEMON_DIR}/src/jami)
else()
if(EXISTS ${LIBJAMI_INCLUDE_DIR}/jami.h)
set(LIBJAMI_INCLUDE_DIRS ${LIBJAMI_INCLUDE_DIR})
elseif(EXISTS ${LIBJAMI_BUILD_DIR}/jami/jami.h)
set(LIBJAMI_INCLUDE_DIRS ${LIBJAMI_BUILD_DIR}/jami)
elseif(EXISTS ${RING_INCLUDE_DIR}/jami.h)
set(LIBJAMI_INCLUDE_DIRS ${RING_INCLUDE_DIR})
elseif(EXISTS ${RING_BUILD_DIR}/jami/jami.h)
set(LIBJAMI_INCLUDE_DIRS ${RING_BUILD_DIR}/jami)
elseif(EXISTS ${CMAKE_INSTALL_PREFIX}/include/jami/jami.h)
set(LIBJAMI_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/include/jami)
elseif(EXISTS ${CMAKE_INSTALL_PREFIX}/daemon/include/jami/jami.h)
set(LIBJAMI_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/daemon/include/jami)
else()
find_path(LIBJAMI_INCLUDE_DIR jami.h PATH_SUFFIXES jami)
if(NOT LIBJAMI_INCLUDE_DIR)
message(STATUS "Jami daemon headers not found!
Set -DLIBJAMI_BUILD_DIR or -DCMAKE_INSTALL_PREFIX")
To build using the daemon git submodule, set -DWITH_DAEMON_SUBMODULE")
set(LIBJAMI_FOUND false)
endif()
endif()
@ -121,5 +110,5 @@ endif()
# Restore the original value of CMAKE_FIND_LIBRARY_SUFFIXES.
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_orig})
message(STATUS "Jami daemon headers are in " ${LIBJAMI_INCLUDE_DIRS})
message(STATUS "Jami daemon headers are in " ${LIBJAMI_INCLUDE_DIR})
message(STATUS "Jami daemon library is at " ${LIBJAMI_LIB})

View File

@ -85,7 +85,7 @@
<id>net.jami.daemon</id>
</requires>
<launchable type="desktop-id">net.jami.Jami.desktop</launchable>
<icon type="stock">jami</icon>
<icon type="stock">net.jami.Jami</icon>
<url type="homepage">https://jami.net/</url>
<url type="bugtracker">https://git.jami.net/savoirfairelinux/jami-client-qt/issues</url>
<url type="faq">https://docs.jami.net/user/faq.html</url>

View File

@ -1,14 +1,13 @@
[Desktop Entry]
Name=Jami
GenericName=Jami
Comment=Privacy-oriented voice, video, chat, and conference platform
Comment[hu]=Adatvédelem-orientált hang-, video-, csevegés- és konferenciaplatform
Comment[ru]=Jami — приложение для защищённой связи с распределённой архитектурой
Comment=Share, freely and privately
Comment[hu]=Megosztás, szabadon és bizalmasan
Exec=jami %u
Icon=jami
Icon=net.jami.Jami
StartupNotify=true
Terminal=false
Type=Application
Categories=Network;Telephony;
Categories=Chat;FileTransfer;InstantMessaging;Network;P2P;Telephony;VideoConference;
Keywords=Qt;chat;talk;im;message;voip;
MimeType=x-scheme-handler/jami;
MimeType=x-scheme-handler/jami;

View File

@ -2,87 +2,101 @@
<!-- Copyright (C) 2015-2025 Savoir-faire Linux Inc. -->
<component type="desktop-application">
<id>net.jami.Jami</id>
<metadata_license>CC-BY-SA-3.0</metadata_license>
<project_license>GPL-3.0+</project_license>
<name>Jami</name>
<summary>Privacy-oriented voice, video, chat, and conference platform</summary>
<summary xml:lang="hu">Adatvédelem-orientált hang-, video-, csevegés- és konferenciaplatform</summary>
<icon type="stock">jami</icon>
<summary>Share, freely and privately</summary>
<summary xml:lang="hu">Megosztás, szabadon és bizalmasan</summary>
<icon type="stock">net.jami.Jami</icon>
<metadata_license>CC-BY-SA-3.0</metadata_license>
<project_license>GPL-3.0+</project_license>
<developer id="net.jami">
<name>Savoir-faire Linux Inc.</name>
</developer>
<releases>
<release version="20250613.0" date="2025-06-13">
<url type="details">https://git.jami.net/savoirfairelinux/jami-client-qt/-/wikis/Changelog#nightlystable-20250613</url>
</release>
</releases>
<branding>
<color type="primary" scheme_preference="light">#03b9e9</color>
<color type="primary" scheme_preference="dark">#005699</color>
</branding>
<!-- https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html#tag-description -->
<description>
<p>Jami, a GNU package, is software for universal and distributed peer-to-peer communication that respects the freedom and privacy of its users.</p>
<p xml:lang="hu">A Jami, egy GNU-csomag, egy univerzális és elosztott társ-társ kommunikációra szolgáló szoftver, amely tiszteletben tartja a felhasználók szabadságát és magánéletét.</p>
<p>Jami is the simplest and easiest way to connect with people (and devices) with instant messaging, audio and video calls over the Internet and LAN/WAN intranets.</p>
<p xml:lang="hu">A Jami a legegyszerűbb és legegyszerűbb módja annak, hogy azonnali üzenetküldéssel, hang- és videohívásokkal kapcsolódjon az emberekhez (és eszközökhöz) az interneten és a LAN/WAN intraneteken keresztül.</p>
<p><em>Jami</em>, a GNU package, is software for universal and distributed peer-to-peer communication that respects the freedom and privacy of its users.</p>
<p xml:lang="hu">A <em>Jami</em>, egy GNU-csomag, egy univerzális és elosztott társ-társ kommunikációt szolgáló szoftver, amely tiszteletben tartja a felhasználók szabadságát és magánéletét.</p>
<p>Jami is the simplest and easiest way to connect with people (and devices) with instant messaging, audio, and video calls over the Internet and LAN/WAN intranets.</p>
<p xml:lang="hu">A Jami a legegyszerűbb és legegyszerűbb módja annak, hogy azonnali üzenetküldéssel, hang- és videohívásokkal kapcsolódjon az emberekhez (és eszközökhöz) az interneten és a LAN/WAN intraneten keresztül.</p>
<p>Jami is a free/libre, end-to-end encrypted, and private communication platform.</p>
<p xml:lang="hu">A Jami egy ingyenes, teljes körűen titkosított és privát kommunikációs platform.</p>
<p>Jami which used to be known as Ring is also an open-source alternative (to Facebook Messenger, Signal, Skype, Teams, Telegram, TikTok, Viber, WhatsApp, Zoom) that prioritizes the privacy of its users.</p>
<p xml:lang="hu">A Jami amelyet korábban Ring néven ismertek egy nyílt forráskódú alternatíva is (a Facebook Messenger, a Signal, a Skype, a Teams, a Telegram, a TikTok, a Viber, a WhatsApp, a Zoom számára), amely előtérbe helyezi a felhasználók magánéletét.</p>
<p>Jami has a professional-looking design and is available for a wide range of platforms. Unlike the alternatives, calls using Jami are directly between users as it does not use servers to handle calls.</p>
<p xml:lang="hu">A Jami professzionális megjelenésű, és platformok széles skálájához elérhető. Az alternatívákkal ellentétben a Jami-t használó hívások közvetlenül a felhasználók között zajlanak, mivel nem használ kiszolgálókat a hívások kezelésére.</p>
<p>This gives the greatest privacy as the distributed nature of Jami means your calls are only between participants.</p>
<p>Jami is open-source software that prioritizes user privacy.</p>
<p xml:lang="hu">A Jami nyílt forráskódú szoftver, amely előnyben részesíti a felhasználók adatvédelmét.</p>
<p>Jami has a professional-looking design and is available for a wide range of platforms. Unlike the alternatives, Jami calls are directly between users, as it does not use servers to handle calls.</p>
<p xml:lang="hu">A Jami professzionális megjelenésű, és platformok széles skálájához elérhető. Az alternatívákkal ellentétben a Jami közvetlenül a felhasználók között zajlik, mivel nem használ kiszolgálókat a hívások kezelésére.</p>
<p>This gives the greatest privacy, as the distributed nature of Jami means your calls are only between participants.</p>
<p xml:lang="hu">Ez biztosítja a legnagyobb magánéletet, mivel a Jami elosztott jellege azt jelenti, hogy a hívások csak a résztvevők között zajlanak.</p>
<p>One-to-one and group conversations with Jami are enhanced with: instant messaging; audio and video calling; recording and sending audio and video messages; file transfers; screen sharing; and, location sharing.</p>
<p xml:lang="hu">A Jamival folytatott személyes és csoportos beszélgetéseket a következők javítják: azonnali üzenetküldés; hang- és videohívások; hang- és videoüzenetek rögzítése és küldése; fájlátvitel; képernyőmegosztás; és helymegosztás.</p>
<p>One-to-one and group conversations with Jami are enhanced with instant messaging, audio and video calling, recording and sending audio and video messages, file transfers, screen sharing, and location sharing.</p>
<p xml:lang="hu">A Jamival folytatott személyes és csoportos beszélgetéseket az azonnali üzenetküldés, hang- és videohívások, hang- és videoüzenetek rögzítése és küldése, fájlátvitel, képernyőmegosztás és helymegosztás továbbfejlesztik.</p>
<p>Jami can also function as a SIP client.</p>
<p xml:lang="hu">A Jami SIP-ügyfélként is működhet.</p>
<p>Jami has multiple extensions available: Audio Filter; Auto Answer; Green Screen; Watermark; and, Whisper Transcript.</p>
<p xml:lang="hu">A Jami-nek több bővítménye is elérhető: hangszűrő; automatikus válasz; zöld képernyő; vízjel; és, suttogó átirat.</p>
<p>Jami can be easily deployed in organizations with the “Jami Account Management Server” (JAMS), allowing users to connect with their corporate credentials or create local accounts. JAMS allows you to manage your own Jami community while taking advantage of Jamis distributed network architecture.</p>
<p xml:lang="hu">A Jami könnyen telepíthető a szervezetekben a JAMS (Jami Account Management Server Jami fiókkezelő kiszolgáló), amely lehetővé teszi a felhasználók számára, hogy csatlakozzanak vállalati hitelesítő adataikhoz, vagy helyi fiókokat hozzanak létre. A JAMS lehetővé teszi saját Jami közösségének kezelését, miközben kihasználja a Jami elosztott hálózati architektúráját.</p>
<p>Jami is available for GNU/Linux, Windows, macOS, iOS, Android, and Android TV, making Jami an interoperable and cross-platform communication framework.</p>
<p xml:lang="hu">A Jami elérhető GNU/Linux, Windows, macOS, iOS, Android és Android TV rendszereken, így a Jami egy interoperábilis és platformok közötti kommunikációs keretrendszer.</p>
<p>Manage multiple SIP accounts, Jami accounts and JAMS accounts with the Jami client installed on one or multiple devices.</p>
<p xml:lang="hu">Kezeljen több SIP-fiókot, Jami-fiókot és JAMS-fiókot az egy vagy több eszközre telepített Jami-ügyféllel.</p>
<p>Jami is free, unlimited, private, advertising free, compatible, fast, autonomous, and anonymous.</p>
<p>Multiple Jami extensions are available: Audio Filter, Auto Answer, Green Screen, Segmentation, Watermark, and Whisper Transcript.</p>
<p xml:lang="hu">Több Jami bővítmény érhető el: hangszűrő, automatikus válasz, zöld képernyő, szegmentálás, vízjel és suttogó átirat.</p>
<p>Jami can be easily deployed in organizations with the <em>JAMS (Jami Account Management Server)</em>, allowing users to connect with their corporate credentials or create local accounts. JAMS allows you to manage your own Jami community while taking advantage of Jamis distributed network architecture.</p>
<p xml:lang="hu">A Jami könnyen telepíthető a szervezetekben a <em>JAMS (Jami Account Management Server — JAMS-kezelő-kiszolgáló)</em> segítségével, amely lehetővé teszi a felhasználók számára, hogy csatlakozzanak vállalati hitelesítő adataikhoz, vagy helyi fiókokat hozzanak létre. A JAMS lehetővé teszi a saját Jami közösség kezelését, miközben kihasználja a Jami elosztott hálózati architektúráját.</p>
<p>Jami is available for GNU/Linux, Windows, macOS, iOS, Android, Android TV, and web browsers, making Jami an interoperable and cross-platform communication framework.</p>
<p xml:lang="hu">A Jami elérhető GNU/Linux, Windows, macOS, iOS, Android, Android TV és webböngészők számára, így a Jami interoperábilis és platformok közötti kommunikációs keretrendszerré válik.</p>
<p>Manage multiple SIP accounts, Jami accounts, and JAMS accounts with the Jami client installed on one or multiple devices.</p>
<p xml:lang="hu">Több SIP-, Jami- és JAMS-fiók kezelése az egy vagy több eszközre telepített a Jami-alkalmazással.</p>
<p>Jami is free, unlimited, private, advertisingfree, compatible, fast, autonomous, and anonymous.</p>
<p xml:lang="hu">A Jami ingyenes, korlátlan, privát, reklámmentes, kompatibilis, gyors, autonóm és névtelen.</p>
<p>Learn more about:</p>
<p xml:lang="hu">További tájékoztatás:</p>
<ul>
<li>Jami: https://jami.net/</li>
<li xml:lang="hu">Jami: https://jami.net/hu/</li>
<li>Jami extensions: https://jami.net/extensions/</li>
<li xml:lang="hu">Jami-bővítmények: https://jami.net/hu/extensions/</li>
<li>“Jami Account Management Server” (JAMS): https://jami.biz/</li>
<li xml:lang="hu">JAMS (Jami Account Management Server Jami fiókkezelő kiszolgáló): https://jami.biz/</li>
<li>Jami documentation: https://docs.jami.net/</li>
<li xml:lang="hu">Jami-dokumentáció: https://docs.jami.net/hu/</li>
</ul>
<p>Follow us for more:</p>
<p xml:lang="hu">Kövess minket a továbbiakért:</p>
<ul>
<li>Mastodon: https://mstdn.io/@Jami</li>
<li xml:lang="hu">Mastodon: https://mstdn.io/@Jami</li>
<li>X: https://x.com/jami_social</li>
<li xml:lang="hu">X: https://x.com/jami_social</li>
<li>YouTube: https://www.youtube.com/@jami9311</li>
<li xml:lang="hu">YouTube: https://www.youtube.com/@jami9311</li>
</ul>
<p>Wed love to hear from you! Join the Jami community:</p>
<p xml:lang="hu">Szívesen hallanánk felőled! Csatlakozzon a Jami közösséghez:</p>
<ul>
<li>Contribute: https://jami.net/contribute/</li>
<li xml:lang="hu">Közreműködés: https://jami.net/hu/contribute/</li>
<li>Forum: https://forum.jami.net/</li>
<li xml:lang="hu">Fórum: https://forum.jami.net/</li>
</ul>
<p>Build with Jami on your IoT project: re-use the universal communications technology of Jami with its portable library on your system of choice.</p>
<p xml:lang="hu">Építsen a Jamival IoT-projektjére: használja újra a Jami univerzális kommunikációs technológiáját a hordozható könyvtárával a választott rendszerén.</p>
<p>Build IoT projects with Jami. Re-use the universal communications technology of Jami with its portable library on your system of choice.</p>
<p xml:lang="hu">Építsen IoT-projekteket Jamival. Használja újra a Jami univerzális kommunikációs technológiáját annak hordozható könyvtárával az Ön által választott rendszeren.</p>
<p>Jami for Android TV is tested on NVIDIA SHIELD TV with Logitech cameras.</p>
<p xml:lang="hu">A Jami for Android TV-t Logitech kamerákkal ellátott NVIDIA SHIELD TV-n tesztelték.</p>
<p>Jami is published under the GPL license, version 3 or higher.</p>
<p xml:lang="hu">A Jami a GPL licenc 3-as vagy újabb verziója alatt jelent meg.</p>
<p>Copyright © Savoir-faire Linux Inc.</p>
<p xml:lang="hu">Szerzői jog © Savoir-faire Linux Inc.</p>
<p xml:lang="hu">A Jami Androidra TV-t Logitech kamerákkal ellátott NVIDIA SHIELD TV-n tesztelték.</p>
<p>Jami is published under the GPL license, version 3 or higher. Copyright © Savoir-faire Linux Inc.</p>
<p xml:lang="hu">A Jami a GPL-licenc 3-as vagy újabb verziója alatt jelent meg. Szerzői jog © Savoir-faire Linux Inc.</p>
</description>
<url type="homepage">https://jami.net/</url>
<requires>
<id>net.jami.daemon</id>
</requires>
<url type="bugtracker">https://git.jami.net/savoirfairelinux/jami-client-qt/issues</url>
<url type="faq">https://docs.jami.net/user/faq.html</url>
<url type="help">https://forum.jami.net/</url>
<url type="homepage">https://jami.net/</url>
<url type="donation">https://jami.net/whydonate/</url>
<url type="translate">https://www.transifex.com/savoirfairelinux/jami</url>
<url type="faq">https://docs.jami.net/user/faq.html</url>
<url type="translate">https://explore.transifex.com/savoirfairelinux/jami/</url>
<url type="contribute">https://jami.net/contribute/</url>
<url type="help">https://docs.jami.net/</url>
<url type="vcs-browser">https://git.jami.net/savoirfairelinux</url>
<provides>
<binary>jami</binary>
<mediatype>x-scheme-handler/jami</mediatype>
</provides>
<!-- Maximum caption length is 60 characters -->
<!-- Officially GIF is not an allowed video format, but it appears to work nonetheless -->
@ -121,7 +135,15 @@
<launchable type="desktop-id">net.jami.Jami.desktop</launchable>
<provides><binary>jami</binary></provides>
<!-- https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html#tag-keywords -->
<keywords>
<keyword translate="no">Qt</keyword>
<keyword>chat</keyword>
<keyword>talk</keyword>
<keyword>im</keyword>
<keyword>message</keyword>
<keyword translate="no">voip</keyword>
</keywords>
<!-- https://specifications.freedesktop.org/menu-spec/latest/apa.html -->
<!-- https://specifications.freedesktop.org/menu-spec/latest/apas02.html -->
@ -142,5 +164,4 @@
<content_attribute id="social-audio">intense</content_attribute>
</content_rating>
<requires><id>net.jami.daemon</id></requires>
</component>

View File

@ -85,6 +85,10 @@ pipeline {
environment {
TARBALLS = '/var/cache/jami' // set the cache directory
BUILD_VERSION = sh(
returnStdout: true,
script: 'date +"%Y%m%d%H%M"'
).trim()
}
stages {

View File

@ -169,12 +169,10 @@ DISTRIBUTIONS := \
ubuntu_24.04 \
ubuntu_24.10 \
ubuntu_25.04 \
fedora_39 \
fedora_40 \
fedora_41 \
fedora_42 \
alma_9 \
opensuse-leap_15.5 \
alma_10 \
opensuse-leap_15.6 \
snap
@ -194,6 +192,7 @@ $(1)-docker-image-name := jami-packaging-$(1)
$(1)-docker-image-file := .docker-image-$$($(1)-docker-image-name)
$(1)-docker-run-command := docker run \
--rm --privileged --security-opt apparmor=docker-default \
-e BUILD_VERSION=${BUILD_VERSION} \
-e RELEASE_VERSION="$(RELEASE_VERSION)" \
-e RELEASE_DIRNAME="$(RELEASE_DIRNAME)" \
-e RELEASE_TARBALL_FILENAME="$(RELEASE_TARBALL_FILENAME)" \
@ -253,7 +252,7 @@ define guix-pack-command
guix pack -C xz -f $(1) -m $(CURDIR)/extras/packaging/gnu-linux/guix/guix-pack-manifest.scm -v3 \
-S /usr/bin/jami=bin/jami \
-S /usr/share/applications/net.jami.Jami.desktop=share/applications/net.jami.Jami.desktop \
-S /usr/share/icons/hicolor/scalable/apps/jami.svg=share/icons/hicolor/scalable/apps/jami.svg \
-S /usr/share/icons/hicolor/scalable/apps/net.jami.Jami.svg=share/icons/hicolor/scalable/apps/net.jami.Jami.svg \
-S /usr/share/icons/hicolor/48x48/apps/jami.png=share/icons/hicolor/48x48/apps/jami.png \
-S /usr/share/metainfo/net.jami.Jami.metainfo.xml=share/metainfo/net.jami.Jami.metainfo.xml \
-S /usr/share/swcatalog/xml/jami.xml=share/swcatalog/xml/jami.xml \

View File

@ -1,105 +1,93 @@
FROM fedora:39
RUN dnf clean all
RUN dnf update -y
FROM almalinux:10
RUN dnf clean all && dnf update -y
RUN dnf install -y epel-release
RUN dnf install -y 'dnf-command(config-manager)'
RUN dnf config-manager --set-enabled crb
RUN dnf config-manager --set-enabled appstream
RUN dnf install -y dnf-command\(builddep\) rpmdevtools && \
dnf install -y mock
RUN dnf groupinstall -y "X Software Development"
RUN yum install -y xorg-x11-xauth
RUN dnf install -y \
git \
rpm-build \
tar \
make \
alsa-lib-devel \
astyle \
autoconf \
automake \
nasm \
speexdsp-devel \
pulseaudio-libs-devel \
libcanberra-devel \
libcurl-devel \
libtool \
mesa-libgbm-devel \
mesa-dri-drivers \
bison \
check \
chrpath \
clang15-devel \
cmake \
cryptopp-devel \
cups-devel \
dbus-devel \
expat-devel \
pcre-devel \
yaml-cpp-devel \
libXext-devel \
libXfixes-devel \
yasm \
speex-devel \
gsm-devel \
chrpath \
check \
astyle \
uuid-c++-devel \
gettext-devel \
gcc-c++ \
which \
alsa-lib-devel \
systemd-devel \
libuuid-devel \
uuid-devel \
gnutls-devel \
nettle-devel \
opus-devel \
patch \
jsoncpp-devel \
libnatpmp-devel \
webkitgtk4-devel \
cryptopp-devel \
libva-devel \
libvdpau-devel \
msgpack-devel \
NetworkManager-libnm-devel \
openssl-devel \
clutter-devel \
clutter-gtk-devel \
libappindicator-gtk3-devel \
libnotify-devel \
libupnp-devel \
qrencode-devel \
libargon2-devel \
libsndfile-devel \
libdrm \
gperf \
bison \
clang \
clang-devel \
llvm-devel \
nodejs \
flex \
fmt-devel \
gcc-c++ \
gettext-devel \
git \
gnutls-devel \
gperf \
gsm-devel \
gstreamer1 gstreamer1-devel \
gstreamer1-plugins-bad-free-devel \
gstreamer1-plugins-base-devel \
gstreamer1-plugins-good \
gstreamer1-plugins-bad-free-devel \
nss-devel \
jsoncpp-devel \
libX11-devel \
libXext-devel \
libXfixes-devel \
libXrender-devel \
libappindicator-gtk3-devel \
libargon2-devel \
libcanberra-devel \
libcurl-devel \
libdrm \
libnatpmp-devel \
libnotify \
libnotify-devel \
libsndfile-devel \
libstdc++-static \
libtool \
libupnp-devel \
libuuid-devel \
libva-devel \
libvdpau-devel \
libxcb* \
libxkb* \
libX11-devel \
vulkan-devel \
libXrender-devel \
xcb-util-* \
xz \
xkeyboard-config \
libnotify \
wget \
libstdc++-static \
sqlite-devel \
perl-generators \
perl-English \
libxshmfence-devel \
llvm15-devel \
make \
mesa-dri-drivers \
mesa-libgbm-devel \
msgpack-devel \
nasm \
nettle-devel \
NetworkManager-libnm-devel \
ninja-build \
clang \
cmake \
fmt-devel \
python3.10 \
cups-devel \
pipewire-devel
nodejs \
nss-devel \
openssl-devel \
opus-devel \
pcre2-devel \
perl-English \
perl-generators \
pipewire-devel \
pulseaudio-libs-devel \
python3-html5lib \
qrencode-devel \
speex-devel \
speexdsp-devel \
sqlite-devel \
systemd-devel \
uuid-devel \
vulkan-devel \
webkitgtk6.0-devel \
wget \
which \
xcb-util-* \
xkeyboard-config \
yaml-cpp-devel \
yasm
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
CMD ["/opt/build-package-rpm.sh"]
CMD ["/opt/build-package-rpm.sh"]

View File

@ -1,105 +0,0 @@
FROM fedora:40
RUN dnf clean all
RUN dnf update -y
RUN dnf install -y dnf-command\(builddep\) rpmdevtools && \
dnf install -y mock
RUN dnf groupinstall -y "X Software Development"
RUN dnf install -y \
git \
rpm-build \
tar \
make \
autoconf \
automake \
nasm \
speexdsp-devel \
pulseaudio-libs-devel \
libcanberra-devel \
libcurl-devel \
libtool \
mesa-libgbm-devel \
mesa-dri-drivers \
dbus-devel \
expat-devel \
pcre-devel \
yaml-cpp-devel \
libXext-devel \
libXfixes-devel \
yasm \
speex-devel \
gsm-devel \
chrpath \
check \
astyle \
uuid-c++-devel \
gettext-devel \
gcc-c++ \
which \
alsa-lib-devel \
systemd-devel \
libuuid-devel \
uuid-devel \
gnutls-devel \
nettle-devel \
opus-devel \
patch \
jsoncpp-devel \
libnatpmp-devel \
webkitgtk4-devel \
cryptopp-devel \
libva-devel \
libvdpau-devel \
msgpack-devel \
NetworkManager-libnm-devel \
openssl-devel \
clutter-devel \
clutter-gtk-devel \
libappindicator-gtk3-devel \
libnotify-devel \
libupnp-devel \
qrencode-devel \
libargon2-devel \
libsndfile-devel \
libdrm \
gperf \
bison \
clang \
clang-devel \
llvm-devel \
nodejs \
flex \
gstreamer1 gstreamer1-devel \
gstreamer1-plugins-base-devel \
gstreamer1-plugins-good \
gstreamer1-plugins-bad-free-devel \
nss-devel \
libxcb* \
libxkb* \
libX11-devel \
vulkan-devel \
libXrender-devel \
xcb-util-* \
xz \
xkeyboard-config \
libnotify \
wget \
libstdc++-static \
sqlite-devel \
perl-generators \
perl-English \
libxshmfence-devel \
ninja-build \
clang \
cmake \
fmt-devel \
python3.10 \
cups-devel \
pipewire-devel
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
CMD ["/opt/build-package-rpm.sh"]

View File

@ -1,117 +0,0 @@
FROM opensuse/leap:15.5
RUN zypper --gpg-auto-import-keys refresh
RUN zypper --non-interactive install -y \
dnf \
dnf-command\(builddep\) \
rpmdevtools \
Mesa-dri-devel Mesa-dri \
git \
gcc10 \
gcc10-c++ \
rpm-build \
tar \
make \
autoconf \
automake \
nasm \
speexdsp-devel \
libpulse-devel \
libcanberra-devel \
libcurl-devel \
libtool \
pcre-devel \
yaml-cpp-devel \
libXext-devel \
libXfixes-devel \
yasm \
speex-devel \
libgsm-devel \
chrpath \
check \
astyle \
gettext-devel \
which \
alsa-lib-devel \
systemd-devel \
libuuid-devel \
uuid-devel \
libopus-devel \
patch \
jsoncpp-devel \
webkit2gtk3-devel \
libcryptopp-devel \
libva-devel \
libvdpau-devel \
msgpack-c-devel \
msgpack-cxx-devel \
clutter-devel \
openssl-devel \
clutter-gtk-devel \
libnma-devel \
libcryptopp-devel \
libexpat-devel \
gnome-icon-theme-symbolic \
libgsm-devel \
gtk3-devel \
libappindicator-devel \
sqlite-devel \
ffmpeg-4-libavutil-devel \
gtk3-devel\
qrencode-devel \
python310 \
python3-python-dateutil \
python3-html5lib \
libsndfile-devel \
libdrm \
gperf \
bison \
flex \
ffmpeg ffmpeg-devel \
nodejs20 \
mozilla-nss-devel \
python-xml \
python3-six \
python3-importlib-metadata \
libxcb* \
libxkb* \
libX11-devel \
libXrender-devel \
libfreetype6 \
xcb-util-image-devel \
xcb-util-keysyms-devel \
xcb-util-renderutil-devel \
xcb-util-wm-devel \
xorg-x11-devel \
xz \
xkeyboard-config \
libnotify \
argon2-devel \
libxshmfence-devel \
xproto-devel \
xcb-proto-devel \
xcb-* \
xorg-* \
vulkan-devel \
ninja \
gstreamer-devel \
gstreamer-plugins-good \
gstreamer-plugins-bad-devel \
gstreamer-plugins-base-devel \
cmake \
wget \
pipewire-devel
# openSUSE Leap 15.5 comes with Python 3.6 by default,
# but we need at least 3.7 to compile Qt 6.6.1
RUN rm /usr/bin/python3 && ln -s /usr/bin/python3.10 /usr/bin/python3
RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 50
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 50
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
ENV CC=gcc
ENV CXX=g++
CMD ["/opt/build-package-rpm.sh"]

View File

@ -24,7 +24,10 @@
(list
;; Minimal requirements of the daemon contrib build system.
"coreutils"
"gcc-toolchain"
;; When using GCC 15, Jami fails to link with errors like:
;; ld: CMakeFiles/jami.dir/src/app/main.cpp.o:(.rodata+0x0):
;; multiple definition of `QtPrivate::IsFloatType_v<_Float16>'
"gcc-toolchain@14"
"git-minimal"
"grep"
"gzip"
@ -42,18 +45,12 @@
"alsa-lib"
"autoconf"
"automake"
"asio"
"bash"
"bzip2"
"cmake"
"dbus"
;; Bundled because broken with GCC 7 upstream (unmaintained). When
;; attempting to use it, it would cause confusing errors such as
;; "ld: ../src/.libs/libring.a(libupnpcontrol_la-upnp_context.o): in
;; function `jami::upnp::UPnPContext::updateMappingList(bool)':
;; upnp_context.cpp:(.text+0xa4be): undefined reference to
;; `std::__cxx11::basic_ostringstream<char, std::char_traits<char>,
;; std::allocator<char> >::basic_ostringstream()'
;;"dbus-c++" ;for dbusxx-xml2cpp
;;"dhtnet" ;bundled because tightly coupled
"diffutils"
"doxygen"
"eudev" ;udev library
@ -67,6 +64,7 @@
"gsm"
"gtk-doc"
"http-parser"
"jack@0"
"jsoncpp"
"libarchive"
"libgit2"
@ -84,16 +82,24 @@
"patch"
"pcre"
"perl"
"pipewire"
;;"pjproject" ;bundled because patched
"pulseaudio"
"sdbus-c++@1"
"speex"
"speexdsp"
"webrtc-audio-processing@0"
"which"
"yaml-cpp"
"yasm"
;; For the Qt client.
"glib"
"hunspell"
"libnotify"
"libxcb"
"libxkbcommon"
"md4c"
"network-manager" ;libnm
"qrencode"
"qtbase"
@ -103,10 +109,13 @@
"qtnetworkauth"
"qtpositioning"
"qtsvg"
"qwindowkit"
"qttools"
"qtwebchannel"
"qtwebengine"
"tidy-html"
"vulkan-headers"
"zxing-cpp"
;; For tests and debugging.
"file"

View File

@ -91,6 +91,7 @@ override_dh_auto_build:
mkdir build && \
cd build && \
cmake \
-DBUILD_VERSION=$(BUILD_VERSION) \
-DCMAKE_INSTALL_PREFIX=/usr \
-DLIBJAMI_BUILD_DIR=$(CURDIR)/daemon/src \
-DENABLE_LIBWRAP=true \

View File

@ -49,7 +49,7 @@ BuildRequires: libXfixes-devel
BuildRequires: libuuid-devel
BuildRequires: libva-devel
BuildRequires: libvdpau-devel
BuildRequires: pcre-devel
BuildRequires: (pcre-devel or pcre2-devel)
BuildRequires: pipewire-devel
BuildRequires: uuid-devel
BuildRequires: yaml-cpp-devel

View File

@ -31,6 +31,7 @@ URL: https://jami.net/
Source: jami-libqt-%{version}.tar.xz
Patch0: 0001-fix-gcc14.patch
Patch1: 0002-qtwebengine-add-missing-chromium-dependencies.patch
Patch2: 0003-fix-embree-linking-errors.patch
%global gst 0.10
%if 0%{?fedora} || 0%{?rhel} > 7
@ -68,6 +69,7 @@ This package contains Qt libraries for Jami.
%setup -n qt-everywhere-src-%{version}
%patch -P 0 -p1
%patch -P 1 -p1
%patch -P 2 -p1
%build
echo "Building Qt using %{job_count} parallel jobs"

View File

@ -1,16 +1,7 @@
%define name jami
%define version RELEASE_VERSION
%define release 0
# The AppStream 1.0 spec says that the catalog file must be put in /usr/share/swcatalog/xml
# (see https://www.freedesktop.org/software/appstream/docs/chap-CatalogData.html).
#
# However, openSUSE Leap still uses the legacy path /usr/share/app-info/xmls as of version 15.5.
%if 0%{?sle_version} && 0%{?sle_version} <= 150500
%define appstream_catalog_dir /share/app-info/xmls
%else
%define appstream_catalog_dir /share/swcatalog/xml
%endif
# Exclude vendored Qt6 from dependency generator
%define __requires_exclude ^libQt6.*$
@ -82,6 +73,7 @@ cd %{_builddir}/jami-%{version} && \
-DAPPSTREAM_CATALOG_DIR=%{appstream_catalog_dir} \
-DWITH_DAEMON_SUBMODULE=true \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_VERSION=${BUILD_VERSION} \
..
make -C %{_builddir}/jami-%{version}/build %{_smp_mflags} V=2
@ -93,7 +85,7 @@ DESTDIR=%{buildroot} make -C %{_builddir}/jami-%{version}/build install V=2
%{_bindir}/jami
%{_datadir}/applications/net.jami.Jami.desktop
%{_datadir}/jami/net.jami.Jami.desktop
%{_datadir}/icons/hicolor/scalable/apps/jami.svg
%{_datadir}/icons/hicolor/scalable/apps/net.jami.Jami.svg
%{_datadir}/icons/hicolor/48x48/apps/jami.png
%{_datadir}/pixmaps/jami.xpm
%{_datadir}/metainfo/net.jami.Jami.metainfo.xml

View File

@ -0,0 +1,29 @@
From 709d0b0cf45b920f63960a70725138dbaf7ec721 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois-Simon=20Fauteux-Chapleau?=
<francois-simon.fauteux-chapleau@savoirfairelinux.com>
Date: Wed, 18 Jun 2025 15:53:55 -0400
Subject: [PATCH] Fix embree linking errors
Patch taken from:
https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=37bd373cd33c36f8dd44e71be25fa6ea24cf4588
---
qtquick3d/src/3rdparty/embree/CMakeLists.txt | 3 +++
1 file changed, 3 insertions(+)
diff --git a/qtquick3d/src/3rdparty/embree/CMakeLists.txt b/qtquick3d/src/3rdparty/embree/CMakeLists.txt
index cf27196de2..332bbd17ca 100644
--- a/qtquick3d/src/3rdparty/embree/CMakeLists.txt
+++ b/qtquick3d/src/3rdparty/embree/CMakeLists.txt
@@ -62,6 +62,9 @@ if (IOS)
endif()
# Use SSE2 only, ignore AVX/SSE4.2 for now
+if (TEST_architecture_arch STREQUAL x86_64)
+ qt_internal_extend_target(BundledEmbree COMPILE_OPTIONS -mno-avx -mno-sse4.2)
+endif()
qt_internal_extend_target(BundledEmbree DEFINES
EMBREE_TARGET_SSE2
__SSE2__
--
2.34.1

View File

@ -270,7 +270,7 @@ parts:
ls
snapcraftctl pull
sed -i -E 's|(tmpName) << (PACKAGE_NAME << "_shm_")|\1 << "snap.jami." << \2|' ./daemon/src/media/video/sinkclient.cpp
sed -i -E 's|^Icon=.*|Icon=${SNAP}/usr/share/icons/hicolor/scalable/apps/jami.svg|' extras/data/net.jami.Jami.desktop
sed -i -E 's|^Icon=.*|Icon=${SNAP}/usr/share/icons/hicolor/scalable/apps/net.jami.Jami.svg|' extras/data/net.jami.Jami.desktop
override-build: |
$SNAPCRAFT_PART_BUILD/extras/packaging/gnu-linux/scripts/install-pipewire-from-source.sh
@ -293,7 +293,8 @@ parts:
cmake .. -DENABLE_LIBWRAP=true \
-DLIBJAMI_BUILD_DIR=$SNAPCRAFT_PART_BUILD/daemon/src \
-DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_VERSION=BUILD_VERSION_PLACEHOLDER
make -j$SNAPCRAFT_PARALLEL_BUILD_COUNT
DESTDIR=$SNAPCRAFT_PART_INSTALL make install
build-packages:

View File

@ -101,16 +101,14 @@ if [ ! -f "${RPM_PATH}" ]; then
# Cache the built Qt RPM package.
if [[ "${DISTRIBUTION:0:4}" == "rhel" ]]; then
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.el8.x86_64.rpm "${RPM_PATH}"
elif [[ "${DISTRIBUTION}" == "fedora_39" ]]; then
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc39.x86_64.rpm "${RPM_PATH}"
elif [[ "${DISTRIBUTION}" == "fedora_40" ]]; then
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc40.x86_64.rpm "${RPM_PATH}"
elif [[ "${DISTRIBUTION}" == "fedora_41" ]]; then
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc41.x86_64.rpm "${RPM_PATH}"
elif [[ "${DISTRIBUTION}" == "fedora_42" ]]; then
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc42.x86_64.rpm "${RPM_PATH}"
elif [[ "${DISTRIBUTION}" == "alma_9" ]]; then
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.el9.x86_64.rpm "${RPM_PATH}"
elif [[ "${DISTRIBUTION}" == "alma_10" ]]; then
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.el10.x86_64.rpm "${RPM_PATH}"
else
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-*.rpm "${RPM_PATH}"
fi
@ -134,7 +132,7 @@ rpmbuild --define "debug_package %{nil}" -ba jami-libclient.spec
rpmbuild --define "debug_package %{nil}" -ba jami-qt.spec
# Build the Qt client.
rpmbuild --define "debug_package %{nil}" -ba jami.spec
rpmbuild --define "debug_package %{nil}" --define "BUILD_VERSION ${BUILD_VERSION}" -ba jami.spec
# Move the built packages to the output directory.
mv /root/rpmbuild/RPMS/*/* /opt/output

View File

@ -29,6 +29,9 @@ cp -r extras/packaging/gnu-linux/rules/snap/${SNAP_PKG_NAME}/snapcraft.yaml .
# set the version and tarball filename
sed -i "s/RELEASE_VERSION/${RELEASE_VERSION}/g" snapcraft.yaml
# set the build version of the app
sed -i "s/BUILD_VERSION_PLACEHOLDER/${BUILD_VERSION}/g" snapcraft.yaml
snapcraft # requires snapcraft >= 4.8
# move the built snap to output

View File

@ -263,7 +263,7 @@ def cmake_build(config_str, env_vars, cmake_build_dir):
return True
def build(config_str, qt_dir, tests, enable_crash_reports, crash_report_url=None):
def build(config_str, qt_dir, tests, build_version, enable_crash_reports, crash_report_url=None):
"""Use cmake to build the project."""
print("Building with Qt at " + qt_dir)
@ -294,6 +294,9 @@ def build(config_str, qt_dir, tests, enable_crash_reports, crash_report_url=None
else:
cmake_options.append("-DENABLE_CRASHREPORTS=OFF")
if build_version:
cmake_options.append("-DBUILD_VERSION=" + build_version)
# Make sure the build directory exists.
if not os.path.exists(build_dir):
os.makedirs(build_dir)
@ -307,11 +310,11 @@ def build(config_str, qt_dir, tests, enable_crash_reports, crash_report_url=None
sys.exit(1)
def deploy_runtimes(config_str, qt_dir):
def deploy_runtimes(qt_dir):
"""Deploy the dependencies to the runtime directory."""
print("Deploying runtime dependencies")
runtime_dir = os.path.join(repo_root_dir, "x64", config_str)
runtime_dir = os.path.join(repo_root_dir, "x64", "Release")
stamp_file = os.path.join(runtime_dir, ".deploy.stamp")
if os.path.exists(stamp_file):
return
@ -470,6 +473,8 @@ def parse_args():
help='Sets the Qt root path')
parser.add_argument(
"-a", "--arch", default="x64", help="Sets the build architecture")
parser.add_argument(
"--build-version", help="Sets the build version string used for defining app build version")
parser.add_argument(
"-t", "--tests", action="store_true", help="Build and run tests")
parser.add_argument(
@ -552,10 +557,11 @@ def main():
def do_build(do_tests):
if not parsed_args.skip_build:
build(config_str, parsed_args.qt, do_tests,
parsed_args.build_version,
parsed_args.enable_crash_reports,
parsed_args.crash_report_url)
if not parsed_args.skip_deploy:
deploy_runtimes(config_str, parsed_args.qt)
deploy_runtimes(parsed_args.qt)
if parsed_args.subcommand == "pack":
do_build(False)

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -17,7 +17,6 @@
import QtQuick
import QtQuick.Controls
import QtWebEngine
import net.jami.Adapters 1.1
import net.jami.Enums 1.1
@ -253,9 +252,15 @@ QtObject {
isCallFullscreen = fullScreenItems
.filter(o => o.item.objectName === "callViewLoader")
.length
isWebFullscreen = fullScreenItems
.filter(o => o.item instanceof WebEngineView)
.length
isWebFullscreen = WITH_WEBENGINE ? fullScreenItems
.filter(o => o.item && (
o.item.objectName === JamiQmlUtils.webEngineNames.mediaPreview ||
o.item.objectName === JamiQmlUtils.webEngineNames.videoPreview ||
o.item.objectName === JamiQmlUtils.webEngineNames.map ||
o.item.objectName === JamiQmlUtils.webEngineNames.general ||
o.item.objectName === JamiQmlUtils.webEngineNames.emojiPicker
))
.length : 0
}
// Listen for a hangup combined with a fullscreen call state and

View File

@ -41,15 +41,20 @@ ApplicationWindow {
onActiveFocusItemChanged: {
focusOverlay.margin = -5;
if (activeFocusItem && ((activeFocusItem.focusReason === Qt.TabFocusReason) || (activeFocusItem.focusReason === Qt.BacktabFocusReason))) {
if (activeFocusItem.focusOnChild) {
focusOverlay.parent = activeFocusItem.parent;
} else if (activeFocusItem.dontShowFocusState) {
focusOverlay.parent = null;
if (activeFocusItem) {
const goodReasonToChangeFocus = activeFocusItem instanceof ItemDelegate || ((activeFocusItem.focusReason === Qt.TabFocusReason) || (activeFocusItem.focusReason === Qt.BacktabFocusReason));
if (goodReasonToChangeFocus) {
if (activeFocusItem.focusOnChild) {
focusOverlay.parent = activeFocusItem.parent;
} else if (activeFocusItem.dontShowFocusState) {
focusOverlay.parent = null;
} else {
if (activeFocusItem.showFocusMargin)
focusOverlay.margin = 0;
focusOverlay.parent = activeFocusItem;
}
} else {
if (activeFocusItem.showFocusMargin)
focusOverlay.margin = 0;
focusOverlay.parent = activeFocusItem;
focusOverlay.parent = null;
}
} else {
focusOverlay.parent = null;

View File

@ -18,7 +18,7 @@
#include "appversionmanager.h"
#include "lrcinstance.h"
#include "version.h"
#include "version_info.h"
#include <QProcess>
#include <QTimer>
@ -73,7 +73,7 @@ struct AppVersionManager::Impl : public QObject
Q_EMIT parent_.updateCheckReplyReceived(false);
return;
}
auto currentVersion = QString(VERSION_STRING).toULongLong();
auto currentVersion = BUILD_VERSION_STRING.toULongLong();
auto latestVersion = latestVersionString.toULongLong();
const QString channelStr = isBeta ? "beta" : "stable";
const auto newVersionFound = latestVersion > currentVersion;

View File

@ -401,6 +401,16 @@ CallOverlayModel::eventFilter(QObject* object, QEvent* event)
}
}
}
// Tab or BackTab key events should trigger a signal that we can use to
// prevent the overlay from fading and to allow the user to navigate
// through the controls.
if (event->type() == QEvent::KeyPress && (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Tab)
|| (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Backtab)) {
Q_EMIT focusKeyPressed();
// Don't absorb the event so that the focus can be changed
// to the next or previous control.
return false;
}
#ifndef HAVE_GLOBAL_PTT
else if (event->type() == QEvent::KeyPress && listener_->getPttState()) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);

View File

@ -140,6 +140,7 @@ public:
Q_SIGNALS:
void mouseMoved(QQuickItem* item);
void focusKeyPressed();
void pttKeyPressed();
void pttKeyReleased();

View File

@ -80,6 +80,8 @@ Popup {
JamiPushButton { QWKSetParentHitTestVisible {}
id: closeButton
Accessible.role: Accessible.Button
Accessible.name: JamiStrings.close
visible: closeButtonVisible

View File

@ -23,12 +23,12 @@ import net.jami.Adapters 1.1
import net.jami.Constants 1.1
SBSMessageBase {
id: root
id: rootDelegate
property var confId: ConfId
property var currentCallId: CurrentCall.id
component JoinCallButton: MaterialButton {
visible: root.isActive && root.currentCallId !== root.confId
visible: rootDelegate.isActive && rootDelegate.currentCallId !== rootDelegate.confId
toolTipText: JamiStrings.joinCall
color: JamiTheme.blackColor
background.opacity: hovered ? 0.2 : 0.1
@ -40,6 +40,20 @@ SBSMessageBase {
textRightPadding: 9
}
Accessible.role: Accessible.StaticText
Accessible.name: {
let name = isOutgoing ? JamiStrings.inReplyToYou : UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
return name + ": " + callLabel.text + " " + formattedDay;
}
Accessible.description: {
let status = "";
if (bubble.isEdited)
status += JamiStrings.edited + " ";
return status + (readers.length > 0 ? JamiStrings.readBy + " " + readers.map(function (uri) {
return UtilsAdapter.getBestNameForUri(CurrentAccount.id, uri);
}).join(", ") : "");
}
property bool isRemoteImage
isOutgoing: Author === CurrentAccount.uri
@ -48,17 +62,17 @@ SBSMessageBase {
formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
bubble.border.color: CurrentConversation.color
bubble.border.width: root.isActive ? 1.5 : 0
bubble.border.width: rootDelegate.isActive ? 1.5 : 0
bubble.color: JamiTheme.messageInBgColor
bubble.opacity: 1
Connections {
target: CurrentConversation
enabled: root.isActive
enabled: rootDelegate.isActive
function onActiveCallsChanged() {
root.isActive = LRCInstance.indexOfActiveCall(root.confId, ActionUri, DeviceId) !== -1;
if (root.isActive) {
rootDelegate.isActive = LRCInstance.indexOfActiveCall(rootDelegate.confId, ActionUri, DeviceId) !== -1;
if (rootDelegate.isActive) {
bubble.mask.border.color = CurrentConversation.color;
bubble.mask.border.width = 1.5;
bubble.mask.z = -2;
@ -66,8 +80,8 @@ SBSMessageBase {
}
}
property bool isActive: LRCInstance.indexOfActiveCall(root.confId, ActionUri, DeviceId) !== -1
visible: isActive || root.confId === "" || Duration > 0
property bool isActive: LRCInstance.indexOfActiveCall(rootDelegate.confId, ActionUri, DeviceId) !== -1
visible: isActive || rootDelegate.confId === "" || Duration > 0
property var baseColor: JamiTheme.messageInBgColor
@ -76,7 +90,7 @@ SBSMessageBase {
id: msg
anchors.right: isOutgoing ? parent.right : undefined
spacing: 10
visible: root.visible
visible: rootDelegate.visible
Image {
id: statusIcon
@ -84,10 +98,10 @@ SBSMessageBase {
width: 10
height: 10
verticalAlignment: Qt.AlignVCenter
visible: !root.isActive
visible: !rootDelegate.isActive
source: {
if (root.isOutgoing) {
if (rootDelegate.isOutgoing) {
if (Duration > 0)
return "qrc:/icons/outgoing-call.svg";
else
@ -104,12 +118,11 @@ SBSMessageBase {
effect: ColorOverlay {
color: {
if (Duration > 0)
return UtilsAdapter.luma(root.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
return JamiTheme.redColor
return UtilsAdapter.luma(rootDelegate.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark;
return JamiTheme.redColor;
}
}
}
}
Text {
@ -120,11 +133,11 @@ SBSMessageBase {
bottomPadding: 8
Layout.fillWidth: true
Layout.rightMargin: root.isActive && root.currentCallId !== root.confId ? 0 : root.timeWidth + 16
Layout.leftMargin: root.isActive ? 10 : -5 /* spacing is 10 and we want 5px with icon */
Layout.rightMargin: rootDelegate.isActive && rootDelegate.currentCallId !== rootDelegate.confId ? 0 : rootDelegate.timeWidth + 16
Layout.leftMargin: rootDelegate.isActive ? 10 : -5 /* spacing is 10 and we want 5px with icon */
text: {
if (root.isActive)
if (rootDelegate.isActive)
return JamiStrings.startedACall;
return Body;
}
@ -136,7 +149,7 @@ SBSMessageBase {
renderType: Text.NativeRendering
textFormat: Text.MarkdownText
color: UtilsAdapter.luma(root.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
color: UtilsAdapter.luma(rootDelegate.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
}
JoinCallButton {
@ -146,7 +159,7 @@ SBSMessageBase {
Layout.bottomMargin: 4
text: JamiStrings.joinWithAudio
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, root.confId, true)
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, rootDelegate.confId, true)
}
JoinCallButton {
@ -156,20 +169,20 @@ SBSMessageBase {
Layout.topMargin: 4
Layout.bottomMargin: 4
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, root.confId)
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, rootDelegate.confId)
Layout.rightMargin: 4
}
}
]
opacity: 0
Behavior on opacity {
Behavior on opacity {
NumberAnimation {
duration: 100
}
}
Component.onCompleted: {
bubble.timestampItem.visible = !root.isActive || root.currentCallId === root.confId;
bubble.timestampItem.visible = !rootDelegate.isActive || rootDelegate.currentCallId === rootDelegate.confId;
opacity = 1;
}
}

View File

@ -21,8 +21,8 @@ import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
Column {
id: root
Control {
id: rootDelegate
property bool showTime: false
property bool showDay: false
@ -36,21 +36,41 @@ Column {
height: timestampItem.height + textLabel.height
spacing: 0
Item {
Accessible.name: {
let name = UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
return name + ": " + Body + " " + formattedTime + " " + formattedDay;
}
Accessible.description: {
let status = "";
if (IsLastSent)
status += JamiStrings.sent + " ";
return status;
}
background: Rectangle {
id: focusIndicator
visible: rootDelegate.activeFocus
border.color: JamiTheme.tintedBlue
border.width: 2
radius: 10
color: "transparent"
z: 1
}
contentItem: Item {
anchors.horizontalCenter: parent.horizontalCenter
height: timestampItem.height + textLabel.height
TimestampInfo {
id: timestampItem
showDay: root.showDay
showTime: root.showTime
formattedTime: root.formattedTime
formattedDay: root.formattedDay
showDay: rootDelegate.showDay
showTime: rootDelegate.showTime
formattedTime: rootDelegate.formattedTime
formattedDay: rootDelegate.formattedDay
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
}
Label {
@ -67,7 +87,7 @@ Column {
}
}
opacity: 0
Behavior on opacity {
Behavior on opacity {
NumberAnimation {
duration: 100
}

View File

@ -24,20 +24,32 @@ import net.jami.Constants 1.1
import net.jami.Adapters 1.1
Loader {
id: root
id: rootDelegate
property var mediaInfo
property bool showTime
property bool showDay
property int timestamp: Timestamp
property string formattedTime: MessagesAdapter.getFormattedTime(root.timestamp)
property string formattedDay: MessagesAdapter.getFormattedDay(root.timestamp)
property string formattedTime: MessagesAdapter.getFormattedTime(rootDelegate.timestamp)
property string formattedDay: MessagesAdapter.getFormattedDay(rootDelegate.timestamp)
property int seq: MsgSeq.single
property string author: Author
property string body: Body
property var tid: TID
property int transferStatus: TransferStatus
Accessible.name: {
let name = UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
return JamiStrings.dataTransfer + name + ": " + JamiStrings.status + TransferStatus + Body + " " + formattedTime + " " + formattedDay;
}
Accessible.description: {
let status = "";
if (IsLastSent)
status += JamiStrings.sent + " ";
return status;
}
onTidChanged: {
if (tid === "") {
sourceComponent = deletedMsgComp;
@ -48,7 +60,7 @@ Loader {
sourceComponent = deletedMsgComp;
return;
} else if (transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED) {
mediaInfo = MessagesAdapter.getMediaInfo(root.body);
mediaInfo = MessagesAdapter.getMediaInfo(rootDelegate.body);
if (Object.keys(mediaInfo).length !== 0 && WITH_WEBENGINE) {
sourceComponent = localMediaMsgComp;
return;
@ -74,13 +86,13 @@ Loader {
id: deletedItem
isOutgoing: Author === CurrentAccount.uri
showTime: root.showTime
seq: root.seq
showTime: rootDelegate.showTime
seq: rootDelegate.seq
author: Author
readers: Readers
timestamp: root.timestamp
formattedTime: root.formattedTime
formattedDay: root.formattedTime
timestamp: rootDelegate.timestamp
formattedTime: rootDelegate.formattedTime
formattedDay: rootDelegate.formattedTime
extraHeight: 0
textContentWidth: textEditId.width
textContentHeight: textEditId.height
@ -122,34 +134,34 @@ Loader {
id: dataTransferItem
transferId: Id
property var transferStats: MessagesAdapter.getTransferStats(transferId, root.transferStatus)
property bool canOpen: root.transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED || isOutgoing
property real maxMsgWidth: root.width - senderMargin - 2 * hPadding - avatarBlockWidth - buttonsLoader.width - 24 - 6 - 24
property var transferStats: MessagesAdapter.getTransferStats(transferId, rootDelegate.transferStatus)
property bool canOpen: rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED || isOutgoing
property real maxMsgWidth: rootDelegate.width - senderMargin - 2 * hPadding - avatarBlockWidth - buttonsLoader.width - 24 - 6 - 24
// Timer to update the translation bar
Loader {
id: timerLoader
active: root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
active: rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
sourceComponent: Timer {
interval: 1000 // Update every second
running: true
repeat: true
onTriggered: {
transferStats = MessagesAdapter.getTransferStats(transferId, root.transferStatus);
transferStats = MessagesAdapter.getTransferStats(transferId, rootDelegate.transferStatus);
}
}
}
isOutgoing: Author === CurrentAccount.uri
showTime: root.showTime
seq: root.seq
showTime: rootDelegate.showTime
seq: rootDelegate.seq
author: Author
location: Body
transferName: TransferName
readers: Readers
timestamp: root.timestamp
formattedTime: root.formattedTime
formattedDay: root.formattedTime
timestamp: rootDelegate.timestamp
formattedTime: rootDelegate.formattedTime
formattedDay: rootDelegate.formattedTime
extraHeight: progressBar.visible ? 25 : 0
innerContent.children: [
@ -178,7 +190,7 @@ Loader {
Layout.margins: 8
sourceComponent: {
switch (root.transferStatus) {
switch (rootDelegate.transferStatus) {
case Interaction.TransferStatus.TRANSFER_CREATED:
case Interaction.TransferStatus.TRANSFER_FINISHED:
iconSource = JamiResources.link_black_24dp_svg;
@ -225,7 +237,7 @@ Loader {
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: {
if (root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING) {
if (rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING) {
MessagesAdapter.cancelFile(transferId);
} else {
buttonsLoader.iconSource = JamiResources.connecting_black_24dp_svg;
@ -287,7 +299,7 @@ Loader {
ProgressBar {
id: progressBar
visible: root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
visible: rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
height: visible * implicitHeight
value: transferStats.progress / transferStats.totalSize
width: transferItem.width
@ -305,15 +317,15 @@ Loader {
isOutgoing: Author === CurrentAccount.uri
transferId: Id
property var transferStats: MessagesAdapter.getTransferStats(transferId, root.transferStatus)
showTime: root.showTime
seq: root.seq
property var transferStats: MessagesAdapter.getTransferStats(transferId, rootDelegate.transferStatus)
showTime: rootDelegate.showTime
seq: rootDelegate.seq
author: Author
location: Body
transferName: TransferName
readers: Readers
formattedTime: MessagesAdapter.getFormattedTime(root.timestamp)
formattedDay: MessagesAdapter.getFormattedDay(root.timestamp)
formattedTime: MessagesAdapter.getFormattedTime(rootDelegate.timestamp)
formattedDay: MessagesAdapter.getFormattedDay(rootDelegate.timestamp)
property real contentWidth

View File

@ -21,7 +21,7 @@ import net.jami.Adapters 1.1
import net.jami.Constants 1.1
Column {
id: root
id: rootDelegate
property bool showTime: false
property bool showDay: false
@ -34,6 +34,18 @@ Column {
spacing: 2
topPadding: 12
bottomPadding: 12
Accessible.name: {
let name = UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
return name + ": " + Body + " " + formattedTime + " " + formattedDay;
}
Accessible.description: {
let status = "";
if (IsLastSent)
status += JamiStrings.sent + " ";
return status;
}
ColumnLayout {
width: parent.width
@ -42,10 +54,10 @@ Column {
TimestampInfo {
id: timestampItem
showDay: root.showDay
showTime: root.showTime
formattedTime: root.formattedTime
formattedDay: root.formattedDay
showDay: rootDelegate.showDay
showTime: rootDelegate.showTime
formattedTime: rootDelegate.formattedTime
formattedDay: rootDelegate.formattedDay
Layout.alignment: Qt.AlignHCenter
}
@ -60,7 +72,7 @@ Column {
}
opacity: 0
Behavior on opacity {
Behavior on opacity {
NumberAnimation {
duration: 100
}

View File

@ -28,6 +28,10 @@ Control {
property string title: ""
property string description: ""
Accessible.role: Accessible.StaticText
Accessible.name: title
Accessible.description: description
width: 190
height: infos.implicitHeight

View File

@ -86,6 +86,7 @@ BaseModalDialog {
JamiPushButton {
id: takePhotoButton
Accessible.name: objectName
objectName: "takePhotoButton"
@ -136,6 +137,8 @@ BaseModalDialog {
height: buttonSize
width: buttonSize
Accessible.name: objectName
normalColor: "transparent"
source: JamiResources.add_photo_alternate_black_24dp_svg
imageColor: hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered

View File

@ -37,12 +37,16 @@ Row {
SystemButton {
id: minButton
Accessible.name: JamiStrings.minimize
Accessible.role: Accessible.Button
source: JamiResources.window_bar_minimize_svg
onClicked: appWindow.showMinimized()
}
SystemButton {
id: maxButton
Accessible.name: JamiStrings.maximize
Accessible.role: Accessible.Button
source: appWindow.visibility === Window.Maximized ?
JamiResources.window_bar_restore_svg :
JamiResources.window_bar_maximize_svg
@ -53,6 +57,8 @@ Row {
SystemButton {
id: closeButton
Accessible.name: JamiStrings.closeApplication
Accessible.role: Accessible.Button
source: JamiResources.window_bar_close_svg
baseColor: "#e81123"
onClicked: appWindow.close()

View File

@ -24,6 +24,7 @@ import net.jami.Constants 1.1
Control {
id: root
Accessible.role: Accessible.StaticText
property alias avatarBlockWidth: avatarBlock.width
property alias innerContent: innerContent
@ -64,6 +65,7 @@ Control {
property bool bigMsg
property bool timeUnderBubble: false
property var type: Type
property var shouldBeVisible: msgRowlayout.msgHovered || root.activeFocus || reply.activeFocus || more.activeFocus || share.activeFocus
// If the ListView attached properties are not available,
// then the root delegate is likely a Loader.
@ -81,6 +83,16 @@ Control {
rightPadding: hPadding
leftPadding: hPadding
background: Rectangle {
id: focusIndicator
visible: rootDelegate.activeFocus
radius: 4
border.color: JamiTheme.tintedBlue
border.width: 2
color: "transparent"
z: 1
}
contentItem: ColumnLayout {
id: mainColumnLayout
@ -301,7 +313,7 @@ Control {
anchors.verticalCenter: parent.verticalCenter
anchors.right: isOutgoing ? optionButtonItem.right : undefined
anchors.left: !isOutgoing ? optionButtonItem.left : undefined
visible: msgRowlayout.msgHovered
visible: shouldBeVisible
source: JamiResources.more_vert_24dp_svg
width: optionButtonItem.width / 4
height: optionButtonItem.height
@ -311,7 +323,7 @@ Control {
function setBindings() {
more.isOpen = false;
visible = Qt.binding(() => msgRowlayout.msgHovered);
visible = Qt.binding(() => shouldBeVisible);
imageColor = Qt.binding(() => hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor);
normalColor = Qt.binding(() => JamiTheme.primaryBackgroundColor);
}
@ -356,7 +368,7 @@ Control {
anchors.rightMargin: 5
anchors.right: isOutgoing ? more.left : undefined
anchors.left: !isOutgoing ? more.right : undefined
visible: msgRowlayout.msgHovered
visible: shouldBeVisible
onClicked: {
MessagesAdapter.editId = "";
@ -380,13 +392,14 @@ Control {
anchors.rightMargin: 5
anchors.right: isOutgoing ? reply.left : undefined
anchors.left: !isOutgoing ? reply.right : undefined
visible: msgRowlayout.msgHovered
visible: shouldBeVisible
property bool isOpen: false
property var obj: undefined
function setBindings() { // when the popup is closed, setBindings is called to reset the icon's visual settings
share.isOpen = false;
visible = Qt.binding(() => msgRowlayout.msgHovered);
visible = Qt.binding(() => shouldBeVisible);
imageColor = Qt.binding(() => hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor);
normalColor = Qt.binding(() => JamiTheme.primaryBackgroundColor);
}

View File

@ -22,6 +22,7 @@ import "contextmenu"
BaseContextMenu {
id: root
property var modelList
signal audioRecordMessageButtonClicked
signal videoRecordMessageButtonClicked
@ -31,35 +32,63 @@ BaseContextMenu {
GeneralMenuItem {
id: audioMessage
Accessible.role: Accessible.MenuItem
Accessible.name: itemName
focusPolicy: Qt.StrongFocus
Keys.onReturnPressed: clicked()
canTrigger: true
iconSource: JamiResources.message_audio_black_24dp_svg
itemName: JamiStrings.leaveAudioMessage
onClicked: {
root.audioRecordMessageButtonClicked();
root.close()
}
KeyNavigation.tab: videoMessage
KeyNavigation.backtab: shareLocation
},
GeneralMenuItem {
id: videoMessage
Accessible.role: Accessible.MenuItem
Accessible.name: itemName
focusPolicy: Qt.StrongFocus
Keys.onReturnPressed: clicked()
canTrigger: true
iconSource: JamiResources.message_video_black_24dp_svg
itemName: JamiStrings.leaveVideoMessage
isActif: VideoDevices.listSize !== 0
onClicked: {
root.videoRecordMessageButtonClicked();
root.close()
}
KeyNavigation.tab: shareLocation
KeyNavigation.backtab: audioMessage
},
GeneralMenuItem {
id: shareLocation
Accessible.role: Accessible.MenuItem
Accessible.name: itemName
focusPolicy: Qt.StrongFocus
Keys.onReturnPressed: clicked()
canTrigger: true
iconSource: JamiResources.localisation_sharing_send_pin_svg
itemName: JamiStrings.shareLocation
onClicked: {
root.showMapClicked();
root.close()
}
KeyNavigation.tab: audioMessage
KeyNavigation.backtab: videoMessage
}
]

View File

@ -24,7 +24,21 @@ import net.jami.Constants 1.1
import net.jami.Enums 1.1
SBSMessageBase {
id: root
id: rootDelegate
Accessible.role: Accessible.StaticText
Accessible.name: {
let name = isOutgoing ? JamiStrings.inReplyToYou : UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
return name + ": " + Body + " " + formattedTime;
}
Accessible.description: {
let status = "";
if (bubble.isEdited)
status += JamiStrings.edited + " ";
return status + (readers.length > 0 ? JamiStrings.readBy + " " + readers.map(function (uri) {
return UtilsAdapter.getBestNameForUri(CurrentAccount.id, uri);
}).join(", ") : "");
}
property bool isRemoteImage
property bool isEmojiOnly: IsEmojiOnly
@ -34,11 +48,11 @@ SBSMessageBase {
Connections {
target: bubble
function onColorChanged(color) {
root.colorUrl = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewLinkColorLight : JamiTheme.chatviewLinkColorDark;
root.colorText = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark;
rootDelegate.colorUrl = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewLinkColorLight : JamiTheme.chatviewLinkColorDark;
rootDelegate.colorText = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark;
// Update parsed body with correct colors
if (Body !== "")
MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), root.colorUrl, bubble.color);
MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), rootDelegate.colorUrl, bubble.color);
}
}
@ -53,7 +67,7 @@ SBSMessageBase {
textContentWidth: textEditId.width
textContentHeight: textEditId.height
bigMsg: textContentWidth >= (2 / 3) * root.maxMsgWidth || extraContent.active
bigMsg: textContentWidth >= (2 / 3) * rootDelegate.maxMsgWidth || extraContent.active
innerContent.children: [
TextEdit {
@ -63,10 +77,10 @@ SBSMessageBase {
topPadding: bubble.isDeleted ? 6 : 10
bottomPadding: bubble.isDeleted ? 6 : 10
anchors.right: isOutgoing ? parent.right : undefined
anchors.rightMargin: isOutgoing && !isEmojiOnly && !bigMsg ? root.timeWidth + root.editedWidth : 0
anchors.rightMargin: isOutgoing && !isEmojiOnly && !bigMsg ? rootDelegate.timeWidth + rootDelegate.editedWidth : 0
text: {
if (Body !== "" && ParsedBody.length === 0) {
MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), root.colorUrl, bubble.color);
MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), rootDelegate.colorUrl, bubble.color);
return "";
}
if (ParsedBody !== "")
@ -82,11 +96,11 @@ SBSMessageBase {
width: {
if (extraContent.active)
Math.max(extraContent.width, Math.min((2 / 3) * root.maxMsgWidth, implicitWidth - avatarBlockWidth, extraContent.minSize) - senderMargin);
Math.max(extraContent.width, Math.min((2 / 3) * rootDelegate.maxMsgWidth, implicitWidth - avatarBlockWidth, extraContent.minSize) - senderMargin);
else if (isEmojiOnly)
Math.min((2 / 3) * root.maxMsgWidth, implicitWidth, innerContent.width - senderMargin - (innerContent.width - senderMargin) % (JamiTheme.chatviewEmojiSize + 2));
Math.min((2 / 3) * rootDelegate.maxMsgWidth, implicitWidth, innerContent.width - senderMargin - (innerContent.width - senderMargin) % (JamiTheme.chatviewEmojiSize + 2));
else
Math.min((2 / 3) * root.maxMsgWidth, implicitWidth + 5, innerContent.width - senderMargin + 5);
Math.min((2 / 3) * rootDelegate.maxMsgWidth, implicitWidth + 5, innerContent.width - senderMargin + 5);
}
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
@ -96,7 +110,7 @@ SBSMessageBase {
renderType: Text.NativeRendering
textFormat: Text.RichText
clip: true
onLinkHovered: root.hoveredLink = hoveredLink
onLinkHovered: rootDelegate.hoveredLink = hoveredLink
onLinkActivated: Qt.openUrlExternally(new URL(hoveredLink))
readOnly: true
color: (ParsedBody !== "") ? getBaseColor() : (UtilsAdapter.luma(bubble.color) ? "white" : "dark")
@ -150,7 +164,7 @@ SBSMessageBase {
HoverHandler {
target: previewContent
onHoveredChanged: {
root.hoveredLink = hovered ? LinkPreviewInfo.url : "";
rootDelegate.hoveredLink = hovered ? LinkPreviewInfo.url : "";
}
cursorShape: Qt.PointingHandCursor
}
@ -204,7 +218,7 @@ SBSMessageBase {
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
renderType: Text.NativeRendering
textFormat: TextEdit.RichText
color: root.colorText
color: rootDelegate.colorText
visible: LinkPreviewInfo.title.length > 0
text: LinkPreviewInfo.title
lineHeight: 1.3
@ -217,9 +231,9 @@ SBSMessageBase {
renderType: Text.NativeRendering
textFormat: TextEdit.RichText
visible: LinkPreviewInfo.description.length > 0
font.underline: root.hoveredLink
font.underline: rootDelegate.hoveredLink
text: LinkPreviewInfo.description
color: root.colorUrl
color: rootDelegate.colorUrl
lineHeight: 1.3
}
Label {
@ -229,7 +243,7 @@ SBSMessageBase {
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
renderType: Text.NativeRendering
textFormat: TextEdit.RichText
color: root.colorText
color: rootDelegate.colorText
text: LinkPreviewInfo.domain
lineHeight: 1.3
}

View File

@ -17,7 +17,6 @@
#pragma once
#include "version.h"
#include "version_info.h"
#include <QVariantMap>
@ -108,7 +107,7 @@ protected:
{"platform", QSysInfo::prettyProductName() + "_" + QSysInfo::currentCpuArchitecture()},
{"client_sha", APP_VERSION_STRING},
{"jamicore_sha", CORE_VERSION_STRING},
{"build_id", QString(VERSION_STRING)},
{"build_id", BUILD_VERSION_STRING},
#if defined(Q_OS_WIN) && defined(BETA)
{"build_variant", "beta"},
#endif

View File

@ -17,7 +17,7 @@
#include "mainapplication.h"
#include "instancemanager.h"
#include "version.h"
#include "version_info.h"
#if defined(Q_OS_MACOS)
#include <os/macos/macutils.h>
#endif
@ -66,7 +66,7 @@ main(int argc, char* argv[])
QApplication::setApplicationName(QStringLiteral("Jami"));
QApplication::setOrganizationDomain(QStringLiteral("jami.net"));
QApplication::setQuitOnLastWindowClosed(false);
QCoreApplication::setApplicationVersion(QString(VERSION_STRING));
QCoreApplication::setApplicationVersion(BUILD_VERSION_STRING);
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
QApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);

View File

@ -464,7 +464,7 @@ MainApplication::initQmlLayer()
void
MainApplication::initSystray()
{
systemTray_->setIcon(QIcon(":/images/jami.svg"));
systemTray_->setIcon(QIcon(":/images/net.jami.Jami.svg"));
QMenu* menu {nullptr};
// If there was a previous menu, reuse it, otherwise create a new one.

View File

@ -85,7 +85,6 @@ Popup {
anchors.rightMargin: 15
spacing: 10
Avatar {
id: avatar
objectName: "accountComboBoxPopupAvatar"
@ -164,8 +163,13 @@ Popup {
imageColor: hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
hoveredColor: JamiTheme.hoverColor
Accessible.role: Accessible.Button
Accessible.name: toolTipText
Accessible.description: JamiStrings.qrCodeExplanation
onClicked: {
viewCoordinator.presentDialog(appWindow, "mainview/components/WelcomePageQrDialog.qml");
listView.currentIndex = -1;
root.close();
}
}
@ -184,34 +188,40 @@ Popup {
toolTipText: !inSettings ? JamiStrings.openSettings : JamiStrings.closeSettings
Accessible.role: Accessible.Button
Accessible.name: toolTipText
KeyNavigation.backtab: shareButton
onClicked: {
!inSettings ? viewCoordinator.present("SettingsView") : viewCoordinator.dismiss("SettingsView");
root.close();
}
KeyNavigation.tab: addAccountItem
}
}
}
}
Rectangle{
Layout.alignment: Qt.AlignHCenter
height: 1
Layout.fillWidth: true
Layout.leftMargin: 15
Layout.rightMargin: 15
color: JamiTheme.smartListHoveredColor
}
JamiListView {
ListView {
id: listView
objectName: "accountList"
Accessible.name: JamiStrings.accountList
Accessible.role: Accessible.List
Accessible.description: JamiStrings.accountListDescription
layer.mipmap: false
clip: true
maximumFlickVelocity: 1024
// HACK: remove after migration to Qt 6.7+
boundsBehavior: Flickable.StopAtBounds
Layout.fillHeight: true
Layout.preferredWidth: parent.width
activeFocusOnTab: true
focus: true
currentIndex: -1 // Set to -1 to avoid initial highlighting
model: SortFilterProxyModel {
sourceModel: AccountListModel
filters: ValueFilter {
@ -221,10 +231,35 @@ Popup {
}
}
highlight: Rectangle {
color: "transparent"
border.color: JamiTheme.primaryBackgroundColor
border.width: 2
radius: 5
Rectangle {
anchors.fill: parent
color: JamiTheme.hoverColor
radius: 5
opacity: 0.3
}
}
delegate: AccountItemDelegate {
height: JamiTheme.accountListItemHeight
width: root.width
Accessible.role: Accessible.ListItem
Accessible.name: Alias || Username
Accessible.description: JamiStrings.switchToAccount
// Update the background to show focus state
background: Rectangle {
color: parent.activeFocus || parent.hovered ? JamiTheme.hoverColor : "transparent"
opacity: parent.activeFocus ? 0.3 : 1
radius: 5
}
onClicked: {
root.close();
// This is a workaround for the synchronicity issue
@ -235,7 +270,7 @@ Popup {
}
}
Rectangle{
Rectangle {
Layout.alignment: Qt.AlignHCenter
height: 1
Layout.fillWidth: true
@ -248,19 +283,24 @@ Popup {
id: addAccountItem
Layout.preferredHeight: 45
Layout.preferredWidth: parent.width -10
Layout.preferredWidth: parent.width - 10
Layout.alignment: Qt.AlignLeft
Layout.leftMargin: 5
Accessible.name: JamiStrings.addAccount
focusPolicy: Qt.StrongFocus
Accessible.name: addAccountText.text
Accessible.role: Accessible.Button
KeyNavigation.tab: manageAccountItem
KeyNavigation.up: listView
KeyNavigation.down: manageAccountItem
background: Rectangle {
color: addAccountItem.hovered ? JamiTheme.hoverColor : JamiTheme.accountComboBoxBackgroundColor
radius: 5
}
RowLayout{
RowLayout {
anchors.left: parent.left
anchors.leftMargin: 18
anchors.verticalCenter: parent.verticalCenter
@ -274,6 +314,7 @@ Popup {
}
Text {
id: addAccountText
Layout.alignment: Qt.AlignLeft
text: JamiStrings.addAccount
textFormat: TextEdit.PlainText
@ -285,18 +326,21 @@ Popup {
root.close();
viewCoordinator.present("WizardView");
}
KeyNavigation.tab: manageAccountItem
}
ItemDelegate {
id: manageAccountItem
focusPolicy: Qt.StrongFocus
Accessible.role: Accessible.Button
Accessible.name: JamiStrings.manageAccount
Accessible.name: manageAccountText.text
KeyNavigation.backtab: addAccountItem
KeyNavigation.tab: shareButton
KeyNavigation.up: addAccountItem
Layout.preferredHeight: 45
Layout.preferredWidth: parent.width-10
Layout.preferredWidth: parent.width - 10
Layout.leftMargin: 5
Layout.bottomMargin: 5
@ -305,7 +349,7 @@ Popup {
radius: 5
}
RowLayout{
RowLayout {
anchors.left: parent.left
anchors.leftMargin: 18
anchors.verticalCenter: parent.verticalCenter
@ -319,8 +363,8 @@ Popup {
color: manageAccountItem.hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
}
Text {
id: manageAccountText
text: JamiStrings.manageAccount
textFormat: TextEdit.PlainText
color: JamiTheme.textColor
font.pointSize: JamiTheme.textFontSize
@ -328,7 +372,7 @@ Popup {
}
onClicked: {
root.close();
viewCoordinator.present("SettingsView")
viewCoordinator.present("SettingsView");
}
}
}

View File

@ -28,17 +28,24 @@ ItemDelegate {
height: JamiTheme.accountListItemHeight
background: Rectangle {
width: root.width - 10
anchors.horizontalCenter: parent.horizontalCenter
anchors {
left: parent ? parent.left : undefined
right: parent ? parent.right : undefined
leftMargin: 5
rightMargin: 5
}
radius: 5
Rectangle{
Rectangle {
id: separationLine
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors {
left: parent.left
right: parent.right
top: parent.top
leftMargin: 10
rightMargin: 10
}
height: 1
width: parent.width - 20
color: JamiTheme.hoverColor
visible: index !== 0
}

View File

@ -25,6 +25,13 @@ import "../../commoncomponents"
Item {
id: root
property string tipTitle: title.text
property string tipDescription: opened ? description.text : JamiStrings.whyBackupAccount
Accessible.name: tipTitle
Accessible.description: tipDescription
width: parent.width
height: backupLayout.height

View File

@ -55,15 +55,30 @@ Control {
signal fullScreenClicked
signal swarmDetailsClicked
// For Keyboard naviguation
property bool isInternalNavigation: false
function exitBarNavigation() {
isInternalNavigation = false;
// Let the parent control take over focus handling
parent.forceActiveFocus();
}
Component {
id: buttonDelegate
CallButtonDelegate {
id: delegateItem
width: root.height
height: width
barWidth: root.width
onSubMenuVisibleChanged: subMenuOpen = subMenuVisible
onHoveredChanged: root.barHovered = hovered
focusPolicy: Qt.StrongFocus
focus: false
Keys.onEscapePressed: root.exitBarNavigation()
}
}
@ -567,6 +582,34 @@ Control {
ComboBox {
id: overflowButton
Accessible.role: Accessible.Button
Accessible.name: JamiStrings.more
Accessible.description: JamiStrings.moreOptions
KeyNavigation.tab: {
if (popup.opened) {
return popup.contentItem.itemAtIndex(0);
}
// Exit bar navigation if we've reached the end
root.exitBarNavigation();
return null;
}
KeyNavigation.backtab: {
if (overflowItemListView.count > 0) {
return overflowItemListView.itemAtIndex(overflowItemListView.count - 1);
}
return itemListView.itemAtIndex(itemListView.count - 1);
}
Keys.onEscapePressed: {
if (popup.opened) {
popup.close();
} else {
root.exitBarNavigation();
}
}
visible: CallOverlayModel.overflowIndex < overflowItemCount - 2
width: root.height
height: width

View File

@ -42,8 +42,18 @@ ItemDelegate {
text: ""
Accessible.role: Accessible.Button
Accessible.name: text
Accessible.description: text
Accessible.name: ItemAction.text
Accessible.description: {
if (!ItemAction?.text)
return "";
if (ItemAction.checkable) {
return JamiStrings.pressToToggle.arg(ItemAction.text).arg(ItemAction.checked ? JamiStrings.active : JamiStrings.inactive);
}
return JamiStrings.pressToAction.arg(ItemAction.text);
}
Accessible.pressed: pressed
Accessible.checkable: ItemAction ? ItemAction.checkable : false
Accessible.checked: ItemAction ? ItemAction.checked : false
z: index
@ -77,7 +87,7 @@ ItemDelegate {
return HalfPill.None;
}
Behavior on color {
Behavior on color {
ColorAnimation {
duration: JamiTheme.shortFadeDuration
}
@ -94,7 +104,7 @@ ItemDelegate {
radius: isLast ? 5 : width / 2
type: isLast ? HalfPill.Right : HalfPill.None
Behavior on color {
Behavior on color {
ColorAnimation {
duration: JamiTheme.shortFadeDuration
}
@ -113,7 +123,7 @@ ItemDelegate {
source: ItemAction ? ItemAction.icon.source : ""
color: ItemAction ? (ItemAction.enabled ? ItemAction.icon.color : Qt.lighter(ItemAction.icon.color)) : null
SequentialAnimation on opacity {
SequentialAnimation on opacity {
loops: Animation.Infinite
running: ItemAction !== undefined && ItemAction.blinksWhenChecked !== undefined && ItemAction.blinksWhenChecked && checked
onStopped: icon.opacity = 1
@ -188,7 +198,7 @@ ItemDelegate {
radius: 4
}
onActivated: index => menuAction.accept(index);
onActivated: index => menuAction.accept(index)
model: visible ? menuAction.listModel : null
delegate: ItemDelegate {
id: menuItem
@ -313,7 +323,6 @@ ItemDelegate {
// it fits within the overlay, with an extra leftward margin of 24 pixels.
return diff > 0 ? xValue - diff - 24 : xValue;
}
}
implicitWidth: contentItem.implicitWidth

View File

@ -26,6 +26,12 @@ ColumnLayout {
id: column
width: parent.width
property string tipTitle: JamiStrings.customize
property string tipDescription: JamiStrings.customizeText
Accessible.name: tipTitle
Accessible.description: tipDescription
property var iconSize: 26
property var margin: 5
property var prefWidth: 170

View File

@ -24,6 +24,13 @@ import "../../commoncomponents"
Item {
id: root
property string tipTitle: title.text
property string tipDescription: content.text
Accessible.name: tipTitle
Accessible.description: tipDescription
focus: true
width: parent.width
height: backupLayout.height

View File

@ -43,6 +43,9 @@ TabButton {
hoverEnabled: true
onClicked: selected()
Accessible.name: root.labelText
Accessible.role: Accessible.Button
Rectangle {
id: contentRect

View File

@ -26,6 +26,12 @@ ColumnLayout {
id: column
width: parent.width
property alias tipTitle: title.text
property alias tipDescription: description.text
Accessible.name: tipTitle
Accessible.description: tipDescription
property real maxHeight: 250
property var iconSize: 26

View File

@ -25,4 +25,6 @@ PushButton {
normalColor: JamiTheme.chatviewBgColor
imageColor: hovered ? JamiTheme.chatviewButtonColor : JamiTheme.chatViewFooterImgColor
Accessible.role: Accessible.Button
Accessible.description: toolTipText
}

View File

@ -228,6 +228,11 @@ Window {
anchors.fill: parent
background: Rectangle {
anchors.fill: parent
color: JamiTheme.backgroundColor
}
// make a list view of keyboardShortcutsModelList[selectionBar.currentIndex]
JamiListView {
id: keyboardShortcutsListView
@ -262,6 +267,7 @@ Window {
Layout.topMargin: 8
Layout.leftMargin: 20
text: description
color: JamiTheme.textColor
background: Rectangle {
width: parent.width + 16
height: parent.height + 16
@ -269,6 +275,7 @@ Window {
border.width: 2
radius: 5
anchors.centerIn: parent
color: JamiTheme.backgroundColor
}
}
Label {
@ -277,6 +284,7 @@ Window {
Layout.topMargin: 8
Layout.rightMargin: 20
text: shortcut
color: JamiTheme.textColor
background: Rectangle {
width: parent.width + 16
height: parent.height + 16
@ -284,6 +292,7 @@ Window {
border.width: 2
radius: 5
anchors.centerIn: parent
color: JamiTheme.backgroundColor
}
}
}
@ -300,6 +309,10 @@ Window {
focus: true
background: Rectangle {
color: JamiTheme.backgroundColor
}
Repeater {
model: [JamiStrings.generalSettingsTitle, JamiStrings.conversationKeyboardShortcuts, JamiStrings.callKeyboardShortcuts, JamiStrings.markdownKeyboardShortcuts, JamiStrings.settings]
@ -339,9 +352,16 @@ Window {
footer: Item {
height: JamiTheme.keyboardShortcutTabBarSize
PageIndicator {
id: pageIndicator
anchors.centerIn: parent
count: selectionBar.count
currentIndex: selectionBar.currentIndex
delegate: Rectangle {
width: 6
height: 6
radius: 3
color: index === pageIndicator.currentIndex ? JamiTheme.textColor : JamiTheme.textColorHoveredHighContrast
}
}
}
}

View File

@ -26,6 +26,17 @@ Item {
property string timeText: "00:00"
property string remoteRecordingLabel
property bool isKeyboardSelectionActive: {
if (!appWindow || !appWindow.activeFocusItem)
return false;
let parent = appWindow.activeFocusItem.parent;
while (parent && parent !== appWindow && parent !== root && parent !== null) {
if (parent.objectName === "callActionBar")
return true;
parent = parent.parent;
}
return false;
}
Connections {
target: CurrentCall
@ -42,7 +53,11 @@ Item {
property alias callActionBar: __callActionBar
property bool frozen: callActionBar.overflowOpen || callActionBar.barHovered || callActionBar.subMenuOpen || participantCallInStatusView.visible
property bool frozen: callActionBar.overflowOpen ||
callActionBar.barHovered ||
callActionBar.subMenuOpen ||
participantCallInStatusView.visible ||
isKeyboardSelectionActive
property string muteAlertMessage: ""
property bool muteAlertActive: false
@ -59,15 +74,27 @@ Item {
Component.onDestruction: CallOverlayModel.setEventFilterActive(appWindow, this, false)
onVisibleChanged: CallOverlayModel.setEventFilterActive(appWindow, this, visible)
function kickOverlay() {
root.opacity = 1;
fadeOutTimer.restart();
}
Connections {
target: CallOverlayModel
function onMouseMoved(item) {
if (item === root) {
root.opacity = 1;
fadeOutTimer.restart();
kickOverlay();
}
}
// This is part of a mechanism used to show the overlay when a focus key is pressed
// and keep it open in the case that the user is navigating with the keyboard over
// the call action bar.
function onFocusKeyPressed() {
// Always show the overlay when a focus key (Tab/BackTab) is pressed
kickOverlay();
}
}
Shortcut {
@ -76,8 +103,7 @@ Item {
context: Qt.ApplicationShortcut
onActivated: {
CallAdapter.muteAudioToggle();
root.opacity = 1;
fadeOutTimer.restart();
kickOverlay();
}
}
@ -87,8 +113,7 @@ Item {
context: Qt.ApplicationShortcut
onActivated: {
CallAdapter.muteCameraToggle();
root.opacity = 1;
fadeOutTimer.restart();
kickOverlay();
}
}

View File

@ -113,9 +113,7 @@ JamiFlickable {
}
}
readonly property bool spellCheckEnabled:
AppSettingsManager.getValue(Settings.EnableSpellCheck) &&
AppSettingsManager.getValue(Settings.SpellLang) !== ""
property bool spellCheckEnabled: AppSettingsManager.getValue(Settings.EnableSpellCheck) && AppSettingsManager.getValue(Settings.SpellLang) !== ""
// Spell check is active under the following conditions:
// 1. Spell check is enabled in settings
@ -192,7 +190,7 @@ JamiFlickable {
onReleased: function (event) {
if (event.button === Qt.RightButton) {
if (spellCheckActive) {
if (spellCheckActive && SpellCheckAdapter.hasLoadedDictionary) {
var position = textArea.positionAt(event.x, event.y);
textArea.moveCursorSelection(position, TextInput.SelectWords);
textArea.selectWord();
@ -254,7 +252,7 @@ JamiFlickable {
function updateSpellCorrection() {
clearUnderlines();
// We iterate over the whole text to find words to check and underline them if needed
if (spellCheckActive) {
if (spellCheckActive && SpellCheckAdapter.hasLoadedDictionary) {
var text = textArea.text;
var words = SpellCheckAdapter.findWords(text);
if (!words)

View File

@ -479,6 +479,9 @@ Rectangle {
anchors.bottom: parent.bottom
enabled: !showPreview
hoverEnabled: !showPreview
Accessible.name: JamiStrings.showMoreMessagingOptions
Accessible.role: Accessible.ComboBox
Accessible.description: JamiStrings.showMoreMessagingOptionsDescription
// Used to choose the correct color for the button.
readonly property bool highlight: down || hovered

View File

@ -24,8 +24,26 @@ import net.jami.Adapters 1.1
import net.jami.Constants 1.1
import "../../commoncomponents"
JamiListView {
ListView {
id: root
property alias verticalScrollBar: verticalScrollBar
layer.mipmap: false
clip: true
ScrollBar.vertical: JamiScrollBar {
id: verticalScrollBar
attachedFlickableMoving: root.moving
}
keyNavigationEnabled: true
keyNavigationWraps: false
focus: true
activeFocusOnTab: true
Accessible.role: Accessible.List
Accessible.name: JamiStrings.conversationMessages
function getDistanceToBottom() {
const scrollDiff = ScrollBar.vertical.position - (1.0 - ScrollBar.vertical.size);
@ -139,7 +157,10 @@ JamiListView {
}
// fade-in mechanism
Component.onCompleted: fadeAnimation.start()
Component.onCompleted: {
positionViewAtBeginning();
fadeAnimation.start();
}
Rectangle {
id: overlay
anchors.fill: parent
@ -194,13 +215,19 @@ JamiListView {
boundsBehavior: Flickable.StopAtBounds
currentIndex: -1
Connections {
target: CurrentConversation
function onIdChanged() {
currentIndex = -1;
}
}
model: MessagesAdapter.messageListModel
delegate: DelegateChooser {
id: delegateChooser
role: "Type"
DelegateChoice {
id: delegateChoice
roleValue: Interaction.Type.TEXT
TextMessageDelegate {

View File

@ -220,7 +220,6 @@ Popup {
id: cancelBtn
objectName: "cancelBtn"
z: 1
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 20
Layout.preferredWidth: 20
@ -323,6 +322,9 @@ Popup {
Layout.alignment: Qt.AlignCenter
preferredSize: btnSize
Accessible.role: Accessible.Button
Accessible.name: toolTipText
source: JamiResources.stop_rectangle_24dp_svg
imageColor: JamiTheme.whiteColor

View File

@ -36,6 +36,36 @@ FocusScope {
property color textColor: JamiTheme.textColor
property color iconColor: JamiTheme.tintedBlue
Accessible.role: Accessible.Paragraph
Accessible.name: {
switch (type) {
case "donation":
return JamiStrings.donation
case "backup":
return JamiStrings.backupAccountBtn
case "customize":
return JamiStrings.customize
case "tip":
return title
default:
return ""
}
}
Accessible.description: {
switch (type) {
case "donation":
return JamiStrings.donationTipBoxText
case "backup":
return JamiStrings.whyBackupAccount
case "customize":
return JamiStrings.customizeText
case "tip":
return description
default:
return ""
}
}
property string customizeTip: "CustomizeTipBox {}"
property string backupTip: "BackupTipBox {" + " onIgnore: {" + " root.ignoreClicked()" + " }" + "}"
@ -75,6 +105,9 @@ FocusScope {
active: type === "donation"
focus: true
sourceComponent: DonationTipBox {
Accessible.name: JamiStrings.donation
Accessible.description: JamiStrings.donationTipBoxText
Accessible.role: Accessible.Link
maxHeight: root.maximumHeight
textColor: root.textColor
iconColor: root.iconColor
@ -86,6 +119,9 @@ FocusScope {
id: loader_backupTip
active: type === "backup"
sourceComponent: BackupTipBox {
Accessible.name: JamiStrings.backupAccountBtn
Accessible.description: JamiStrings.whyBackupAccount
Accessible.role: Accessible.Link
onIgnore: {
root.ignoreClicked();
}
@ -95,20 +131,24 @@ FocusScope {
}
width: parent.width
}
Loader {
id: loader_customizeTip
active: type === "customize"
sourceComponent: CustomizeTipBox {
Accessible.role: Accessible.Link
textColor: root.textColor
iconColor: root.iconColor
}
width: parent.width
focus: true
}
Loader {
id: loader_infoTip
active: type === "tip"
sourceComponent: InformativeTipBox {
Accessible.role: Accessible.Link
maxHeight: root.maximumHeight
textColor: root.textColor
iconColor: root.iconColor
@ -159,6 +199,8 @@ FocusScope {
id: component_btnClose
PushButton {
id: btnClose
Accessible.role: Accessible.Button
Accessible.name: JamiStrings.dismissTip
width: 20
height: 20

View File

@ -18,7 +18,6 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
import net.jami.Enums 1.1
@ -39,6 +38,10 @@ ListSelectionView {
onPresented: LRCInstance.deselectConversation()
leftPaneItem: viewCoordinator.getView("SidePanel", true)
Accessible.role: Accessible.Pane
Accessible.name: title
Accessible.description: JamiStrings.description
property variant uiCustomization: CurrentAccount.uiCustomization
onUiCustomizationChanged: {
@ -277,6 +280,9 @@ ListSelectionView {
preferredWidth: textSize.width + 2 * JamiTheme.preferredMarginSize
text: JamiStrings.aboutJami
Accessible.role: Accessible.Button
Accessible.name: text
onClicked: viewCoordinator.presentDialog(appWindow, "mainview/components/AboutPopUp.qml")
}

View File

@ -25,6 +25,14 @@ Item {
property string qmlFilePrefix: "file:/"
readonly property string base64StringTitle: "data:image/png;base64,"
readonly property var webEngineNames: {
"mediaPreview": "mediaPreviewWebEngine",
"videoPreview": "videoPreviewWebEngine",
"map": "mapWebEngine",
"general": "generalWebEngine",
"emojiPicker": "emojiPickerWebEngine"
}
property var accountCreationInputParaObject: ({})
function setUpAccountCreationInputPara(inputPara) {

View File

@ -915,7 +915,7 @@ Item {
property string checkSpelling: qsTr("Check spelling while typing")
property string systemDictionary: qsTr("System")
property string textLanguage: qsTr("Text language")
property string spellchecking: qsTr("Spell checker")
property string spellChecker: qsTr("Spell checker")
property string searchTextLanguages: qsTr("Search text languages")
property string searchAvailableTextLanguages: qsTr("Search for available text languages")
property string noDictionariesFoundFor: qsTr("No dictionaries found for '%1'")
@ -924,4 +924,31 @@ Item {
property string spellCheckDownloadFailed: qsTr("Download failed for dictionary '%1'")
property string showInstalledDictionaries: qsTr("Show installed")
property string showInstalledDictionariesDescription: qsTr("Only show dictionaries that are currently installed")
// Accessibility
property string switchToAccount: qsTr("Press enter to switch to this account")
property string qrCodeExplanation: qsTr("Display your QR code to allow other users to scan it and add you as a contact")
property string accountList: qsTr("Account list")
property string accountListDescription: qsTr("Use arrows to switch between available account")
property string languageComboBoxExplanation: qsTr("Select the user interface language")
property string backButtonExplanation: qsTr("Go back to the previous page")
property string adviceBox: qsTr("Advice Box")
property string backButton: qsTr("Back button")
property string adviceBoxExplanation: qsTr("Open the advice popup that contains information about Jami")
property string more: qsTr("More")
property string pressToAction: qsTr("Press to %1")
property string pressToToggle: qsTr("Press to toggle %1 (%2)")
property string active: qsTr("active")
property string inactive: qsTr("inactive")
property string minimize: qsTr("Minimize application")
property string maximize: qsTr("Maximize application")
property string closeApplication: qsTr("Close application")
property string dismissTip: qsTr("Dismiss this tip")
property string tipDescription: qsTr("Tips to help you use Jami more effectively")
property string showMoreMessagingOptions: qsTr("Show more messaging options")
property string showMoreMessagingOptionsDescription: qsTr("Open a menu that allows you to send voice and video messages as well as sharing your location")
property string conversationMessages: qsTr("Conversation messages list. Use arrow keys to navigate through messages.")
property string dataTransfer: qsTr("Data transfer")
property string status: qsTr("Status")
property string readBy: qsTr("Read by")
}

View File

@ -83,6 +83,7 @@ Item {
property color editBackgroundColor: darkTheme ? "#373737" : lightGrey_
property color textColor: primaryForegroundColor
property color textColorHovered: darkTheme ? "#cccccc" : "#333333"
property color textColorHoveredHighContrast: darkTheme ? "#6a6a6a" : "#a7a7a7"
property color tabbarBorderColor: darkTheme ? blackColor : "#e3e3e3"
property color popupOverlayColor: darkTheme ? Qt.rgba(255, 255, 255, 0.22) : Qt.rgba(0, 0, 0, 0.33)
property real formsRadius: 30
@ -103,7 +104,7 @@ Item {
property color pressedButtonColor: darkTheme ? pressColor : "#a0a0a0"
property color hoveredButtonColor: darkTheme ? "#4d4d4d" : "#dedede"
property color hoveredButtonColorWizard: darkTheme ? "#4d4d4d" : "#dedede"
property color normalButtonColor: darkTheme ? backgroundColor : "#e0e0e0"
property color normalButtonColor: darkTheme ? "#bfbfbf" : "#e0e0e0"
property color invertedPressedButtonColor: Qt.rgba(0, 0, 0, 0.5)
property color invertedHoveredButtonColor: Qt.rgba(0, 0, 0, 0.6)

View File

@ -49,12 +49,12 @@ SettingsPageBase {
spacing: JamiTheme.settingsCategorySpacing
Text {
id: spellcheckingTitle
id: spellCheckerTitle
Layout.alignment: Qt.AlignLeft
Layout.preferredWidth: parent.width
text: JamiStrings.spellchecking
text: JamiStrings.spellChecker
color: JamiTheme.textColor
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter

View File

@ -134,6 +134,9 @@ SpellCheckAdapter::setDictionary(const QString& locale)
auto localPath = dictionaryListModel_->pathForLocale(locale);
if (spellChecker_.replaceDictionary(localPath)) {
settingsManager_->setValue(Settings::Key::SpellLang, locale);
set_hasLoadedDictionary(true);
Q_EMIT dictionaryChanged();
} else {
qWarning() << "Failed to set dictionary for locale:" << locale;
}
}

View File

@ -33,6 +33,7 @@ class SpellCheckAdapter final : public QObject
QML_SINGLETON
QML_RO_PROPERTY(int, installedDictionaryCount)
QML_RO_PROPERTY(bool, hasLoadedDictionary)
public:
static SpellCheckAdapter* create(QQmlEngine* engine, QJSEngine*)

View File

@ -31,24 +31,28 @@
SpellChecker::SpellChecker()
: hunspell_(new Hunspell("", ""))
{}
{
// Initialize with default UTF-8 codec
codec_ = QTextCodec::codecForName("UTF-8");
}
bool
SpellChecker::spell(const QString& word)
{
// Encode from Unicode to the encoding used by current dictionary
return hunspell_->spell(word.toStdString()) != 0;
return hunspell_->spell(codec_->fromUnicode(word).toStdString()) != 0;
}
QStringList
SpellChecker::suggest(const QString& word)
{
// Encode from Unicode to the encoding used by current dictionary
std::vector<std::string> numSuggestions = hunspell_->suggest(word.toStdString());
std::vector<std::string> numSuggestions = hunspell_->suggest(
codec_->fromUnicode(word).constData());
QStringList suggestions;
for (size_t i = 0; i < numSuggestions.size(); ++i) {
suggestions << QString::fromStdString(numSuggestions.at(i));
for (const auto& suggestion : numSuggestions) {
suggestions << codec_->toUnicode(suggestion.c_str());
}
return suggestions;
}
@ -74,26 +78,26 @@ SpellChecker::replaceDictionary(const QString& dictionaryPath)
QString dictFile = dictionaryPath + ".dic";
QString affixFile = dictionaryPath + ".aff";
QByteArray dictFilePath = dictFile.toLocal8Bit();
QByteArray affixFilePath = affixFile.toLocal8Bit();
hunspell_.reset(new Hunspell(affixFilePath.constData(), dictFilePath.constData()));
// detect encoding analyzing the SET option in the affix file
encoding_ = "ISO8859-1";
QFile _affixFile(affixFile);
if (_affixFile.open(QIODevice::ReadOnly)) {
QTextStream stream(&_affixFile);
QRegExp enc_detector("^\\s*SET\\s+([A-Z0-9\\-]+)\\s*", Qt::CaseInsensitive);
for (QString line = stream.readLine(); !line.isEmpty(); line = stream.readLine()) {
if (enc_detector.indexIn(line) > -1) {
encoding_ = enc_detector.cap(1);
break;
}
}
_affixFile.close();
// Check if dictionary files exist
if (!QFile::exists(dictFile) || !QFile::exists(affixFile)) {
qWarning() << "Dictionary files not found:" << dictFile << affixFile;
return false;
}
codec_ = QTextCodec::codecForName(this->encoding_.toLatin1().constData());
QByteArray dictFilePath = dictFile.toLocal8Bit();
QByteArray affixFilePath = affixFile.toLocal8Bit();
std::unique_ptr<Hunspell> hunspell(
new Hunspell(affixFilePath.constData(), dictFilePath.constData()));
auto encoding = hunspell->get_dic_encoding();
auto codec = QTextCodec::codecForName(encoding);
if (!codec) {
return false;
}
hunspell_ = std::move(hunspell);
codec_ = codec;
currentDictionaryPath_ = dictionaryPath;
return true;
}

View File

@ -57,6 +57,5 @@ private:
std::unique_ptr<Hunspell> hunspell_;
QString currentDictionaryPath_;
QString encoding_;
QTextCodec* codec_;
};

View File

@ -146,7 +146,7 @@ void
SystemTray::onNotificationCountChanged(int count)
{
if (count == 0) {
setIcon(QIcon(":/images/jami.svg"));
setIcon(QIcon(":/images/net.jami.Jami.svg"));
} else {
setIcon(QIcon(":/images/jami-new.svg"));
}
@ -258,9 +258,9 @@ SystemTray::showNotification(const QString& message,
setOnClickedCallback(std::move(onClickedCb));
if (from.isEmpty())
showMessage(message, "", QIcon(":images/jami.svg"));
showMessage(message, "", QIcon(":images/net.jami.Jami.svg"));
else
showMessage(from, message, QIcon(":images/jami.svg"));
showMessage(from, message, QIcon(":images/net.jami.Jami.svg"));
}
template<typename Func>

View File

@ -20,7 +20,6 @@
#include "lrcinstance.h"
#include "systemtray.h"
#include "utils.h"
#include "version.h"
#include "version_info.h"
#include "global.h"
#include <api/datatransfermodel.h>
@ -128,7 +127,7 @@ UtilsAdapter::getProjectCredits()
const QString
UtilsAdapter::getBuildIDStr()
{
return QString(VERSION_STRING);
return BUILD_VERSION_STRING;
}
const QString

View File

@ -1,59 +0,0 @@
#pragma once
// clang-format off
#define BUILD_YEAR_CH0 (__DATE__[ 7])
#define BUILD_YEAR_CH1 (__DATE__[ 8])
#define BUILD_YEAR_CH2 (__DATE__[ 9])
#define BUILD_YEAR_CH3 (__DATE__[10])
#define BUILD_MONTH_IS_JAN (__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n')
#define BUILD_MONTH_IS_FEB (__DATE__[0] == 'F')
#define BUILD_MONTH_IS_MAR (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r')
#define BUILD_MONTH_IS_APR (__DATE__[0] == 'A' && __DATE__[1] == 'p')
#define BUILD_MONTH_IS_MAY (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y')
#define BUILD_MONTH_IS_JUN (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n')
#define BUILD_MONTH_IS_JUL (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l')
#define BUILD_MONTH_IS_AUG (__DATE__[0] == 'A' && __DATE__[1] == 'u')
#define BUILD_MONTH_IS_SEP (__DATE__[0] == 'S')
#define BUILD_MONTH_IS_OCT (__DATE__[0] == 'O')
#define BUILD_MONTH_IS_NOV (__DATE__[0] == 'N')
#define BUILD_MONTH_IS_DEC (__DATE__[0] == 'D')
#define BUILD_MONTH_CH0 \
((BUILD_MONTH_IS_OCT || BUILD_MONTH_IS_NOV || BUILD_MONTH_IS_DEC) ? '1' : '0')
#define BUILD_MONTH_CH1 \
( \
(BUILD_MONTH_IS_JAN) ? '1' : \
(BUILD_MONTH_IS_FEB) ? '2' : \
(BUILD_MONTH_IS_MAR) ? '3' : \
(BUILD_MONTH_IS_APR) ? '4' : \
(BUILD_MONTH_IS_MAY) ? '5' : \
(BUILD_MONTH_IS_JUN) ? '6' : \
(BUILD_MONTH_IS_JUL) ? '7' : \
(BUILD_MONTH_IS_AUG) ? '8' : \
(BUILD_MONTH_IS_SEP) ? '9' : \
(BUILD_MONTH_IS_OCT) ? '0' : \
(BUILD_MONTH_IS_NOV) ? '1' : \
(BUILD_MONTH_IS_DEC) ? '2' : \
/* error default */ '?' \
)
#define BUILD_DAY_CH0 ((__DATE__[4] >= '0') ? (__DATE__[4]) : '0')
#define BUILD_DAY_CH1 (__DATE__[ 5])
#define BUILD_HOUR_CH0 (__TIME__[0])
#define BUILD_HOUR_CH1 (__TIME__[1])
#define BUILD_MIN_CH0 (__TIME__[ 3])
#define BUILD_MIN_CH1 (__TIME__[ 4])
const char VERSION_STRING[] = {
BUILD_YEAR_CH0, BUILD_YEAR_CH1, BUILD_YEAR_CH2, BUILD_YEAR_CH3,
BUILD_MONTH_CH0, BUILD_MONTH_CH1,
BUILD_DAY_CH0, BUILD_DAY_CH1,
BUILD_HOUR_CH0, BUILD_HOUR_CH1,
BUILD_MIN_CH0, BUILD_MIN_CH1,
'\0'
};
// clang-format on

View File

@ -22,6 +22,7 @@ import net.jami.Constants 1.1
WebEngineView {
id: root
objectName: JamiQmlUtils.webEngineNames.general
property string onCompletedLoadHtml: ""
property string onCompletedUrl: "qrc" + onCompletedLoadHtml

View File

@ -26,6 +26,7 @@ import "../commoncomponents"
WebEngineView {
id: wev
objectName: JamiQmlUtils.webEngineNames.mediaPreview
property bool isVideo
property string html
readonly property real minSize: 192

View File

@ -28,6 +28,7 @@ Rectangle {
WebEngineView {
id: wev
objectName: JamiQmlUtils.webEngineNames.videoPreview
anchors.fill: parent
anchors.verticalCenter: root.verticalCenter

View File

@ -77,6 +77,7 @@ Popup {
GeneralWebEngineView {
id: emojiPickerWebView
objectName: JamiQmlUtils.webEngineNames.emojiPicker
width: JamiTheme.emojiPickerWidth
height: JamiTheme.emojiPickerHeight

View File

@ -132,6 +132,7 @@ Item {
WebEngineView {
id: webView
objectName: JamiQmlUtils.webEngineNames.map
layer.enabled: !isFullScreen
layer.effect: OpacityMask {

View File

@ -135,10 +135,16 @@ Rectangle {
id: usernameEdit
accountId: ""
Accessible.role: Accessible.EditableText
Accessible.name: invalidLabel.text
icon: PushButton {
id: infoBox
z: 1
Accessible.role: Accessible.StaticText
Accessible.name: textInfo.text
normalColor: "transparent"
imageColor: infoBox.checked ? JamiTheme.inviteHoverColor : JamiTheme.buttonTintedBlue
source: JamiResources.i_informations_black_24dp_svg
@ -273,6 +279,7 @@ Rectangle {
font.capitalization: Font.AllUppercase
text: joinJamiButton.text
}
Accessible.description: invalidLabel.text
objectName: "joinJamiButton"
@ -371,6 +378,10 @@ Rectangle {
imageContainerWidth: 20
source: JamiResources.ic_arrow_back_24dp_svg
Accessible.role: Accessible.Button
Accessible.name: JamiStrings.backButton
Accessible.description: JamiStrings.backButtonExplanation
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: JamiTheme.wizardViewPageBackButtonMargins
@ -397,6 +408,10 @@ Rectangle {
preferredSize: 36
checkedImageColor: JamiTheme.chatviewButtonColor
Accessible.role: Accessible.Button
Accessible.name: JamiStrings.adviceBox
Accessible.description: JamiStrings.adviceBoxExplanation
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: JamiTheme.wizardViewPageBackButtonMargins

View File

@ -86,6 +86,7 @@ BaseModalDialog {
Layout.topMargin: 20
KeyNavigation.up: passwordEdit
KeyNavigation.down: KeyNavigation.up
Accessible.description: JamiStrings.encryptDescription
onDynamicTextChanged: {
button1.enabled = passwordEdit.dynamicText === passwordConfirmEdit.dynamicText && passwordEdit.dynamicText.length !== 0

View File

@ -38,6 +38,9 @@ Rectangle {
signal showThisPage
color: JamiTheme.secondaryBackgroundColor
Accessible.role: Accessible.Pane
Accessible.name: joinJami.text
Accessible.description: JamiStrings.customizeAccountDescription
Connections {
target: WizardViewStepModel
@ -132,6 +135,9 @@ Rectangle {
KeyNavigation.tab: displayNameLineEdit
KeyNavigation.right: KeyNavigation.tab
KeyNavigation.down: saveProfileButton
Accessible.role: Accessible.Button
Accessible.name: JamiStrings.selectImage
Accessible.description: JamiStrings.customizeOptional
anchors.centerIn: parent
@ -280,6 +286,10 @@ Rectangle {
objectName: "createAccountPageBackButton"
Accessible.role: Accessible.Button
Accessible.name: JamiStrings.backButton
Accessible.description: JamiStrings.backButtonExplanation
preferredSize: 36
imageContainerWidth: 20
source: JamiResources.ic_arrow_back_24dp_svg

View File

@ -49,15 +49,19 @@ BaseModalDialog {
root.close();
}
button1.onClicked: root.close()
button2.Accessible.name: JamiStrings.chooseAUsername
button2.Accessible.description: JamiStrings.joinJamiNoPassword
popupContent: Text {
width: root.width - 2 * root.popupMargins
font.pixelSize: JamiTheme.popuptextSize
Accessible.role: Accessible.Dialog
Accessible.name: title
Accessible.description: JamiStrings.joinJamiNoPassword
width: root.width - 2 * root.popupMargins
font.pixelSize: JamiTheme.popuptextSize
lineHeight: JamiTheme.wizardViewTextLineHeight
wrapMode: Text.WordWrap
lineHeight: JamiTheme.wizardViewTextLineHeight
wrapMode: Text.WordWrap
color: JamiTheme.textColor
text: JamiStrings.joinJamiNoPassword
}
color: JamiTheme.textColor
text: JamiStrings.joinJamiNoPassword
}
}

View File

@ -54,6 +54,9 @@ Rectangle {
if (visible)
forceActiveFocus();
}
Accessible.role: Accessible.Pane
Accessible.name: introduction.text
Accessible.description: JamiStrings.description
KeyNavigation.tab: newAccountButton
KeyNavigation.up: newAccountButton
@ -71,6 +74,8 @@ Rectangle {
width: 800
Item {
Accessible.name: introduction.text
Accessible.description: JamiStrings.description
Layout.alignment: Qt.AlignCenter | Qt.AlignTop
Layout.preferredWidth: JamiTheme.welcomeLogoWidth
@ -373,8 +378,9 @@ Rectangle {
fontSize: JamiTheme.wizardViewAboutJamiFontPixelSize
KeyNavigation.tab: backButton.visible ? backButton : newAccountButton
KeyNavigation.up: connectAccountManagerButton
KeyNavigation.up: showAdvanced ? newSIPAccountButton : showAdvancedButton
KeyNavigation.down: KeyNavigation.tab
KeyNavigation.backtab: KeyNavigation.up
text: JamiStrings.aboutJami
@ -382,8 +388,10 @@ Rectangle {
}
}
JamiPushButton { QWKSetParentHitTestVisible {}
JamiPushButton {
id: backButton
QWKSetParentHitTestVisible {
}
objectName: "welcomePageBackButton"
@ -404,6 +412,9 @@ Rectangle {
}
visible: UtilsAdapter.getAccountListSize()
Accessible.role: Accessible.Button
Accessible.name: JamiStrings.backButton
Accessible.description: JamiStrings.backButtonExplanation
KeyNavigation.tab: newAccountButton
KeyNavigation.up: showAdvanced ? newSIPAccountButton : showAdvancedButton
@ -415,12 +426,19 @@ Rectangle {
SettingsComboBox {
id: langComboBoxSetting
// This component is not yet accessible via keyboard navigation because our comboboxes
// are currently broken from an accessibility standpoint. The fix would be to
// refactor to use the base Qt ComboBox.
height: JamiTheme.preferredFieldHeight + 20
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: JamiTheme.wizardViewPageBackButtonMargins
Accessible.role: Accessible.ComboBox
Accessible.name: JamiStrings.language
Accessible.description: JamiStrings.languageComboBoxExplanation
labelText: JamiStrings.language
labelText: JamiStrings.userInterfaceLanguage
tipText: JamiStrings.language
comboModel: ListModel {
id: langModel

View File

@ -48,7 +48,7 @@ set(CMAKE_MODULE_PATH
${CMAKE_MODULE_PATH} "${EXTRAS_DIR}/build/cmake/modules")
find_package(LibJami REQUIRED)
if(LIBJAMI_FOUND)
include_directories(${LIBJAMI_INCLUDE_DIRS})
include_directories(${LIBJAMI_INCLUDE_DIR})
endif()
string(SUBSTRING ${CMAKE_GENERATOR} 0 14 CMAKE_GENERATOR_SHORT)

View File

@ -2391,7 +2391,7 @@ ConversationModelPimpl::slotMessageReceived(const QString& accountId,
}
Q_EMIT linked.newInteraction(conversationId, msgId, msg);
Q_EMIT linked.modelChanged();
if (msg.transferStatus == interaction::TransferStatus::TRANSFER_AWAITING_HOST && updateUnread) {
if (msg.transferStatus == interaction::TransferStatus::TRANSFER_AWAITING_HOST) {
handleIncomingFile(conversationId,
msgId,
QString(message.body.value("totalSize")).toInt());

View File

@ -39,7 +39,7 @@ else()
endif()
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${LIBJAMI_INCLUDE_DIRS})
include_directories(${LIBJAMI_INCLUDE_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../dbus)

View File

@ -1,4 +1,5 @@
#include "version_info.h"
const QString APP_VERSION_STRING = "@APP_VERSION_STRING@";
const QString CORE_VERSION_STRING = "@CORE_VERSION_STRING@";
const QString CORE_VERSION_STRING = "@CORE_VERSION_STRING@";
const QString BUILD_VERSION_STRING = "@BUILD_VERSION_STRING@";

View File

@ -4,3 +4,4 @@
extern const QString APP_VERSION_STRING;
extern const QString CORE_VERSION_STRING;
extern const QString BUILD_VERSION_STRING;

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16)
enable_testing(true)
enable_testing()
set(QT_TESTING_MODULES
${QT_MODULES}
@ -85,7 +85,9 @@ else()
set(PTHREAD_LIB pthread)
endif()
string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE)
if(CMAKE_BUILD_TYPE)
string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE)
endif()
set(QML_TESTS_SOURCE_FILES
${CMAKE_SOURCE_DIR}/tests/qml/main.cpp

View File

@ -35,7 +35,7 @@
#include <QtQuickTest/quicktest.h>
#include <QSignalSpy>
#ifdef WITH_WEBENGINE
#if WITH_WEBENGINE
#include <QtWebEngineCore>
#include <QtWebEngineQuick>
#endif
@ -206,7 +206,7 @@ main(int argc, char** argv)
// Allow the user to enable fatal warnings for certain tests.
Utils::remove_argument(argv, argc, "--failonwarn", [&]() { qputenv("QT_FATAL_WARNINGS", "1"); });
#ifdef WITH_WEBENGINE
#if WITH_WEBENGINE
QtWebEngineQuick::initialize();
#endif
QTEST_SET_MAIN_SOURCE_PATH

View File

@ -28,34 +28,36 @@ import "../../../src/app/mainview"
import "../../../src/app/mainview/components"
import "../../../src/app/commoncomponents"
ListSelectionView {
id: viewNode
objectName: "ConversationView"
managed: false
TestWrapper {
ListSelectionView {
id: viewNode
objectName: "ConversationView"
managed: false
leftPaneItem: Rectangle {}
leftPaneItem: Rectangle {}
rightPaneItem: ChatView {
id: uut
rightPaneItem: ChatView {
id: uut
inCallView: false
inCallView: false
TestCase {
name: "Check basic visibility for header buttons"
function test_checkBasicVisibility() {
var chatviewHeader = findChild(uut, "chatViewHeader")
var detailsButton = findChild(chatviewHeader, "detailsButton")
compare(detailsButton.visible, true)
TestCase {
name: "Check basic visibility for header buttons"
function test_checkBasicVisibility() {
var chatviewHeader = findChild(uut, "chatViewHeader")
var detailsButton = findChild(chatviewHeader, "detailsButton")
compare(detailsButton.visible, true)
var chatViewFooter = findChild(uut, "chatViewFooter")
CurrentConversation.isTemporary = true
compare(chatViewFooter.visible, true)
CurrentConversation.isTemporary = false
CurrentConversation.isRequest = true
compare(chatViewFooter.visible, false)
CurrentConversation.isRequest = false
CurrentConversation.needsSyncing = true
compare(chatViewFooter.visible, false)
var chatViewFooter = findChild(uut, "chatViewFooter")
CurrentConversation.isTemporary = true
compare(chatViewFooter.visible, true)
CurrentConversation.isTemporary = false
CurrentConversation.isRequest = true
compare(chatViewFooter.visible, false)
CurrentConversation.isRequest = false
CurrentConversation.needsSyncing = true
compare(chatViewFooter.visible, false)
}
}
}
}

View File

@ -26,40 +26,42 @@ import net.jami.Enums 1.1
import "../../../src/app/settingsview"
import "../../../src/app/commoncomponents"
SettingsSidePanel {
id: uut
TestWrapper {
SettingsSidePanel {
id: uut
SignalSpy {
id: spyUpdated
SignalSpy {
id: spyUpdated
target: uut
signalName: "updated"
}
SignalSpy {
id: spyChangeLang
target: UtilsAdapter
signalName: "changeLanguage"
}
TestCase {
name: "WelcomePage to different account creation page and return back"
when: windowShown
function test_retranslate() {
spyUpdated.clear()
UtilsAdapter.setAppValue(Settings.Key.LANG, "en_EN")
spyChangeLang.wait(1000)
compare(spyChangeLang.count, 1)
spyUpdated.wait(1000)
compare(spyUpdated.count, 1)
UtilsAdapter.setAppValue(Settings.Key.LANG, "fr")
spyChangeLang.wait(1000)
compare(spyChangeLang.count, 2)
spyUpdated.wait(1000)
compare(spyUpdated.count, 2)
target: uut
signalName: "updated"
}
}
SignalSpy {
id: spyChangeLang
target: UtilsAdapter
signalName: "changeLanguage"
}
TestCase {
name: "WelcomePage to different account creation page and return back"
when: windowShown
function test_retranslate() {
spyUpdated.clear()
UtilsAdapter.setAppValue(Settings.Key.LANG, "en_EN")
spyChangeLang.wait(1000)
compare(spyChangeLang.count, 1)
spyUpdated.wait(1000)
compare(spyUpdated.count, 1)
UtilsAdapter.setAppValue(Settings.Key.LANG, "fr")
spyChangeLang.wait(1000)
compare(spyChangeLang.count, 2)
spyUpdated.wait(1000)
compare(spyUpdated.count, 2)
}
}
}
}

View File

@ -26,192 +26,194 @@ import net.jami.Enums 1.1
import "../../../src/app/wizardview"
import "../../../src/app/commoncomponents"
WizardView {
id: uut
TestWrapper {
WizardView {
id: uut
width: 400
height: 600
width: 400
height: 600
function clearSignalSpy() {
spyAccountIsReady.clear()
spyAccountIsRemoved.clear()
spyAccountConfigFinalized.clear()
spyReportFailure.clear()
spyCloseWizardView.clear()
function clearSignalSpy() {
spyAccountIsReady.clear()
spyAccountIsRemoved.clear()
spyAccountConfigFinalized.clear()
spyReportFailure.clear()
spyCloseWizardView.clear()
spyBackButtonVisible.target = undefined
}
SignalSpy {
id: spyAccountIsReady
target: WizardViewStepModel
signalName: "accountIsReady"
}
SignalSpy {
id: spyAccountIsRemoved
target: AccountAdapter
signalName: "accountRemoved"
}
SignalSpy {
id: spyAccountStatusChanged
target: AccountAdapter
signalName: "accountStatusChanged"
}
SignalSpy {
id: spyAccountConfigFinalized
target: AccountAdapter
signalName: "accountConfigFinalized"
}
SignalSpy {
id: spyReportFailure
target: AccountAdapter
signalName: "reportFailure"
}
SignalSpy {
id: spyCloseWizardView
target: WizardViewStepModel
signalName: "closeWizardView"
}
SignalSpy {
id: spyBackButtonVisible
signalName: "visibleChanged"
}
TestCase {
name: "WelcomePage to different account creation page and return back"
when: windowShown
function test_welcomePageStepInStepOut() {
var controlPanelStackView = findChild(uut, "controlPanelStackView")
var welcomePage = findChild(uut, "welcomePage")
var createAccountPage = findChild(uut, "createAccountPage")
var importFromDevicePage = findChild(uut, "importFromDevicePage")
var importFromBackupPage = findChild(uut, "importFromBackupPage")
var connectToAccountManagerPage = findChild(uut, "connectToAccountManagerPage")
var createSIPAccountPage = findChild(uut, "createSIPAccountPage")
// Go to createAccount page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.CreateJamiAccount)
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
createAccountPage)
WizardViewStepModel.previousStep()
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
welcomePage)
// Go to CreateRendezVous page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.CreateRendezVous)
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
createAccountPage)
WizardViewStepModel.previousStep()
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
welcomePage)
// Go to CreateRendezVous page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.ImportFromDevice)
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
importFromDevicePage)
WizardViewStepModel.previousStep()
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
welcomePage)
// Go to ImportFromBackup page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.ImportFromBackup)
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
importFromBackupPage)
WizardViewStepModel.previousStep()
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
welcomePage)
// Go to ConnectToAccountManager page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.ConnectToAccountManager)
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
connectToAccountManagerPage)
WizardViewStepModel.previousStep()
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
welcomePage)
// Go to CreateSipAccount page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.CreateSipAccount)
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
createSIPAccountPage)
WizardViewStepModel.previousStep()
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
welcomePage)
spyBackButtonVisible.target = undefined
}
}
TestCase {
name: "Create Sip account ui flow"
when: windowShown
SignalSpy {
id: spyAccountIsReady
function test_createSipAccountUiFlow() {
uut.clearSignalSpy()
target: WizardViewStepModel
signalName: "accountIsReady"
}
var controlPanelStackView = findChild(uut, "controlPanelStackView")
SignalSpy {
id: spyAccountIsRemoved
var welcomePage = findChild(uut, "welcomePage")
var createSIPAccountPage = findChild(uut, "createSIPAccountPage")
target: AccountAdapter
signalName: "accountRemoved"
}
var sipUsernameEdit = findChild(createSIPAccountPage, "sipUsernameEdit")
var sipPasswordEdit = findChild(createSIPAccountPage, "sipPasswordEdit")
var sipServernameEdit = findChild(createSIPAccountPage, "sipServernameEdit")
var createAccountButton = findChild(createSIPAccountPage, "createSIPAccountButton")
SignalSpy {
id: spyAccountStatusChanged
// Go to createSipAccount page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.CreateSipAccount)
target: AccountAdapter
signalName: "accountStatusChanged"
}
// Set up paras
var userName = "testUserName"
var serverName = "testServerName"
var password = "testPassword"
var proxy = "testProxy"
SignalSpy {
id: spyAccountConfigFinalized
sipUsernameEdit.dynamicText = userName
sipPasswordEdit.dynamicText = password
sipServernameEdit.dynamicText = serverName
target: AccountAdapter
signalName: "accountConfigFinalized"
}
createAccountButton.clicked()
SignalSpy {
id: spyReportFailure
// Wait until the account creation is finished
spyAccountIsReady.wait()
compare(spyAccountIsReady.count, 1)
target: AccountAdapter
signalName: "reportFailure"
}
// Check if paras match with setup
compare(CurrentAccount.username, userName)
compare(CurrentAccount.hostname, serverName)
compare(CurrentAccount.password, password)
SignalSpy {
id: spyCloseWizardView
WizardViewStepModel.nextStep()
target: WizardViewStepModel
signalName: "closeWizardView"
}
spyCloseWizardView.wait()
compare(spyCloseWizardView.count, 1)
SignalSpy {
id: spyBackButtonVisible
AccountAdapter.deleteCurrentAccount()
signalName: "visibleChanged"
}
// Wait until the account removal is finished
spyAccountIsRemoved.wait()
compare(spyAccountIsRemoved.count, 1)
TestCase {
name: "WelcomePage to different account creation page and return back"
when: windowShown
function test_welcomePageStepInStepOut() {
var controlPanelStackView = findChild(uut, "controlPanelStackView")
var welcomePage = findChild(uut, "welcomePage")
var createAccountPage = findChild(uut, "createAccountPage")
var importFromDevicePage = findChild(uut, "importFromDevicePage")
var importFromBackupPage = findChild(uut, "importFromBackupPage")
var connectToAccountManagerPage = findChild(uut, "connectToAccountManagerPage")
var createSIPAccountPage = findChild(uut, "createSIPAccountPage")
// Go to createAccount page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.CreateJamiAccount)
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
createAccountPage)
WizardViewStepModel.previousStep()
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
welcomePage)
// Go to CreateRendezVous page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.CreateRendezVous)
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
createAccountPage)
WizardViewStepModel.previousStep()
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
welcomePage)
// Go to CreateRendezVous page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.ImportFromDevice)
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
importFromDevicePage)
WizardViewStepModel.previousStep()
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
welcomePage)
// Go to ImportFromBackup page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.ImportFromBackup)
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
importFromBackupPage)
WizardViewStepModel.previousStep()
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
welcomePage)
// Go to ConnectToAccountManager page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.ConnectToAccountManager)
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
connectToAccountManagerPage)
WizardViewStepModel.previousStep()
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
welcomePage)
// Go to CreateSipAccount page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.CreateSipAccount)
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
createSIPAccountPage)
WizardViewStepModel.previousStep()
compare(controlPanelStackView.children[controlPanelStackView.currentIndex],
welcomePage)
}
}
TestCase {
name: "Create Sip account ui flow"
when: windowShown
function test_createSipAccountUiFlow() {
uut.clearSignalSpy()
var controlPanelStackView = findChild(uut, "controlPanelStackView")
var welcomePage = findChild(uut, "welcomePage")
var createSIPAccountPage = findChild(uut, "createSIPAccountPage")
var sipUsernameEdit = findChild(createSIPAccountPage, "sipUsernameEdit")
var sipPasswordEdit = findChild(createSIPAccountPage, "sipPasswordEdit")
var sipServernameEdit = findChild(createSIPAccountPage, "sipServernameEdit")
var createAccountButton = findChild(createSIPAccountPage, "createSIPAccountButton")
// Go to createSipAccount page
WizardViewStepModel.startAccountCreationFlow(
WizardViewStepModel.AccountCreationOption.CreateSipAccount)
// Set up paras
var userName = "testUserName"
var serverName = "testServerName"
var password = "testPassword"
var proxy = "testProxy"
sipUsernameEdit.dynamicText = userName
sipPasswordEdit.dynamicText = password
sipServernameEdit.dynamicText = serverName
createAccountButton.clicked()
// Wait until the account creation is finished
spyAccountIsReady.wait()
compare(spyAccountIsReady.count, 1)
// Check if paras match with setup
compare(CurrentAccount.username, userName)
compare(CurrentAccount.hostname, serverName)
compare(CurrentAccount.password, password)
WizardViewStepModel.nextStep()
spyCloseWizardView.wait()
compare(spyCloseWizardView.count, 1)
AccountAdapter.deleteCurrentAccount()
// Wait until the account removal is finished
spyAccountIsRemoved.wait()
compare(spyAccountIsRemoved.count, 1)
}
}
}
}

View File

@ -49,7 +49,7 @@ TEST_F(MessageParserFixture, TextIsParsedCorrectly)
// Wait for the messageParsed signal which should be emitted once.
messageParsedSpy.wait();
EXPECT_EQ(messageParsedSpy.count(), 1);
ASSERT_EQ(messageParsedSpy.count(), 1);
QList<QVariant> messageParserArguments = messageParsedSpy.takeFirst();
EXPECT_TRUE(messageParserArguments.at(0).typeId() == qMetaTypeId<QString>());
@ -60,7 +60,7 @@ TEST_F(MessageParserFixture, TextIsParsedCorrectly)
// No link info should be returned.
linkInfoReadySpy.wait();
EXPECT_EQ(linkInfoReadySpy.count(), 0);
ASSERT_EQ(linkInfoReadySpy.count(), 0);
}
/*!
@ -84,7 +84,7 @@ TEST_F(MessageParserFixture, ALinkIsParsedCorrectly)
// Wait for the messageParsed signal which should be emitted once.
messageParsedSpy.wait();
EXPECT_EQ(messageParsedSpy.count(), 1);
ASSERT_EQ(messageParsedSpy.count(), 1);
QList<QVariant> messageParserArguments = messageParsedSpy.takeFirst();
EXPECT_TRUE(messageParserArguments.at(0).typeId() == qMetaTypeId<QString>());
@ -96,7 +96,7 @@ TEST_F(MessageParserFixture, ALinkIsParsedCorrectly)
// Wait for the linkInfoReady signal which should be emitted once.
linkInfoReadySpy.wait();
EXPECT_EQ(linkInfoReadySpy.count(), 1);
ASSERT_EQ(linkInfoReadySpy.count(), 1);
QList<QVariant> linkInfoReadyArguments = linkInfoReadySpy.takeFirst();
EXPECT_TRUE(linkInfoReadyArguments.at(0).typeId() == qMetaTypeId<QString>());
@ -180,7 +180,7 @@ TEST_F(MessageParserFixture, EndOfLineCharactersAreParsedCorrectly)
// Wait for the messageParsed signal which should be emitted once.
messageParsedSpy.wait();
EXPECT_EQ(messageParsedSpy.count(), 1);
ASSERT_EQ(messageParsedSpy.count(), 1);
QList<QVariant> messageParserArguments = messageParsedSpy.takeFirst();
EXPECT_TRUE(messageParserArguments.at(0).typeId() == qMetaTypeId<QString>());
@ -210,7 +210,7 @@ TEST_F(MessageParserFixture, FencedCodeIsParsedCorrectly)
// Wait for the messageParsed signal which should be emitted once.
messageParsedSpy.wait();
EXPECT_EQ(messageParsedSpy.count(), 1);
ASSERT_EQ(messageParsedSpy.count(), 1);
QList<QVariant> messageParserArguments = messageParsedSpy.takeFirst();
EXPECT_TRUE(messageParserArguments.at(0).typeId() == qMetaTypeId<QString>());
@ -242,14 +242,14 @@ TEST_F(MessageParserFixture, YoutubeLinkIsParsedCorrectly)
// Wait for the messageParsed signal which should be emitted once.
messageParsedSpy.wait();
EXPECT_EQ(messageParsedSpy.count(), 1);
ASSERT_EQ(messageParsedSpy.count(), 1);
QList<QVariant> messageParserArguments = messageParsedSpy.takeFirst();
EXPECT_TRUE(messageParserArguments.at(0).typeId() == qMetaTypeId<QString>());
// Wait for the linkInfoReady signal which should be emitted once.
linkInfoReadySpy.wait();
EXPECT_EQ(linkInfoReadySpy.count(), 1);
ASSERT_EQ(linkInfoReadySpy.count(), 1);
QList<QVariant> linkInfoReadyArguments = linkInfoReadySpy.takeFirst();
EXPECT_TRUE(linkInfoReadyArguments.at(0).typeId() == qMetaTypeId<QString>());