Compare commits

..

1 Commits

Author SHA1 Message Date
01da668098 snapcraft: fix build
Change-Id: I8ac570f0ad859b2ea2ee83ca8fe3927848148ff3
2024-04-15 13:29:42 -04:00
286 changed files with 7133 additions and 23703 deletions

View File

@ -29,8 +29,7 @@ else()
project(jami)
endif()
set(CMAKE_SCRIPTS_DIR ${PROJECT_SOURCE_DIR}/extras/build/cmake)
include(${CMAKE_SCRIPTS_DIR}/extra_tools.cmake)
include(${PROJECT_SOURCE_DIR}/extras/build/cmake/extra_tools.cmake)
option(WITH_DAEMON_SUBMODULE "Build with daemon submodule" ON)
option(JAMICORE_AS_SUBDIR "Build Jami-core as a subdir dependency" OFF)
@ -50,13 +49,9 @@ if(ENABLE_ASAN AND NOT MSVC)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
endif()
# Enable this option when building for production.
option(ENABLE_CRASHREPORTS "Enable crash reports" OFF)
# These values are exposed to QML and are better off being defined as values.
define_macro_with_value(WITH_WEBENGINE)
define_macro_with_value(APPSTORE)
define_macro_with_value(ENABLE_CRASHREPORTS)
# jami-core
if(NOT WITH_DAEMON_SUBMODULE)
@ -76,6 +71,12 @@ set(CLIENT_INCLUDE_DIRS, "")
set(CLIENT_LINK_DIRS, "")
set(CLIENT_LIBS, "")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT MSVC)
set(CMAKE_CXX_FLAGS_DEBUG "-Og -ggdb")
endif()
include(${PROJECT_SOURCE_DIR}/extras/build/cmake/contrib_tools.cmake)
set(EXTRA_PATCHES_DIR ${PROJECT_SOURCE_DIR}/extras/patches)
@ -85,17 +86,6 @@ list(APPEND QWINDOWKIT_OPTIONS
QWINDOWKIT_BUILD_STATIC ON
)
if(WIN32)
# Beta config
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()
endif()
if(WIN32)
list(APPEND QWINDOWKIT_OPTIONS QWINDOWKIT_ENABLE_WINDOWS_SYSTEM_BORDERS OFF)
endif()
@ -119,44 +109,6 @@ add_fetch_content(
list(APPEND CLIENT_INCLUDE_DIRS ${QWindowKit_BINARY_DIR}/include)
list(APPEND CLIENT_LIBS QWindowKit::Quick)
# If ENABLE_CRASHREPORTS is enabled, we will use crashpad_cmake for now.
if(ENABLE_CRASHREPORTS)
set(ENABLE_CRASHPAD ON)
set(CRASH_REPORT_URL "http://localhost:8080/submit" CACHE STRING "URL for crash handler uploads")
endif()
add_definitions(-DCRASH_REPORT_URL="${CRASH_REPORT_URL}")
# Crash-report client: crashpad
if(ENABLE_CRASHPAD)
message(STATUS "Crashpad enabled for client")
if(WIN32)
set(CMAKE_OBJECT_PATH_MAX 256)
add_definitions(-DNOMINMAX)
endif()
add_fetch_content(
TARGET crashpad_cmake
URL https://github.com/TheAssemblyArmada/crashpad-cmake.git
BRANCH 80573adcc845071401c73c99eaec7fd9847d45fb
)
add_definitions(-DENABLE_CRASHPAD)
if (WIN32)
# This makes sure the console window doesn't show up when running the
# crashpad_handler executable.
set_target_properties(crashpad_handler PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS")
# Set the output directory for the crashpad_handler executable. On Windows,
# we use either the Release or Beta directory depending on the BETA option
# which is set above.
set_target_properties(crashpad_handler PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${JAMI_OUTPUT_DIRECTORY_RELEASE}")
endif()
endif()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT MSVC)
set(CMAKE_CXX_FLAGS_DEBUG "-Og -ggdb")
endif()
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
@ -165,7 +117,6 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
# src
set(LIBCLIENT_SRC_DIR ${PROJECT_SOURCE_DIR}/src/libclient)
set(APP_SRC_DIR ${PROJECT_SOURCE_DIR}/src/app)
set(VERSION_INFO_DIR ${PROJECT_SOURCE_DIR}/src/version_info)
# doc
set(DOC_DIR ${PROJECT_SOURCE_DIR}/doc)
# extras
@ -259,28 +210,11 @@ include(FindPython3)
find_package(Python3 3.6 REQUIRED COMPONENTS Interpreter)
set(PYTHON_EXEC ${Python3_EXECUTABLE})
# Versioning and build ID generation
set(VERSION_FILE ${CMAKE_CURRENT_BINARY_DIR}/version_info.cpp)
# Touch the file to make sure it exists at configure time as
# we add it to the target_sources below.
file(TOUCH ${VERSION_FILE})
add_custom_target(
generate_version_info ALL
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND}
-DAPP_SOURCE_DIR=${CMAKE_SOURCE_DIR}
-DAPP_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}
-DCORE_SOURCE_DIR=${DAEMON_DIR}
-DCPP_INT_FILE=${VERSION_INFO_DIR}/version_info.cpp.in
-P ${CMAKE_SCRIPTS_DIR}/generate_version_info.cmake
)
list(APPEND CLIENT_INCLUDE_DIRS ${VERSION_INFO_DIR})
# Resource auto-gen
# QML and related code files
# Check files in the app's src directory and force a reconfigure if it
# changes.
# Only include WebEngine resources if specified.
# Only include webengine resources if specified.
if(WITH_WEBENGINE)
set(GEN_QRC_ARGS "--with-webengine")
endif()
@ -313,7 +247,6 @@ set(QML_IMPORT_PATH ${QML_DIRS}
add_definitions(-DQT_NO_KEYWORDS)
set(COMMON_SOURCES
${VERSION_FILE}
${APP_SRC_DIR}/bannedlistmodel.cpp
${APP_SRC_DIR}/accountlistmodel.cpp
${APP_SRC_DIR}/networkmanager.cpp
@ -370,8 +303,7 @@ set(COMMON_SOURCES
${APP_SRC_DIR}/imagedownloader.cpp
${APP_SRC_DIR}/pluginversionmanager.cpp
${APP_SRC_DIR}/connectioninfolistmodel.cpp
${APP_SRC_DIR}/pluginversionmanager.cpp
)
${APP_SRC_DIR}/pluginversionmanager.cpp)
set(COMMON_HEADERS
${APP_SRC_DIR}/global.h
@ -440,10 +372,7 @@ set(COMMON_HEADERS
${APP_SRC_DIR}/imagedownloader.h
${APP_SRC_DIR}/pluginversionmanager.h
${APP_SRC_DIR}/connectioninfolistmodel.h
${APP_SRC_DIR}/pttlistener.h
${APP_SRC_DIR}/crashreportclient.h
${APP_SRC_DIR}/crashreporter.h
)
${APP_SRC_DIR}/pttlistener.h)
# For libavutil/avframe.
set(LIBJAMI_CONTRIB_DIR "${DAEMON_DIR}/contrib")
@ -462,15 +391,6 @@ endif()
# Define PREFER_VULKAN to prefer Vulkan over the default API
# on GNU/Linux and Windows. Metal is always preferred on macOS.
if(ENABLE_CRASHREPORTS)
set(CRASHREPORT_CLIENT_DIR ${APP_SRC_DIR}/crashreportclients)
if(ENABLE_CRASHPAD)
list(APPEND CLIENT_LIBS crashpad_client)
list(APPEND COMMON_SOURCES ${CRASHREPORT_CLIENT_DIR}/crashpad.cpp)
list(APPEND COMMON_HEADERS ${CRASHREPORT_CLIENT_DIR}/crashpad.h)
endif()
endif()
if(MSVC)
set(WINDOWS_SYS_LIBS
windowsapp.lib
@ -516,6 +436,16 @@ if(MSVC)
set(JAMID_SRC_PATH ${DAEMON_DIR}/contrib/msvc/include)
set(GNUTLS_LIB ${DAEMON_DIR}/contrib/msvc/lib/x64/libgnutls.lib)
# Beta config
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()
include_directories(
${JAMID_SRC_PATH}
${LIBCLIENT_SRC_DIR}
@ -696,9 +626,6 @@ qt_add_executable(
${QML_RESOURCES_QML}
${SFPM_OBJECTS})
# Make sure we can find the generated version file
add_dependencies(${PROJECT_NAME} generate_version_info)
foreach(MODULE ${QT_MODULES})
list(APPEND QT_LIBS "Qt::${MODULE}")
endforeach()
@ -765,13 +692,13 @@ elseif (NOT APPLE)
# Install .desktop in XDG desktop direcory so that it is recognized
# by the system.
install(
FILES ${DATA_DIR}/net.jami.Jami.desktop
FILES ${DATA_DIR}/jami.desktop
DESTINATION ${JAMI_DATA_PREFIX}/applications)
# Install .desktop in the jami data directory, so that it can be
# copied to the autostart directory by the client.
install(
FILES ${DATA_DIR}/net.jami.Jami.desktop
FILES ${DATA_DIR}/jami.desktop
DESTINATION "${JAMI_DATA_PREFIX}/${PROJECT_NAME}"
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) # 644
@ -806,19 +733,9 @@ elseif (NOT APPLE)
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps
RENAME jami.xpm)
# AppStream metadata
#
# MetaInfo file:
install(
FILES ${DATA_DIR}/net.jami.Jami.metainfo.xml
FILES ${DATA_DIR}/jami.appdata.xml
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/metainfo)
# Catalog file:
if(NOT DEFINED APPSTREAM_CATALOG_DIR)
set(APPSTREAM_CATALOG_DIR /share/swcatalog/xml)
endif()
install(
FILES ${DATA_DIR}/jami.xml
DESTINATION ${CMAKE_INSTALL_PREFIX}${APPSTREAM_CATALOG_DIR})
# man page
install(
@ -902,20 +819,12 @@ else()
"-framework Security"
compression
resolv
)
set(APP_CONTAINER "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app/Contents")
# ringtones. Copy the entire directory to the app bundle.
# daemon/ringtones -> Jami.app/Contents/Resources/ringtones
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_directory
${DAEMON_DIR}/ringtones
${APP_CONTAINER}/Resources/ringtones
)
)
# translations
if(Qt${QT_VERSION_MAJOR}LinguistTools_FOUND)
set(APP_CONTAINER
"${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app/Contents")
file(GLOB TS_FILES ${PROJECT_SOURCE_DIR}/translations/*.ts)
# Generate lproj folders.
@ -943,26 +852,26 @@ else()
MACOSX_BUNDLE_SHORT_VERSION_STRING "${JAMI_VERSION}"
MACOSX_BUNDLE_BUNDLE_VERSION "${JAMI_BUILD}"
MACOSX_BUNDLE_COPYRIGHT "${PROJ_COPYRIGHT}")
if(APPSTORE)
message(STATUS "app store version")
set_target_properties(${PROJECT_NAME} PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/resources/entitlements/appstore/Jami.entitlements")
else()
set_target_properties(${PROJECT_NAME} PROPERTIES
SPARKLE_URL "${SPARKLE_URL}"
SPARKLE_PUBLIC_KEY "${SPARKLE_PUBLIC_KEY}"
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/resources/entitlements/Jami.entitlements"
XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME TRUE)
endif()
if(DEPLOY)
execute_process(COMMAND
"${CMAKE_PREFIX_PATH}/bin/macdeployqt"
"${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app"
-qmldir=${QML_SRC_DIR})
if(${ENABLE_SPARKLE} MATCHES true)
file(COPY ${SPARKLE_FRAMEWORK} DESTINATION ${EXE_NAME}/Contents/Frameworks/)
endif()
endif()
if(APPSTORE)
message(STATUS "app store version")
set_target_properties(${PROJECT_NAME} PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/resources/entitlements/appstore/Jami.entitlements")
else()
set_target_properties(${PROJECT_NAME} PROPERTIES
SPARKLE_URL "${SPARKLE_URL}"
SPARKLE_PUBLIC_KEY "${SPARKLE_PUBLIC_KEY}"
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/resources/entitlements/Jami.entitlements"
XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME TRUE)
endif()
if(DEPLOY)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -DQML_SRC_DIR=${SRC_DIR}
-DMAC_DEPLOY_QT_PATH=${CMAKE_PREFIX_PATH}/bin
-DEXE_NAME="${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app"
-DSPARKLE_PATH=${SPARKLE_FRAMEWORK}
-DENABLE_SPARKLE=${ENABLE_SPARKLE}
-P ${EXTRAS_DIR}/build/cmake/macos_qt_deploy.cmake)
endif()
endif()
target_include_directories(${PROJECT_NAME} PRIVATE ${CLIENT_INCLUDE_DIRS})

View File

@ -14,7 +14,7 @@ So, you will need to get Qt 6.6 first. For this, there is 3 methods:
### Qt from our repo (recommended)
If your distribution is supported, we provide a Qt package (libqt-jami) on our repo.
The files will be installed to `/usr/lib/libqt-jami` on Debian-like distributions. For RPM based distributions the files will be installed to `/usr/lib64/qt-jami`.
The files will be installed in `/usr/lib/libqt-jami`.
#### Install libqt-jami, Ubuntu based
@ -288,7 +288,7 @@ Once the build has finished, you should then be able to use the Visual Studio So
**Set up**
- macOS minimum version 11.0
- macOS minimum version 10.15
- install python3
- download xcode
- install Qt 6.6
@ -343,24 +343,9 @@ Built client could be find in `build/Jami`
- These environment variables will be temporarily set when using build-windows.py to run tests.
## Troubleshooting
Build errors may occur if daemon dependencies have been updated since the last build, as the script may not fully detect or rebuild them in the correct order. For the same reason, you may also occasionally encounter linker errors.
To resolve this, clean and restart the installation to ensure a fresh rebuild of all components (delete all temporary files generated by the build process):
```bash
./build.py --clean
./build.py --install
```
## Debugging
Compile the client with `-DCMAKE_BUILD_TYPE=Debug`. Then, if you want to enable logging when running `jami`, launch it with `-d` or `--debug`.
Compile the client with with `-DCMAKE_BUILD_TYPE=Debug`.
To diagnose a crash, use GDB:
```sh
./build.py --install --debug
gdb -ex run --args ./jami --debug
```
Then, if you want to enable logging when running `jami`, launch it
with `-d` or `--debug`.

View File

@ -1,4 +1,3 @@
/obj
/bin
AppComponents.wxs
CrtComponents.wxs
Components.wxs

View File

@ -3,11 +3,13 @@
<?define Name="Jami" ?>
<?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 ?>
<?define Manufacturer="Savoir-Faire Linux"?>
<?define UcrtDir="C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x64"?>
</Include>

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-16"?>
<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DeployedProjects />
<DirectoryMappings />
<FileMappings />

View File

@ -1,8 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0"
DefaultTargets="Build"
InitialTargets="EnsureWixToolsetInstalled"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureWixToolsetInstalled" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x64</Platform>
@ -19,7 +16,7 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>AppHarvestPath=..\x64\Release;CrtHarvestPath=$(VC_CRT_Dir)</DefineConstants>
<DefineConstants>HarvestPath=..\x64\Release</DefineConstants>
<SuppressPdbOutput>True</SuppressPdbOutput>
<CompilerAdditionalOptions>
</CompilerAdditionalOptions>
@ -29,7 +26,7 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Beta|x64' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>AppHarvestPath=..\x64\Beta;CrtHarvestPath=$(VC_CRT_Dir)</DefineConstants>
<DefineConstants>HarvestPath=..\x64\Beta</DefineConstants>
<SuppressPdbOutput>True</SuppressPdbOutput>
<CompilerAdditionalOptions>
</CompilerAdditionalOptions>
@ -38,8 +35,8 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="Product.wxs" />
<Compile Include="AppComponents.wxs" />
<Compile Include="CrtComponents.wxs" />
<Compile Include="StandardComponents.wxs" />
<Compile Include="Components.wxs" />
</ItemGroup>
<ItemGroup>
<Content Include="Config.wxi" />
@ -61,36 +58,13 @@
<ItemGroup>
<EmbeddedResource Include="Localization.wxl" />
</ItemGroup>
<Import Project="$(WixTargetsPath)"
Condition=" '$(WixTargetsPath)' != '' " />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets"
Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
<Target Name="EnsureWixToolsetInstalled"
Condition=" '$(WixTargetsImported)' != 'true' ">
<Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
<Target Name="EnsureWixToolsetInstalled" Condition=" '$(WixTargetsImported)' != 'true' ">
<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)"
PreprocessorVariable="var.AppHarvestPath"
OutputFile="AppComponents.wxs"
ComponentGroupName="AppHeatGenerated"
DirectoryRefId="APPLICATIONFOLDER"
AutogenerateGuids="true"
ToolPath="$(WixToolPath)"
SuppressFragments="true"
SuppressRegistry="true"
SuppressRootDirectory="true"
Transforms="HarvestFilter.xslt" />
<HeatDirectory Directory="$(VC_CRT_Dir)"
PreprocessorVariable="var.CrtHarvestPath"
OutputFile="CrtComponents.wxs"
ComponentGroupName="CrtHeatGenerated"
DirectoryRefId="APPLICATIONFOLDER"
AutogenerateGuids="true"
ToolPath="$(WixToolPath)"
SuppressFragments="true"
SuppressRegistry="true"
SuppressRootDirectory="true" />
<HeatDirectory Directory="..\x64\$(Configuration)" PreprocessorVariable="var.HarvestPath" OutputFile="Components.wxs" ComponentGroupName="HeatGenerated" DirectoryRefId="APPLICATIONFOLDER" AutogenerateGuids="true" ToolPath="$(WixToolPath)" SuppressFragments="true" SuppressRegistry="true" SuppressRootDirectory="true" Transforms="HarvestFilter.xslt" />
</Target>
<Target Name="AfterBuild">
</Target>

View File

@ -2,14 +2,6 @@
<WixLocalization Culture="en-us"
xmlns="http://schemas.microsoft.com/wix/2006/localization">
<String Id="AdvancedWelcomeEulaDlgDescriptionPerMachine">By installing this software you agree to the terms in the license agreement</String>
<UI Dialog="ExitDialog"
Control="OptionalCheckBox"
Width="10"
Height="10"
X="135"
Y="110" />
<UI Dialog="ExitDialog"
Control="OptionalText"
X="150"
Y="110" />
<UI Dialog="ExitDialog" Control="OptionalCheckBox" Width="10" Height="10" X="135" Y="110" />
<UI Dialog="ExitDialog" Control="OptionalText" X="150" Y="110" />
</WixLocalization>

View File

@ -1,220 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<?include Config.wxi?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*"
Name="$(var.Name)"
Language="1033"
Version="$(fun.AutoVersion(1.0))"
Manufacturer="$(var.Manufacturer)"
UpgradeCode="7c45b52b-0390-4fe8-947a-3f13e82dd346">
<Package InstallerVersion="301"
Compressed="yes"
InstallScope="perMachine" />
<Product Id="*" Name="$(var.Name)" Language="1033" Version="$(fun.AutoVersion(1.0))" Manufacturer="$(var.Manufacturer)" UpgradeCode="7c45b52b-0390-4fe8-947a-3f13e82dd346">
<Package InstallerVersion="301" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade Schedule="afterInstallInitialize"
AllowDowngrades="yes"/>
<MediaTemplate EmbedCab="yes"
CompressionLevel="high"
MaximumUncompressedMediaSize="4" />
<MajorUpgrade Schedule="afterInstallInitialize" AllowDowngrades="yes"/>
<MediaTemplate EmbedCab="yes" CompressionLevel="high" MaximumUncompressedMediaSize="4" />
<!--Disables interaction of the package with the Restart Manager.-->
<Property Id="MSIRESTARTMANAGERCONTROL"
Value="Disable" />
<Property Id="MSIRESTARTMANAGERCONTROL" Value="Disable" />
<!--Icon File should be in release folder(not wix project), otherwise cannot be read-->
<Icon Id="icon.ico"
SourceFile="$(var.ReleaseDir)\jami.ico" />
<Property Id="ARPPRODUCTICON"
Value="icon.ico" />
<Property Id="ARPNOMODIFY"
Value="1" />
<Icon Id="icon.ico" SourceFile="$(var.ReleaseDir)\jami.ico" />
<Property Id="ARPPRODUCTICON" Value="icon.ico" />
<Property Id="ARPNOMODIFY" Value="1" />
<!-- It seems that QtWebEngineProcess.exe versioning requires us to force reinstall. -->
<Property Id="REINSTALLMODE"
Value="dms" />
<Property Id="REINSTALLMODE" Value="dms" />
<Feature Id="ProductFeature"
Title="Main"
Level="1"
Absent="disallow">
<ComponentGroupRef Id="MainExecutable"
Primary="yes" />
<ComponentGroupRef Id="AppHeatGenerated" />
<ComponentGroupRef Id="CrtHeatGenerated" />
<Feature Id="ProductFeature" Title="Main" Level="1" Absent="disallow">
<ComponentGroupRef Id="StandardComponents" Primary="yes" />
<ComponentGroupRef Id="HeatGenerated" />
<ComponentRef Id="ApplicationShortcutDesktop" />
<ComponentRef Id="ApplicationShortcutStartMenu" />
<ComponentRef Id="RegistryEntries" />
<ComponentRef Id="URLProtocolRegistryEntries" />
</Feature>
<SetProperty After="FindRelatedProducts"
Id="FirstInstall"
Value="true">
<!--Visual C++ Redist merge module-->
<DirectoryRef Id="TARGETDIR">
<Merge Id="VCRedist" SourceFile="$(env.VCRedistMergeModule)" DiskId="1" Language="0" />
</DirectoryRef>
<Feature Id="VCRedist" Title="Visual C++ Runtime" AllowAdvertise="no" Display="hidden" Level="1">
<MergeRef Id="VCRedist"/>
</Feature>
<SetProperty After="FindRelatedProducts" Id="FirstInstall" Value="true">
NOT Installed AND NOT WIX_UPGRADE_DETECTED AND NOT WIX_DOWNGRADE_DETECTED
</SetProperty>
<SetProperty After="SetFirstInstall"
Id="Upgrading"
Value="true">
<SetProperty After="SetFirstInstall" Id="Upgrading" Value="true">
WIX_UPGRADE_DETECTED AND NOT (REMOVE="ALL")
</SetProperty>
<SetProperty After="RemoveExistingProducts"
Id="RemovingForUpgrade"
Sequence="execute"
Value="true"> (REMOVE="ALL") AND UPGRADINGPRODUCTCODE
<SetProperty After="RemoveExistingProducts" Id="RemovingForUpgrade" Sequence="execute" Value="true">
(REMOVE="ALL") AND UPGRADINGPRODUCTCODE
</SetProperty>
<SetProperty After="SetUpgrading"
Id="Uninstalling"
Value="true">
<SetProperty After="SetUpgrading" Id="Uninstalling" Value="true">
Installed AND (REMOVE="ALL") AND NOT (WIX_UPGRADE_DETECTED OR UPGRADINGPRODUCTCODE)
</SetProperty>
<SetProperty After="SetUninstalling"
Id="Maintenance"
Value="true">
<SetProperty After="SetUninstalling" Id="Maintenance" Value="true">
Installed AND NOT Upgrading AND NOT Uninstalling AND NOT UPGRADINGPRODUCTCODE
</SetProperty>
<!--SetDirectory of APPLICATIONFOLDER -->
<SetDirectory Id="APPLICATIONFOLDER"
Value="[ProgramFiles64Folder][ApplicationFolderName]">APPLICATIONFOLDER=""</SetDirectory>
<SetProperty Id="ARPINSTALLLOCATION"
Value="[APPLICATIONFOLDER]"
After="CostFinalize" />
<SetDirectory Id="APPLICATIONFOLDER" Value="[ProgramFiles64Folder][ApplicationFolderName]">APPLICATIONFOLDER=""</SetDirectory>
<SetProperty Id="ARPINSTALLLOCATION" Value="[APPLICATIONFOLDER]" After="CostFinalize" />
<UIRef Id="CustomUI" />
<WixVariable Id="WixUIInfoIcon"
Value="icon.ico"/>
<WixVariable Id="WixUIBannerBmp"
Value="top-banner.bmp" />
<WixVariable Id="WixUIDialogBmp"
Value="main-banner.bmp" />
<WixVariable Id="WixUISupportPerUser"
Value="0" />
<WixVariable Id="WixUIInfoIcon" Value="icon.ico"/>
<WixVariable Id="WixUIBannerBmp" Value="top-banner.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="main-banner.bmp" />
<WixVariable Id="WixUISupportPerUser" Value="0" />
<CustomAction Id="RemoveOldJamiFiles"
Directory="APPLICATIONFOLDER"
ExeCommand="cmd /c &quot;del vc_redist.x64.exe; del uninstall.exe; del WinSparkle.dll;&quot;"
Execute="deferred"
Return="ignore"
HideTarget="no"
Impersonate="no"/>
Directory="APPLICATIONFOLDER"
ExeCommand="cmd /c &quot;del vc_redist.x64.exe; del uninstall.exe; del WinSparkle.dll;&quot;"
Execute="deferred"
Return="ignore"
HideTarget="no"
Impersonate="no"/>
<Property Id="QtExecCmdLine"
Value='"[APPLICATIONFOLDER]/$(var.ExeName).exe" --term'/>
Value='"[APPLICATIONFOLDER]/$(var.ExeName).exe" --term'/>
<CustomAction Id="TerminateAppProcess"
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Execute="immediate"
Return="ignore"/>
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Execute="immediate"
Return="ignore"/>
</Product>
<Fragment Id="DirectoryStructure">
<Directory Id="TARGETDIR"
Name="SourceDir">
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFiles64Folder">
<Directory Id="APPLICATIONFOLDER"
Name="$(var.Name)" />
<Directory Id="APPLICATIONFOLDER" Name="$(var.Name)" />
</Directory>
<Directory Id="DesktopFolder"
Name="Desktop" />
<Directory Id="DesktopFolder" Name="Desktop" />
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" />
</Directory>
<Directory Id="WindowsFolder"
Name="WINDOWS"/>
<Directory Id="WindowsFolder" Name="WINDOWS"/>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="MainExecutable"
Directory="APPLICATIONFOLDER">
<Component Id="cmp9CFEE34E3A162AB05264E8B756EC1DEC"
Guid="*">
<File Id="fileMain.exe"
KeyPath="yes"
Source="$(var.ReleaseDir)\$(var.ExeName).exe" />
</Component>
</ComponentGroup>
</Fragment>
<Fragment Id="Shortcuts">
<DirectoryRef Id="DesktopFolder">
<Component Id="ApplicationShortcutDesktop"
Guid="*"
Win64="yes">
<Shortcut Id="ApplicationShortcutDesktop"
Name="$(var.Name)"
Description="Launch $(var.Name)"
Target="[#fileMain.exe]"
WorkingDirectory="INSTALLFOLDER" />
<RemoveFolder Id="DesktopFolder"
On="uninstall" />
<RegistryValue Root="HKCU"
Key="Software\jami.net\$(var.Name)"
Name="desktop"
Type="integer"
Value="1"
KeyPath="yes" />
<Component Id="ApplicationShortcutDesktop" Guid="*" Win64="yes">
<Shortcut Id="ApplicationShortcutDesktop" Name="$(var.Name)" Description="Launch $(var.Name)" Target="[#fileMain.exe]" WorkingDirectory="INSTALLFOLDER" />
<RemoveFolder Id="DesktopFolder" On="uninstall" />
<RegistryValue Root="HKCU" Key="Software\jami.net\$(var.Name)" Name="desktop" Type="integer" Value="1" KeyPath="yes" />
<Condition>FirstInstall</Condition>
</Component>
</DirectoryRef>
<DirectoryRef Id="ApplicationProgramsFolder">
<Component Id="ApplicationShortcutStartMenu"
Guid="*"
Win64="yes">
<Shortcut Id="ApplicationShortcutStartMenu"
Name="$(var.Name)"
Description="Launch $(var.Name)"
Target="[#fileMain.exe]"
WorkingDirectory="INSTALLFOLDER" />
<RemoveFolder Id="StartMenuFolder"
On="uninstall" />
<RegistryValue Root="HKCU"
Key="Software\jami.net\$(var.Name)"
Name="startmenu"
Type="integer"
Value="1"
KeyPath="yes" />
<Component Id="ApplicationShortcutStartMenu" Guid="*" Win64="yes">
<Shortcut Id="ApplicationShortcutStartMenu" Name="$(var.Name)" Description="Launch $(var.Name)" Target="[#fileMain.exe]" WorkingDirectory="INSTALLFOLDER" />
<RemoveFolder Id="StartMenuFolder" On="uninstall" />
<RegistryValue Root="HKCU" Key="Software\jami.net\$(var.Name)" Name="startmenu" Type="integer" Value="1" KeyPath="yes" />
</Component>
</DirectoryRef>
</Fragment>
<Fragment Id="OtherRegistryEntries">
<DirectoryRef Id="TARGETDIR">
<Component Id="RegistryEntries"
Guid="*"
Win64="yes">
<RegistryValue Root="HKCU"
Key="Software\jami.net\$(var.AppName)"
Name="hasRun"
Type="integer"
Value="0"
KeyPath="yes" />
<Component Id="RegistryEntries" Guid="*" Win64="yes">
<RegistryValue Root="HKCU" Key="Software\jami.net\$(var.AppName)" Name="hasRun" Type="integer" Value="0" KeyPath="yes" />
</Component>
</DirectoryRef>
</Fragment>
<Fragment Id="URLProtocol">
<DirectoryRef Id="TARGETDIR">
<Component Id="URLProtocolRegistryEntries"
Guid="*"
Win64="yes">
<RegistryKey Root="HKCR"
Key="jami"
ForceCreateOnInstall="yes"
ForceDeleteOnUninstall="yes">
<RegistryValue Type="string"
Name="URL Protocol"
Value="" />
<RegistryValue Type="string"
Value="URL:jami"/>
<Component Id="URLProtocolRegistryEntries" Guid="*" Win64="yes">
<RegistryKey Root="HKCR" Key="jami" ForceCreateOnInstall="yes" ForceDeleteOnUninstall="yes">
<RegistryValue Type="string" Name="URL Protocol" Value="" />
<RegistryValue Type="string" Value="URL:jami"/>
<RegistryKey Key="DefaultIcon">
<RegistryValue Type="string"
Value="[APPLICATIONFOLDER]$(var.ExeName).exe" />
<RegistryValue Type="string" Value="[APPLICATIONFOLDER]$(var.ExeName).exe" />
</RegistryKey>
<RegistryKey Key="shell\open\command">
<RegistryValue Type="string"
Value='"[APPLICATIONFOLDER]$(var.ExeName).exe" "%1"' />
<RegistryValue Type="string" Value='"[APPLICATIONFOLDER]$(var.ExeName).exe" "%1"' />
</RegistryKey>
</RegistryKey>
</Component>
@ -223,88 +137,47 @@
<Fragment Id="UI">
<UI Id="CustomUI">
<Property Id="WixAppFolder"
Value="WixPerMachineFolder" />
<Property Id="WixAppFolder" Value="WixPerMachineFolder" />
<!--APPLICATIONFOLDER required by WixUI_Advanced, ApplicationFolderName reset APPLICATIONFOLDER path-->
<Property Id="ApplicationFolderName"
Value="$(var.Manufacturer)\$(var.AppName)" />
<Property Id="ApplicationFolderName" Value="$(var.Manufacturer)\$(var.AppName)" />
<UIRef Id="WixUI_Advanced" />
<!--Remove User Exit Dialog-->
<Publish Dialog="AdvancedWelcomeEulaDlg"
Control="Cancel"
Property="AbortInstall"
Value="1">1</Publish>
<Publish Dialog="InstallDirDlg"
Control="Cancel"
Property="AbortInstall"
Value="1">1</Publish>
<Publish Dialog="FeaturesDlg"
Control="Cancel"
Property="AbortInstall"
Value="1">1</Publish>
<Publish Dialog="MaintenanceWelcomeDlg"
Control="Cancel"
Property="AbortInstall"
Value="1">1</Publish>
<Publish Dialog="MaintenanceTypeDlg"
Control="Cancel"
Property="AbortInstall"
Value="1">1</Publish>
<Publish Dialog="AdvancedWelcomeEulaDlg" Control="Cancel" Property="AbortInstall" Value="1">1</Publish>
<Publish Dialog="InstallDirDlg" Control="Cancel" Property="AbortInstall" Value="1">1</Publish>
<Publish Dialog="FeaturesDlg" Control="Cancel" Property="AbortInstall" Value="1">1</Publish>
<Publish Dialog="MaintenanceWelcomeDlg" Control="Cancel" Property="AbortInstall" Value="1">1</Publish>
<Publish Dialog="MaintenanceTypeDlg" Control="Cancel" Property="AbortInstall" Value="1">1</Publish>
<!--Launch Program If Checkbox is clicked-->
<Publish Dialog="ExitDialog"
Control="Finish"
Event="DoAction"
Value="LaunchApplication">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchApplication">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
<InstallUISequence>
<Show Dialog="UserExit"
OnExit="cancel">NOT AbortInstall = 1</Show>
<Custom Action="Overwrite_WixSetDefaultPerMachineFolder"
After="WixSetDefaultPerMachineFolder" />
<Show Dialog="UserExit" OnExit="cancel">NOT AbortInstall = 1</Show>
<Custom Action="Overwrite_WixSetDefaultPerMachineFolder" After="WixSetDefaultPerMachineFolder" />
</InstallUISequence>
</UI>
<InstallExecuteSequence>
<Custom Action='TerminateAppProcess'
Before='InstallValidate'/>
<Custom Action="RemoveOldJamiFiles"
After="RemoveFiles" />
<Custom Action="LaunchApplication_nonUI"
After="InstallFinalize"> WIXNONUILAUNCH </Custom>
<Custom Action="Overwrite_WixSetDefaultPerMachineFolder"
After="WixSetDefaultPerMachineFolder" />
<Custom Action='TerminateAppProcess' Before='InstallValidate'/>
<Custom Action="RemoveOldJamiFiles" After="RemoveFiles" />
<Custom Action="LaunchApplication_nonUI" After="InstallFinalize"> WIXNONUILAUNCH </Custom>
<Custom Action="Overwrite_WixSetDefaultPerMachineFolder" After="WixSetDefaultPerMachineFolder" />
</InstallExecuteSequence>
<!--License check box text, Launch check box text (auto check)-->
<Property Id="WIXUI_EXITDIALOGOPTIONALTEXT"
Value="Launch $(var.Name)" />
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT"
Value="Launch $(var.Name)" />
<Property Id="WIXUI_EXITDIALOGOPTIONALTEXT" Value="Launch $(var.Name)" />
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch $(var.Name)" />
<!--CheckBox Default Set to One-->
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX"
Value="1"/>
<Property Id="LicenseAccepted"
Value="1"/>
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1"/>
<Property Id="LicenseAccepted" Value="1"/>
<Property Id="WixShellExecTarget"
Value="[#fileMain.exe]" />
<CustomAction Id="LaunchApplication"
BinaryKey="WixCA"
DllEntry="WixShellExec"
Impersonate="yes" />
<CustomAction Id="LaunchApplication_nonUI"
BinaryKey="WixCA"
DllEntry="WixShellExec"
Impersonate="yes"/>
<CustomAction Id="Overwrite_WixSetDefaultPerMachineFolder"
Property="WixPerMachineFolder"
Value="[ProgramFiles64Folder][ApplicationFolderName]"
Execute="immediate" />
<Property Id="WixShellExecTarget" Value="[#fileMain.exe]" />
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
<CustomAction Id="LaunchApplication_nonUI" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes"/>
<CustomAction Id="Overwrite_WixSetDefaultPerMachineFolder" Property="WixPerMachineFolder" Value="[ProgramFiles64Folder][ApplicationFolderName]" Execute="immediate" />
<!--License File-->
<WixVariable Id="WixUILicenseRtf"
Value="$(var.ReleaseDir)\License.rtf"/>
<WixVariable Id="WixUILicenseRtf" Value="$(var.ReleaseDir)\License.rtf"/>
</Fragment>
</Wix>

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Generated with WiX's heat tool using the command:
heat.exe dir x64\Release -ag -cg ProductComponents -dr APPLICATIONFOLDER -srd -var var.ReleaseDir -out JamiInstaller\Components.wxs
Includes:
- the api-ms-win dlls missing parts of vc merge module for windows 7 support
- Jami.exe with a named Id so we can reference it in Product.wxs to launch after install
We run heat in the prebuild step on x64\Release without Jami.exe (instead of an XSLT file), to harvest everything else.
-->
<?include Config.wxi?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<ComponentGroup Id="StandardComponents">
<Component Id="cmp9C61F84AF9761955FBF397AFAE21C11B" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil2089BEC9A7AB899CED5A5EE501789299" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-core-file-l1-2-0.dll" />
</Component>
<Component Id="cmp03BB2697EE10869C4A329E3EA987EFAA" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil44C27F2C97596734BB3BEB7C21F7B71C" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-core-file-l2-1-0.dll" />
</Component>
<Component Id="cmp6B6AA7AEA5A4D324A4EE7DAE1B1193E0" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil1D16BE23D323A1E37FC1FC7354A9305F" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-core-localization-l1-2-0.dll" />
</Component>
<Component Id="cmpB5454FB66442C9BFD2145AE30B32D7A9" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil031B78DF53F7A3AC109410907624FC3E" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-core-processthreads-l1-1-1.dll" />
</Component>
<Component Id="cmp9F6D22CD9B1739E4F75F92F3A07E4CA1" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="filE9A3672FA504AA8E518DD72A02CD3E77" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-core-synch-l1-2-0.dll" />
</Component>
<Component Id="cmp9451422B7074D46F019614C3DE73BD17" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil3C902CA2889BB8855D285C3FBABB334F" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-core-timezone-l1-1-0.dll" />
</Component>
<Component Id="cmp349250459EC2D8C328EED5138B073E7A" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil2466F3D9FBA095A007D0909040D4D688" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-conio-l1-1-0.dll" />
</Component>
<Component Id="cmpCC880F2B054A87EF5FC68232652231BF" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil605A691486569535A1C3548F7DCE753C" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-convert-l1-1-0.dll" />
</Component>
<Component Id="cmpD6CB40D5A5AFF2161B7B4B4F06F03301" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil09AE032A32E2E542A232F7941AC77320" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-environment-l1-1-0.dll" />
</Component>
<Component Id="cmpC02538029646A27A9F786AD690EB3C8E" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil7DE9C3CADCA188356922B0CBD8E313E7" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-filesystem-l1-1-0.dll" />
</Component>
<Component Id="cmp8E50197B377636123F0F1F94FFB004E7" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil584F158D11B8A380C73F1EFE8BBA92B4" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-heap-l1-1-0.dll" />
</Component>
<Component Id="cmp7F729C94A363C73DC4D91B6F48E4F859" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil4BE19B924B98D56F3155B66496D574E5" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-locale-l1-1-0.dll" />
</Component>
<Component Id="cmp7C573E66B0904BA73880788F7057AF88" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="filE8495C446FA1237E92562498D20261AA" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-math-l1-1-0.dll" />
</Component>
<Component Id="cmp7A91CED53D8F6E5F20F2049B3B5CD143" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil06951EB208628753677745AF15CC12A5" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-multibyte-l1-1-0.dll" />
</Component>
<Component Id="cmp1CE713C705A95306A1D246AC3AB9DE25" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil8D102BB81768F998470C34797459E306" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-private-l1-1-0.dll" />
</Component>
<Component Id="cmp35840DFBF4D6AE827AFC4EF2A17BB3EB" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="filC6C457BD901F940DCB673D271728F9FE" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-process-l1-1-0.dll" />
</Component>
<Component Id="cmpEBB86BDA48FE3B9E2043C1A80D26ACD5" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="filE1B2095225B01DEFA5DA9895B432FBCB" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-runtime-l1-1-0.dll" />
</Component>
<Component Id="cmp80C8534B553078EA8B86F100FF542776" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="filDAFE58019AD70832B8304DCEA534B5EE" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-stdio-l1-1-0.dll" />
</Component>
<Component Id="cmpCFC348111B5343749A2273A62421C07C" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil1F1B38DB330CA413655F715578D4BE1A" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-string-l1-1-0.dll" />
</Component>
<Component Id="cmp7D5450E04EC419244107942A00DF7DDF" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="filEB03BCF3155C5BAE2C2EDBF036EB659D" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-time-l1-1-0.dll" />
</Component>
<Component Id="cmp5456679BDCC818B2E9476B416F71AAA5" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fil5B120DD384CABED37DFC2652C6462666" KeyPath="yes" Source="$(var.UcrtDir)\api-ms-win-crt-utility-l1-1-0.dll" />
</Component>
<Component Id="cmpF23755F862A15FFCBD109C85599B7F20" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="filD6887AD9110E4A8D49143C9A8F0B5843" KeyPath="yes" Source="$(var.UcrtDir)\ucrtbase.dll" />
</Component>
<Component Id="cmp9CFEE34E3A162AB05264E8B756EC1DEC" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="fileMain.exe" KeyPath="yes" Source="$(var.ReleaseDir)\$(var.ExeName).exe" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>

View File

@ -100,7 +100,7 @@ ZYPPER_DEPENDENCIES = [
'speexdsp-devel', 'speex-devel', 'libdbus-c++-devel', 'jsoncpp-devel', 'yaml-cpp-devel',
'yasm', 'libuuid-devel', 'libnettle-devel', 'libopus-devel', 'libexpat-devel',
'libgnutls-devel', 'msgpack-c-devel', 'msgpack-cxx-devel', 'libavcodec-devel', 'libavdevice-devel', 'pcre-devel',
'alsa-devel', 'libpulse-devel', 'libudev-devel', 'libva-devel', 'libvdpau-devel', 'pipewire-devel',
'alsa-devel', 'libpulse-devel', 'libudev-devel', 'libva-devel', 'libvdpau-devel',
'libopenssl-devel', 'libavutil-devel',
]
@ -130,7 +130,7 @@ DNF_DEPENDENCIES = [
'gcc-c++', 'which', 'alsa-lib-devel', 'systemd-devel', 'libuuid-devel',
'uuid-devel', 'gnutls-devel', 'nettle-devel', 'opus-devel', 'speexdsp-devel',
'yaml-cpp-devel', 'swig', 'jsoncpp-devel',
'patch', 'libva-devel', 'openssl-devel', 'libvdpau-devel', 'pipewire-devel', 'msgpack-devel',
'patch', 'libva-devel', 'openssl-devel', 'libvdpau-devel', 'msgpack-devel',
'sqlite-devel', 'openssl-static', 'pandoc', 'nasm',
'bzip2'
]
@ -154,7 +154,7 @@ APT_DEPENDENCIES = [
'libopus-dev', 'libpcre3-dev', 'libpulse-dev', 'libssl-dev',
'libspeex-dev', 'libspeexdsp-dev', 'libswscale-dev', 'libtool',
'libudev-dev', 'libyaml-cpp-dev', 'sip-tester', 'swig',
'uuid-dev', 'yasm', 'libjsoncpp-dev', 'libva-dev', 'libvdpau-dev', 'libpipewire-0.3-dev', 'libmsgpack-dev',
'uuid-dev', 'yasm', 'libjsoncpp-dev', 'libva-dev', 'libvdpau-dev', 'libmsgpack-dev',
'pandoc', 'nasm', 'dpkg-dev', 'libsystemd-dev'
]
@ -299,7 +299,7 @@ def run_dependencies(args):
print("The win32 version does not install dependencies with this script.\nPlease continue with the --install instruction.")
sys.exit(1)
elif args.distribution == 'guix':
print(f"Building the profile defined in '{GUIX_MANIFEST}'")
print(f"Building the profile defined in '{GUIX_MANIFEST}'...")
execute_script([f'guix shell --manifest={GUIX_MANIFEST} -- true'])
else:
@ -316,7 +316,7 @@ def run_init(args):
client_hooks_dir = '.git/hooks'
daemon_hooks_dir = '.git/modules/daemon/hooks'
print("Installing commit-msg hooks")
print("Installing commit-msg hooks...")
# Copy the commit-msg hook to all modules in the same way.
for hooks_dir in [client_hooks_dir, daemon_hooks_dir]:
if not os.path.exists(hooks_dir):
@ -324,7 +324,7 @@ def run_init(args):
copy_file("./extras/scripts/commit-msg",
os.path.join(hooks_dir, "commit-msg"))
print("Installing pre-commit hooks")
print("Installing pre-commit hooks...")
format_script = "./extras/scripts/format.py"
# Prepend with the python executable if on Windows (not WSL).
if sys.platform == 'win32':
@ -604,7 +604,7 @@ def run_run(args):
client_process.wait()
except KeyboardInterrupt:
print("\nCaught KeyboardInterrupt")
print("\nCaught KeyboardInterrupt...")
finally:
if args.debug:
@ -614,7 +614,7 @@ def run_run(args):
try:
# Only kill the processes if they are running, as they
# could have been closed by the user.
print("Killing processes")
print("Killing processes...")
if args.no_libwrap:
jamid_log.close()
if jamid_process.poll() is None:

2
daemon

Submodule daemon updated: 70a9c0b8a8...59a8e41ca8

View File

@ -1,34 +0,0 @@
find_package(Git QUIET REQUIRED)
message(STATUS "Generating version information...")
function(configure_version_string SOURCE_DIR VERSION_STRING_OUT)
# Get short git SHA
execute_process(
COMMAND "${GIT_EXECUTABLE}" rev-parse --short HEAD
WORKING_DIRECTORY "${SOURCE_DIR}"
OUTPUT_VARIABLE _GIT_SHA
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Output the VERSION_STRING_OUT to the caller
set(${VERSION_STRING_OUT} "${_GIT_SHA}" PARENT_SCOPE)
endfunction()
# These need to be set to the parent scripts values for configure_file to work,
# as it prepends CMAKE_CURRENT_SOURCE_DIR to the <input> and CMAKE_CURRENT_BINARY_DIR
# to <output>.
set(CMAKE_CURRENT_SOURCE_DIR ${APP_SOURCE_DIR})
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)
# Get output file names with the .in extension removed
get_filename_component(VERSION_CPP_FILENAME ${CPP_INT_FILE} NAME_WE)
set(VERSION_CPP_FILE "${CMAKE_CURRENT_BINARY_DIR}/${VERSION_CPP_FILENAME}.cpp")
message(STATUS "infiles: ${CPP_INT_FILE}")
message(STATUS "outfiles: ${VERSION_CPP_FILE}")
configure_file(${CPP_INT_FILE} ${VERSION_CPP_FILE})

View File

@ -0,0 +1,7 @@
message("Qt deploying in dir " ${QML_SRC_DIR})
execute_process(COMMAND "${MAC_DEPLOY_QT_PATH}/macdeployqt"
${EXE_NAME}
-qmldir=${QML_SRC_DIR})
if(${ENABLE_SPARKLE} MATCHES true)
file(COPY ${SPARKLE_PATH} DESTINATION ${EXE_NAME}/Contents/Frameworks/)
endif()

View File

@ -66,7 +66,6 @@ RUN apt-get install -y -o Acquire::Retries=10 \
libvdpau-dev \
libssl-dev
RUN apt-get install -y pandoc \
libcppunit-dev \
googletest \
libgtest-dev \
wget

View File

@ -1,9 +0,0 @@
# python virtual environment
venv/
# python compiled files
*.pyc
# python cache
__pycache__
# example output
crash_reports/

View File

@ -1,51 +0,0 @@
# Crash report submission server examples
## Overview
This directory contains an example of a crash report submission server. This server is responsible for receiving crash reports from clients and storing them. The example is written in Python and uses the Flask web framework with Waitress as the WSGI server. It exposes one endpoint for submitting crash reports on the `/submit` path using the POST method on port `8080`.
It also contains an example of a crash report access server. This server is responsible for displaying the crash reports. It uses port `8081` and provides a simple HTML page that lists crash reports by page.
## Running the examples
To run the examples, you need to have Python 3 installed. You can just use the virtual environment provided in this directory. To activate the virtual environment, run the following commands:
```
python3 -m venv venv
source venv/bin/activate
python3 -m pip install -r requirements.txt
```
> ⚠️ On Windows, you need to use `venv\Scripts\activate` instead of `source venv/bin/activate`.
After activating the virtual environment, you can should be able to execute the example submission server. To run the example submission server that uses the Crashpad format, run the following command:
```
python3 crashpad_submit_server.py
```
To run a server that displays the crash reports, run the following command:
```
python3 report_access_server.py
```
> ⚠️ It is recommended to run the report access server in a way that is not publicly accessible.
Either server can be run on the same machine or on different machines, and each can be run using the `--debug` flag to enable debugging.
## Metadata
The crash report submission servers expect the crash reports to contain a JSON object. The JSON object should contain the following basic metadata:
```
{
"build_id": "202410021437",
"client_sha": "77149ebd62",
"guid": "50c4218a-bcb9-48a9-8093-a06e6435cd61",
"jamicore_sha": "cbf8f0af6",
"platform": "Ubuntu 22.04.4 LTS_x86_64"
}
```
The `build_id` field is the build identifier of the client application. The `client_sha` field is the SHA-1 hash of the client application. The `guid` field is a unique identifier for the crash report. The `jamicore_sha` field is the SHA-1 hash of the Jami core library. The `platform` field is the platform on which the client application is running.

View File

@ -1,60 +0,0 @@
#!/usr/bin/env python3
import os
from flask import Flask, request, jsonify
import json
import argparse
app = Flask(__name__)
BASE_PATH = 'crash_reports'
@app.route('/submit', methods=['POST'])
def submit():
try:
print("Received a crash report GUID: %s" % request.form.get('guid', 'No GUID provided'))
file_storage = request.files.get('upload_file_minidump')
dump_id = ""
if file_storage:
dump_id = file_storage.filename
# Create a directory to store the crash reports if it doesn't exist
if not os.path.exists(BASE_PATH):
os.makedirs(BASE_PATH)
filepath = os.path.join(BASE_PATH, dump_id)
# Attempt to write the file, fail gracefully if it already exists
if os.path.exists(filepath):
print(f"File {filepath} already exists.")
return 'File already exists', 409
with open(filepath, 'wb') as f:
f.write(file_storage.read())
print(f"File saved successfully at {filepath}")
# Now save the metadata in {request.form} as separate filename <UID>.info.
metadata_filepath = os.path.join(BASE_PATH, f"{dump_id}.info")
with open(metadata_filepath, 'w') as f:
f.write(str(json.dumps(dict(request.form), indent=4)))
else:
print("No file found for the key 'upload_file_minidump'")
return 'No file found', 400
return 'Crash report received', 200
except OSError as e:
print(f"Error creating directory or writing file: {e}")
return 'Internal Server Error', 500
except Exception as e:
print(f"An unexpected error occurred: {e}")
return 'Internal Server Error', 500
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Crash report submission server')
parser.add_argument('--debug', action='store_true', help='Run in debug mode')
args = parser.parse_args()
if args.debug:
app.run(port=8080, debug=True)
else:
from waitress import serve
print("Starting production server on port 8080...")
serve(app, host='0.0.0.0', port=8080)

View File

@ -1,252 +0,0 @@
#!/usr/bin/env python3
import os
from flask import Flask, request, jsonify, render_template_string, send_file
import json
from datetime import datetime
import argparse
app = Flask(__name__)
BASE_PATH = 'crash_reports'
@app.route('/', methods=['GET'])
def list_reports():
try:
if not os.path.exists(BASE_PATH):
return jsonify({"error": "No reports directory found"}), 404
# Get page number from query parameters, default to 1
page = int(request.args.get('page', 1))
per_page = 10
reports = os.listdir(BASE_PATH)
if not reports:
return render_template_string("""
<h1>Crash Reports</h1>
<p>No crash reports found.</p>
""")
# Build report pairs with metadata
report_pairs = []
for report in reports:
if not report.endswith('.info'):
info_file = f"{report}.info"
if info_file in reports:
try:
dump_path = os.path.join(BASE_PATH, report)
timestamp = os.path.getctime(dump_path)
upload_time = datetime.fromtimestamp(timestamp)
with open(os.path.join(BASE_PATH, info_file), 'r') as f:
metadata = json.load(f)
report_pairs.append({
'dump_file': report,
'info_file': info_file,
'metadata': metadata,
'sort_key': f"{metadata.get('client_sha', '')}-{metadata.get('jamicore_sha', '')}",
'download_name': f"{metadata.get('client_sha', 'unknown')}-{metadata.get('jamicore_sha', 'unknown')}-{metadata.get('platform', 'unknown').replace(' ', '_')}",
'upload_time': upload_time
})
except json.JSONDecodeError:
print(f"Error parsing metadata file: {info_file}")
continue
# Sort reports by upload time (most recent first), then by SHA
report_pairs.sort(key=lambda x: (-x['upload_time'].timestamp(), x['sort_key']))
# Calculate pagination values
total_reports = len(report_pairs)
total_pages = (total_reports + per_page - 1) // per_page
page = min(max(1, page), total_pages or 1) # Handle case when total_pages is 0
start_idx = (page - 1) * per_page
end_idx = start_idx + per_page
# Get current page's reports
current_page_reports = report_pairs[start_idx:end_idx]
return render_template_string("""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Crash Reports</title>
<style>
body { font-family: Arial, sans-serif; margin: 2em; }
.header {
margin-bottom: 2em;
}
.header h1 {
margin-bottom: 0.5em;
}
.report-list { list-style: none; padding: 0; }
.report-item { margin: 1em 0; padding: 1em; border: 1px solid #ddd; border-radius: 4px; }
.download-link {
display: inline-block;
padding: 8px 16px;
background-color: #0066cc;
color: white;
text-decoration: none;
border-radius: 4px;
margin: 8px 0;
}
.download-link:hover { background-color: #0052a3; }
.metadata-table {
border-collapse: collapse;
width: 100%;
margin: 8px 0;
}
.metadata-table td {
padding: 4px 8px;
border-bottom: 1px solid #ddd;
}
.metadata-table td:first-child {
font-weight: bold;
width: 150px;
}
.upload-time {
color: #666;
font-size: 0.9em;
margin-bottom: 8px;
}
.pagination {
margin: 1em 0;
text-align: center;
}
.pagination a, .pagination span {
display: inline-block;
padding: 8px 16px;
margin: 0 4px;
border: 1px solid #ddd;
border-radius: 4px;
text-decoration: none;
color: #0066cc;
}
.pagination .current {
background-color: #0066cc;
color: white;
border-color: #0066cc;
}
.pagination a:hover {
background-color: #f5f5f5;
}
.pagination-info {
text-align: center;
color: #666;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="header">
<h1>Crash Reports</h1>
<div class="pagination-info">
Showing {{ start_idx + 1 }}-{{ [end_idx, total_reports] | min }} of {{ total_reports }} reports
</div>
<div class="pagination">
{% if page > 1 %}
<a href="{{ url_for('list_reports', page=1) }}">&laquo; First</a>
<a href="{{ url_for('list_reports', page=page-1) }}">&lsaquo; Previous</a>
{% endif %}
{% for p in range([1, page-2] | max, [total_pages + 1, page + 3] | min) %}
{% if p == page %}
<span class="current">{{ p }}</span>
{% else %}
<a href="{{ url_for('list_reports', page=p) }}">{{ p }}</a>
{% endif %}
{% endfor %}
{% if page < total_pages %}
<a href="{{ url_for('list_reports', page=page+1) }}">Next &rsaquo;</a>
<a href="{{ url_for('list_reports', page=total_pages) }}">Last &raquo;</a>
{% endif %}
</div>
</div>
<div class="report-list">
{% for report in reports %}
<div class="report-item">
<h3>Report: {{ report['sort_key'] }}</h3>
<div class="upload-time">
Uploaded: {{ report['upload_time'].strftime('%Y-%m-%d %H:%M:%S') }}
</div>
<table class="metadata-table">
<tr>
<td>Platform:</td>
<td>{{ report['metadata']['platform'] }}</td>
</tr>
<tr>
<td>Client SHA:</td>
<td>{{ report['metadata']['client_sha'] }}</td>
</tr>
<tr>
<td>Jami Core SHA:</td>
<td>{{ report['metadata']['jamicore_sha'] }}</td>
</tr>
<tr>
<td>Build ID:</td>
<td>{{ report['metadata']['build_id'] }}</td>
</tr>
<tr>
<td>GUID:</td>
<td>{{ report['metadata']['guid'] }}</td>
</tr>
</table>
<a class="download-link" href="{{ url_for('download_report_bundle', dump_file=report['dump_file'], info_file=report['info_file'], download_name=report['download_name']) }}">
Download Report Bundle
</a>
</div>
{% endfor %}
</div>
</body>
</html>
""", reports=current_page_reports, page=page, total_pages=total_pages,
start_idx=start_idx, end_idx=end_idx, total_reports=total_reports)
except Exception as e:
print(f"Error listing reports: {e}")
return 'Internal Server Error', 500
@app.route('/download-bundle/<path:dump_file>/<path:info_file>/<path:download_name>')
def download_report_bundle(dump_file, info_file, download_name):
try:
import zipfile
from io import BytesIO
# Create a memory file for the zip
memory_file = BytesIO()
# Create the zip file
with zipfile.ZipFile(memory_file, 'w', zipfile.ZIP_DEFLATED) as zf:
# Add the dump file
dump_path = os.path.join(BASE_PATH, dump_file)
zf.write(dump_path, f"{download_name}.dmp")
# Add the info file
info_path = os.path.join(BASE_PATH, info_file)
zf.write(info_path, f"{download_name}.info")
# Seek to the beginning of the memory file
memory_file.seek(0)
return send_file(
memory_file,
mimetype='application/zip',
as_attachment=True,
download_name=f"{download_name}.zip"
)
except Exception as e:
print(f"Error creating zip bundle: {e}")
return 'Internal Server Error', 500
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Crash reports viewing server')
parser.add_argument('--debug', action='store_true', help='Run in debug mode')
args = parser.parse_args()
if args.debug:
app.run(port=8081, debug=True)
else:
from waitress import serve
print("Starting production server on port 8081...")
serve(app, host='0.0.0.0', port=8081)

View File

@ -1,6 +0,0 @@
Flask==3.0.3
requests==2.24.0
markupsafe==2.1.1
itsdangerous==2.1.2
werkzeug==3.0.0
waitress==3.0.2

View File

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2015-2024 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>
<description>
<p>
An end-to-end encrypted secure and distributed voice, video, and
chat communication platform that requires no central server and
leaves the power of privacy and freedom in the hands of users.
</p>
<p>
Jami supports the following key features:
</p>
<ul>
<li>One-to-one conversations</li>
<li>File sharing</li>
<li>Audio calls and conferences</li>
<li>Video calls and conferences</li>
<li>Screen sharing in video calls and conferences</li>
<li>Recording and sending audio messages</li>
<li>Recording and sending video messages</li>
<li>Functioning as a SIP phone software</li>
</ul>
<p>
Client applications for GNU/Linux, Windows, macOS, iOS, Android,
and Android TV are available, making Jami an interoperable and
cross-platform communication framework.
</p>
<p xml:lang="hu">
Végpontokig titkosított biztonságos és elosztott hang-, videó-
és csevegés-kommunikációs platform, amely nem igényel központi
kiszolgálót, és a felhasználók kezében hagyja a magánélet és a
szabadság hatalmát.
</p>
<p xml:lang="hu">
A Jami a következő főbb funkciókat támogatja:
</p>
<ul>
<li xml:lang="hu">Személyes beszélgetések</li>
<li xml:lang="hu">Fájlmegosztás</li>
<li xml:lang="hu">Hanghívások és konferenciák</li>
<li xml:lang="hu">Videohívások és konferenciák</li>
<li xml:lang="hu">Képernyőmegosztás videohívásokban és
konferenciákon</li>
<li xml:lang="hu">Hangüzenetek rögzítése és küldése</li>
<li xml:lang="hu">Videoüzenetek rögzítése és küldése</li>
<li xml:lang="hu">SIP-telefonszoftverként működik</li>
</ul>
<p xml:lang="hu">
Elérhetők a GNU/Linux, Windows, macOS, iOS, Android és Android TV
ügyfélalkalmazásai, így a Jami interoperábilis és többplatformos
kommunikációs keretrendszerré válik.
</p>
</description>
<url type="homepage">https://jami.net/</url>
<url type="bugtracker">https://git.jami.net/savoirfairelinux/jami-client-qt/issues</url>
<url type="faq">https://jami.net/help/</url>
<url type="help">https://docs.jami.net</url>
<url type="donation">https://www.paypal.com/donate/?hosted_button_id=MGUDJLQZ4TP5W</url>
<url type="translate">https://www.transifex.com/savoirfairelinux/jami</url>
<!-- Maximum caption length is 60 characters -->
<!-- Officially GIF is not an allowed video format, but it appears to work nonetheless -->
<screenshots>
<screenshot type="default">
<caption>Send Audio, Video and Chat messages</caption>
<caption xml:lang="hu">Hang-, video- és csevegőüzeneteket küldhet</caption>
<image type="source" width="1310" height="650">https://dl.jami.net/media-resources/screenshots/jami_linux_audiovideo.png</image>
</screenshot>
<screenshot>
<caption>Easily share desktop contents</caption>
<caption xml:lang="hu">Könnyen megoszthatja az asztali tartalmat</caption>
<image type="source" width="1016" height="659">https://dl.jami.net/media-resources/screenshots/jami_linux_screenshare.png</image>
</screenshot>
<screenshot>
<caption>Crystal clear audio calls between Jami users</caption>
<caption xml:lang="hu">Kristálytiszta hanghívások a Jami felhasználók között</caption>
<video container="webm" codec="vp9" width="600" height="400">https://dl.jami.net/media-resources/gifs_features/conversiongif_webm/audio-call_web.webm</video>
</screenshot>
<screenshot>
<caption>Conference calls with an unlimited participants</caption>
<caption xml:lang="hu">Konferenciahívások korlátlan számú résztvevővel</caption>
<video container="webm" codec="vp9" width="600" height="400">https://dl.jami.net/media-resources/gifs_features/conversiongif_webm/conference_web.webm</video>
</screenshot>
<screenshot>
<caption>Encrypted and secure text messaging, no servers</caption>
<caption xml:lang="hu">Titkosított és biztonságos csevegési üzenetküldés, kiszolgálók nélkül</caption>
<video container="webm" codec="vp9" width="600" height="400">https://dl.jami.net/media-resources/gifs_features/conversiongif_webm/chat_web.webm</video>
</screenshot>
<screenshot>
<caption>Send files of any size</caption>
<caption xml:lang="hu">Bármilyen méretű fájl küldése</caption>
<video container="webm" codec="vp9" width="600" height="400">https://dl.jami.net/media-resources/gifs_features/conversiongif_webm/files-share_web.webm</video>
</screenshot>
</screenshots>
<launchable type="desktop-id">jami.desktop</launchable>
<provides><binary>jami</binary></provides>
<!-- https://specifications.freedesktop.org/menu-spec/latest/apa.html -->
<!-- https://specifications.freedesktop.org/menu-spec/latest/apas02.html -->
<categories>
<category>Chat</category>
<category>Communication</category>
<category>FileTransfer</category>
<category>InstantMessaging</category>
<category>Network</category>
<category>P2P</category>
<category>Productivity</category>
</categories>
<translation type="gettext">jami-client-qt</translation>
<content_rating type="oars-1.1">
<content_attribute id="social-chat">intense</content_attribute>
<content_attribute id="social-audio">intense</content_attribute>
</content_rating>
<requires><id>net.jami.daemon</id></requires>
</component>

View File

@ -1,153 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<components version="0.14" origin="jami">
<component type="desktop-application">
<id>net.jami.Jami</id>
<pkgname>jami</pkgname>
<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>
<project_license>GPL-3.0+</project_license>
<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>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>Jami is a free/libre, end-to-end encrypted, and private communication 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>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>This gives the greatest privacy as the distributed nature of Jami means your calls are only between participants.</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>Jami can also function as a SIP client.</p>
<p>Jami has multiple extensions available: Audio Filter; Auto Answer; Green Screen; Watermark; and, Whisper Transcript.</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>Jami is available for GNU/Linux, Windows, macOS, iOS, Android, and Android TV, making Jami an interoperable and cross-platform communication framework.</p>
<p>Manage multiple SIP accounts, Jami accounts and JAMS accounts with the Jami client installed on one or multiple devices.</p>
<p>Jami is free, unlimited, private, advertising free, compatible, fast, autonomous, and anonymous.</p>
<p>Learn more about:</p>
<ul>
<li>Jami: https://jami.net/</li>
<li>Jami extensions: https://jami.net/extensions/</li>
<li>“Jami Account Management Server” (JAMS): https://jami.biz/</li>
<li>Jami documentation: https://docs.jami.net/</li>
</ul>
<p>Follow us for more:</p>
<ul>
<li>Mastodon: https://mstdn.io/@Jami</li>
<li>X: https://x.com/jami_social</li>
<li>YouTube: https://www.youtube.com/@jami9311</li>
</ul>
<p>Wed love to hear from you! Join the Jami community:</p>
<ul>
<li>Contribute: https://jami.net/contribute/</li>
<li>Forum: 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>Jami for Android TV is tested on NVIDIA SHIELD TV with Logitech cameras.</p>
<p>Jami is published under the GPL license, version 3 or higher.</p>
<p>Copyright © Savoir-faire Linux Inc.</p>
</description>
<description xml:lang="hu">
<p>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>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>A Jami egy ingyenes, teljes körűen titkosított és privát kommunikációs platform.</p>
<p>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>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>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>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>A Jami SIP-ügyfélként is működhet.</p>
<p>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>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>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>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>A Jami ingyenes, korlátlan, privát, reklámmentes, kompatibilis, gyors, autonóm és névtelen.</p>
<p>További tájékoztatás:</p>
<ul>
<li>Jami: https://jami.net/hu/</li>
<li>Jami-bővítmények: https://jami.net/hu/extensions/</li>
<li>JAMS (Jami Account Management Server Jami fiókkezelő kiszolgáló): https://jami.biz/</li>
<li>Jami-dokumentáció: https://docs.jami.net/hu/</li>
</ul>
<p>Kövess minket a továbbiakért:</p>
<ul>
<li>Mastodon: https://mstdn.io/@Jami</li>
<li>X: https://x.com/jami_social</li>
<li>YouTube: https://www.youtube.com/@jami9311</li>
</ul>
<p>Szívesen hallanánk felőled! Csatlakozzon a Jami közösséghez:</p>
<ul>
<li>Közreműködés: https://jami.net/hu/contribute/</li>
<li>Fórum: https://forum.jami.net/</li>
</ul>
<p>É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>A Jami for Android TV-t Logitech kamerákkal ellátott NVIDIA SHIELD TV-n tesztelték.</p>
<p>A Jami a GPL licenc 3-as vagy újabb verziója alatt jelent meg.</p>
<p>Szerzői jog © Savoir-faire Linux Inc.</p>
</description>
<requires>
<id>net.jami.daemon</id>
</requires>
<launchable type="desktop-id">net.jami.Jami.desktop</launchable>
<icon type="stock">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>
<url type="help">https://forum.jami.net/</url>
<url type="donation">https://jami.net/whydonate/</url>
<url type="translate">https://www.transifex.com/savoirfairelinux/jami</url>
<categories>
<category>Chat</category>
<category>FileTransfer</category>
<category>InstantMessaging</category>
<category>Network</category>
<category>P2P</category>
<category>Telephony</category>
<category>VideoConference</category>
</categories>
<provides>
<binary>jami</binary>
<mediatype>x-scheme-handler/jami</mediatype>
</provides>
<screenshots>
<screenshot type="default">
<caption>Send chat messages and talk with audio and video</caption>
<caption xml:lang="hu">Csevegőüzenetek küldése, valamint hang- és videobeszélgetés</caption>
<image type="source" width="1310" height="650">https://dl.jami.net/media-resources/screenshots/jami_linux_audiovideo.png</image>
</screenshot>
<screenshot>
<caption>Screen sharing</caption>
<caption xml:lang="hu">Képernyőmegosztás</caption>
<image type="source" width="1016" height="659">https://dl.jami.net/media-resources/screenshots/jami_linux_screenshare.png</image>
</screenshot>
<screenshot>
<caption>Crystal clear audio calls between Jami users</caption>
<caption xml:lang="hu">Kristálytiszta hanghívások a Jami felhasználók között</caption>
<video codec="vp9" container="webm" width="600" height="400">https://dl.jami.net/media-resources/gifs_features/conversiongif_webm/audio-call_web.webm</video>
</screenshot>
<screenshot>
<caption>Conference calls with an unlimited number of participants</caption>
<caption xml:lang="hu">Konferenciahívások korlátlan számú résztvevővel</caption>
<video codec="vp9" container="webm" width="600" height="400">https://dl.jami.net/media-resources/gifs_features/conversiongif_webm/conference_web.webm</video>
</screenshot>
<screenshot>
<caption>Encrypted and secure text messaging without servers</caption>
<caption xml:lang="hu">Titkosított és biztonságos csevegési üzenetküldés, kiszolgálók nélkül</caption>
<video codec="vp9" container="webm" width="600" height="400">https://dl.jami.net/media-resources/gifs_features/conversiongif_webm/chat_web.webm</video>
</screenshot>
<screenshot>
<caption>Transfer files of any size</caption>
<caption xml:lang="hu">Bármilyen méretű fájl küldése</caption>
<video codec="vp9" container="webm" width="600" height="400">https://dl.jami.net/media-resources/gifs_features/conversiongif_webm/files-share_web.webm</video>
</screenshot>
</screenshots>
<keywords>
<keyword>Qt</keyword>
<keyword>chat</keyword>
<keyword>talk</keyword>
<keyword>im</keyword>
<keyword>message</keyword>
<keyword>voip</keyword>
</keywords>
<content_rating type="oars-1.1">
<content_attribute id="social-chat">intense</content_attribute>
<content_attribute id="social-audio">intense</content_attribute>
</content_rating>
</component>
</components>

View File

@ -1,146 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2015-2024 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>
<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>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 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>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 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>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>
</description>
<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>
<url type="help">https://forum.jami.net/</url>
<url type="donation">https://jami.net/whydonate/</url>
<url type="translate">https://www.transifex.com/savoirfairelinux/jami</url>
<!-- Maximum caption length is 60 characters -->
<!-- Officially GIF is not an allowed video format, but it appears to work nonetheless -->
<screenshots>
<screenshot type="default">
<image type="source" width="1310" height="650">https://dl.jami.net/media-resources/screenshots/jami_linux_audiovideo.png</image>
<caption>Send chat messages and talk with audio and video</caption>
<caption xml:lang="hu">Csevegőüzenetek küldése, valamint hang- és videobeszélgetés</caption>
</screenshot>
<screenshot>
<image type="source" width="1016" height="659">https://dl.jami.net/media-resources/screenshots/jami_linux_screenshare.png</image>
<caption>Screen sharing</caption>
<caption xml:lang="hu">Képernyőmegosztás</caption>
</screenshot>
<screenshot>
<video container="webm" codec="vp9" width="600" height="400">https://dl.jami.net/media-resources/gifs_features/conversiongif_webm/audio-call_web.webm</video>
<caption>Crystal clear audio calls between Jami users</caption>
<caption xml:lang="hu">Kristálytiszta hanghívások a Jami felhasználók között</caption>
</screenshot>
<screenshot>
<video container="webm" codec="vp9" width="600" height="400">https://dl.jami.net/media-resources/gifs_features/conversiongif_webm/conference_web.webm</video>
<caption>Conference calls with an unlimited number of participants</caption>
<caption xml:lang="hu">Konferenciahívások korlátlan számú résztvevővel</caption>
</screenshot>
<screenshot>
<video container="webm" codec="vp9" width="600" height="400">https://dl.jami.net/media-resources/gifs_features/conversiongif_webm/chat_web.webm</video>
<caption>Encrypted and secure text messaging without servers</caption>
<caption xml:lang="hu">Titkosított és biztonságos csevegési üzenetküldés, kiszolgálók nélkül</caption>
</screenshot>
<screenshot>
<video container="webm" codec="vp9" width="600" height="400">https://dl.jami.net/media-resources/gifs_features/conversiongif_webm/files-share_web.webm</video>
<caption>Transfer files of any size</caption>
<caption xml:lang="hu">Bármilyen méretű fájl küldése</caption>
</screenshot>
</screenshots>
<launchable type="desktop-id">net.jami.Jami.desktop</launchable>
<provides><binary>jami</binary></provides>
<!-- https://specifications.freedesktop.org/menu-spec/latest/apa.html -->
<!-- https://specifications.freedesktop.org/menu-spec/latest/apas02.html -->
<categories>
<category>Chat</category>
<category>FileTransfer</category>
<category>InstantMessaging</category>
<category>Network</category>
<category>P2P</category>
<category>Telephony</category>
<category>VideoConference</category>
</categories>
<translation type="gettext">jami-client-qt</translation>
<content_rating type="oars-1.1">
<content_attribute id="social-chat">intense</content_attribute>
<content_attribute id="social-audio">intense</content_attribute>
</content_rating>
<requires><id>net.jami.daemon</id></requires>
</component>

View File

@ -49,7 +49,7 @@ QT_MAJOR := 6
QT_MINOR := 6
QT_PATCH := 1
QT_TARBALL_CHECKSUM := dd3668f65645fe270bc615d748bd4dc048bd17b9dc297025106e6ecc419ab95d
DEBIAN_QT_VERSION := $(QT_MAJOR).$(QT_MINOR).$(QT_PATCH)-1
DEBIAN_QT_VERSION := $(QT_MAJOR).$(QT_MINOR).$(QT_PATCH)-0
DEBIAN_QT_DSC_FILENAME := libqt-jami_$(DEBIAN_QT_VERSION).dsc
QT_JAMI_PREFIX := /usr/lib/libqt-jami
@ -166,13 +166,11 @@ DISTRIBUTIONS := \
debian_unstable \
ubuntu_20.04 \
ubuntu_22.04 \
ubuntu_24.04 \
ubuntu_24.10 \
ubuntu_23.04 \
ubuntu_23.10 \
fedora_37 \
fedora_38 \
fedora_39 \
fedora_40 \
fedora_41 \
alma_9 \
opensuse-leap_15.4 \
opensuse-leap_15.5 \
@ -252,11 +250,10 @@ GUIX_PACK_FORMATS = deb rpm
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/applications/jami.desktop=share/applications/jami.desktop \
-S /usr/share/icons/hicolor/scalable/apps/jami.svg=share/icons/hicolor/scalable/apps/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 \
-S /usr/share/metainfo/jami.appdata.xml=share/metainfo/jami.appdata.xml \
$(and $(findstring deb,$(1)), \
--postinst-file=$(CURDIR)/extras/packaging/gnu-linux/guix/guix-pack-deb.postinst)
endef

View File

@ -12,7 +12,7 @@ RUN apt-get update --allow-releaseinfo-change && \
nasm
# As of January 2024, the default compiler on Debian testing is GCC 13.2.0, which
# is unable to build one of Qt 6.6.1's dependencies, see:
# can't build one of Qt 6.6.1's dependencies, see:
# https://github.com/qt/qtquick3d-assimp/commit/253f8bfa621a9fa6cd2c36291cdaa8c60c99322c
# The linked commit above fixes the problem and is included in more recent versions of Qt.
# For now, we use GCC 12 as a temporary workaround:
@ -27,7 +27,9 @@ RUN /opt/prebuild-package-debian.sh qt-deps
COPY extras/packaging/gnu-linux/rules/debian/control /tmp/builddeps/debian/control
RUN /opt/prebuild-package-debian.sh jami-deps
RUN apt-get remove -y libre2-dev libre2-11
# Install CMake 3.21 for Qt 6
ADD extras/packaging/gnu-linux/scripts/install-cmake.sh /opt/install-cmake.sh
RUN /opt/install-cmake.sh
ADD extras/packaging/gnu-linux/scripts/build-package-debian.sh /opt/build-package-debian.sh
CMD ["/opt/build-package-debian.sh"]

View File

@ -12,7 +12,7 @@ RUN apt-get update && \
wget
# As of January 2024, the default compiler on Debian unstable is GCC 13.2.0, which
# is unable to build one of Qt 6.6.1's dependencies, see:
# can't build one of Qt 6.6.1's dependencies, see:
# https://github.com/qt/qtquick3d-assimp/commit/253f8bfa621a9fa6cd2c36291cdaa8c60c99322c
# The linked commit above fixes the problem and is included in more recent versions of Qt.
# For now, we use GCC 12 as a temporary workaround:
@ -27,7 +27,9 @@ RUN /opt/prebuild-package-debian.sh qt-deps
COPY extras/packaging/gnu-linux/rules/debian/control /tmp/builddeps/debian/control
RUN /opt/prebuild-package-debian.sh jami-deps
RUN apt-get remove -y libre2-dev libre2-11
# Install CMake 3.21 for Qt 6
ADD extras/packaging/gnu-linux/scripts/install-cmake.sh /opt/install-cmake.sh
RUN /opt/install-cmake.sh
ADD extras/packaging/gnu-linux/scripts/build-package-debian.sh /opt/build-package-debian.sh
CMD ["/opt/build-package-debian.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,103 +0,0 @@
FROM fedora:41
RUN dnf clean all
RUN dnf update -y
RUN dnf install -y dnf-command\(builddep\) rpmdevtools && \
dnf install -y mock
RUN dnf group install -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 \
clang18-devel \
llvm18-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 \
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,6 +1,6 @@
FROM opensuse/leap:15.5
RUN zypper --gpg-auto-import-keys refresh
RUN zypper refresh
RUN zypper --non-interactive install -y \
dnf \

View File

@ -1,12 +1,10 @@
# This file is based on the examples at the following links:
# https://snapcraft.io/docs/build-on-docker
# https://github.com/canonical/snapcraft/issues/5079#issuecomment-2414199613
ARG RISK=edge
ARG UBUNTU=focal
FROM ubuntu:$UBUNTU AS builder
FROM ubuntu:$UBUNTU as builder
ARG RISK
ARG UBUNTU
RUN echo "Building snapcraft in ubuntu:$UBUNTU"
RUN echo "Building snapcraft:$RISK in ubuntu:$UBUNTU"
# Grab dependencies
RUN apt-get update
@ -16,23 +14,33 @@ RUN apt-get install --yes \
jq \
squashfs-tools
# Download and unpack the core22 snap
RUN curl -L $(curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/snaps/details/core22' | jq '.download_url' -r) --output core22.snap
# Grab the core snap (for backwards compatibility) from the stable channel and
# unpack it in the proper place.
RUN curl -L $(curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/snaps/details/core' | jq '.download_url' -r) --output core.snap
RUN mkdir -p /snap/core
RUN unsquashfs -d /snap/core/current core.snap
# Grab the core22 snap (which snapcraft uses as a base) from the stable channel
# and unpack it in the proper place.
RUN curl -L $(curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/snaps/details/core22?channel='$RISK | jq '.download_url' -r) --output core22.snap
RUN mkdir -p /snap/core22
RUN unsquashfs -d /snap/core22/current core22.snap
# Download and unpack the core20 snap
# Grab the core20 snap (which snapcraft uses as a base) from the stable channel
# and unpack it in the proper place.
RUN curl -L $(curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/snaps/details/core20' | jq '.download_url' -r) --output core20.snap
RUN mkdir -p /snap/core20
RUN unsquashfs -d /snap/core20/current core20.snap
# Download and unpack the core24 snap
RUN curl -L $(curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/snaps/details/core24' | jq '.download_url' -r) --output core24.snap
RUN mkdir -p /snap/core24
RUN unsquashfs -d /snap/core24/current core24.snap
# Grab the core20 snap (which snapcraft uses as a base) from the stable channel
# and unpack it in the proper place.
RUN curl -L $(curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/snaps/details/lxd' | jq '.download_url' -r) --output lxd.snap
RUN mkdir -p /snap/lxd
RUN unsquashfs -d /snap/lxd/current lxd.snap
# Download and unpack snapcraft
RUN curl -L $(curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/snaps/details/snapcraft' | jq '.download_url' -r) --output snapcraft.snap
# Grab the snapcraft snap from the $RISK channel and unpack it in the proper
# place.
RUN curl -L $(curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/snaps/details/snapcraft?channel='$RISK | jq '.download_url' -r) --output snapcraft.snap
RUN mkdir -p /snap/snapcraft
RUN unsquashfs -d /snap/snapcraft/current snapcraft.snap
@ -42,21 +50,21 @@ RUN unlink /snap/snapcraft/current/usr/bin/python3
RUN ln -s /snap/snapcraft/current/usr/bin/python3.* /snap/snapcraft/current/usr/bin/python3
RUN echo /snap/snapcraft/current/lib/python3.*/site-packages >> /snap/snapcraft/current/usr/lib/python3/dist-packages/site-packages.pth
# Create a snapcraft runner
# Create a snapcraft runner (TODO: move version detection to the core of
# snapcraft).
RUN mkdir -p /snap/bin
RUN echo "#!/bin/sh" > /snap/bin/snapcraft
RUN snap_version="$(awk '/^version:/{print $2}' /snap/snapcraft/current/meta/snap.yaml | tr -d \')" && echo "export SNAP_VERSION=\"$snap_version\"" >> /snap/bin/snapcraft
RUN echo 'exec "/snap/snapcraft/current/bin/python3" -m snapcraft "$@"' >> /snap/bin/snapcraft
RUN echo 'exec "$SNAP/usr/bin/python3" "$SNAP/bin/snapcraft" "$@"' >> /snap/bin/snapcraft
RUN chmod +x /snap/bin/snapcraft
# Multi-stage build, only need the snaps from the builder. Copy them one at a
# time so they can be cached.
FROM ubuntu:$UBUNTU
COPY --from=builder /snap/core20 /snap/core20
COPY --from=builder /snap/core /snap/core
COPY --from=builder /snap/core22 /snap/core22
COPY --from=builder /snap/core24 /snap/core24
COPY --from=builder /snap/core20 /snap/core20
COPY --from=builder /snap/lxd /snap/lxd
COPY --from=builder /snap/snapcraft /snap/snapcraft
COPY --from=builder /snap/bin/snapcraft /snap/bin/snapcraft
@ -75,6 +83,7 @@ ENV PATH="/snap/bin:/snap/snapcraft/current/usr/bin:/snap/snapcraft/current/libe
ENV SNAP="/snap/snapcraft/current"
ENV SNAP_NAME="snapcraft"
ENV SNAP_ARCH="amd64"
ENV DISABLE_PIPEWIRE=true
ADD extras/packaging/gnu-linux/scripts/build-package-snap.sh /opt/build-package-snap.sh
CMD ["/opt/build-package-snap.sh"]

View File

@ -1,4 +1,4 @@
FROM ubuntu:24.04
FROM ubuntu:23.04
ENV DEBIAN_FRONTEND=noninteractive
@ -10,9 +10,6 @@ RUN apt-get update && \
python-is-python3 \
wget
ADD extras/packaging/gnu-linux/scripts/install-gcc-debian.sh /opt/install-gcc-debian.sh
RUN /opt/install-gcc-debian.sh 13
ADD extras/packaging/gnu-linux/scripts/prebuild-package-debian.sh /opt/prebuild-package-debian.sh
COPY extras/packaging/gnu-linux/rules/debian-qt/control /tmp/builddeps/debian/control

View File

@ -1,4 +1,4 @@
FROM ubuntu:24.10
FROM ubuntu:23.10
ENV DEBIAN_FRONTEND=noninteractive
@ -10,6 +10,14 @@ RUN apt-get update && \
python-is-python3 \
wget
# The default compiler on Ubuntu 23.10, GCC 13.2.0, can't build one of Qt 6.6.1's
# dependencies, see:
# https://github.com/qt/qtquick3d-assimp/commit/253f8bfa621a9fa6cd2c36291cdaa8c60c99322c
# The linked commit above fixes the problem and is included in more recent versions of Qt.
# For now, we use GCC 12 as a temporary workaround:
ADD extras/packaging/gnu-linux/scripts/install-gcc-debian.sh /opt/install-gcc-debian.sh
RUN /opt/install-gcc-debian.sh 12
ADD extras/packaging/gnu-linux/scripts/prebuild-package-debian.sh /opt/prebuild-package-debian.sh
COPY extras/packaging/gnu-linux/rules/debian-qt/control /tmp/builddeps/debian/control
@ -18,12 +26,5 @@ RUN /opt/prebuild-package-debian.sh qt-deps
COPY extras/packaging/gnu-linux/rules/debian/control /tmp/builddeps/debian/control
RUN /opt/prebuild-package-debian.sh jami-deps
# Remove the libre2-dev package in order to force Qt to build using the bundled
# version of the RE2 library. This is necessary because the system version of the
# library on Ubuntu 24.10 (libre2-11) is not compatible with the one used in
# Qt 6.6.1 due to an API change:
# https://codereview.qt-project.org/c/qt/qtwebengine/+/516094
RUN apt-get remove -y libre2-dev libre2-11
ADD extras/packaging/gnu-linux/scripts/build-package-debian.sh /opt/build-package-debian.sh
CMD ["/opt/build-package-debian.sh"]

View File

@ -92,7 +92,6 @@ Build-Depends: debhelper (>= 9),
libgl1-mesa-dri,
# pkg-kde-tools (>= 0.15.17~),
python3:any,
python3-bs4,
python3-html5lib,
# qtbase5-private-dev (>= 5.15.2+dfsg~),
xauth <!nocheck>,

View File

@ -1,348 +0,0 @@
From 24fb774485f719df1e84dda31605d3f69202d69f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois-Simon=20Fauteux-Chapleau?=
<francois-simon.fauteux-chapleau@savoirfairelinux.com>
Date: Thu, 8 Aug 2024 14:59:17 -0400
Subject: [PATCH] qtwebengine: enable building with Python 3.12
Replace the deprecated imp module by importlib:
https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/524014
https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/534568
Update six to fix html5lib import failure:
https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/535605
https://issues.chromium.org/issues/40286977
---
.../protobufs/binary_proto_generator.py | 8 ++++++--
.../mojo/public/tools/mojom/mojom/fileutil.py | 1 -
.../tools/mojom/mojom/fileutil_unittest.py | 5 +----
.../mojom/mojom/generate/generator_unittest.py | 7 ++-----
.../mojom/mojom/generate/translate_unittest.py | 4 ----
.../tools/mojom/mojom/parse/ast_unittest.py | 6 ------
.../mojom/parse/conditional_features_unittest.py | 8 ++------
.../mojo/public/tools/mojom/mojom/parse/lexer.py | 1 -
.../tools/mojom/mojom/parse/lexer_unittest.py | 7 ++-----
.../tools/mojom/mojom/parse/parser_unittest.py | 5 -----
.../third_party/catapult/third_party/six/six.py | 16 ++++++++++++++++
11 files changed, 29 insertions(+), 39 deletions(-)
diff --git a/qtwebengine/src/3rdparty/chromium/components/resources/protobufs/binary_proto_generator.py b/qtwebengine/src/3rdparty/chromium/components/resources/protobufs/binary_proto_generator.py
index 2a1802dccdc..8b9de65ed0b 100755
--- a/qtwebengine/src/3rdparty/chromium/components/resources/protobufs/binary_proto_generator.py
+++ b/qtwebengine/src/3rdparty/chromium/components/resources/protobufs/binary_proto_generator.py
@@ -9,7 +9,7 @@
"""
from __future__ import print_function
import abc
-import imp
+from importlib import util as imp_util
import optparse
import os
import re
@@ -68,7 +68,11 @@ class GoogleProtobufModuleImporter:
raise ImportError(fullname)
filepath = self._fullname_to_filepath(fullname)
- return imp.load_source(fullname, filepath)
+ spec = imp_util.spec_from_file_location(fullname, filepath)
+ loaded = imp_util.module_from_spec(spec)
+ spec.loader.exec_module(loaded)
+
+ return loaded
class BinaryProtoGenerator:
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil.py
index 29daec367c5..124f12c134b 100644
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil.py
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil.py
@@ -3,7 +3,6 @@
# found in the LICENSE file.
import errno
-import imp
import os.path
import sys
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil_unittest.py
index 48eaf4eca94..c93d22898d2 100644
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil_unittest.py
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil_unittest.py
@@ -2,19 +2,16 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
import os.path
import shutil
-import sys
import tempfile
import unittest
from mojom import fileutil
-
class FileUtilTest(unittest.TestCase):
def testEnsureDirectoryExists(self):
- """Test that EnsureDirectoryExists fuctions correctly."""
+ """Test that EnsureDirectoryExists functions correctly."""
temp_dir = tempfile.mkdtemp()
try:
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/generator_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
index 76cda3981f3..7143e07c4d7 100644
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
@@ -2,12 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
+import importlib.util
import os.path
import sys
import unittest
-
def _GetDirAbove(dirname):
"""Returns the directory "above" this file containing |dirname| (which must
also be "above" this file)."""
@@ -20,12 +19,11 @@ def _GetDirAbove(dirname):
try:
- imp.find_module("mojom")
+ importlib.util.find_spec("mojom")
except ImportError:
sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
from mojom.generate import generator
-
class StringManipulationTest(unittest.TestCase):
"""generator contains some string utilities, this tests only those."""
@@ -69,6 +67,5 @@ class StringManipulationTest(unittest.TestCase):
self.assertEquals("SNAKE_D3D11_CASE",
generator.ToUpperSnakeCase("snakeD3d11Case"))
-
if __name__ == "__main__":
unittest.main()
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/translate_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
index 4259374513f..558e71e1193 100644
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
@@ -2,16 +2,12 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
-import os.path
-import sys
import unittest
from mojom.generate import module as mojom
from mojom.generate import translate
from mojom.parse import ast
-
class TranslateTest(unittest.TestCase):
"""Tests |parser.Parse()|."""
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/ast_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
index c36376712e7..b289f7b11f6 100644
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
@@ -2,14 +2,10 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
-import os.path
-import sys
import unittest
from mojom.parse import ast
-
class _TestNode(ast.NodeBase):
"""Node type for tests."""
@@ -20,13 +16,11 @@ class _TestNode(ast.NodeBase):
def __eq__(self, other):
return super().__eq__(other) and self.value == other.value
-
class _TestNodeList(ast.NodeListBase):
"""Node list type for tests."""
_list_item_type = _TestNode
-
class ASTTest(unittest.TestCase):
"""Tests various AST classes."""
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
index 5fc582025ee..2fa5d2be6ab 100644
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
@@ -2,12 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
+import importlib.util
import os
import sys
import unittest
-
def _GetDirAbove(dirname):
"""Returns the directory "above" this file containing |dirname| (which must
also be "above" this file)."""
@@ -18,9 +17,8 @@ def _GetDirAbove(dirname):
if tail == dirname:
return path
-
try:
- imp.find_module('mojom')
+ importlib.util.find_spec("mojom")
except ImportError:
sys.path.append(os.path.join(_GetDirAbove('pylib'), 'pylib'))
import mojom.parse.ast as ast
@@ -29,7 +27,6 @@ import mojom.parse.parser as parser
ENABLED_FEATURES = frozenset({'red', 'green', 'blue'})
-
class ConditionalFeaturesTest(unittest.TestCase):
"""Tests |mojom.parse.conditional_features|."""
@@ -356,6 +353,5 @@ class ConditionalFeaturesTest(unittest.TestCase):
conditional_features.RemoveDisabledDefinitions,
definition, ENABLED_FEATURES)
-
if __name__ == '__main__':
unittest.main()
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer.py
index 73ca15df94c..1083a1af7bb 100644
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer.py
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer.py
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
import os.path
import sys
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
index ce376da66e0..bc9f8354316 100644
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
@@ -2,12 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
+import importlib.util
import os.path
import sys
import unittest
-
def _GetDirAbove(dirname):
"""Returns the directory "above" this file containing |dirname| (which must
also be "above" this file)."""
@@ -18,17 +17,15 @@ def _GetDirAbove(dirname):
if tail == dirname:
return path
-
sys.path.insert(1, os.path.join(_GetDirAbove("mojo"), "third_party"))
from ply import lex
try:
- imp.find_module("mojom")
+ importlib.util.find_spec("mojom")
except ImportError:
sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
import mojom.parse.lexer
-
# This (monkey-patching LexToken to make comparison value-based) is evil, but
# we'll do it anyway. (I'm pretty sure ply's lexer never cares about comparing
# for object identity.)
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/parser_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
index 0513343ec7e..0a26307b1a3 100644
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
@@ -2,16 +2,12 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
-import os.path
-import sys
import unittest
from mojom.parse import ast
from mojom.parse import lexer
from mojom.parse import parser
-
class ParserTest(unittest.TestCase):
"""Tests |parser.Parse()|."""
@@ -1375,6 +1371,5 @@ class ParserTest(unittest.TestCase):
r" *associated\? MyInterface& a;$"):
parser.Parse(source3, "my_file.mojom")
-
if __name__ == "__main__":
unittest.main()
diff --git a/qtwebengine/src/3rdparty/chromium/third_party/catapult/third_party/six/six.py b/qtwebengine/src/3rdparty/chromium/third_party/catapult/third_party/six/six.py
index 83f69783d1a..5e7f0ce4437 100644
--- a/qtwebengine/src/3rdparty/chromium/third_party/catapult/third_party/six/six.py
+++ b/qtwebengine/src/3rdparty/chromium/third_party/catapult/third_party/six/six.py
@@ -71,6 +71,11 @@ else:
MAXSIZE = int((1 << 63) - 1)
del X
+if PY34:
+ from importlib.util import spec_from_loader
+else:
+ spec_from_loader = None
+
def _add_doc(func, doc):
"""Add documentation to a function."""
@@ -186,6 +191,11 @@ class _SixMetaPathImporter(object):
return self
return None
+ def find_spec(self, fullname, path, target=None):
+ if fullname in self.known_modules:
+ return spec_from_loader(fullname, self)
+ return None
+
def __get_module(self, fullname):
try:
return self.known_modules[fullname]
@@ -223,6 +233,12 @@ class _SixMetaPathImporter(object):
return None
get_source = get_code # same as get_code
+ def create_module(self, spec):
+ return self.load_module(spec.name)
+
+ def exec_module(self, module):
+ pass
+
_importer = _SixMetaPathImporter(__name__)
--
2.34.1

View File

@ -1,16 +0,0 @@
qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp b/qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp
index 3488120543..120e47a76f 100644
--- a/qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp
+++ b/qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp
@@ -472,7 +472,7 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
}
catch (const DeadlyImportError& e)
{
- if (!is64bits && (length > std::numeric_limits<std::uint32_t>::max())) {
+ if (!is64bits && (length > std::numeric_limits<uint32_t>::max())) {
throw DeadlyImportError("The FBX file is invalid. This may be because the content is too big for this older version (", ai_to_string(version), ") of the FBX format. (", e.what(), ")");
}
throw;

View File

@ -1,26 +0,0 @@
From cf208d11dc8a9a02160a57283596ec8bab964a09 Mon Sep 17 00:00:00 2001
From: Sebastien Blin <sebastien.blin@savoirfairelinux.com>
Date: Mon, 27 May 2024 16:01:21 -0400
Subject: [PATCH] qtwayland: downgrade wl-seat to avoid high-resolution
scrolling events
---
qtwayland/src/client/qwaylandinputdevice.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/qtwayland/src/client/qwaylandinputdevice.cpp b/qtwayland/src/client/qwaylandinputdevice.cpp
index a4f8757e3c..ad0aa7941c 100644
--- a/qtwayland/src/client/qwaylandinputdevice.cpp
+++ b/qtwayland/src/client/qwaylandinputdevice.cpp
@@ -383,7 +383,7 @@ QWaylandInputDevice::Touch::~Touch()
}
QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version, uint32_t id)
- : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 9))
+ : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 7))
, mQDisplay(display)
, mDisplay(display->wl_display())
{
--
2.45.0

View File

@ -1,40 +0,0 @@
From 420b3e5ac2e91b7a99488ac34577e2798a84a68c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois-Simon=20Fauteux-Chapleau?=
<francois-simon.fauteux-chapleau@savoirfairelinux.com>
Date: Tue, 6 Aug 2024 17:35:56 -0400
Subject: [PATCH] qtbase: fix CMake error
For more information, see:
https://github.com/qt/qtbase/commit/3411f2984a5325a35e3bed1f961e5973d8a565b9
---
qtbase/configure.cmake | 1 +
qtbase/src/corelib/CMakeLists.txt | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/qtbase/configure.cmake b/qtbase/configure.cmake
index 43de2aa026..37a82dcdb6 100644
--- a/qtbase/configure.cmake
+++ b/qtbase/configure.cmake
@@ -18,6 +18,7 @@ if(TARGET ZLIB::ZLIB)
set_property(TARGET ZLIB::ZLIB PROPERTY IMPORTED_GLOBAL TRUE)
endif()
+qt_find_package(Threads PROVIDED_TARGETS Threads::Threads)
qt_find_package(WrapOpenSSLHeaders PROVIDED_TARGETS WrapOpenSSLHeaders::WrapOpenSSLHeaders MODULE_NAME core)
# openssl_headers
# OPENSSL_VERSION_MAJOR is not defined for OpenSSL 1.1.1
diff --git a/qtbase/src/corelib/CMakeLists.txt b/qtbase/src/corelib/CMakeLists.txt
index 31b81734e8..b62e2f763b 100644
--- a/qtbase/src/corelib/CMakeLists.txt
+++ b/qtbase/src/corelib/CMakeLists.txt
@@ -1,7 +1,6 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-qt_find_package(Threads PROVIDED_TARGETS Threads::Threads)
qt_find_package(WrapPCRE2 PROVIDED_TARGETS WrapPCRE2::WrapPCRE2)
qt_find_package(WrapZLIB PROVIDED_TARGETS WrapZLIB::WrapZLIB)
--
2.34.1

View File

@ -1,40 +0,0 @@
From 4c7360faeb0fb7f1dfd995619fb8c596b4e15606 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois-Simon=20Fauteux-Chapleau?=
<francois-simon.fauteux-chapleau@savoirfairelinux.com>
Date: Thu, 8 Aug 2024 10:29:43 -0400
Subject: [PATCH] qtwebengine: add missing chromium dependencies
For more information, see:
https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/555586
---
chromium/content/public/browser/BUILD.gn | 1 +
chromium/extensions/browser/api/declarative_net_request/BUILD.gn | 1 +
2 files changed, 2 insertions(+)
diff --git a/qtwebengine/src/3rdparty/chromium/content/public/browser/BUILD.gn b/qtwebengine/src/3rdparty/chromium/content/public/browser/BUILD.gn
index b25bf5764e7..dfbfb2ec77b 100644
--- a/qtwebengine/src/3rdparty/chromium/content/public/browser/BUILD.gn
+++ b/qtwebengine/src/3rdparty/chromium/content/public/browser/BUILD.gn
@@ -515,6 +515,7 @@ jumbo_source_set("browser_sources") {
"//cc",
"//components/services/storage/public/cpp",
"//components/viz/host",
+ "//components/spellcheck:buildflags",
"//content/browser", # Must not be public_deps!
"//device/fido",
"//gpu",
diff --git a/qtwebengine/src/3rdparty/chromium/extensions/browser/api/declarative_net_request/BUILD.gn b/qtwebengine/src/3rdparty/chromium/extensions/browser/api/declarative_net_request/BUILD.gn
index 1fc492f5a0c..13a266e22f1 100644
--- a/qtwebengine/src/3rdparty/chromium/extensions/browser/api/declarative_net_request/BUILD.gn
+++ b/qtwebengine/src/3rdparty/chromium/extensions/browser/api/declarative_net_request/BUILD.gn
@@ -23,6 +23,7 @@ source_set("declarative_net_request") {
"//extensions/common",
"//extensions/common/api",
"//services/preferences/public/cpp",
+ "//components/web_cache/browser",
]
public_deps = [ "//extensions/browser:browser_sources" ]
--
2.34.1

View File

@ -1,49 +0,0 @@
From ab6d5bebaf68a9f4d00440b2adbaffe0e5b2ae6c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois-Simon=20Fauteux-Chapleau?=
<francois-simon.fauteux-chapleau@savoirfairelinux.com>
Date: Thu, 8 Aug 2024 10:55:08 -0400
Subject: [PATCH] qtwebengine: fix libxml2 build error
Version 2.12 of libxml2 introduced a change that broke chromium's build,
see: https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/523633
---
.../third_party/blink/renderer/core/xml/xslt_processor.h | 5 +++++
.../blink/renderer/core/xml/xslt_processor_libxslt.cc | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor.h b/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor.h
index d53835e9675..72536e4fd7d 100644
--- a/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor.h
+++ b/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor.h
@@ -77,7 +77,12 @@ class XSLTProcessor final : public ScriptWrappable {
void reset();
+#if LIBXML_VERSION >= 21200
+ static void ParseErrorFunc(void* user_data, const xmlError*);
+#else
static void ParseErrorFunc(void* user_data, xmlError*);
+#endif
+
static void GenericErrorFunc(void* user_data, const char* msg, ...);
// Only for libXSLT callbacks
diff --git a/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor_libxslt.cc b/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor_libxslt.cc
index 133e0b3355d..e8e6a09f485 100644
--- a/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor_libxslt.cc
+++ b/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor_libxslt.cc
@@ -66,7 +66,11 @@ void XSLTProcessor::GenericErrorFunc(void*, const char*, ...) {
// It would be nice to do something with this error message.
}
+#if LIBXML_VERSION >= 21200
+void XSLTProcessor::ParseErrorFunc(void* user_data, const xmlError* error) {
+#else
void XSLTProcessor::ParseErrorFunc(void* user_data, xmlError* error) {
+#endif
FrameConsole* console = static_cast<FrameConsole*>(user_data);
if (!console)
return;
--
2.34.1

View File

@ -1,34 +0,0 @@
From 6e0848a1c51c6494e3b7410c5fe38941d48fcb36 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, 16 Oct 2024 22:32:12 -0400
Subject: [PATCH] qtwebengine: fix v8 build error
In file included from ../../../3rdparty/chromium/v8/src/heap/cppgc/sweeper.h:14,
from ./../../../3rdparty/chromium/v8/src/heap/cppgc/sweeper.cc:5,
from gen/v8/cppgc_base_jumbo_7.cc:5:
../../../3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h: In member function 'void cppgc::internal::StatsCollector::ForAllAllocationObservers(Callback)':
../../../3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h:401:48: error: cannot convert 'std::vector&lt;cppgc::internal::StatsCollector::AllocationObserver*&gt;::iterator' to 'const char*'
401 | std::remove(allocation_observers_.begin(), allocation_observers_.end(),
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
| |
| std::vector&lt;cppgc::internal::StatsCollector::AllocationObserver*&gt;::iterator
---
.../src/3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/qtwebengine/src/3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h b/qtwebengine/src/3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h
index 2cf728489d..d8414ae3c6 100644
--- a/qtwebengine/src/3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h
+++ b/qtwebengine/src/3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h
@@ -8,6 +8,7 @@
#include <stddef.h>
#include <stdint.h>
+#include <algorithm>
#include <atomic>
#include <vector>
--
2.47.0

View File

@ -1,7 +0,0 @@
0001-qtwebengine-enable-building-with-Python-3.12.patch
0002-fix-binary-tokenizer.patch
0003-qtwayland-downgrade-wl-seat-to-avoid-high-resolution.patch
0004-qtbase-fix-CMake-error.patch
0005-qtwebengine-add-missing-chromium-dependencies.patch
0006-qtwebengine-fix-libxml2-build-error.patch
0007-qtwebengine-fix-v8-build-error.patch

View File

@ -22,7 +22,7 @@ Build-Depends: debhelper (>= 9),
libpulse-dev,
libasound2-dev,
libexpat1-dev,
libpcre3-dev | libpcre2-dev,
libpcre3-dev,
libyaml-cpp-dev,
libboost-dev,
libxext-dev,

View File

@ -99,10 +99,10 @@ if [ -f /etc/os-release ]; then
ENDTAG="ubuntu_20.04"
elif [ "${UBUNTU_CODENAME}" = "jammy" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_22.04" ]; then
ENDTAG="ubuntu_22.04"
elif [ "${UBUNTU_CODENAME}" = "noble" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_24.04" ]; then
ENDTAG="ubuntu_24.04"
elif [ "${UBUNTU_CODENAME}" = "oracular" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_24.10" ]; then
ENDTAG="ubuntu_24.10"
elif [ "${UBUNTU_CODENAME}" = "lunar" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_23.04" ]; then
ENDTAG="ubuntu_23.04"
elif [ "${UBUNTU_CODENAME}" = "mantic" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_23.10" ]; then
ENDTAG="ubuntu_23.10"
elif [ "${ID}" = "debian" ] && \
[ "$(command -v lsb_release)" ] && \
[ "$(lsb_release -rs)" = "testing" ]; then

View File

@ -29,8 +29,6 @@ License: GPLv3+
Vendor: Savoir-faire Linux Inc.
URL: https://jami.net/
Source: jami-libqt-%{version}.tar.xz
Patch0: 0001-fix-gcc14.patch
Patch1: 0002-qtwebengine-add-missing-chromium-dependencies.patch
%global gst 0.10
%if 0%{?fedora} || 0%{?rhel} > 7
@ -66,8 +64,6 @@ This package contains Qt libraries for Jami.
%prep
%setup -n qt-everywhere-src-%{version}
%patch -P 0 -p1
%patch -P 1 -p1
%build
echo "Building Qt using %{job_count} parallel jobs"

View File

@ -2,16 +2,6 @@
%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.*$
@ -79,7 +69,6 @@ cd %{_builddir}/jami-%{version} && \
-DLIBJAMI_BUILD_DIR=%{_builddir}/jami-%{version}/daemon/src \
-DCMAKE_INSTALL_PREFIX=%{_prefix} \
-DCMAKE_INSTALL_LIBDIR=%{_libdir} \
-DAPPSTREAM_CATALOG_DIR=%{appstream_catalog_dir} \
-DWITH_DAEMON_SUBMODULE=true \
-DCMAKE_BUILD_TYPE=Release \
..
@ -91,12 +80,11 @@ DESTDIR=%{buildroot} make -C %{_builddir}/jami-%{version}/build install V=2
%files
%defattr(-,root,root,-)
%{_bindir}/jami
%{_datadir}/applications/net.jami.Jami.desktop
%{_datadir}/jami/net.jami.Jami.desktop
%{_datadir}/applications/jami.desktop
%{_datadir}/jami/jami.desktop
%{_datadir}/icons/hicolor/scalable/apps/jami.svg
%{_datadir}/icons/hicolor/48x48/apps/jami.png
%{_datadir}/pixmaps/jami.xpm
%{_datadir}/metainfo/net.jami.Jami.metainfo.xml
%{_prefix}%{appstream_catalog_dir}/jami.xml
%{_datadir}/metainfo/jami.appdata.xml
%{_datadir}/jami/translations/*
%doc %{_mandir}/man1/jami*

View File

@ -1,26 +0,0 @@
From 9721082687c9529fe6ae3c5304dcf079158e8a77 Mon Sep 17 00:00:00 2001
From: Sam James <sam@gentoo.org>
Date: Sun, 04 Jun 2023 04:15:16 +0100
Subject: [PATCH] heap: Add missing <algorithm> include for std::remove
GCC 14 changes some internal includes within libstdc++ so this transient
include gets lost. Include <algorithm> explicitly for std::remove.
Change-Id: Iab8a2c751a0f9c9dc6a770d6296ad6de724ef3bb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4583222
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#88037}
---
diff --git a/qtwebengine/src/3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h b/qtwebengine/src/3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h
index 2cf728489d..d8414ae3c6 100644
--- a/qtwebengine/src/3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h
+++ b/qtwebengine/src/3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h
@@ -8,6 +8,7 @@
#include <stddef.h>
#include <stdint.h>
+#include <algorithm>
#include <atomic>
#include <vector>

View File

@ -1,40 +0,0 @@
From 04778c7f54c8a1a0e7fced75c5ef39ced82cece1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois-Simon=20Fauteux-Chapleau?=
<francois-simon.fauteux-chapleau@savoirfairelinux.com>
Date: Sat, 12 Oct 2024 16:21:35 -0400
Subject: [PATCH] qtwebengine: add missing chromium dependencies
For more information, see:
https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/555586
---
chromium/content/public/browser/BUILD.gn | 1 +
chromium/extensions/browser/api/declarative_net_request/BUILD.gn | 1 +
2 files changed, 2 insertions(+)
diff --git a/qtwebengine/src/3rdparty/chromium/content/public/browser/BUILD.gn b/qtwebengine/src/3rdparty/chromium/content/public/browser/BUILD.gn
index b25bf5764e7..dfbfb2ec77b 100644
--- a/qtwebengine/src/3rdparty/chromium/content/public/browser/BUILD.gn
+++ b/qtwebengine/src/3rdparty/chromium/content/public/browser/BUILD.gn
@@ -515,6 +515,7 @@ jumbo_source_set("browser_sources") {
"//cc",
"//components/services/storage/public/cpp",
"//components/viz/host",
+ "//components/spellcheck:buildflags",
"//content/browser", # Must not be public_deps!
"//device/fido",
"//gpu",
diff --git a/qtwebengine/src/3rdparty/chromium/extensions/browser/api/declarative_net_request/BUILD.gn b/qtwebengine/src/3rdparty/chromium/extensions/browser/api/declarative_net_request/BUILD.gn
index 1fc492f5a0c..13a266e22f1 100644
--- a/qtwebengine/src/3rdparty/chromium/extensions/browser/api/declarative_net_request/BUILD.gn
+++ b/qtwebengine/src/3rdparty/chromium/extensions/browser/api/declarative_net_request/BUILD.gn
@@ -23,6 +23,7 @@ source_set("declarative_net_request") {
"//extensions/common",
"//extensions/common/api",
"//services/preferences/public/cpp",
+ "//components/web_cache/browser",
]
public_deps = [ "//extensions/browser:browser_sources" ]
--
2.47.0

View File

@ -51,53 +51,37 @@ icon: common/icons/jami.svg
license: GPL-3.0+
summary: 'Privacy-oriented voice, video, chat, and conference platform and SIP phone'
description: |
Jami, a GNU package, is software for universal and distributed peer-to-peer communication that respects the freedom and privacy of its users.
Jami is free software for universal communication that respects the
freedom and privacy of its users.
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.
Jami is an end-to-end encrypted secure and distributed voice, video,
and chat communication platform. Jami requires no central server
for these communications and leaves the power of privacy and freedom
in the hands of users.
Jami is a free/libre, end-to-end encrypted, and private communication platform.
Jami provides the following key features to its users:
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.
* Synchronized and enriched one-to-one conversations
* Group conversations (Beta)
* Audio/video calls and conferences
* Screen sharing in video calls and conferences
* Unlimited peer-to-peer file sharing
* Recording and sending audio/video messages
* Jami plugins SDK for additional functionality (green screen,
watermark, audio filters, and more)
* Use Jami as your SIP phone with a variety of media codecs
and VoIP providers
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.
Client applications for GNU/Linux, Windows, macOS, iOS, Android,
and Android TV are available, making Jami an interoperable and
cross-platform communication framework.
This gives the greatest privacy as the distributed nature of Jami means your calls are only between participants.
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.
Jami can also function as a SIP client.
Jami has multiple extensions available: Audio Filter; Auto Answer; Green Screen; Watermark; and, Whisper Transcript.
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.
Jami is available for GNU/Linux, Windows, macOS, iOS, Android, and Android TV, making Jami an interoperable and cross-platform communication framework.
Manage multiple SIP accounts, Jami accounts and JAMS accounts with the Jami client installed on one or multiple devices.
Jami is free, unlimited, private, advertising free, compatible, fast, autonomous, and anonymous.
Learn more about:
Jami: https://jami.net/
Jami extensions: https://jami.net/extensions/
“Jami Account Management Server” (JAMS): https://jami.biz/
Jami documentation: https://docs.jami.net/
Follow us for more:
Mastodon: https://mstdn.io/@Jami
X: https://x.com/jami_social
YouTube: https://www.youtube.com/@jami9311
Wed love to hear from you! Join the Jami community:
Contribute: https://jami.net/contribute/
Forum: https://forum.jami.net/
Build with Jami on your IoT project: re-use the universal communications technology of Jami with its portable library on your system of choice.
Jami for Android TV is tested on NVIDIA SHIELD TV with Logitech cameras.
Jami is published under the GPL license, version 3 or higher.
Copyright © Savoir-faire Linux Inc.
Seamlessly deploy Jami in your organization using
Jami Account Management Server (JAMS). With JAMS, you can enable
your users to connect using their LDAP/ActiveDirectory credentials
or create local accounts, enabling you to manage your own
Jami community while taking advantage of Jami's distributed network
architecture. For more information, please visit https://jami.biz.
confinement: strict
grade: stable
@ -151,13 +135,9 @@ apps:
- bin/desktop-launch
- snap/command-chain/alsa-launch
- bin/jami-wrapper
autostart: net.jami.Jami.desktop
autostart: jami.desktop
common-id: net.jami.Jami
desktop: usr/share/applications/net.jami.Jami.desktop
environment:
PIPEWIRE_CONFIG_NAME: "$SNAP/usr/share/pipewire/pipewire.conf"
PIPEWIRE_MODULE_DIR: "$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/pipewire-0.3"
SPA_PLUGIN_DIR: "$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/spa-0.2"
desktop: usr/share/applications/jami.desktop
slots:
- dbus-jami
- dbus-ring
@ -188,7 +168,7 @@ package-repositories:
components: [main]
suites: [jami]
key-id: A295D773307D25A33AE72F2F64CD5FA175348F84
url: https://dl.jami.net/internal/ubuntu_20.04/
url: https://dl.jami.net/nightly/ubuntu_20.04/
parts:
desktop-launch:
@ -258,7 +238,7 @@ parts:
after: [alsa-mixin]
source: .
plugin: nil
parse-info: [usr/share/metainfo/net.jami.Jami.metainfo.xml]
parse-info: [usr/share/metainfo/jami.appdata.xml]
stage:
- -usr/lib/x86_64-linux-gnu/liblber-2.4.so*
- -usr/lib/x86_64-linux-gnu/libldap_r-2.4.so*
@ -273,10 +253,8 @@ 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/jami.svg|' extras/data/jami.desktop
override-build: |
$SNAPCRAFT_PART_BUILD/extras/packaging/gnu-linux/scripts/install-pipewire-from-source.sh
cd $SNAPCRAFT_PART_BUILD/daemon/contrib
mkdir -p native
cd native
@ -326,6 +304,7 @@ parts:
- libswscale-dev
- libva-dev
- libvdpau-dev
- libpipewire-0.2-dev
- libargon2-0-dev # opendht
- libexpat1-dev
- libjsoncpp-dev
@ -341,12 +320,6 @@ parts:
- libgnutls28-dev # TLS
- gnutls-bin
- libssl-dev
- git # PipeWire build dependencies
- libasound2-dev #
- libdbus-1-dev # These packages are needed by the
- libglib2.0-dev # install-pipewire-from-source.sh
- ninja-build # script in order to build PipeWire
- pkg-config # from source.
stage-packages:
- libgnutls30
- libavutil56

View File

@ -44,23 +44,6 @@ QUILT_REFRESH_ARGS="-p 1"
if [ ! -f "${qt_deb_path}" ] || [ "${FORCE_REBUILD_QT}" = "true" ]; then
(
# HACK: For now on ubuntu 24.04 there is no python3.10 package
# So create a PyEnv environment to install the required packages
if cat /etc/os-release | grep -Eq "24.04"; then
apt-get install git gcc make python3-pip libssl-dev curl libreadline-dev -y
curl https://pyenv.run | bash
export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
pyenv install 3.10.0
pyenv local 3.10.0
python -m pip install html5lib
python -m pip install six
fi
flock 9 # block until the lock file is gone
test -f "${qt_deb_path}" && exit 0 # check again

View File

@ -35,7 +35,6 @@ rpmdev-setuptree
# Copy the source tarball.
cp --reflink=auto "/src/$RELEASE_TARBALL_FILENAME" /root/rpmbuild/SOURCES
cp patches/*.patch /root/rpmbuild/SOURCES/
QT_JAMI_PREFIX="/usr/lib64/qt-jami"
PATH="${QT_JAMI_PREFIX}/bin:${PATH}"
@ -44,8 +43,8 @@ PKG_CONFIG_PATH="${QT_JAMI_PREFIX}/lib/pkgconfig:${PKG_CONFIG_PATH}"
CMAKE_PREFIX_PATH="${QT_JAMI_PREFIX}/lib/cmake:${CMAKE_PREFIX_PATH}"
QT_MAJOR=6
QT_MINOR=6
QT_PATCH=3
QT_RELEASE_PATCH=0
QT_PATCH=1
QT_RELEASE_PATCH=1
QT_MAJOR_MINOR=${QT_MAJOR}.${QT_MINOR}
QT_MAJOR_MINOR_PATCH=${QT_MAJOR}.${QT_MINOR}.${QT_PATCH}
@ -53,7 +52,7 @@ QT_MAJOR_MINOR_PATCH=${QT_MAJOR}.${QT_MINOR}.${QT_PATCH}
QT_TARBALL_URL=https://download.qt.io/archive/qt/$QT_MAJOR_MINOR/\
$QT_MAJOR_MINOR_PATCH/single/qt-everywhere-src-$QT_MAJOR_MINOR_PATCH.tar.xz
QT_TARBALL_SHA256="69d0348fef415da98aa890a34651e9cfb232f1bffcee289b7b4e21386bf36104"
QT_TARBALL_SHA256="dd3668f65645fe270bc615d748bd4dc048bd17b9dc297025106e6ecc419ab95d"
QT_TARBALL_FILE_NAME=$(basename "$QT_TARBALL_URL")
CACHED_QT_TARBALL=$TARBALLS/$QT_TARBALL_FILE_NAME
@ -112,10 +111,6 @@ if [ ! -f "${RPM_PATH}" ]; then
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc38.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}" == "alma_9" ]]; then
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.el9.x86_64.rpm "${RPM_PATH}"
else

View File

@ -1,35 +0,0 @@
#!/usr/bin/env bash
# The purpose of this script is to build PipeWire from source in a snap based on core20 / Ubuntu 20.04
# It must be called in the "override-build" section of the relevant part in snapcraft.yaml
set -e
OLD_WD=$(pwd)
cd /tmp
# Get a version of Meson that's recent enough to build PipeWire 1.0.5 (the one available via apt is too old)
wget -q https://github.com/mesonbuild/meson/releases/download/0.61.1/meson-0.61.1.tar.gz
echo "feb2cefb325b437dbf36146df7c6b87688ddff0b0205caa31dc64055c6da410c meson-0.61.1.tar.gz" | sha256sum --check
tar xzf meson-0.61.1.tar.gz
# Build PipeWire 1.0.5 and install it in the /usr directory of the build environment
wget -q https://gitlab.freedesktop.org/pipewire/pipewire/-/archive/1.0.5/pipewire-1.0.5.tar.gz
echo "c5a5de26d684a1a84060ad7b6131654fb2835e03fccad85059be92f8e3ffe993 pipewire-1.0.5.tar.gz" | sha256sum --check
tar xzf pipewire-1.0.5.tar.gz
cd pipewire-1.0.5
../meson-0.61.1/meson.py setup builddir -Dsession-managers=media-session -Dalsa=disabled -Dprefix=/usr
../meson-0.61.1/meson.py compile -C builddir
../meson-0.61.1/meson.py install -C builddir
# The files installed by the previous command are only for the "Build" step of the snap
# creation process (https://snapcraft.io/docs/how-snapcraft-builds). In order to ensure
# that PipeWire is installed in the final snap archive, we also need to copy all the
# required files under the $SNAPCRAFT_PART_INSTALL directory.
../meson-0.61.1/meson.py configure builddir -Dprefix=$SNAPCRAFT_PART_INSTALL/usr/
../meson-0.61.1/meson.py install -C builddir
# Cleanup
cd /tmp
rm -rf meson-0.61.1 meson-0.61.1.tar.gz pipewire-1.0.5 pipewire-1.0.5.tar.gz
cd $OLD_WD

View File

@ -34,7 +34,7 @@ cat << EOFILE > ${REPO_FOLDER}/${SPARKLE_FILE}
<pubDate>$DATE_RFC2822</pubDate>
<sparkle:version>${BUILD}</sparkle:version>
<sparkle:shortVersionString>${VERSION}</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>11.0</sparkle:minimumSystemVersion>
<sparkle:minimumSystemVersion>10.15.0</sparkle:minimumSystemVersion>
<enclosure url="${REPO_URL}/$(basename ${PACKAGE})" type="application/octet-stream" $(./sign_update ${PACKAGE}) />
</item>
$(echo -e "${ITEMS}")

View File

@ -99,12 +99,6 @@ for ARCH in "${ARCHS[@]}"; do
echo "$ARCH"
cd "$DAEMON"
HOST="${ARCH}-apple-darwin"
SDKROOT=$(xcrun --sdk macosx --show-sdk-path)
CC="xcrun -sdk macosx clang"
CXX="xcrun -sdk macosx clang++"
CFLAGS="-arch $ARCH -isysroot $SDKROOT"
CXXFLAGS="-std=c++17 $CFLAGS"
CONFIGURE_FLAGS=" --without-dbus --host=${HOST} -with-contrib=$DAEMON/contrib/${ARCH}-apple-darwin${OS_VER} --prefix=${INSTALL}/daemon/$ARCH"
if [ "${debug}" = "true" ]; then
@ -119,11 +113,7 @@ for ARCH in "${ARCHS[@]}"; do
mkdir -p "build-macos-${ARCH}"
cd "build-macos-${ARCH}"
"$DAEMON"/configure $CONFIGURE_FLAGS ARCH="$ARCH" \
CC="$CC $CFLAGS" \
CXX="$CXX $CXXFLAGS" \
CFLAGS="$CFLAGS" \
CXXFLAGS="$CXXFLAGS" || exit 1
"$DAEMON"/configure $CONFIGURE_FLAGS ARCH="$ARCH" || exit 1
echo "$CONFIGURE_FLAGS"

View File

@ -1,89 +1,61 @@
#!/usr/bin/env bash
set -e
# Usage:
# ./build_qrencode.sh -a <architecture>
# Accepted architectures: arm64, x86_64, unified
# If no architecture is specified, the script builds for the host architecture.
# Flags:
# -a: architecture to build. Accepted values arm64, x86_64, unified
# Initialize variables
arch=''
while getopts "a:" OPT; do
case "$OPT" in
a)
arch="${OPTARG}"
;;
\?)
echo "Invalid option: -$OPTARG" >&2
echo "Usage: $0 [-a architecture]"
echo "Accepted architectures: arm64, x86_64, unified"
exit 1
;;
a)
arch="${OPTARG}"
;;
\?)
exit 1
;;
esac
done
# Determine architectures to build
if [[ "$arch" == 'unified' ]]; then
ARCHS=("arm64" "x86_64")
elif [[ "$arch" == '' ]]; then
# Detect host architecture
HOST_ARCH=$(uname -m)
case "$HOST_ARCH" in
x86_64|arm64)
ARCHS=("$HOST_ARCH")
;;
*)
echo "Unsupported host architecture: $HOST_ARCH"
echo "Supported architectures are: arm64, x86_64, unified"
exit 1
;;
esac
ARCHS=("arm64")
else
# Validate specified architecture
case "$arch" in
x86_64|arm64)
ARCHS=("$arch")
;;
*)
echo "Invalid architecture specified: $arch"
echo "Accepted architectures are: arm64, x86_64, unified"
exit 1
;;
esac
ARCHS=("$arch")
fi
TOP="$(pwd)"
QRENCODEDIR="${TOP}/3rdparty/libqrencode"
BUILDDIR="${QRENCODEDIR}/build-libqrencode"
LIBDIR="${QRENCODEDIR}/lib"
INCLUDEDIR="${QRENCODEDIR}/include"
if [ -z "$NPROC" ]; then
NPROC=$(sysctl -n hw.ncpu || echo -n 1)
fi
# Clean up build directory
echo "Preparing clean build directory"
rm -rf "$BUILDDIR"
mkdir -p "$BUILDDIR"
for ARCH in "${ARCHS[@]}"; do
cd "$QRENCODEDIR" || exit 1
BUILDDIR="$ARCH-libqrencode"
mkdir "$BUILDDIR"
make clean
./autogen.sh
./configure --host="$ARCH" --without-png --prefix="${QRENCODEDIR}/${BUILDDIR}" CFLAGS=" -arch $ARCH $CFLAGS"
make -j"$NPROC"
make install
done
mkdir -p "$QRENCODEDIR"/lib
mkdir -p "$QRENCODEDIR"/include
# Clean output directories
rm -rf "$LIBDIR" "$INCLUDEDIR"
mkdir -p "$LIBDIR"
mkdir -p "$INCLUDEDIR"
if ((${#ARCHS[@]} == "2")); then
echo "Making fat lib for ${ARCHS[0]} and ${ARCHS[1]}"
LIBFILES="$QRENCODEDIR/${ARCHS[0]}-libqrencode/lib/*.a"
for f in $LIBFILES; do
libFile=${f##*/}
echo "$libFile"
lipo -create "$QRENCODEDIR/${ARCHS[0]}-libqrencode/lib/$libFile" \
"$QRENCODEDIR/${ARCHS[1]}-libqrencode/lib/$libFile" \
-output "${QRENCODEDIR}/lib/$libFile"
done
else
echo "No need for fat lib"
rsync -ar --delete "$QRENCODEDIR/${ARCHS[0]}-libqrencode/lib/"*.a "${QRENCODEDIR}/lib/"
fi
# Convert architectures to semicolon-separated format for cmake
ARCHS_SEMICOLON_SEPARATED=$(IFS=";"; echo "${ARCHS[*]}")
echo "Configuring CMake for architectures: ${ARCHS[*]}"
cd "$BUILDDIR"
cmake "$QRENCODEDIR" \
-DCMAKE_OSX_ARCHITECTURES="$ARCHS_SEMICOLON_SEPARATED" \
-DCMAKE_INSTALL_PREFIX="$QRENCODEDIR" \
-DWITHOUT_PNG=ON \
-DBUILD_SHARED_LIBS=OFF \
-G "Xcode"
echo "Building libqrencode for architectures: ${ARCHS[*]}"
cmake --build . --config Release
echo "Installing libqrencode to $LIBDIR and $INCLUDEDIR"
cmake --install . --config Release
echo "Build and installation completed successfully, with outputs in $LIBDIR and $INCLUDEDIR."
rsync -ar --delete "$QRENCODEDIR/${ARCHS[0]}-libqrencode/include/"* "${QRENCODEDIR}/include/"

View File

@ -139,14 +139,14 @@ def main():
if args.type in ["cpp", "both"]:
if command_exists("clang-format-" + CFVERSION):
CLANGFORMAT = "clang-format-" + CFVERSION
CLANGFORMAT = "clang-format-" + CFVERSION
elif command_exists("clang-format"):
CLANGFORMAT = "clang-format"
CLANGFORMAT = "clang-format"
if CLANGFORMAT is not None:
print("Using source formatter: " + CLANGFORMAT)
else:
print("clang-format not found, unable to format source files")
print("clang-format not found. can't format source files")
if args.qt is not None and args.type in ["qml", "both"]:
global QMLFORMAT # pylint: disable=global-statement
@ -154,7 +154,7 @@ def main():
if QMLFORMAT is not None:
print("Using qmlformatter: " + QMLFORMAT)
else:
print("qmlformat not found, unable to format QML files")
print("qmlformat not found, can't format QML files")
if args.install:
if CLANGFORMAT is not None or QMLFORMAT is not None:
@ -171,10 +171,10 @@ def main():
exit_if_no_files()
else:
if src_files and args.type in ["cpp", "both"] and CLANGFORMAT:
print("Formatting source files")
print("Formatting source files...")
clang_format_files(src_files)
if qml_files and args.type in ["qml", "both"] and QMLFORMAT:
print("Formatting QML files")
print("Formatting QML files...")
qml_format_files(qml_files)

View File

@ -1,92 +1,92 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2022-2024 Savoir-faire Linux Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
# USA.
"""
Generate qrc file for qml and related code files recursively within the source
directory.
"""
import os
# These paths should be relative to the working directory of the
# script as set in the project CMakeLists, which should in turn be
# where the resources.qrc will be located (currently 'src/app').
app_src_dir = os.path.join('..', '..', 'src', 'app')
resfile = os.path.join('qml.qrc')
def path_contains_dir(filepath, dir_str):
""" Return True if the given filepath contains the given directory. """
# Split the filepath into its components
path_components = os.path.normpath(filepath).split(os.sep)
# Return True if the given directory is in the path
return dir_str in path_components
def posix_path(path):
"""
Force the use of POSIX path separators for the resource prefixes
and paths (useful only if versioning the qml.qrc file).
"""
return path.replace(os.sep, '/')
def gen_qml_qrc(with_webengine):
""" Generate the qml.qrc file. """
print("Generating qml.qrc file")
with open(resfile, 'w', encoding='utf-8') as qrc:
qrc.write('<RCC>\n')
for root, _, files in os.walk(app_src_dir):
# Skip the nowebengine directory if we can use WebEngine
if with_webengine and path_contains_dir(root, 'nowebengine'):
continue
# Skip the webengine directory if WebEngine is unable to be used
if not with_webengine and path_contains_dir(root, 'webengine'):
continue
filtered = [k for k in files if k.endswith('.qml') or
k.endswith('.js') or k.endswith('.html') or
k.endswith('.css') or k.endswith('.conf') or
k == 'qmldir']
# if there are no files of interest in this directory, skip it
if not filtered:
continue
# For now, get the relative resource prefix for this directory,
# remove the leading slash, and add it as a comment to the line.
# Ideally, we should use the actual resource prefix instead of /,
# but this will require some refactoring of the QML code.
prefix = root.split(app_src_dir)[-1][1:]
qrc.write(
f'\t<qresource prefix="/"> <!--{posix_path(prefix)}-->\n')
for file in filtered:
relpath = os.path.relpath(
os.path.join(root, file), app_src_dir)
qrc.write(f'\t\t<file>{posix_path(relpath)}</file>\n')
qrc.write('\t</qresource>\n')
qrc.write('</RCC>')
if __name__ == '__main__':
# WebEngine is unable to be used if building for macOS App Store
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--with-webengine', action='store_true',
default=False, help='Include WebEngine resources')
args = parser.parse_args()
gen_qml_qrc(args.with_webengine)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2022-2024 Savoir-faire Linux Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
# USA.
"""
Generate qrc file for qml and related code files recursively within the source
directory.
"""
import os
# These paths should be relative to the working directory of the
# script as set in the project CMakeLists, which should in turn be
# where the resources.qrc will be located (currently 'src/app').
app_src_dir = os.path.join('..', '..', 'src', 'app')
resfile = os.path.join('qml.qrc')
def path_contains_dir(filepath, dir_str):
""" Return True if the given filepath contains the given directory. """
# Split the filepath into its components
path_components = os.path.normpath(filepath).split(os.sep)
# Return True if the given directory is in the path
return dir_str in path_components
def posix_path(path):
"""
Force the use of POSIX path separators for the resource prefixes
and paths (useful only if versioning the qml.qrc file).
"""
return path.replace(os.sep, '/')
def gen_qml_qrc(with_webengine):
""" Generate the qml.qrc file. """
print("Generating qml.qrc file ...")
with open(resfile, 'w', encoding='utf-8') as qrc:
qrc.write('<RCC>\n')
for root, _, files in os.walk(app_src_dir):
# Skip the nowebengine directory if we can use webengine
if with_webengine and path_contains_dir(root, 'nowebengine'):
continue
# Skip the webengine directory if we can't use webengine
if not with_webengine and path_contains_dir(root, 'webengine'):
continue
filtered = [k for k in files if k.endswith('.qml') or
k.endswith('.js') or k.endswith('.html') or
k.endswith('.css') or k.endswith('.conf') or
k == 'qmldir']
# if there are no files of interest in this directory, skip it
if not filtered:
continue
# For now, get the relative resource prefix for this directory,
# remove the leading slash, and add it as a comment to the line.
# Ideally, we should use the actual resource prefix instead of /,
# but this will require some refactoring of the QML code.
prefix = root.split(app_src_dir)[-1][1:]
qrc.write(
f'\t<qresource prefix="/"> <!--{posix_path(prefix)}-->\n')
for file in filtered:
relpath = os.path.relpath(
os.path.join(root, file), app_src_dir)
qrc.write(f'\t\t<file>{posix_path(relpath)}</file>\n')
qrc.write('\t</qresource>\n')
qrc.write('</RCC>')
if __name__ == '__main__':
# We can't use webengine if we're building for macOS app store
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--with-webengine', action='store_true',
default=False, help='Include webengine resources')
args = parser.parse_args()
gen_qml_qrc(args.with_webengine)

View File

@ -64,7 +64,7 @@ def gen_resources_qrc(with_webengine):
qrc.write('<RCC>\n')
qml.write('pragma Singleton\nimport QtQuick\nQtObject {\n')
for root, _, files in os.walk(resdir):
# Skip the WebEngine directory if WebEngine is unable to used
# Skip the webengine directory if we can't use webengine
if not with_webengine and path_contains_dir(root, 'webengine'):
continue
prefix = root.rsplit(os.sep, 1)[-1]
@ -90,10 +90,10 @@ def gen_resources_qrc(with_webengine):
if __name__ == '__main__':
# WebEngine is unable to be used if building for macOS App Store
# We can't use webengine if we're building for macOS app store
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--with-webengine', action='store_true',
default=False, help='Include WebEngine resources')
default=False, help='Include webengine resources')
args = parser.parse_args()
gen_resources_qrc(args.with_webengine)

View File

@ -3,6 +3,9 @@
##
## Copyright (C) 2016-2024 Savoir-faire Linux Inc.
##
## Author: Edric Milaret <edric.ladent-milaret@savoirfairelinux.com>
## Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
@ -23,9 +26,9 @@ import shutil
print("== Updating from sources")
if os.system("lupdate jami.pro -no-obsolete"):
print("Attempting with 'lupdate-qt5'")
print("trying with 'lupdate-qt5'")
if os.system("lupdate-qt5 jami.pro -no-obsolete"):
raise RuntimeError("Unable to find any suitable lupdate Qt tool on this system. Stopping")
raise RuntimeError("unable to find any suitable lupdate Qt tool on this system. Stopping")
print("== Pushing sources")
os.system("tx push -s")

View File

@ -17,7 +17,7 @@
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<string>10.15</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>LSApplicationCategoryType</key>

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="M480-313 287-506l43-43 120 120v-371h60v371l120-120 43 43-193 193ZM220-160q-24 0-42-18t-18-42v-143h60v143h520v-143h60v143q0 24-18 42t-42 18H220Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M42 13.85V39q0 1.2-.9 2.1-.9.9-2.1.9H9q-1.2 0-2.1-.9Q6 40.2 6 39V9q0-1.2.9-2.1Q7.8 6 9 6h25.15Zm-3 1.35L32.8 9H9v30h30ZM24 35.75q2.15 0 3.675-1.525T29.2 30.55q0-2.15-1.525-3.675T24 25.35q-2.15 0-3.675 1.525T18.8 30.55q0 2.15 1.525 3.675T24 35.75ZM11.65 18.8h17.9v-7.15h-17.9ZM9 15.2V39 9Z"/></svg>

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 369 B

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<path d="M17.1,15.7c-0.8,0-1.5,0.3-2.1,0.8l-5.3-3.3C9.9,12.8,10,12.4,10,12c0-0.4-0.1-0.8-0.3-1.2l5.3-3.3c0.6,0.5,1.3,0.8,2.1,0.8
c1.7,0,3.1-1.4,3.1-3.1S18.9,2,17.1,2C15.4,2,14,3.4,14,5.1c0,0.4,0.1,0.8,0.3,1.2L8.9,9.6C8.3,9.1,7.6,8.9,6.9,8.9
c-1.7,0-3.1,1.4-3.1,3.1s1.4,3.1,3.1,3.1c0.8,0,1.5-0.3,2.1-0.8l5.3,3.3C14.1,18,14,18.4,14,18.9c0,1.7,1.4,3.1,3.1,3.1
c1.7,0,3.1-1.4,3.1-3.1S18.9,15.7,17.1,15.7z M17.1,20.6c-1,0-1.8-0.8-1.8-1.8s0.8-1.8,1.8-1.8c1,0,1.8,0.8,1.8,1.8
S18.1,20.6,17.1,20.6z M17.1,3.4c1,0,1.8,0.8,1.8,1.8s-0.8,1.8-1.8,1.8c-1,0-1.8-0.8-1.8-1.8S16.2,3.4,17.1,3.4z M6.9,13.8
c-1,0-1.8-0.8-1.8-1.8s0.8-1.8,1.8-1.8S8.6,11,8.6,12S7.8,13.8,6.9,13.8z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1019 B

View File

@ -1,13 +1,11 @@
<p>%1</p>
<h4 align="left"><span style="font-weight:600">%2</span></h4>
<h4 align="left"><span style="font-weight:600"> Created by</span></h4>
<p>Abhishek Ojha<br>
Adrien Béraud<br>
Albert Babí<br>
Alexander Lussier-Cullen<br>
Alexandr Sergheev<br>
Alexandre Eberhardt<br>
Alexandre Lision<br>
Alexandr Sergheev<br>
Alexandre Viau<br>
Alexander Lussier-Cullen<br>
Aline Bonnet<br>
Aline Gondim Santos<br>
Alireza Toghiani<br>
@ -20,6 +18,7 @@ Brando Tovar<br>
Capucine Berthet<br>
Charles-Francis Damedey<br>
Cyrille Béraud<br>
Dorina Mosku<br>
Eden Abitbol<br>
Édric Milaret<br>
Éloi Bail<br>
@ -34,16 +33,11 @@ Guillaume Roguez<br>
Hadrien De Sousa<br>
Hugo Lefeuvre<br>
Julien Grossholtz<br>
Julien Robert<br>
Kateryna Kostiuk<br>
Kessler DuPont-Teevin<br>
Léo Banno-Cloutier<br>
Léopold Chappuis<br>
Liam Courdoson<br>
Loïc Siret<br>
Louis Maillard<br>
Mathéo Joseph<br>
Michel Schmit<br>
Mingrui Zhang<br>
Mohamed Chibani<br>
Mohamed Amine Younes Bouacida<br>
@ -63,7 +57,6 @@ Rayan Osseiran<br>
Romain Bertozzi<br>
Saher Azer<br>
Sébastien Blin<br>
Seva Ivanov<br>
Silbino Gonçalves Matado<br>
Simon Désaulniers<br>
Simon Zeni<br>
@ -71,21 +64,9 @@ Stepan Salenikovich<br>
Thibault Wittemberg<br>
Thomas Ballasi<br>
Trevor Tabah<br>
Vitalii Nikitchyn<br>
Vsevolod Ivanov<br>
Xavier Jouslin de Noray<br>
Yang Wang<br>
</p>
<h4 align="left"><span style="font-weight:600"> %3</span></h4>
Yang Wang<br></p>
<h4 align="left"><span style="font-weight:600"> Artwork by</span></h4>
<p>Charlotte Hoffmann<br>
Marianne Forget<br></p>
<h4 align="left"><span style="font-weight:600"> %4</span></h4>
<p>Dorina Mosku<br>
Cabrel Tambue<br>
Loïc Bogino<br></p>
<h4 align="left"><span style="font-weight:600"> %5</span></h4>
<p>Anna<br>
Elys<br>
VeroJeanLuc<br>
</p>
<p>%6</p>

View File

@ -145,16 +145,6 @@ ApplicationWindow {
LRCInstance.selectConversation(convUid);
}
}
ListElement {
label: "Account ID"
type: "combobox"
getDataModel: () => AccountListModel
displayRole: AccountList.Username
onIndexChanged: function(model, index) {
const accountId = JamiQmlUtils.getModelData(model, index, AccountList.ID);
LRCInstance.currentAccountId = accountId;
}
}
ListElement {
label: "Force local preview"
type: "checkbox"

View File

@ -85,7 +85,7 @@ ApplicationWindow {
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
Layout.topMargin: preferredMargin
text: connectionFailed ? JamiStrings.reconnectWarn : JamiStrings.reconnectAttempt
text: connectionFailed ? JamiStrings.reconnectWarn : JamiStrings.reconnectTry
font.pointSize: 11
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter

View File

@ -116,8 +116,7 @@ ApplicationWindow {
function close(force = false) {
// If we're in the onboarding wizard or 'MinimizeOnClose'
// is set, then we can quit
var minimizeToTray = UtilsAdapter.getAppValue(Settings.MinimizeOnClose) && UtilsAdapter.isSystemTrayIconVisible();
if (force || !minimizeToTray || !UtilsAdapter.getAccountListSize()) {
if (force || !UtilsAdapter.getAppValue(Settings.MinimizeOnClose) || !UtilsAdapter.getAccountListSize()) {
Qt.quit();
} else {
layoutManager.closeToTray();
@ -210,22 +209,6 @@ ApplicationWindow {
// Dbus error handler for Linux.
if (Qt.platform.os.toString() !== "windows" && Qt.platform.os.toString() !== "osx")
DBusErrorHandler.setActive(true);
// Handle potential crash recovery.
var crashedLastRun = crashReporter.getHasPendingReport();
if (crashedLastRun) {
// A crash was detected during the last session. We need to inform the user and offer to send a crash report.
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/ConfirmDialog.qml", {
"title": JamiStrings.crashReportTitle,
"textLabel": JamiStrings.crashReportMessage + "\n\n" + JamiStrings.crashReportMessageExtra,
"confirmLabel": JamiStrings.send,
"rejectLabel": JamiStrings.dontSend,
"textHAlign": Text.AlignLeft,
"textMaxWidth": 400,
});
dlg.accepted.connect(function () { crashReporter.uploadLastReport(); });
dlg.rejected.connect(function () { crashReporter.clearReports(); });
}
}
Loader {
@ -378,7 +361,7 @@ ApplicationWindow {
function onUpdateCheckReplyReceived(ok, found) {
if (!ok) {
// Show an error dialog describing that an update check failed.
// Show an error dialog describing that we could not successfully check for an update.
presentUpdateInfoDialog(JamiStrings.updateCheckError);
return;
}

View File

@ -145,7 +145,7 @@ QtObject {
if (!view.managed)
view.presented();
}, props)) {
print("An error occurred while creating view:", viewName);
print("could not create view:", viewName);
}
}
@ -184,7 +184,7 @@ QtObject {
} else
view = rootView.pop(StackView.Immediate);
if (!view) {
print("An error occurred while attempting to pop view:", obj.objectName);
print("could not pop view:", obj.objectName);
resolveStack();
return;
}
@ -194,7 +194,7 @@ QtObject {
if (view.managed) {
var objectName = view ? view.objectName : obj.objectName;
if (!viewManager.destroyView(resources[objectName])) {
print("An error occurred while attempting to destroy view:", objectName);
print("could not destroy view:", objectName);
} else {
print("destroyed view:", objectName);
}

View File

@ -114,7 +114,7 @@ AccountAdapter::createJamiAccount(const QVariantMap& settings)
&lrcInstance_->accountModel(),
&lrc::api::AccountModel::accountAdded,
[this, registeredName, settings](const QString& accountId) {
lrcInstance_->accountModel().setAvatar(accountId, settings["avatar"].toString(), true,1);
lrcInstance_->accountModel().setAvatar(accountId, settings["avatar"].toString());
Utils::oneShotConnect(&lrcInstance_->accountModel(),
&lrc::api::AccountModel::accountDetailsChanged,
[this](const QString& accountId) {
@ -303,8 +303,13 @@ AccountAdapter::setCurrentAccountAvatarFile(const QString& source)
return;
}
QByteArray ba;
QBuffer bu(&ba);
bu.open(QIODevice::WriteOnly);
image.save(&bu, "PNG");
auto str = QString::fromLocal8Bit(ba.toBase64());
auto accountId = lrcInstance_->get_currentAccountId();
lrcInstance_->accountModel().setAvatar(accountId, source);
lrcInstance_->accountModel().setAvatar(accountId, str);
});
}
@ -313,7 +318,7 @@ AccountAdapter::setCurrentAccountAvatarBase64(const QString& data)
{
auto futureResult = QtConcurrent::run([this, data]() {
auto accountId = lrcInstance_->get_currentAccountId();
lrcInstance_->accountModel().setAvatar(accountId, data, true, 1);
lrcInstance_->accountModel().setAvatar(accountId, data);
});
}

View File

@ -29,35 +29,6 @@ AccountListModel::AccountListModel(LRCInstance* instance, QObject* parent)
: AbstractListModelBase(parent)
{
lrcInstance_ = instance;
// Avoid resetting/redrawing the model when the account status changes.
QObject::connect(&lrcInstance_->accountModel(),
&AccountModel::accountStatusChanged,
this,
[&](const QString& accountId) {
auto accountList = lrcInstance_->accountModel().getAccountList();
auto index = accountList.indexOf(accountId);
if (index != -1) {
QModelIndex modelIndex = QAbstractListModel::index(index, 0);
Q_EMIT dataChanged(modelIndex, modelIndex /*, ALL ROLES */);
}
});
// If there's a reorder, it's reasonable to reset the model for simplicity, instead
// of computing the difference. The same goes for accounts being added and removed.
// These operations will only occur when the list is hidden, unless dbus is used while
// the list is visible.
QObject::connect(&lrcInstance_->accountModel(),
&AccountModel::accountsReordered,
this,
&AccountListModel::reset);
QObject::connect(&lrcInstance_->accountModel(),
&AccountModel::accountAdded,
this,
&AccountListModel::reset);
QObject::connect(&lrcInstance_->accountModel(),
&AccountModel::accountRemoved,
this,
&AccountListModel::reset);
}
int
@ -120,7 +91,6 @@ AccountListModel::roleNames() const
void
AccountListModel::reset()
{
// Used to invalidate proxy models.
beginResetModel();
endResetModel();
}

View File

@ -24,7 +24,8 @@
#include <QCoreApplication>
#include <QLibraryInfo>
#include <QDir>
#include <locale.h>
const QString defaultDownloadPath = QStandardPaths::writableLocation(
QStandardPaths::DownloadLocation);

View File

@ -26,6 +26,7 @@
#include <QStandardPaths>
#include <QWindow> // for QWindow::AutomaticVisibility
#include <QSettings>
#include <QDir>
#include <QTranslator>
@ -42,7 +43,7 @@ extern const QString defaultDownloadPath;
// Common key-value pairs for both APPSTORE and non-APPSTORE builds
#define COMMON_KEYS \
X(MinimizeOnClose, true) \
X(MinimizeOnClose, false) \
X(DownloadPath, defaultDownloadPath) \
X(ScreenshotPath, {}) \
X(EnableNotifications, true) \
@ -73,9 +74,7 @@ extern const QString defaultDownloadPath;
X(ShowSendOption, false) \
X(EnablePtt, false) \
X(PttKeys, 32) \
X(UseFramelessWindow, USE_FRAMELESS_WINDOW_DEFAULT) \
X(EnableCrashReporting, true) \
X(EnableAutomaticCrashReporting, false)
X(UseFramelessWindow, USE_FRAMELESS_WINDOW_DEFAULT)
#if APPSTORE
#define KEYS COMMON_KEYS
#else

View File

@ -165,7 +165,7 @@ AvAdapter::shareWayland(bool entireScreen)
int err = portal->getPipewireFd();
if (err == EACCES) {
qInfo() << "Unable to share screen: permission denied";
qInfo() << "Can't share screen: permission denied";
return;
} else if (err != 0) {
qWarning() << "Failed to get PipeWire fd. Error code:" << err;
@ -178,7 +178,7 @@ AvAdapter::shareWayland(bool entireScreen)
.arg(portal->pipewireFd)
.arg(portal->pipewireNode);
#ifndef ENABLE_LIBWRAP
// If the daemon is running as a separate process, then it is unable to directly use the
// If the daemon is running as a separate process, then it can't directly use the
// PipeWire file descriptor opened by the client, so it will attempt to duplicate
// it using the pidfd_getfd system call. This requires the daemon process to have
// ptrace permission on the client process. On some systems, this will be true by

View File

@ -56,15 +56,11 @@ AvatarRegistry::addOrUpdateImage(const QString& id)
}
return uid;
}
// HACK: There is still a timing issue with when this function is called.
// The reason that avatar duplication was happening is that when the LRC account id is changed via
// the account combobox, the ui updates itself and calls getUID for the avatars that it needs to
// load, although by this point, the cache has not yet been cleared here. This ends up executing
// after the getUID calls.
void
AvatarRegistry::connectAccount()
{
clearCache();
uidMap_.clear();
connect(lrcInstance_->getCurrentContactModel(),
&ContactModel::profileUpdated,
this,
@ -97,9 +93,3 @@ AvatarRegistry::getUid(const QString& id)
}
return it.value();
}
void
AvatarRegistry::clearCache()
{
uidMap_.clear();
}

View File

@ -45,8 +45,6 @@ public:
// add or update a specific image in the cache
Q_SLOT QString addOrUpdateImage(const QString& id);
Q_INVOKABLE void clearCache();
Q_SIGNALS:
void avatarUidChanged(const QString& id);

View File

@ -354,11 +354,10 @@ CallAdapter::onCallInfosChanged(const QString& accountId, const QString& callId)
}
void
CallAdapter::onCallAddedToConference(const QString& callId, const QString& conversationId, const QString& confId)
CallAdapter::onCallAddedToConference(const QString& callId, const QString& confId)
{
Q_UNUSED(callId)
Q_UNUSED(confId)
Q_UNUSED(conversationId)
saveConferenceSubcalls();
}
@ -390,13 +389,16 @@ CallAdapter::hangUpACall(const QString& accountId, const QString& convUid)
}
void
CallAdapter::setCallMedia(const QString& accountId, const QString& convUid, bool videoMuted)
CallAdapter::setCallMedia(const QString& accountId, const QString& convUid, bool video)
{
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId);
if (convInfo.uid.isEmpty())
return;
lrcInstance_->getAccountInfo(accountId).callModel->setVideoMuted(convInfo.callId, videoMuted);
try {
lrcInstance_->getAccountInfo(accountId).callModel->updateCallMediaList(convInfo.callId,
video);
} catch (...) {
}
}
void

View File

@ -67,7 +67,7 @@ public:
Q_INVOKABLE void placeAudioOnlyCall();
Q_INVOKABLE void placeCall();
Q_INVOKABLE void hangUpACall(const QString& accountId, const QString& convUid);
Q_INVOKABLE void setCallMedia(const QString& accountId, const QString& convUid, bool videoMuted);
Q_INVOKABLE void setCallMedia(const QString& accountId, const QString& convUid, bool video);
Q_INVOKABLE void acceptACall(const QString& accountId, const QString& convUid);
Q_INVOKABLE void connectCallModel(const QString& accountId);
@ -122,7 +122,7 @@ public Q_SLOTS:
void onAccountChanged();
void onCallStatusChanged(const QString& accountId, const QString& callId);
void onCallStatusChanged(const QString& callId, int code);
void onCallAddedToConference(const QString& callId, const QString& conversationId, const QString& confId);
void onCallAddedToConference(const QString& callId, const QString& confId);
void onCallStarted(const QString& callId);
void onCallEnded(const QString& callId);
void onCallInfosChanged(const QString& accountId, const QString& callId);

View File

@ -57,6 +57,7 @@ Popup {
id: container
property color color: JamiTheme.secondaryBackgroundColor
anchors.centerIn: parent
leftPadding: popupMargins
bottomPadding: action1.visible || action2.visible ? 10 :popupMargins

View File

@ -51,7 +51,7 @@ SBSMessageBase {
bubble.border.color: CurrentConversation.color
bubble.border.width: root.isActive ? 1.5 : 0
bubble.color: JamiTheme.messageInBgColor
bubble.opacity: 1
bubble.opacity: 0.6
Connections {
target: CurrentConversation
@ -113,7 +113,7 @@ SBSMessageBase {
}
Text {
TextEdit {
id: callLabel
objectName: "callLabel"
@ -141,19 +141,19 @@ SBSMessageBase {
}
JoinCallButton {
id: joinCallWithAudio
objectName: "joinCallWithAudio"
id: joinCallInAudio
objectName: "joinCallInAudio"
Layout.topMargin: 4
Layout.bottomMargin: 4
text: JamiStrings.joinWithAudio
text: JamiStrings.joinInAudio
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, root.confId, true)
}
JoinCallButton {
id: joinCallWithVideo
objectName: "joinCallWithVideo"
text: JamiStrings.joinWithVideo
id: joinCallInVideo
objectName: "joinCallInVideo"
text: JamiStrings.joinInVideo
Layout.topMargin: 4
Layout.bottomMargin: 4

View File

@ -26,15 +26,10 @@ BaseModalDialog {
id: root
signal accepted
signal rejected
property string confirmLabel: ""
property string rejectLabel
property string textLabel: ""
property int textHAlign: Text.AlignHCenter
property real textMaxWidth: width - JamiTheme.preferredMarginSize * 4
autoClose: false
closeButtonVisible: false
button1.text: confirmLabel
button1.contentColorProvider: JamiTheme.redButtonColor
@ -42,11 +37,8 @@ BaseModalDialog {
close();
accepted();
}
button2.text: rejectLabel ? rejectLabel : JamiStrings.optionCancel
button2.onClicked: {
close();
rejected();
}
button2.text: JamiStrings.optionCancel
button2.onClicked: close()
button1Role: DialogButtonBox.AcceptRole
button2Role: DialogButtonBox.RejectRole
@ -58,7 +50,7 @@ BaseModalDialog {
id: labelAction
Layout.alignment: Qt.AlignHCenter
Layout.maximumWidth: textMaxWidth
Layout.maximumWidth: root.width - JamiTheme.preferredMarginSize * 4
color: JamiTheme.textColor
text: root.textLabel
@ -66,7 +58,7 @@ BaseModalDialog {
font.pointSize: JamiTheme.textFontSize
font.kerning: true
horizontalAlignment: textHAlign
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
wrapMode: Text.Wrap
}

View File

@ -22,6 +22,7 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import net.jami.Models 1.1
import net.jami.Constants 1.1
import net.jami.Adapters 1.1
@ -39,18 +40,9 @@ Loader {
property int seq: MsgSeq.single
property string author: Author
property string body: Body
property var tid: TID
property int transferStatus: TransferStatus
onTidChanged: {
if (tid === "") {
sourceComponent = deletedMsgComp;
}
}
property int transferStatus: Status
onTransferStatusChanged: {
if (tid === "") {
sourceComponent = deletedMsgComp;
return;
} else if (transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED) {
if (transferStatus === Interaction.Status.TRANSFER_FINISHED) {
mediaInfo = MessagesAdapter.getMediaInfo(root.body);
if (Object.keys(mediaInfo).length !== 0 && WITH_WEBENGINE) {
sourceComponent = localMediaMsgComp;
@ -63,61 +55,9 @@ Loader {
width: ListView.view ? ListView.view.width : 0
opacity: 0
Behavior on opacity {
NumberAnimation {
duration: 100
}
}
Behavior on opacity { NumberAnimation { duration: 100 } }
onLoaded: opacity = 1
Component {
id: deletedMsgComp
SBSMessageBase {
id: deletedItem
isOutgoing: Author === CurrentAccount.uri
showTime: root.showTime
seq: root.seq
author: Author
readers: Readers
timestamp: root.timestamp
formattedTime: root.formattedTime
formattedDay: root.formattedTime
extraHeight: 0
textContentWidth: textEditId.width
textContentHeight: textEditId.height
innerContent.children: [
TextEdit {
id: textEditId
anchors.right: isOutgoing ? parent.right : undefined
anchors.rightMargin: isOutgoing ? timeWidth : 0
bottomPadding: 6
topPadding: 6
leftPadding: 10
text: UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author) + " " + JamiStrings.deletedMedia
horizontalAlignment: Text.AlignLeft
width: Math.min((2 / 3) * parent.width, implicitWidth + 18, innerContent.width - senderMargin + 18)
font.pointSize: JamiTheme.smallFontSize
font.hintingPreference: Font.PreferNoHinting
renderType: Text.NativeRendering
textFormat: Text.RichText
clip: true
readOnly: true
color: getBaseColor()
opacity: 0.5
function getBaseColor() {
bubble.isDeleted = true;
return UtilsAdapter.luma(bubble.color) ? "white" : "dark";
}
}
]
}
}
Component {
id: dataTransferMsgComp
@ -126,8 +66,10 @@ Loader {
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 bool canOpen: root.transferStatus === Interaction.Status.TRANSFER_FINISHED || isOutgoing
property real maxMsgWidth: root.width - senderMargin -
2 * hPadding - avatarBlockWidth
- buttonsLoader.width - 24 - 6 - 24
isOutgoing: Author === CurrentAccount.uri
showTime: root.showTime
@ -151,12 +93,14 @@ Loader {
enabled: canOpen
onHoveredChanged: {
if (enabled && hovered) {
dataTransferItem.hoveredLink = UtilsAdapter.urlFromLocalPath(location);
dataTransferItem.hoveredLink = UtilsAdapter.urlFromLocalPath(location)
} else {
dataTransferItem.hoveredLink = "";
dataTransferItem.hoveredLink = ""
}
}
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
cursorShape: enabled ?
Qt.PointingHandCursor :
Qt.ArrowCursor
}
Loader {
id: buttonsLoader
@ -168,23 +112,23 @@ Loader {
sourceComponent: {
switch (root.transferStatus) {
case Interaction.TransferStatus.TRANSFER_CREATED:
case Interaction.TransferStatus.TRANSFER_FINISHED:
iconSource = JamiResources.link_black_24dp_svg;
return terminatedComp;
case Interaction.TransferStatus.TRANSFER_CANCELED:
case Interaction.TransferStatus.TRANSFER_ERROR:
case Interaction.TransferStatus.TRANSFER_UNJOINABLE_PEER:
case Interaction.TransferStatus.TRANSFER_TIMEOUT_EXPIRED:
case Interaction.TransferStatus.TRANSFER_AWAITING_HOST:
iconSource = JamiResources.download_black_24dp_svg;
return optionsComp;
case Interaction.TransferStatus.TRANSFER_ONGOING:
iconSource = JamiResources.close_black_24dp_svg;
return optionsComp;
case Interaction.Status.TRANSFER_CREATED:
case Interaction.Status.TRANSFER_FINISHED:
iconSource = JamiResources.link_black_24dp_svg
return terminatedComp
case Interaction.Status.TRANSFER_CANCELED:
case Interaction.Status.TRANSFER_ERROR:
case Interaction.Status.TRANSFER_UNJOINABLE_PEER:
case Interaction.Status.TRANSFER_TIMEOUT_EXPIRED:
case Interaction.Status.TRANSFER_AWAITING_HOST:
iconSource = JamiResources.download_black_24dp_svg
return optionsComp
case Interaction.Status.TRANSFER_ONGOING:
iconSource = JamiResources.close_black_24dp_svg
return optionsComp
default:
iconSource = JamiResources.error_outline_black_24dp_svg;
return terminatedComp;
iconSource = JamiResources.error_outline_black_24dp_svg
return terminatedComp
}
}
Component {
@ -214,10 +158,10 @@ Loader {
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: {
if (root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING) {
return MessagesAdapter.cancelFile(transferId);
if (root.transferStatus === Interaction.Status.TRANSFER_ONGOING) {
return MessagesAdapter.cancelFile(transferId)
} else {
return MessagesAdapter.acceptFile(transferId);
return MessagesAdapter.acceptFile(transferId)
}
}
}
@ -229,21 +173,27 @@ Loader {
TextEdit {
width: Math.min(implicitWidth, maxMsgWidth)
topPadding: 10
text: CurrentConversation.isSwarm ? transferName : location
text: CurrentConversation.isSwarm ?
transferName :
location
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
font.pointSize: 11
renderType: Text.NativeRendering
readOnly: true
color: UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
color: UtilsAdapter.luma(bubble.color)
? JamiTheme.chatviewTextColorLight
: JamiTheme.chatviewTextColorDark
MouseArea {
anchors.fill: parent
cursorShape: canOpen ? Qt.PointingHandCursor : Qt.ArrowCursor
cursorShape: canOpen ?
Qt.PointingHandCursor :
Qt.ArrowCursor
onClicked: function (mouse) {
if (canOpen) {
dataTransferItem.hoveredLink = UtilsAdapter.urlFromLocalPath(location);
Qt.openUrlExternally(new URL(dataTransferItem.hoveredLink));
dataTransferItem.hoveredLink = UtilsAdapter.urlFromLocalPath(location)
Qt.openUrlExternally(new Url(dataTransferItem.hoveredLink))
} else {
dataTransferItem.hoveredLink = "";
dataTransferItem.hoveredLink = ""
}
}
}
@ -254,27 +204,30 @@ Loader {
width: Math.min(implicitWidth, maxMsgWidth)
bottomPadding: 10
text: {
var res = "";
var res = ""
if (transferStats.totalSize !== undefined) {
if (transferStats.progress !== 0 && transferStats.progress !== transferStats.totalSize) {
res += UtilsAdapter.humanFileSize(transferStats.progress) + " / ";
if (transferStats.progress !== 0 &&
transferStats.progress !== transferStats.totalSize) {
res += UtilsAdapter.humanFileSize(transferStats.progress) + " / "
}
var totalSize = transferStats.totalSize !== 0 ? transferStats.totalSize : TotalSize;
res += UtilsAdapter.humanFileSize(totalSize);
var totalSize = transferStats.totalSize !== 0 ? transferStats.totalSize : TotalSize
res += UtilsAdapter.humanFileSize(totalSize)
}
return res;
return res
}
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
font.pointSize: 10
renderType: Text.NativeRendering
color: UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
color: UtilsAdapter.luma(bubble.color)
? JamiTheme.chatviewTextColorLight
: JamiTheme.chatviewTextColorDark
}
}
},
ProgressBar {
}
,ProgressBar {
id: progressBar
visible: root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
visible: root.transferStatus === Interaction.Status.TRANSFER_ONGOING
height: visible * implicitHeight
value: transferStats.progress / transferStats.totalSize
width: transferItem.width
@ -306,23 +259,23 @@ Loader {
Component.onCompleted: {
if (transferStats.totalSize !== undefined) {
var totalSize = transferStats.totalSize !== 0 ? transferStats.totalSize : TotalSize;
var txt = UtilsAdapter.humanFileSize(totalSize);
var totalSize = transferStats.totalSize !== 0 ? transferStats.totalSize : TotalSize
var txt = UtilsAdapter.humanFileSize(totalSize)
}
bubble.timestampItem.timeLabel.text += " - " + txt;
bubble.color = "transparent";
bubble.timestampItem.timeLabel.text += " - " + txt
bubble.color = "transparent"
if (mediaInfo.isImage)
bubble.z = 1;
bubble.z = 1
else
timeUnderBubble = true;
timeUnderBubble = true
}
onContentWidthChanged: {
if (bubble.timestampItem.timeLabel.width > contentWidth)
timeUnderBubble = true;
timeUnderBubble = true
else {
bubble.timestampItem.timeColor = JamiTheme.whiteColor;
bubble.timestampItem.timeLabel.opacity = 1;
bubble.timestampItem.timeColor = JamiTheme.whiteColor
bubble.timestampItem.timeLabel.opacity = 1
}
}
@ -336,10 +289,10 @@ Loader {
height: sourceComponent.height
sourceComponent: {
if (mediaInfo.isImage)
return imageComp;
return imageComp
if (mediaInfo.isAnimatedImage)
return animatedImageComp;
return avComp;
return animatedImageComp
return avComp
}
Component {
@ -347,11 +300,10 @@ Loader {
Loader {
Component.onCompleted: {
var qml = WITH_WEBENGINE ? "qrc:/webengine/MediaPreviewBase.qml" : "qrc:/nowebengine/MediaPreviewBase.qml";
setSource(qml, {
isVideo: mediaInfo.isVideo,
html: mediaInfo.html
});
var qml = WITH_WEBENGINE ?
"qrc:/webengine/MediaPreviewBase.qml" :
"qrc:/nowebengine/MediaPreviewBase.qml"
setSource( qml, { isVideo: mediaInfo.isVideo, html: mediaInfo.html } )
}
}
}
@ -372,7 +324,9 @@ Loader {
asynchronous: true
source: UtilsAdapter.urlFromLocalPath(Body)
property real aspectRatio: implicitWidth / implicitHeight
property real adjustedWidth: Math.min(maxSize, Math.max(minSize, innerContent.width - senderMargin))
property real adjustedWidth: Math.min(maxSize,
Math.max(minSize,
innerContent.width - senderMargin))
width: adjustedWidth
height: Math.ceil(adjustedWidth / aspectRatio)
Rectangle {
@ -392,7 +346,7 @@ Loader {
}
onWidthChanged: {
localMediaMsgItem.contentWidth = width;
localMediaMsgItem.contentWidth = width
}
Component.onCompleted: localMediaMsgItem.bubble.imgSource = source
@ -418,54 +372,71 @@ Loader {
Component {
id: imageComp
Rectangle {
border.color: img.useBox ? (JamiTheme.darkTheme ? "white" : JamiTheme.blackColor) : JamiTheme.transparentColor
color: JamiTheme.transparentColor
anchors.right: isOutgoing ? parent.right : undefined
border.width: 1
radius: msgRadius
Image {
id: img
implicitWidth: img.width + (img.useBox ? 20 : 0)
implicitHeight: img.height + (img.useBox ? 20 : 0)
onWidthChanged: {
localMediaMsgItem.contentWidth = width;
anchors.right: isOutgoing ? parent.right : undefined
cache: true
fillMode: Image.PreserveAspectFit
mipmap: true
antialiasing: true
autoTransform: true
asynchronous: true
Component.onCompleted: {
source = UtilsAdapter.urlFromLocalPath(Body);
localMediaMsgItem.bubble.imgSource = source;
}
Image {
id: img
// The sourceSize represents the maximum source dimensions.
// This should not be a dynamic binding, as property changes
// (resizing the chat view) here will trigger a reload of the image.
sourceSize: Qt.size(256, 256)
anchors.centerIn: parent
cache: true
fillMode: Image.PreserveAspectFit
mipmap: true
antialiasing: true
autoTransform: true
asynchronous: true
Component.onCompleted: {
source = UtilsAdapter.urlFromLocalPath(Body);
localMediaMsgItem.bubble.imgSource = source;
// Now we setup bindings for the destination image component size.
// This based on the width available (width of the chat view), and
// a restriction on the height.
readonly property real aspectRatio: paintedWidth / paintedHeight
readonly property real idealWidth: innerContent.width - senderMargin
onStatusChanged: {
if (img.status == Image.Ready && aspectRatio) {
height = Qt.binding(() => JamiQmlUtils.clamp(idealWidth / aspectRatio, 64, 256))
width = Qt.binding(() => height * aspectRatio)
}
}
// Scale down the image if it's too wide or too tall.
property real maxWidth: localMediaMsgItem.width - 170
property bool xOverflow: sourceSize.width > maxWidth
property bool yOverflow: sourceSize.height > JamiTheme.maxImageHeight
property real scaleFactor: (xOverflow || yOverflow) ? Math.min(maxWidth / sourceSize.width, JamiTheme.maxImageHeight / sourceSize.height) : 1
width: sourceSize.width * scaleFactor
height: sourceSize.height * scaleFactor
onWidthChanged: {
localMediaMsgItem.contentWidth = width
}
// Add a bounding box around the image if it's small (along at least one
// dimension) to ensure that it's easy for users to see it and click on it.
property bool useBox: (paintedWidth < 40) || (paintedHeight < 40)
layer.enabled: !useBox
layer.effect: OpacityMask {
maskSource: MessageBubble {
out: isOutgoing
type: seq
width: img.width
height: img.height
radius: msgRadius
Rectangle {
color: JamiTheme.previewImageBackgroundColor
z: -1
anchors.fill: parent
}
layer.enabled: true
layer.effect: OpacityMask {
maskSource: MessageBubble {
out: isOutgoing
type: seq
width: img.width
height: img.height
radius: msgRadius
}
}
LinearGradient {
id: gradient
anchors.fill: parent
start: Qt.point(0, height / 3)
gradient: Gradient {
GradientStop {
position: 0.0
color: JamiTheme.transparentColor
}
GradientStop {
position: 1.0
color: JamiTheme.darkGreyColorOpacityFade
}
}
}

View File

@ -73,7 +73,7 @@ BaseModalDialog {
Layout.bottomMargin: 5
color: JamiTheme.textColor
text: JamiStrings.confirmDeleteAccount
text: JamiStrings.confirmDeleteQuestion
font.pointSize: JamiTheme.textFontSize
font.kerning: true

View File

@ -121,7 +121,10 @@ Item {
font.pixelSize : text.length > 16 ? JamiTheme.jamiIdSmallFontSize : JamiTheme.bigFontSize
property string registeredName: CurrentAccount.registeredName
property string infohash: CurrentAccount.uri
text: (btnId.clicked && registeredName) ? registeredName : infohash
text: registeredName ? registeredName : infohash
onRegisteredNameChanged: {
text = registeredName ? registeredName : infohash
}
}
}
}
@ -151,7 +154,7 @@ Item {
JamiIdControlButton {
id: btnEdit
anchors.leftMargin: JamiTheme.pushButtonMargins
visible: CurrentAccount.registeredName === "" && CurrentAccount.enabled
visible: CurrentAccount.registeredName === ""
imageColor: enabled ? JamiTheme.tintedBlue : JamiTheme.buttonTintedBlack
border.color: usernameTextEdit.editMode ? jamiId.contentColor : "transparent"
enabled: {
@ -228,9 +231,11 @@ Item {
toolTipText: JamiStrings.identifierURI
onClicked: {
if (clicked) {
usernameLabel.text = Qt.binding(function() {return CurrentAccount.uri} );
usernameTextEdit.staticText = Qt.binding(function() {return CurrentAccount.uri} );
btnId.toolTipText = JamiStrings.identifierRegisterName;
} else {
usernameLabel.text = Qt.binding(function() {return CurrentAccount.registeredName} );
usernameTextEdit.staticText = Qt.binding(function() {return CurrentAccount.registeredName} );
btnId.toolTipText = JamiStrings.identifierURI;
}

View File

@ -86,7 +86,7 @@ SplitView {
// size (4 pixels). This is done to make it easier to grab small scroll-view handles that are
// adjacent to the SplitView handle. Note: vertically oriented handles are not offset.
readonly property real extraHandleSize: 4
readonly property real handleXPosition: !UtilsAdapter.isRTL ? 0 : -extraHandleSize
readonly property real handleXPosition: !isRTL ? 0 : -extraHandleSize
readonly property real handleSize: handleRoot.defaultSize + extraHandleSize
x: control.orientation === Qt.Horizontal ? handleXPosition : 0

View File

@ -164,7 +164,7 @@ BaseModalDialog {
appWindow,
"commoncomponents/JamiFileDialog.qml",
{
title: JamiStrings.selectProfilePicture,
title: JamiStrings.selectAvatarImage,
fileMode: JamiFileDialog.OpenFile,
folder: StandardPaths.writableLocation(
StandardPaths.PicturesLocation),

View File

@ -230,12 +230,6 @@ Control {
RowLayout {
id: msgRowlayout
HoverHandler {
id: parenthandler
}
property bool msgHovered: CurrentAccount.type !== Profile.Type.SIP && root.type !== Interaction.Type.CALL && Body !== "" && (bubbleArea.bubbleHovered || hovered || more.hovered || share.hovered || parenthandler.hovered)
Layout.preferredHeight: {
var h = innerContent.height + root.extraHeight;
if (emojiReactions.emojis !== "")
@ -284,10 +278,14 @@ Control {
anchors.right: isOutgoing ? bubble.left : undefined
anchors.left: !isOutgoing ? bubble.right : undefined
width: JamiTheme.emojiPushButtonSize * 4
width: JamiTheme.emojiPushButtonSize * 2
height: JamiTheme.emojiPushButtonSize
anchors.verticalCenter: bubble.verticalCenter
HoverHandler {
id: bgHandler
}
PushButton {
id: more
objectName: "more"
@ -301,24 +299,24 @@ Control {
anchors.verticalCenter: parent.verticalCenter
anchors.right: isOutgoing ? optionButtonItem.right : undefined
anchors.left: !isOutgoing ? optionButtonItem.left : undefined
visible: msgRowlayout.msgHovered
visible: CurrentAccount.type !== Profile.Type.SIP && root.type !== Interaction.Type.CALL && Body !== "" && (bubbleArea.bubbleHovered || hovered || reply.hovered || bgHandler.hovered)
source: JamiResources.more_vert_24dp_svg
width: optionButtonItem.width / 4
width: optionButtonItem.width / 2
height: optionButtonItem.height
circled: false
property bool isOpen: false
property var obj: undefined
function setBindings() {
function bind() {
more.isOpen = false;
visible = Qt.binding(() => msgRowlayout.msgHovered);
visible = Qt.binding(() => CurrentAccount.type !== Profile.Type.SIP && root.type !== Interaction.Type.CALL && Body !== "" && (bubbleArea.bubbleHovered || hovered || reply.hovered || bgHandler.hovered));
imageColor = Qt.binding(() => hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor);
normalColor = Qt.binding(() => JamiTheme.primaryBackgroundColor);
}
onClicked: {
if (more.isOpen) {
more.setBindings();
more.bind();
obj.close();
} else {
var component = Qt.createComponent("qrc:/commoncomponents/ShowMoreMenu.qml");
@ -334,7 +332,7 @@ Control {
});
obj.open();
more.isOpen = true;
visible = true; // the button stay visible as long the popup is open even if it's not hovered
visible = true;
imageColor = JamiTheme.chatViewFooterImgHoverColor;
normalColor = JamiTheme.hoveredButtonColor;
}
@ -350,75 +348,19 @@ Control {
normalColor: JamiTheme.primaryBackgroundColor
toolTipText: JamiStrings.reply
source: JamiResources.reply_black_24dp_svg
width: optionButtonItem.width / 4
width: optionButtonItem.width / 2
height: optionButtonItem.height
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: 5
anchors.right: isOutgoing ? more.left : undefined
anchors.left: !isOutgoing ? more.right : undefined
visible: msgRowlayout.msgHovered
visible: CurrentAccount.type !== Profile.Type.SIP && root.type !== Interaction.Type.CALL && Body !== "" && (bubbleArea.bubbleHovered || hovered || more.hovered || bgHandler.hovered)
onClicked: {
MessagesAdapter.editId = "";
MessagesAdapter.replyToId = Id;
}
}
PushButton {
id: share
objectName: "share"
circled: false
imageColor: hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor
normalColor: JamiTheme.primaryBackgroundColor
toolTipText: JamiStrings.share
source: JamiResources.share_black_24dp_svg
width: optionButtonItem.width / 4
height: optionButtonItem.height
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: 5
anchors.right: isOutgoing ? reply.left : undefined
anchors.left: !isOutgoing ? reply.right : undefined
visible: msgRowlayout.msgHovered
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);
imageColor = Qt.binding(() => hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor);
normalColor = Qt.binding(() => JamiTheme.primaryBackgroundColor);
}
onClicked: {
if (share.isOpen) {
share.setBindings();
obj.close();
} else {
if (root.type === 2 || root.type === 5) {
// 2=TEXT and 5=DATA_TRANSFER (any kind of file) defined in interaction.h
var component = Qt.createComponent("qrc:/commoncomponents/ShareMessageMenu.qml");
obj = component.createObject(share, {
"isOutgoing": isOutgoing,
"msgId": Id,
"msgBody": Body,
"type": root.type,
"transferName": TransferName,
"msgBubble": bubble,
"listView": listView,
"author": UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author),
"formattedTime": formattedTime
});
obj.open();
share.isOpen = true;
visible = true; // the PushButton stay visible as long the popup is open even if it's not hovered
imageColor = JamiTheme.chatViewFooterImgHoverColor;
normalColor = JamiTheme.hoveredButtonColor;
}
}
}
}
}
MessageBubble {
@ -440,7 +382,7 @@ Control {
property bool bubbleHovered
property string imgSource
width: (root.type === Interaction.Type.TEXT || isDeleted ? root.textContentWidth + (IsEmojiOnly || root.bigMsg ? 0 : root.timeWidth + root.editedWidth) : innerContent.childrenRect.width)
width: (root.type === Interaction.Type.TEXT ? root.textContentWidth + (IsEmojiOnly || root.bigMsg ? 0 : root.timeWidth + root.editedWidth) : innerContent.childrenRect.width)
height: innerContent.childrenRect.height + (visible ? root.extraHeight : 0) + (root.bigMsg ? 15 : 0)
HoverHandler {
@ -482,9 +424,9 @@ Control {
id: editedRow
anchors.left: root.bigMsg ? bubble.left : timestampItem.left
anchors.bottom: parent.bottom
anchors.bottomMargin: root.bigMsg ? 6 : 10
anchors.bottomMargin: root.bigMsg || bubble.isDeleted ? 6 : 10
anchors.leftMargin: root.bigMsg ? 10 : -timestampItem.width - 16
visible: bubble.isEdited && !bubble.isDeleted
visible: bubble.isEdited
z: 1
ResponsiveImage {
id: editedImage
@ -524,11 +466,6 @@ Control {
MessagesAdapter.openUrl(root.hoveredLink);
}
}
onDoubleClicked: {
MessagesAdapter.editId = "";
MessagesAdapter.replyToId = Id;
}
property bool bubbleHovered: containsMouse || textHovered
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
}
@ -646,7 +583,6 @@ Control {
id: status
Layout.alignment: Qt.AlignBottom
width: JamiTheme.avatarReadReceiptSize
property bool isAlone: CurrentConversation.members.count === 1
Rectangle {
id: sending
@ -657,7 +593,7 @@ Control {
border.color: JamiTheme.sending
border.width: 1
color: JamiTheme.transparentColor
visible: isOutgoing && Status === Interaction.Status.SENDING && !status.isAlone
visible: isOutgoing && Status === Interaction.Status.SENDING
anchors.bottom: parent.bottom
}
@ -688,22 +624,6 @@ Control {
anchors.bottom: parent.bottom
readers: root.readers
}
Component {
id: selfReadIconComp
Avatar {
width: JamiTheme.avatarReadReceiptSize
height: JamiTheme.avatarReadReceiptSize
mode: Avatar.Mode.Account
imageId: CurrentAccount.id
showPresenceIndicator: false
}
}
Loader {
active: status.isAlone && CurrentConversation.lastSelfMessageId === Id
sourceComponent: selfReadIconComp
anchors.bottom: parent.bottom
}
}
}

View File

@ -1,264 +0,0 @@
/*
* Copyright (C) 2024 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import net.jami.Constants 1.1
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import SortFilterProxyModel 0.2
import "contextmenu"
import "../commoncomponents"
import "../mainview/components"
BaseContextMenu {
id: mainMenu
height: 330 + Math.min(messageInput.height, textareaMaxHeight)
width: 400
required property string msgId
required property string msgBody
required property bool isOutgoing
required property int type
required property string transferName
required property Item msgBubble
required property ListView listView
required property string author
required property string formattedTime
property var selectedUids: []
property string shareToId: msgId
property string fileLink: msgBody
property int textareaMaxHeight: 350
function xPosition(width) {
// Use the width at function scope to retrigger property evaluation.
const listViewWidth = listView.width;
const parentX = parent.x;
if (isOutgoing) {
return parentX - width - 20;
} else {
return parentX + 20;
}
}
x: xPosition(width)
y: parent.y
function xPositionProvider(width) {
// Use the width at function scope to retrigger property evaluation.
const listViewWidth = listView.width;
if (isOutgoing) {
return -5 - width;
} else {
const rightMargin = listViewWidth - (msgBubble.x + width);
return width > rightMargin + 35 ? -5 - width : 35;
}
}
function yPositionProvider(height) {
const topOffset = msgBubble.mapToItem(listView, 0, 0).y;
const listViewHeight = listView.height;
const bottomMargin = listViewHeight - height - topOffset;
if (bottomMargin < 0 || (topOffset < 0 && topOffset + height > 0)) {
return 30 - height;
} else {
return 0;
}
}
SortFilterProxyModel {
id: shareConvProxyModel
sourceModel: ConversationsAdapter.convListProxyModel
filterCaseSensitivity: Qt.CaseInsensitive
}
Rectangle {
id: header
width: parent.width
height: 0
}
Rectangle {
id: sendButton
height: JamiTheme.chatViewFooterButtonSize
anchors.right: parent.right
anchors.rightMargin: 10
anchors.topMargin: 10
anchors.top: header.bottom
color: JamiTheme.transparentColor
PushButton {
id: shareMessageButton
height: JamiTheme.chatViewFooterButtonSize
width: scale * JamiTheme.chatViewFooterButtonSize
anchors.right: parent.right
visible: true
radius: JamiTheme.chatViewFooterButtonRadius
preferredSize: JamiTheme.chatViewFooterButtonIconSize - 6
imageContainerWidth: 25
imageContainerHeight: 25
toolTipText: JamiStrings.share
mirror: UtilsAdapter.isRTL
source: JamiResources.send_black_24dp_svg
hoverEnabled: enabled
normalColor: enabled ? JamiTheme.chatViewFooterSendButtonColor : JamiTheme.chatViewFooterSendButtonDisableColor
imageColor: enabled ? JamiTheme.chatViewFooterSendButtonImgColor : JamiTheme.chatViewFooterSendButtonImgColorDisable
hoveredColor: JamiTheme.buttonTintedBlueHovered
pressedColor: hoveredColor
opacity: 1
scale: opacity
MouseArea {
anchors.fill: parent
onClicked: {
var selectedContacts = mainMenu.selectedUids;
var hasText = messageInput.text && selectedContacts.length > 0;
function sendMessageOrFile(uid) {
if (Type === 2) {
// 2=TEXT and 5=DATA_TRANSFER (any kind of file) defined in interaction.h
MessagesAdapter.sendMessageToUid(msgBody, uid);
} else {
MessagesAdapter.sendFileToUid(fileLink, uid);
}
}
for (var i = 0; i < selectedContacts.length; i++) {
var uid = selectedContacts[i];
sendMessageOrFile(uid);
if (hasText) {
MessagesAdapter.sendMessageToUid(messageInput.text, uid);
}
}
messageInput.text = "";
mainMenu.destroy();
}
}
}
}
Rectangle {
id: searchConv
height: 300
width: parent.width
anchors.top: header.bottom
anchors.topMargin: 10
property int type: ContactList.CONVERSATION
color: JamiTheme.transparentColor
ColumnLayout {
id: contactPickerPopupRectColumnLayout
anchors.fill: parent
Searchbar {
id: contactPickerContactSearchBar
width: parent.width - 20 - JamiTheme.chatViewFooterButtonSize
anchors.leftMargin: 10
Layout.preferredHeight: 35
placeHolderText: "Share to..."
onSearchBarTextChanged: function (text) {
shareConvProxyModel.filterRole = shareConvProxyModel.roleForName("Title");
shareConvProxyModel.filterPattern = text;
}
}
JamiListView {
id: contactPickerListView
Layout.alignment: Qt.AlignCenter
Layout.fillWidth: true
Layout.preferredHeight: 255
Layout.bottomMargin: JamiTheme.preferredMarginSize
Layout.topMargin: 5
model: shareConvProxyModel
delegate: ConversationPickerItemDelegate {
id: conversationDelegate
}
}
}
}
Flickable {
id: messageInputContainer
height: Math.min(contentHeight, mainMenu.textareaMaxHeight)
width: parent.width - 20
contentHeight: messageInput.height
anchors.left: parent.left
anchors.leftMargin: 10
anchors.rightMargin: 10
anchors.topMargin: 10
anchors.top: searchConv.bottom
flickableDirection: Flickable.VerticalFlick
clip: true
ScrollBar.vertical: JamiScrollBar {
policy: ScrollBar.AsNeeded
}
onContentHeightChanged: {
if (contentHeight > height) {
contentY = contentHeight - height;
}
}
TextArea {
id: messageInput
height: contentHeight + 12
width: parent.width
placeholderText: "Add a comment"
placeholderTextColor: JamiTheme.messageBarPlaceholderTextColor
font.pointSize: JamiTheme.textFontSize + 2
color: JamiTheme.textColor
wrapMode: Text.WordWrap
background: Rectangle {
color: JamiTheme.transparentColor
radius: 5
border.color: JamiTheme.chatViewFooterRectangleBorderColor
border.width: 2
}
}
}
// destroy() and setBindings() are needed to unselect the share icon from SBSMessageBase
onAboutToHide: {
mainMenu.destroy();
}
Component.onDestruction: {
parent.setBindings();
}
}

View File

@ -89,10 +89,8 @@ BaseContextMenu {
emojiPicker.emojiIsPicked.connect(function (content) {
if (emojiReplied.includes(content)) {
MessagesAdapter.removeEmojiReaction(CurrentConversation.id, content, msgId);
parent.setBindings();
} else {
MessagesAdapter.addEmojiReaction(CurrentConversation.id, content, msgId);
parent.setBindings();
}
});
if (emojiPicker !== null) {
@ -109,11 +107,8 @@ BaseContextMenu {
property bool isScrolling: listView.verticalScrollBar.active
onOpened: root.closeWithoutAnimation = false
onClosed: {
if (emojiPicker) {
emojiPicker.closeEmojiPicker();
}
}
onClosed: if (emojiPicker)
emojiPicker.closeEmojiPicker()
function getQuickEmojiListModel() {
const defaultModel = ["👍", "👎", "😂"];
@ -158,7 +153,7 @@ BaseContextMenu {
GeneralMenuItem {
id: removeLocally
canTrigger: type === Interaction.Type.DATA_TRANSFER && TransferStatus === Interaction.TransferStatus.TRANSFER_FINISHED
canTrigger: type === Interaction.Type.DATA_TRANSFER && Status === Interaction.Status.TRANSFER_FINISHED
iconSource: JamiResources.trash_black_24dp_svg
itemName: JamiStrings.removeLocally
onClicked: {
@ -180,7 +175,7 @@ BaseContextMenu {
GeneralMenuItem {
id: deleteMessage
canTrigger: root.isOutgoing && (type === Interaction.Type.TEXT || type === Interaction.Type.DATA_TRANSFER)
canTrigger: root.isOutgoing && type === Interaction.Type.TEXT
iconSource: JamiResources.delete_svg
itemName: JamiStrings.deleteMessage
onClicked: {
@ -203,13 +198,11 @@ BaseContextMenu {
root.loadMenuItems(menuItems);
}
// destroy() and setBindings() are needed to unselect the share icon from SBSMessageBase
onAboutToHide: {
root.destroy();
}
Component.onDestruction: {
parent.setBindings();
parent.bind();
}
}

View File

@ -173,10 +173,10 @@ static void
logConnectionInfo(NMActiveConnection* connection)
{
if (connection) {
C_INFO << "Primary network connection:" << nm_active_connection_get_uuid(connection)
<< "default:" << (nm_active_connection_get_default(connection) ? "yes" : "no");
C_INFO << "primary network connection:" << nm_active_connection_get_uuid(connection)
<< "default: " << (nm_active_connection_get_default(connection) ? "yes" : "no");
} else {
C_WARN << "No primary network connection detected, check network settings";
C_WARN << "no primary network connection detected, check network settings";
}
}
@ -193,7 +193,7 @@ nmClientCallback(G_GNUC_UNUSED GObject* source_object, GAsyncResult* result, Con
{
GError* error = nullptr;
if (auto nm_client = nm_client_new_finish(result, &error)) {
C_INFO << "NetworkManager client initialized, version:" << nm_client_get_version(nm_client)
C_INFO << "NetworkManager client initialized, version: " << nm_client_get_version(nm_client)
<< ", daemon running:" << (nm_client_get_nm_running(nm_client) ? "yes" : "no")
<< ", networking enabled:"
<< (nm_client_networking_get_enabled(nm_client) ? "yes" : "no");
@ -206,7 +206,7 @@ nmClientCallback(G_GNUC_UNUSED GObject* source_object, GAsyncResult* result, Con
cm);
} else {
C_WARN << "Error initializing NetworkManager client:" << error->message;
C_WARN << "error initializing NetworkManager client: " << error->message;
g_clear_error(&error);
}
}

View File

@ -143,7 +143,7 @@ ConversationListProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
}
} else {
Q_FOREACH (const auto& filter, toFilter)
if (rx.isValid() && rx.match(filter).hasMatch()) {
if (rx.match(filter).hasMatch()) {
match = true;
break;
}

View File

@ -99,7 +99,7 @@ ConversationListModelBase::dataForItem(item_t item, int role) const
}
case Role::Draft: {
if (!item.uid.isEmpty())
return lrcInstance_->getContentDraft(item.uid, item.accountId)["text"];
return lrcInstance_->getContentDraft(item.uid, item.accountId);
return {};
}
case Role::ActiveCallsCount: {
@ -125,11 +125,7 @@ ConversationListModelBase::dataForItem(item_t item, int role) const
if (interaction.type == interaction::Type::UPDATE_PROFILE) {
lastInteractionBody = interaction::getProfileUpdatedString();
} else if (interaction.type == interaction::Type::DATA_TRANSFER) {
if (interaction.commit.value("tid").isEmpty()) {
lastInteractionBody = tr("Deleted media");
} else {
lastInteractionBody = interaction.commit.value("displayName");
}
lastInteractionBody = interaction.commit.value("displayName");
} else if (interaction.type == lrc::api::interaction::Type::CALL) {
const auto isOutgoing = interaction.authorUri == accInfo.profileInfo.uri;
lastInteractionBody = interaction::getCallInteractionString(isOutgoing, interaction);
@ -137,7 +133,7 @@ ConversationListModelBase::dataForItem(item_t item, int role) const
auto bestName = interaction.authorUri == accInfo.profileInfo.uri
? accInfo.accountModel->bestNameForAccount(accInfo.id)
: accInfo.contactModel->bestNameForContact(
interaction.authorUri);
interaction.authorUri);
lastInteractionBody
= interaction::getContactInteractionString(bestName,
interaction::to_action(

View File

@ -246,8 +246,8 @@ ConversationsAdapter::onNewTrustRequest(const QString& accountId,
auto contactPhoto = Utils::contactPhoto(lrcInstance_, peerUri, QSize(50, 50), accountId);
auto notifId = QString("%1;%2").arg(accountId, conv);
systemTray_->showNotification(notifId,
tr("%1 received a new invitation").arg(to),
"New invitation from " + peerBestName,
tr("%1 received a new trust request").arg(to),
"New request from " + peerBestName,
SystemTray::NotificationType::REQUEST,
Utils::QImageToByteArray(contactPhoto));
};

View File

@ -1,131 +0,0 @@
/*
* Copyright (C) 2024 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "version.h"
#include "version_info.h"
#include <QVariantMap>
class AppSettingsManager;
/**
* In the context of Jami where we employ GnuTLS, OpenSSL, and other cryptographic
* components as part of secure communication protocols, it is essential to configure
* crash reports with security in mind to prevent sensitive data from being exposed in crash
* reports. We must assume that attackers may attempt to exploit vulnerabilities based on
* stack data including values of cryptographic keys, certificates, etc. that may be used
* in some way to compromise the security of the user's account.
*
* We attempt to mitigate this risk by configuring crash reports to avoid collecting stack
* data beyond the offending function that caused the crash. We make the assumption that
* cryptographically sensitive data is not stored on the stack by 3rd party libraries.
*
* We also take care to avoid sending crash reports automatically and instead require user
* consent before uploading the last report.
*
* IMPORTANT: The opt-in approach is crucial, and the potential implications of transmitting
* these reports must be communicated to the user. The user should be informed that we cannot
* guarantee the security of the data in the crash report, even if we take steps to avoid
* leaking any sensitive information.
*
* We offer the following configuration options to enhance security:
*
* - (Option) EnableCrashReporting (default - true):
* An application settings allowing users to disable crash handling entirely.
*
* - (Option) EnableAutomaticCrashReporting (default - false):
* This setting allows users to opt-in to automatic crash reporting, which should be disabled
* by default. When the application crashes, the user should be prompted to upload the last
* crash report. If the user agrees, the report will be uploaded to the server. If this
* setting is enabled, no prompt will be shown, and the report will be uploaded automatically
* when the application crashes.
*
* Further considerations:
*
* - **Annotations**:
* Allows the inclusion of custom metadata in crash reports, such as the application version
* and build number, without exposing sensitive information. We must include this information
* to use the crash reports constructively.
*/
class CrashReportClient : public QObject
{
Q_OBJECT
public:
explicit CrashReportClient(AppSettingsManager* settingsManager, QObject* parent = nullptr)
: QObject(parent)
, settingsManager_(settingsManager)
, crashReportUrl_(CRASH_REPORT_URL)
{}
~CrashReportClient() = default;
virtual void syncHandlerWithSettings() = 0;
virtual void uploadLastReport() = 0;
virtual void clearReports() = 0;
// Used by the QML interface to query whether the application crashed last run.
bool getHasPendingReport()
{
// In builds that do not support crashpad, this will always return false, and
// thus will never trigger the dialog asking the user to upload the last report.
return crashedLastRun_;
}
protected:
// This function is used to toggle automatic crash reporting.
virtual void setUploadsEnabled(bool enabled) = 0;
// We will need to access the crash report related settings.
AppSettingsManager* settingsManager_;
// The endpoint URL that crash reports will be uploaded to.
QString crashReportUrl_;
// We store if the last run resulted in
bool crashedLastRun_ {false};
// This is the metadata that will be sent with each crash report.
// This data is required to correlate crash reports with the build so we can
// effectively load and analyze the mini-dumps.
QVariantMap metaData_ {
{"platform", QSysInfo::prettyProductName() + "_" + QSysInfo::currentCpuArchitecture()},
{"client_sha", APP_VERSION_STRING},
{"jamicore_sha", CORE_VERSION_STRING},
{"build_id", QString(VERSION_STRING)},
};
};
// Null implementation of the crash report client
class NullCrashReportClient : public CrashReportClient
{
Q_OBJECT
public:
explicit NullCrashReportClient(AppSettingsManager* settingsManager, QObject* parent = nullptr)
: CrashReportClient(settingsManager, parent)
{}
void syncHandlerWithSettings() override {}
void uploadLastReport() override {}
void clearReports() override {}
protected:
void setUploadsEnabled(bool enabled) override {}
};

View File

@ -1,313 +0,0 @@
/*
* Copyright (C) 2024 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "crashpad.h"
#include "appsettingsmanager.h"
#include "global.h"
#include <client/crash_report_database.h>
#include <client/settings.h>
#include <client/crashpad_info.h>
#include <QDir>
#include <QCoreApplication>
#include <QStandardPaths>
#include <QThreadPool>
#include <thread>
#if defined(OS_WIN)
#define FILEPATHSTR(Qs) Qs.toStdWString()
#define STRFILEPATH(Qs) QString::fromStdWString(Qs)
#define CRASHPAD_EXECUTABLE "crashpad_handler.exe"
#else
#define FILEPATHSTR(Qs) Qs.toStdString()
#define STRFILEPATH(Qs) QString::fromStdString(Qs)
#define CRASHPAD_EXECUTABLE "crashpad_handler"
#endif // OS_WIN
// We need the number of reports in the database to determine if the application crashed last time.
static int
getReportCount(base::FilePath dbPath)
{
auto database = crashpad::CrashReportDatabase::Initialize(base::FilePath(dbPath));
if (database == nullptr) {
return 0;
}
std::vector<crashpad::CrashReportDatabase::Report> completedReports;
database->GetCompletedReports(&completedReports);
return completedReports.size();
}
static void
clearCompletedReports(crashpad::CrashReportDatabase* database)
{
using namespace crashpad;
using OperationStatus = CrashReportDatabase::OperationStatus;
std::vector<CrashReportDatabase::Report> reports;
auto status = database->GetCompletedReports(&reports);
if (OperationStatus::kNoError != status) {
C_WARN << "Could not retrieve completed reports";
return;
}
for (const auto& report : reports) {
C_INFO.noquote() << QString("Deleting report: %1").arg(report.uuid.ToString().c_str());
status = database->DeleteReport(report.uuid);
if (OperationStatus::kNoError != status) {
C_WARN << "Failed to delete report";
}
}
}
CrashPadClient::CrashPadClient(AppSettingsManager* settingsManager, QObject* parent)
: CrashReportClient(settingsManager, parent)
{
try {
C_INFO << "Crashpad crash reporting enabled";
// We store the crashpad database in the application's local data.
const auto dataPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
if (dataPath.isEmpty()) {
throw std::runtime_error("Failed to retrieve writable location for AppLocalData");
}
dbPath_ = base::FilePath(FILEPATHSTR(QDir(dataPath).absoluteFilePath("crash_db")));
// Make sure the database directory exists.
if (!QDir().mkpath(STRFILEPATH(dbPath_.value()))) {
throw std::runtime_error("Failed to create crash database directory");
}
// The crashpad_handler executable is in the same directory as this executable.
const auto appBinPath = QCoreApplication::applicationDirPath();
if (appBinPath.isEmpty()) {
throw std::runtime_error("Failed to retrieve application directory path");
}
handlerPath_ = base::FilePath(FILEPATHSTR(QDir(appBinPath).filePath(CRASHPAD_EXECUTABLE)));
C_DBG << "Handler runtime path: " << handlerPath_.value();
// Check if the application crashed last time it was run by checking the crashpad database
// report count. If there is at least one report, we set the crashedLastRun_ flag to true.
// The flag will be queried by the QML interface to display a dialog. If the user accepts,
// the uploadLastReport function will be called, otherwise the reports will be cleared
// to avoid a build up of crash reports on the user's system.
using key = Settings::Key;
auto automaticReporting = settingsManager_->getValue(key::EnableAutomaticCrashReporting)
.toBool();
if (getReportCount(dbPath_) > 0 && !automaticReporting) {
crashedLastRun_ = true;
}
// If we crashed last time and need to send off a report, then uploadLastReport will call
// startHandler, and considering the `restartable` option for `StartHandler` is unused,
// and that restarting the handler will cause an assertion failure when debugging Linux,
// we will just not start the handler here in that case. The handler will be started in
// the clearReports function after the reports are cleared.
if (!crashedLastRun_) {
startHandler();
}
} catch (const std::exception& e) {
C_ERR << "Error initializing CrashPadClient: " << e.what();
} catch (...) {
C_ERR << "Unknown error initializing CrashPadClient";
}
}
CrashPadClient::~CrashPadClient()
{
// Remove any remaining stale crash reports.
// We use sleep to ensure that the reports are cleared after a forced upload,
// and it's possible that the reports are still being processed. This is a
// workaround for the lack of a synchronous `report-uploaded` signal/event.
if (auto database = crashpad::CrashReportDatabase::Initialize(base::FilePath(dbPath_))) {
clearCompletedReports(database.get());
}
}
void
CrashPadClient::startHandler()
{
std::vector<std::string> arguments;
// We disable rate-limiting because we want to upload crash reports as soon as possible.
// Perhaps we should enable rate-limiting in the future to avoid spamming the server, but
// we will need to investigate how that works in crashpad's implementation.
arguments.push_back("--no-rate-limit");
// We disable gzip compression because we want to be able to read the reports easily.
arguments.push_back("--no-upload-gzip");
// Convert the client metadata to map-string-string.
std::map<std::string, std::string> annotations;
Q_FOREACH (auto key, metaData_.keys()) {
annotations[key.toStdString()] = metaData_[key].toString().toStdString();
}
C_INFO << "Starting crashpad handler";
bool success = client_.StartHandler(handlerPath_, // handler
dbPath_, // database_dir
{}, // metrics_dir
crashReportUrl_.toStdString(), // url to upload reports
annotations, // Annotations
arguments, // Arguments
false, // restartable (this doesn't do anything)
false, // asynchronous_start (this doesn't do anything)
std::vector<base::FilePath>() // Attachments
);
if (!success) {
C_WARN << "Crashpad initialization failed";
return;
}
// Update the handler settings after starting the handler (we may have restarted it).
syncHandlerWithSettings();
}
void
CrashPadClient::syncHandlerWithSettings()
{
// Configure the crashpad handler with the settings from the settings manager.
using key = Settings::Key;
using namespace crashpad;
CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
// Optionally disable crashpad handler.
auto enableReportsAppSetting = settingsManager_->getValue(key::EnableCrashReporting).toBool();
crashpad_info->set_crashpad_handler_behavior(enableReportsAppSetting ? TriState::kEnabled
: TriState::kDisabled);
// Enable automatic crash reporting if the user has opted in.
auto automaticReporting = settingsManager_->getValue(key::EnableAutomaticCrashReporting).toBool();
setUploadsEnabled(automaticReporting);
}
void
CrashPadClient::setUploadsEnabled(bool enabled)
{
auto database = crashpad::CrashReportDatabase::Initialize(base::FilePath(dbPath_));
if (database != nullptr && database->GetSettings() != nullptr) {
database->GetSettings()->SetUploadsEnabled(enabled);
}
}
void
CrashPadClient::clearReports()
{
auto database = crashpad::CrashReportDatabase::Initialize(base::FilePath(dbPath_));
if (database == nullptr) {
return;
}
C_DBG << "Clearing completed crash reports";
const time_t secondsToWaitForReportLocks = 1;
database->CleanDatabase(secondsToWaitForReportLocks);
::clearCompletedReports(database.get());
// If the crashedLastRun_ flag is set, then we should follow up and start the handler.
// Refer to the comment in constructor for more information on why the handler wasn't
// started in the constructor if we crashed last time.
if (crashedLastRun_) {
startHandler();
}
}
void
CrashPadClient::uploadLastReport()
{
using namespace crashpad;
// Find the latest crash report.
auto database = CrashReportDatabase::Initialize(base::FilePath(dbPath_));
if (database == nullptr) {
C_WARN << "Crashpad database initialization failed";
return;
}
std::vector<CrashReportDatabase::Report> reports;
using OperationStatus = CrashReportDatabase::OperationStatus;
auto status = database->GetCompletedReports(&reports);
if (OperationStatus::kNoError != status) {
C_WARN << "Crashpad database GetCompletedReports failed";
return;
}
if (reports.empty()) {
C_WARN << "Crashpad database contains no completed reports";
return;
}
auto report = reports.back();
// Force the report to be uploaded (should change the report state to pending).
C_INFO << "Requesting report upload:" << report.uuid.ToString().c_str();
status = database->RequestUpload(report.uuid);
if (status != CrashReportDatabase::kNoError) {
// This may indicate that the report has already been removed from the database.
C_WARN << "Failed to request upload, status: " << status;
return;
}
// In this case, unless we restart the crashpad handler, the report won't
// be uploaded until the application is terminated.
startHandler();
// Let's wait for the report to be uploaded then clear all reports on a
// separate thread to avoid blocking the UI.
QThreadPool::globalInstance()->start([this, uuid = report.uuid]() {
auto database = CrashReportDatabase::Initialize(base::FilePath(dbPath_));
if (database == nullptr) {
C_WARN << "Crashpad database initialization failed";
return;
}
// Wait up to 10 seconds for the report to be uploaded.
// Around 5s has been observed running the submission server locally when the client
// is on Windows.
const int maxAttempts = 40;
int attempts = 0;
auto timeout = std::chrono::milliseconds(250);
C_INFO << "Waiting for report to be uploaded";
while (attempts++ < maxAttempts) {
CrashReportDatabase::Report report;
if (database->LookUpCrashReport(uuid, &report) == CrashReportDatabase::kNoError) {
if (report.uploaded
&& database->DeleteReport(uuid) == CrashReportDatabase::kNoError) {
C_INFO << "Report uploaded and deleted successfully";
return;
}
}
std::this_thread::sleep_for(timeout);
}
// Note: This usually indicates that the submission server is inaccessible.
C_WARN << "Failed to delete report. It may not have been uploaded successfully";
// If we failed to delete the report, we should still try to clear any dangling reports.
// This will prevent the database from growing indefinitely by removing at least the
// previous unsuccessfully removed report. In this case, we don't know if the report
// was successfully uploaded or not. This is a best-effort attempt.
::clearCompletedReports(database.get());
});
}

View File

@ -1,47 +0,0 @@
/*
* Copyright (C) 2024 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "crashreportclient.h"
#include <client/crashpad_client.h>
class AppSettingsManager;
class CrashPadClient final : public CrashReportClient
{
Q_OBJECT
public:
explicit CrashPadClient(AppSettingsManager* settingsManager, QObject* parent = nullptr);
~CrashPadClient();
void syncHandlerWithSettings() override;
void uploadLastReport() override;
void clearReports() override;
protected:
void setUploadsEnabled(bool enabled) override;
private:
void startHandler();
crashpad::CrashpadClient client_;
base::FilePath dbPath_;
base::FilePath handlerPath_;
};

View File

@ -1,63 +0,0 @@
/*
* Copyright (C) 2024 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
// Implementation choice
#include "crashreportclient.h"
#if not ENABLE_CRASHREPORTS
using CrashReportClientImpl = NullCrashReportClient;
#elif defined(ENABLE_CRASHPAD)
#include "crashreportclients/crashpad.h"
using CrashReportClientImpl = CrashPadClient;
#else
#pragma GCC error "No crash report client enabled, but reports are enabled."
#endif
#include <memory>
class CrashReporter : public QObject
{
Q_OBJECT
public:
explicit CrashReporter(AppSettingsManager* settingsManager, QObject* parent = nullptr)
: QObject(parent)
{
client_ = std::make_unique<CrashReportClientImpl>(settingsManager, this);
}
Q_INVOKABLE void syncHandlerWithSettings()
{
client_->syncHandlerWithSettings();
}
Q_INVOKABLE void uploadLastReport()
{
client_->uploadLastReport();
}
Q_INVOKABLE void clearReports()
{
client_->clearReports();
}
Q_INVOKABLE bool getHasPendingReport()
{
return client_->getHasPendingReport();
}
private:
std::unique_ptr<CrashReportClient> client_;
};

View File

@ -152,7 +152,7 @@ CurrentConversation::updateData()
updateProfile(convId);
updateActiveCalls(accountId, convId);
} catch (...) {
qWarning() << "Error while updating current conversation data for" << convId;
qWarning() << "Can't update current conversation data for" << convId;
}
}
@ -227,7 +227,7 @@ CurrentConversation::updateProfile(const QString& convId)
try {
if (auto optConv = convModel->getConversationForUid(convId)) {
auto& convInfo = optConv->get();
// Now, update call information (rdvAccount/device)
// Now, update call informations (rdvAccount/device)
if (convInfo.infos.contains("rdvAccount")) {
set_rdvAccount(convInfo.infos["rdvAccount"]);
} else {
@ -270,7 +270,7 @@ CurrentConversation::connectModel()
auto currentConversationModel = lrcInstance_->getCurrentConversationModel();
auto currentCallModel = lrcInstance_->getCurrentCallModel();
if (!currentConversationModel || !currentCallModel) {
C_DBG << "CurrentConversation: unable to connect to unavailable models";
C_DBG << "CurrentConversation: can't connect to unavailable models";
return;
}
@ -320,7 +320,7 @@ CurrentConversation::updateErrors(const QString& convId)
} else if (code == 3) {
newErrors.append(tr("An invalid message was detected"));
} else if (code == 4) {
newErrors.append(tr("Insufficient permission to update conversation information"));
newErrors.append(tr("Not authorized to update conversation information"));
} else if (code == 5) {
newErrors.append(tr("An error occurred while committing a new message"));
} else {

View File

@ -29,13 +29,13 @@ void
DBusErrorHandler::errorCallback()
{
qDebug() << "Dring has possibly crashed, "
"or has been killed will wait 2.5 seconds before attempting to reconnect.";
"or has been killed... will wait 2.5 seconds and try to reconnect";
Q_EMIT showDaemonReconnectPopup(true);
QTimer::singleShot(2500, this, [this]() {
if ((!lrc::api::Lrc::isConnected()) || (!lrc::api::Lrc::dbusIsValid())) {
qDebug() << "An error occurred while attempting to reconnect to the daemon.";
qDebug() << "Could not reconnect to the daemon";
Q_EMIT daemonReconnectFailed();
} else {
static_cast<DBusErrorHandler&>(GlobalInstances::dBusErrorHandler())

View File

@ -141,14 +141,14 @@ private Q_SLOTS:
connection_ = server_->nextPendingConnection();
connect(connection_, &QLocalSocket::readyRead, this, [this] {
QLocalSocket* clientSocket = (QLocalSocket*) sender();
QByteArray receivedData;
receivedData = clientSocket->readAll();
if (receivedData == terminateSeq_) {
QByteArray recievedData;
recievedData = clientSocket->readAll();
if (recievedData == terminateSeq_) {
qWarning() << "Received terminate signal.";
mainAppInstance_->quit();
} else {
qDebug() << "Received start URI:" << receivedData;
auto startUri = QString::fromLatin1(receivedData);
qDebug() << "Received start URI:" << recievedData;
auto startUri = QString::fromLatin1(recievedData);
mainAppInstance_->handleUriAction(startUri);
}
});

View File

@ -1,2 +0,0 @@
Language: JavaScript
BasedOnStyle: Google

View File

@ -1,279 +0,0 @@
/*
* Copyright (C) 2020-2024 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// This file contains the functions that allow the user to format the text in
// the message bar by adding bold, italic, underline, strikethrough, ordered
// list, and unordered list styles.
function isStyle(ta, text, char1, char2) {
const start = ta.selectionStart;
const end = ta.selectionEnd;
if (char1 === '**') {
return isStarStyle(ta, text, 'bold');
}
if (char1 === '*') {
return isStarStyle(ta, text, 'italic');
}
const selectedText = text.substring(start - char1.length, end + char2.length);
return (selectedText.startsWith(char1) && selectedText.endsWith(char2));
}
function isStarStyle(ta, text, type) {
const selectionStart = ta.selectionStart;
const selectionEnd = ta.selectionEnd;
let start = selectionStart;
while (start > 0 && text[start - 1] === '*') {
start--;
}
let end = selectionEnd;
while (end < text.length && text[end] === '*') {
end++;
}
const starCount = Math.min(selectionStart - start, end - selectionEnd);
if (type === 'italic') {
return starCount === 1 || starCount === 3;
}
return starCount === 2 || starCount === 3;
}
function addStyle(ta, text, char1, char2) {
const start = ta.selectionStart;
const end = ta.selectionEnd;
// Get the selected text with markdown effect
var selectedText = text.substring(start - char1.length, end + char2.length);
// If the selected text is already formatted with the given characters, remove
// them
if (isStyle(ta, text, char1, char2)) {
selectedText = text.substring(start, end);
ta.text = text.substring(0, start - char1.length) + selectedText +
text.substring(end + char2.length);
ta.selectText(start - char1.length, end - char1.length);
return;
}
// Otherwise, add the formatting characters to the selected text
ta.text = text.substring(0, start) + char1 + text.substring(start, end) +
char2 + text.substring(end);
ta.selectText(start + char1.length, end + char1.length);
}
function isPrefixSyle(ta, message, delimiter, isOrderedList) {
const selectionStart = ta.selectionStart;
const selectionEnd = ta.selectionEnd;
// Represents all the selected lines
var multilineSelection;
var newPrefix;
var newSuffix;
var newStartPos;
var newEndPos;
function nextIndexOf(text, char1, startPos) {
return text.indexOf(char1, startPos + 1);
}
// Get the previous index of the multilineSelection text
if (message[selectionStart] === '\n')
newStartPos = message.lastIndexOf('\n', selectionStart - 1);
else
newStartPos = message.lastIndexOf('\n', selectionStart);
// Get the next index of the multilineSelection text
if (message[selectionEnd] === '\n' || message[selectionEnd] === undefined)
newEndPos = selectionEnd;
else
newEndPos = nextIndexOf(message, '\n', selectionEnd);
// If the text is empty
if (newStartPos === -1) newStartPos = 0;
newPrefix = message.slice(0, newStartPos);
multilineSelection = message.slice(newStartPos, newEndPos);
newSuffix = message.slice(newEndPos);
var isFirstLineSelected =
!multilineSelection.startsWith('\n') || newPrefix === '';
var getDelimiter_counter = 1;
function getDelimiter() {
return `${getDelimiter_counter++}. `;
}
function getHasCurrentMarkdown() {
const linesQuantity = (multilineSelection.match(/\n/g) || []).length;
const newLinesWithDelimitersQuantity =
(multilineSelection.match(new RegExp(`\n${delimiter}`, 'g')) ||
[]).length;
if (newLinesWithDelimitersQuantity === linesQuantity &&
!isFirstLineSelected)
return true;
return linesQuantity === newLinesWithDelimitersQuantity &&
multilineSelection.startsWith(delimiter);
}
function getHasCurrentMarkdownBullet() {
const linesQuantity = (multilineSelection.match(/\n/g) || []).length;
const newLinesWithDelimitersQuantity =
(multilineSelection.match(/\n\d+\. /g) || []).length;
if (newLinesWithDelimitersQuantity === linesQuantity &&
!isFirstLineSelected)
return true;
return linesQuantity === newLinesWithDelimitersQuantity &&
(/^\d\. /).test(multilineSelection);
}
var newValue;
var newStart;
var newEnd;
var count;
var startPos;
var multilineSelectionLength;
if (!isOrderedList) {
return getHasCurrentMarkdown();
} else {
return getHasCurrentMarkdownBullet();
}
}
function addPrefixStyle(ta, message, delimiter, isOrderedList) {
const selectionStart = ta.selectionStart;
const selectionEnd = ta.selectionEnd;
// Represents all the selected lines
var multilineSelection;
var newPrefix;
var newSuffix;
var newStartPos;
var newEndPos;
function nextIndexOf(text, char1, startPos) {
return text.indexOf(char1, startPos + 1);
}
// Get the previous index of the multilineSelection text
if (message[selectionStart] === '\n')
newStartPos = message.lastIndexOf('\n', selectionStart - 1);
else
newStartPos = message.lastIndexOf('\n', selectionStart);
// Get the next index of the multilineSelection text
if (message[selectionEnd] === '\n' || message[selectionEnd] === undefined)
newEndPos = selectionEnd;
else
newEndPos = nextIndexOf(message, '\n', selectionEnd);
// If the text is empty
if (newStartPos === -1) newStartPos = 0;
newPrefix = message.slice(0, newStartPos);
multilineSelection = message.slice(newStartPos, newEndPos);
newSuffix = message.slice(newEndPos);
var isFirstLineSelected =
!multilineSelection.startsWith('\n') || newPrefix === '';
var getDelimiter_counter = 1;
function getDelimiter() {
return `${getDelimiter_counter++}. `;
}
function getHasCurrentMarkdown() {
const linesQuantity = (multilineSelection.match(/\n/g) || []).length;
const newLinesWithDelimitersQuantity =
(multilineSelection.match(new RegExp(`\n${delimiter}`, 'g')) ||
[]).length;
if (newLinesWithDelimitersQuantity === linesQuantity &&
!isFirstLineSelected)
return true;
return linesQuantity === newLinesWithDelimitersQuantity &&
multilineSelection.startsWith(delimiter);
}
function getHasCurrentMarkdownBullet() {
const linesQuantity = (multilineSelection.match(/\n/g) || []).length;
const newLinesWithDelimitersQuantity =
(multilineSelection.match(/\n\d+\. /g) || []).length;
if (newLinesWithDelimitersQuantity === linesQuantity &&
!isFirstLineSelected)
return true;
return linesQuantity === newLinesWithDelimitersQuantity &&
(/^\d\. /).test(multilineSelection);
}
var newValue;
var newStart;
var newEnd;
var count;
var startPos;
var multilineSelectionLength;
if (!isOrderedList) {
if (getHasCurrentMarkdown()) {
// Clear first line from delimiter
if (isFirstLineSelected)
multilineSelection = multilineSelection.slice(delimiter.length);
newValue = newPrefix +
multilineSelection.replace(new RegExp(`\n${delimiter}`, 'g'), '\n') +
newSuffix;
count = 0;
if (isFirstLineSelected) count++;
count += (multilineSelection.match(/\n/g) || []).length;
newStart = Math.max(selectionStart - delimiter.length, 0);
newEnd = Math.max(selectionEnd - (delimiter.length * count), 0);
} else {
newValue = newPrefix +
multilineSelection.replace(/\n/g, `\n${delimiter}`) + newSuffix;
count = 0;
if (isFirstLineSelected) {
newValue = delimiter + newValue;
count++;
}
count += (multilineSelection.match(new RegExp('\\n', 'g')) || []).length;
newStart = selectionStart + delimiter.length;
newEnd = selectionEnd + (delimiter.length * count);
}
} else if (getHasCurrentMarkdownBullet()) {
if (message[selectionStart] === '\n')
startPos = message.lastIndexOf('\n', selectionStart - 1) + 1;
else
startPos = message.lastIndexOf('\n', selectionStart) + 1;
newStart = startPos;
multilineSelection = multilineSelection.replace(/^\d+\.\s/gm, '');
newValue = newPrefix + multilineSelection + newSuffix;
multilineSelectionLength = multilineSelection.length;
// If the first line is not selected, we need to remove the first "\n" of
// multilineSelection
if (newStart) multilineSelectionLength = multilineSelection.length - 1;
newEnd = Math.max(newStart + multilineSelectionLength, 0);
} else {
if (message[selectionStart] === '\n')
startPos = message.lastIndexOf('\n', selectionStart - 1) + 1;
else
startPos = message.lastIndexOf('\n', selectionStart) + 1;
newStart = startPos;
// If no text is selected
if (selectionStart === selectionEnd) newStart = newStart + 3;
if (isFirstLineSelected)
multilineSelection = getDelimiter() + multilineSelection;
const selectionArr = Array.from(multilineSelection);
for (var i = 0; i < selectionArr.length; i++) {
if (selectionArr[i] === '\n') selectionArr[i] = `\n${getDelimiter()}`;
}
multilineSelection = selectionArr.join('');
newValue = newPrefix + multilineSelection + newSuffix;
multilineSelectionLength = multilineSelection.length;
// If the first line is not selected, we meed to remove the first "\n" of
// multilineSelection
if (startPos) multilineSelectionLength = multilineSelection.length - 1;
newEnd = Math.max(startPos + multilineSelectionLength, 0);
}
ta.text = newValue;
ta.selectText(newStart, newEnd);
}

View File

@ -43,8 +43,9 @@ LRCInstance::LRCInstance(const QString& updateUrl,
muteDaemon_ = muteDaemon;
threadPool_->setMaxThreadCount(1);
// Update the current account when the account list changes.
connect(&accountModel(), &AccountModel::accountsReordered, this, [this] {
connect(this, &LRCInstance::currentAccountIdChanged, [this] {
// save to config, editing the accountlistmodel's underlying data
accountModel().setTopAccount(currentAccountId_);
Q_EMIT accountListChanged();
profile::Info profileInfo;
@ -61,11 +62,6 @@ LRCInstance::LRCInstance(const QString& updateUrl,
set_currentAccountAvatarSet(!profileInfo.avatar.isEmpty());
});
connect(this, &LRCInstance::currentAccountIdChanged, [this] {
// This will trigger `AccountModel::accountsReordered`.
accountModel().setTopAccount(currentAccountId_);
});
connect(&accountModel(), &AccountModel::profileUpdated, this, [this](const QString& id) {
if (id != currentAccountId_)
return;
@ -352,36 +348,30 @@ LRCInstance::stopAudioMeter()
});
}
QVariantMap
QString
LRCInstance::getContentDraft(const QString& convUid, const QString& accountId)
{
auto draftKey = accountId + "_" + convUid;
QVariantMap draftMap;
draftMap["text"] = contentDrafts_[draftKey];
draftMap["files"] = fileDrafts_[draftKey];
return draftMap;
return contentDrafts_[draftKey];
}
void
LRCInstance::setContentDraft(const QString& convUid,
const QString& accountId,
const QString& textDraft,
const QList<QString>& filePathDraft)
const QString& content)
{
if (accountId.isEmpty() || convUid.isEmpty()) {
return;
}
auto draftKey = accountId + "_" + convUid;
// prevent a senseless dataChanged signal from the
// model if nothing has changed
if (contentDrafts_[draftKey] == textDraft && fileDrafts_[draftKey] == filePathDraft) {
if (contentDrafts_[draftKey] == content)
return;
}
contentDrafts_[draftKey] = textDraft;
fileDrafts_[draftKey] = filePathDraft;
contentDrafts_[draftKey] = content;
// this signal is only needed to update the current smartlist
Q_EMIT draftSaved(convUid);
}
@ -412,7 +402,7 @@ LRCInstance::indexOfActiveCall(const QString& confId, const QString& uri, const
{
if (auto optConv = getCurrentConversationModel()->getConversationForUid(selectedConvUid_)) {
auto& convInfo = optConv->get();
return convInfo.indexOfActiveCall(confId, uri, deviceId);
return convInfo.indexOfActiveCall({{"confId", confId}, {"uri", uri}, {"device", deviceId}});
}
return -1;
}

Some files were not shown because too many files have changed in this diff Show More