diff --git a/ci/Jenkinsfile.android b/ci/Jenkinsfile.android
index 78ddb5d4ae2..3605a361dbc 100644
--- a/ci/Jenkinsfile.android
+++ b/ci/Jenkinsfile.android
@@ -63,6 +63,7 @@ pipeline {
/* Control output the filename */
APP_TYPE = "${utils.getAppType()}"
PLATFORM = "android/arm64"
+ MAKEFLAGS = "-j${utils.getProcCount()} V=${params.VERBOSE}"
QT_VERSION = "6.9.2"
QT_ANDROID_PATH = "/opt/qt/${env.QT_VERSION}/android_arm64_v8a"
QMAKE = "/opt/qt/${env.QT_VERSION}/android_arm64_v8a/bin/qmake"
diff --git a/ci/Jenkinsfile.ios b/ci/Jenkinsfile.ios
index 80cf3785c2d..b4a0917a451 100644
--- a/ci/Jenkinsfile.ios
+++ b/ci/Jenkinsfile.ios
@@ -45,7 +45,7 @@ pipeline {
GOTMPDIR = "${env.WORKSPACE_TMP}"
PLATFORM = "ios/${getArch()}"
/* Improve make performance */
- MAKEFLAGS = "-j4 V=${params.VERBOSE}"
+ MAKEFLAGS = "-j${utils.getProcCount()} V=${params.VERBOSE}"
QT_VERSION="6.9.2"
QMAKE = "/Users/admin/${QT_VERSION}/ios/bin/qmake"
QT_HOST_PATH = "/Users/admin/${QT_VERSION}/macos"
diff --git a/ci/Jenkinsfile.linux b/ci/Jenkinsfile.linux
index 36100bfdfa0..f236df0a1ff 100644
--- a/ci/Jenkinsfile.linux
+++ b/ci/Jenkinsfile.linux
@@ -76,7 +76,7 @@ pipeline {
environment {
PLATFORM = "linux/${getArch()}${(params.USE_NWAKU ?: false) ? '-nwaku' : ''}"
/* Improve make performance */
- MAKEFLAGS = "-j4 V=${params.VERBOSE}"
+ MAKEFLAGS = "-j${utils.getProcCount()} V=${params.VERBOSE}"
/* Avoid weird bugs caused by stale cache. */
QML_DISABLE_DISK_CACHE = "true"
/* Set USE_NWAKU before VERSION since version.sh uses it */
diff --git a/ci/Jenkinsfile.linux-nix b/ci/Jenkinsfile.linux-nix
index 1f636061d33..06a3b383432 100644
--- a/ci/Jenkinsfile.linux-nix
+++ b/ci/Jenkinsfile.linux-nix
@@ -59,7 +59,7 @@ pipeline {
environment {
PLATFORM = "linux-nix/${getArch()}"
/* Improve make performance */
- MAKEFLAGS = "-j4 V=${params.VERBOSE}"
+ MAKEFLAGS = "-j${utils.getProcCount()} V=${params.VERBOSE}"
/* Avoid weird bugs caused by stale cache. */
QML_DISABLE_DISK_CACHE = "true"
/* Control output the filename */
diff --git a/ci/Jenkinsfile.macos b/ci/Jenkinsfile.macos
index 6dd8424d361..11b03fb61d8 100644
--- a/ci/Jenkinsfile.macos
+++ b/ci/Jenkinsfile.macos
@@ -70,7 +70,7 @@ pipeline {
environment {
PLATFORM = "macos/${getArch()}${(params.USE_NWAKU ?: false) ? '-nwaku' : ''}"
/* Improve make performance */
- MAKEFLAGS = "-j4 V=${params.VERBOSE}"
+ MAKEFLAGS = "-j${utils.getProcCount()} V=${params.VERBOSE}"
QT_VERSION="6.9.2"
QMAKE = "/Users/admin/${QT_VERSION}/macos/bin/qmake"
/* QMAKE = sh(script: "which qmake", returnStdout: true).trim() */
diff --git a/ci/Jenkinsfile.tests-ui b/ci/Jenkinsfile.tests-ui
index 4c40d037b1e..013708cfef5 100644
--- a/ci/Jenkinsfile.tests-ui
+++ b/ci/Jenkinsfile.tests-ui
@@ -47,7 +47,7 @@ pipeline {
environment {
PLATFORM = 'tests/ui'
/* Improve make performance */
- MAKEFLAGS = "-j4 V=${params.VERBOSE}"
+ MAKEFLAGS = "-j${utils.getProcCount()} V=${params.VERBOSE}"
/* Makefile assumes the compiler folder is included */
QTDIR = "/opt/qt/6.9.2/gcc_64"
PATH = "${env.QTDIR}/bin:${env.PATH}"
@@ -70,8 +70,10 @@ pipeline {
stage('Check Translations') {
steps {
- script {
- checkTranslations()
+ catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
+ script {
+ checkTranslations()
+ }
}
}
}
diff --git a/mobile/android/qt6/AndroidManifest.xml b/mobile/android/qt6/AndroidManifest.xml
index c912e3bcaad..7c554f0744c 100644
--- a/mobile/android/qt6/AndroidManifest.xml
+++ b/mobile/android/qt6/AndroidManifest.xml
@@ -11,19 +11,21 @@
+
+
+
+
+
+
-
-
-
-
+
+
-
-
-
-
+
+
diff --git a/mobile/scripts/buildNimStatusClient.sh b/mobile/scripts/buildNimStatusClient.sh
index 1384dca0d20..ca6aa116f9f 100755
--- a/mobile/scripts/buildNimStatusClient.sh
+++ b/mobile/scripts/buildNimStatusClient.sh
@@ -58,6 +58,7 @@ env $FEATURE_FLAGS ./vendor/nimbus-build-system/scripts/env.sh nim c "${PLATFORM
--cpu:"$CARCH" \
--noMain:on \
-d:release \
+ -d:production \
--clang.exe="$CC" \
--clang.linkerexe="$CC" \
--dynlibOverrideAll \
@@ -67,4 +68,3 @@ env $FEATURE_FLAGS ./vendor/nimbus-build-system/scripts/env.sh nim c "${PLATFORM
mkdir -p "$LIB_DIR"
cp "$STATUS_DESKTOP/bin/libnim_status_client$LIB_EXT" "$LIB_DIR/libnim_status_client$LIB_EXT"
-
diff --git a/src/app/modules/main/profile_section/advanced/module.nim b/src/app/modules/main/profile_section/advanced/module.nim
index 789b4985ffd..5fa35f28c4e 100644
--- a/src/app/modules/main/profile_section/advanced/module.nim
+++ b/src/app/modules/main/profile_section/advanced/module.nim
@@ -63,7 +63,7 @@ method onFleetSet*(self: Module) =
quit(QuitSuccess) # quits the app TODO: change this to logout instead when supported
method getLogDir*(self: Module): string =
- return url_fromLocalFile(constants.LOGDIR)
+ return constants.LOGDIR
method getWakuV2LightClientEnabled*(self: Module): bool =
return self.controller.getWakuV2LightClientEnabled()
diff --git a/src/app_service/service/chat/async_tasks.nim b/src/app_service/service/chat/async_tasks.nim
index a3240f4bef3..099b3b23d6d 100644
--- a/src/app_service/service/chat/async_tasks.nim
+++ b/src/app_service/service/chat/async_tasks.nim
@@ -105,7 +105,6 @@ type
AsyncSendImagesTaskArg = ref object of QObjectTaskArg
chatId: string
imagePathsJson: string
- tempDir: string
msg: string
replyTo: string
preferredUsername: string
@@ -118,14 +117,12 @@ const asyncSendImagesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
try:
var images = Json.decode(arg.imagePathsJson, seq[string])
var imagePaths: seq[string] = @[]
- var tempPaths: seq[string] = @[]
for imagePathOrSource in images.mitems:
if utils.isBase64DataUrl(imagePathOrSource):
- let imagePath = save_byte_image_to_file(imagePathOrSource, arg.tempDir)
+ let imagePath = save_byte_image_to_file(imagePathOrSource)
if imagePath != "":
imagePaths.add(imagePath)
- tempPaths.add(imagePath)
else:
imagePaths.add(imagePathOrSource)
@@ -140,9 +137,6 @@ const asyncSendImagesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
arg.paymentRequests
)
- for imagePath in tempPaths:
- removeFile(imagePath)
-
arg.finish(%* {
"response": response,
"chatId": arg.chatId,
diff --git a/src/app_service/service/chat/service.nim b/src/app_service/service/chat/service.nim
index 646ae7f281f..3f4c5f236e9 100644
--- a/src/app_service/service/chat/service.nim
+++ b/src/app_service/service/chat/service.nim
@@ -15,7 +15,6 @@ import backend/group_chat as status_group_chat
import app/global/[global_singleton, utils]
import app/core/eventemitter
import app/core/signals/types
-import constants
import ../../common/message as message_common
@@ -428,7 +427,6 @@ QtObject:
slot: "onAsyncSendImagesDone",
chatId: chatId,
imagePathsJson: imagePathsJson,
- tempDir: TMPDIR.replace("\\", "\\\\"), # Escape backslashes so that the JSON sent is valid (Windows issue)
msg: msg,
replyTo: replyTo,
preferredUsername: preferredUsername,
diff --git a/storybook/CMakeLists.txt b/storybook/CMakeLists.txt
index 00aacd5104d..896f9800f6d 100644
--- a/storybook/CMakeLists.txt
+++ b/storybook/CMakeLists.txt
@@ -29,7 +29,7 @@ if(MSVC)
endif()
if (APPLE)
- set(MACOS_VERSION_MIN_FLAGS -mmacosx-version-min=11.0)
+ set(MACOS_VERSION_MIN_FLAGS -mmacosx-version-min=14.0)
endif()
find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core)
diff --git a/storybook/pages/StatusNavBarTabButtonPage.qml b/storybook/pages/StatusNavBarTabButtonPage.qml
index f9ef772b2d4..eda7d0b7bb4 100644
--- a/storybook/pages/StatusNavBarTabButtonPage.qml
+++ b/storybook/pages/StatusNavBarTabButtonPage.qml
@@ -4,6 +4,9 @@ import QtQuick.Controls
import StatusQ.Controls
import StatusQ.Components.private
import StatusQ.Core.Theme
+import StatusQ.Popups
+
+import shared.controls.chat.menuItems
import Models
import Storybook
@@ -13,6 +16,7 @@ SplitView {
ButtonGroup {
buttons: column.children
+ onClicked: button => console.info("Clicked button:", button.tooltip.text)
}
Rectangle {
@@ -82,6 +86,38 @@ SplitView {
// when: ctrlNewBadgeGradient.checked
// }
}
+ StatusNavBarTabButton {
+ icon.name: "info"
+ tooltip.text: "With context menu"
+ thirdpartyServicesEnabled: thirdpartyServicesCtrl.checked
+ popupMenu: popupMenuComp
+ }
+ }
+ }
+
+ Component {
+ id: popupMenuComp
+ StatusMenu {
+ StatusAction {
+ text: qsTr("Invite People")
+ icon.name: "share-ios"
+ }
+
+ StatusAction {
+ text: qsTr("Community Info")
+ icon.name: "info"
+ }
+
+ StatusAction {
+ text: qsTr("Community Rules")
+ icon.name: "text"
+ }
+
+ StatusMenuSeparator {}
+
+ MuteChatMenuItem {
+ title: qsTr("Mute Community")
+ }
}
}
diff --git a/storybook/pages/SupportedTokenListsPanelPage.qml b/storybook/pages/SupportedTokenListsPanelPage.qml
index add231f65c8..e05bff0370a 100644
--- a/storybook/pages/SupportedTokenListsPanelPage.qml
+++ b/storybook/pages/SupportedTokenListsPanelPage.qml
@@ -6,7 +6,6 @@ import Storybook
import Models
import AppLayouts.Profile.panels
-import AppLayouts.Profile.stores
import StatusQ
@@ -39,7 +38,6 @@ SplitView {
Logs { id: logs }
-
Pane {
SplitView.fillWidth: true
SplitView.fillHeight: true
@@ -48,8 +46,6 @@ SplitView {
anchors.fill: parent
sourcesOfTokensModel: root.sourcesOfTokensModel
tokensListModel: root.tokensProxyModel
-
- onItemClicked: logs.logEvent("SupportedTokenListsPanel::onItemClicked --> Key --> " + key)
}
}
diff --git a/ui/StatusQ/include/StatusQ/urlutils.h b/ui/StatusQ/include/StatusQ/urlutils.h
index 3fea917e6f1..0103aef852d 100644
--- a/ui/StatusQ/include/StatusQ/urlutils.h
+++ b/ui/StatusQ/include/StatusQ/urlutils.h
@@ -40,4 +40,8 @@ class UrlUtils : public QObject
// ["jpg", "jpe", "jp", "jpeg", "png", "webp", "gif", "svg"]
QStringList m_allImgExtensions;
QStringList allValidImageExtensions() const { return m_allImgExtensions; }
+
+#ifdef Q_OS_ANDROID
+ QString resolveAndroidContentUrl(const QString& urlPath) const;
+#endif
};
diff --git a/ui/StatusQ/src/StatusQ/Components/StatusMessage.qml b/ui/StatusQ/src/StatusQ/Components/StatusMessage.qml
index 6101ed18f82..d9887b011e6 100644
--- a/ui/StatusQ/src/StatusQ/Components/StatusMessage.qml
+++ b/ui/StatusQ/src/StatusQ/Components/StatusMessage.qml
@@ -334,7 +334,7 @@ Control {
albumCount: root.messageDetails.albumCount > 0 ? root.messageDetails.albumCount : 1
imageWidth: Math.min(messageLayout.width / root.messageDetails.albumCount - 9 * (root.messageDetails.albumCount - 1), 144)
shapeType: StatusImageMessage.ShapeType.LEFT_ROUNDED
- onImageClicked: root.imageClicked(image, mouse, imageSource)
+ onImageClicked: (image, mouse, imageSource) => root.imageClicked(image, mouse, imageSource)
}
}
}
diff --git a/ui/StatusQ/src/StatusQ/Controls/StatusNavBarTabButton.qml b/ui/StatusQ/src/StatusQ/Controls/StatusNavBarTabButton.qml
index 7a2605f4876..1b89f91042c 100644
--- a/ui/StatusQ/src/StatusQ/Controls/StatusNavBarTabButton.qml
+++ b/ui/StatusQ/src/StatusQ/Controls/StatusNavBarTabButton.qml
@@ -1,9 +1,9 @@
import QtQuick
+import QtQuick.Controls
import StatusQ.Core
import StatusQ.Components
import StatusQ.Controls
-import StatusQ.Popups
import StatusQ.Core.Theme
StatusIconTabButton {
@@ -28,7 +28,7 @@ StatusIconTabButton {
visible: statusNavBarTabButton.hovered && !!statusTooltip.text
delay: 50
orientation: StatusToolTip.Orientation.Right
- x: statusNavBarTabButton.width + 16
+ x: statusNavBarTabButton.width + Theme.padding
y: statusNavBarTabButton.height / 2 - height / 2 + 4
}
@@ -62,36 +62,14 @@ StatusIconTabButton {
border.width: 2
}
- StatusMouseArea {
- anchors.fill: parent
- cursorShape: Qt.PointingHandCursor
- acceptedButtons: Qt.LeftButton | Qt.RightButton
- onClicked: function(mouse) {
- if (mouse.button === Qt.RightButton) {
- if (!!popupMenuSlot.sourceComponent && !popupMenuSlot.active)
- popupMenuSlot.active = true
- if (popupMenuSlot.active) {
- statusNavBarTabButton.highlighted = true
- let btnWidth = statusNavBarTabButton.width
- popupMenuSlot.item.popup(parent.x + btnWidth + 4, -2)
- }
- } else if (mouse.button === Qt.LeftButton) {
- statusNavBarTabButton.toggle()
- statusNavBarTabButton.clicked()
- }
- }
+ function openContextMenu(pos) {
+ if (!popupMenu)
+ return
+ const menu = popupMenu.createObject(statusNavBarTabButton)
+ statusTooltip.hide()
+ menu.popup(pos)
}
- Loader {
- id: popupMenuSlot
- sourceComponent: statusNavBarTabButton.popupMenu
- active: false
- onLoaded: {
- popupMenuSlot.item.closeHandler = function () {
- statusNavBarTabButton.highlighted = false
- popupMenuSlot.active = false
- }
- }
- }
+ ContextMenu.onRequested: pos => openContextMenu(pos)
+ onPressAndHold: openContextMenu(Qt.point(statusNavBarTabButton.pressX, statusNavBarTabButton.pressY))
}
-
diff --git a/ui/StatusQ/src/StatusQ/Controls/StatusToolTip.qml b/ui/StatusQ/src/StatusQ/Controls/StatusToolTip.qml
index 521a4fb9ead..095da79b4c3 100644
--- a/ui/StatusQ/src/StatusQ/Controls/StatusToolTip.qml
+++ b/ui/StatusQ/src/StatusQ/Controls/StatusToolTip.qml
@@ -2,10 +2,11 @@ import QtQuick
import QtQuick.Controls
import StatusQ.Core
+import StatusQ.Core.Utils
import StatusQ.Core.Theme
ToolTip {
- id: statusToolTip
+ id: root
enum Orientation {
Top,
@@ -20,24 +21,30 @@ ToolTip {
property alias arrow: arrow
property color color: Theme.palette.black
- implicitWidth: Math.min(maxWidth, implicitContentWidth + 16)
- padding: 8
- margins: 8
- delay: 200
+ implicitWidth: Math.min(maxWidth,
+ Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ )
+ horizontalPadding: Theme.padding
+ verticalPadding: Theme.halfPadding
+ margins: Theme.halfPadding
+ delay: Utils.isMobile ? Application.styleHints.mousePressAndHoldInterval
+ : 200
+
background: Item {
id: statusToolTipBackground
Rectangle {
id: statusToolTipContentBackground
- color: statusToolTip.color
- radius: 8
+ color: root.color
+ radius: Theme.radius
anchors.fill: parent
- anchors.bottomMargin: 8
+ anchors.bottomMargin: Theme.halfPadding
}
Rectangle {
id: arrow
color: statusToolTipContentBackground.color
- height: 20
- width: 20
+ height: Theme.padding
+ width: Theme.padding
rotation: 45
radius: 1
x: {
@@ -45,10 +52,10 @@ ToolTip {
return statusToolTipBackground.width / 2 - width / 2 + offset
}
if (orientation === StatusToolTip.Orientation.Left) {
- return statusToolTipContentBackground.width - (width / 2) - 8 + offset
+ return statusToolTipContentBackground.width - statusToolTipContentBackground.radius - radius*2 + offset
}
if (orientation === StatusToolTip.Orientation.Right) {
- return -width/2 + 8 + offset
+ return -width/2 + radius*2 + offset
}
}
y: {
@@ -65,14 +72,14 @@ ToolTip {
}
}
contentItem: StatusBaseText {
- text: statusToolTip.text
+ text: root.text
color: Theme.palette.white
linkColor: Theme.palette.white
wrapMode: Text.Wrap
font.pixelSize: Theme.additionalTextSize
font.weight: Font.Medium
horizontalAlignment: Text.AlignHCenter
- bottomPadding: 8
+ bottomPadding: Theme.halfPadding
textFormat: Text.RichText
elide: Text.ElideRight
}
diff --git a/ui/StatusQ/src/StatusQ/Popups/Dialog/StatusFileDialog.qml b/ui/StatusQ/src/StatusQ/Popups/Dialog/StatusFileDialog.qml
index fb7eb5a9515..5082dc2fa71 100644
--- a/ui/StatusQ/src/StatusQ/Popups/Dialog/StatusFileDialog.qml
+++ b/ui/StatusQ/src/StatusQ/Popups/Dialog/StatusFileDialog.qml
@@ -18,7 +18,9 @@ QObject {
property alias currentFolder: dlg.currentFolder
property string picturesShortcut: Utils.isIOS ? "assets-library://" :
- StandardPaths.writableLocation(StandardPaths.PicturesLocation)
+ d.standardPictureLocations.length > 1 ? d.standardPictureLocations[1] // [0] is writable, don't need it here, we have StatusSaveFileDialog for that
+ : d.standardPictureLocations.length > 0 ? d.standardPictureLocations[0]
+ : ""
signal accepted
signal rejected
@@ -33,6 +35,9 @@ QObject {
QtObject {
id: d
+
+ readonly property list standardPictureLocations: StandardPaths.standardLocations(StandardPaths.PicturesLocation)
+
readonly property url resolvedFile: resolveFile(dlg.selectedFile)
readonly property var resolvedFiles: resolveSelectedFiles(dlg.selectedFiles)
@@ -45,6 +50,7 @@ QObject {
resolvedLocalFile = "file:" + resolvedLocalFile
return resolvedLocalFile
}
+
function resolveSelectedFiles(selectedFiles) {
if (selectedFiles.length === 0)
return []
diff --git a/ui/StatusQ/src/systemutilsinternal.cpp b/ui/StatusQ/src/systemutilsinternal.cpp
index 7c19c93b67e..17db141f1c1 100644
--- a/ui/StatusQ/src/systemutilsinternal.cpp
+++ b/ui/StatusQ/src/systemutilsinternal.cpp
@@ -1,23 +1,21 @@
-#ifdef Q_OS_ANDROID
-#include
-#include
-#endif
#include "StatusQ/systemutilsinternal.h"
-#include
#include
#include
#include
#include
#include
#include
+#include
#ifdef Q_OS_ANDROID
#include
#include
#endif
+#ifdef Q_OS_IOS
#include "ios_utils.h"
+#endif
class QuitFilter : public QObject
{
@@ -26,7 +24,7 @@ class QuitFilter : public QObject
public:
using QObject::QObject;
- bool eventFilter(QObject* obj, QEvent* ev)
+ bool eventFilter(QObject* obj, QEvent* ev) override
{
if (ev->type() == QEvent::Quit)
emit quit(ev->spontaneous());
@@ -60,15 +58,8 @@ void SystemUtilsInternal::restartApplication() const
QMetaObject::invokeMethod(QCoreApplication::instance(), &QCoreApplication::exit, Qt::QueuedConnection, EXIT_SUCCESS);
}
-#if defined(Q_OS_IOS)
-void save(const QByteArray& imageData)
-{
- saveImageToPhotosAlbum(imageData);
-}
-#else
void save(const QByteArray& imageData, const QString& targetDir)
{
-
// Get current Date/Time information to use in naming of the image file
const auto dateTimeString = QDateTime::currentDateTime().toString(
QStringLiteral("dd-MM-yyyy_hh-mm-ss"));
@@ -99,7 +90,6 @@ void save(const QByteArray& imageData, const QString& targetDir)
"Downloading image failed while saving to file:"
<< targetFile;
}
-#endif
void SystemUtilsInternal::downloadImageByUrl(
const QUrl& url, const QString& path) const
@@ -110,10 +100,15 @@ void SystemUtilsInternal::downloadImageByUrl(
QNetworkReply *reply = manager.get(QNetworkRequest(url));
// accept both "file:/foo/bar" and "/foo/bar"
- auto targetDir = QUrl::fromUserInput(path).toLocalFile();
+ auto targetDir = QUrl::fromUserInput(path)
+#ifndef Q_OS_ANDROID
+ .toLocalFile();
+#else
+ .toString(); // don't touch the "content://" URI
+#endif
if (targetDir.isEmpty())
- targetDir = QDir::homePath();
+ targetDir = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
QObject::connect(reply, &QNetworkReply::finished, this, [reply, targetDir] {
if(reply->error() != QNetworkReply::NoError) {
@@ -125,11 +120,11 @@ void SystemUtilsInternal::downloadImageByUrl(
// Extract the image data to be able to load and save it
const auto btArray = reply->readAll();
Q_ASSERT(!btArray.isEmpty());
- #ifdef Q_OS_IOS
- save(btArray);
- #else
+#ifdef Q_OS_IOS
+ saveImageToPhotosAlbum(btArray);
+#else
save(btArray, targetDir);
- #endif
+#endif
});
}
diff --git a/ui/StatusQ/src/urlutils.cpp b/ui/StatusQ/src/urlutils.cpp
index 169cb0be0d4..7821b4453db 100644
--- a/ui/StatusQ/src/urlutils.cpp
+++ b/ui/StatusQ/src/urlutils.cpp
@@ -1,10 +1,14 @@
#include "StatusQ/urlutils.h"
+#include
#include
#include
+#include
#include
+#ifdef Q_OS_IOS
#include "ios_utils.h"
+#endif
namespace {
constexpr auto webpMime = "image/webp";
@@ -30,34 +34,65 @@ UrlUtils::UrlUtils(QObject *parent): QObject(parent) {
bool UrlUtils::isValidImageUrl(const QUrl &url) const
{
- QString mimeType;
- if (url.isLocalFile())
- mimeType = m_mimeDb.mimeTypeForFile(url.toLocalFile(), QMimeDatabase::MatchContent).name();
- else
- mimeType = m_mimeDb.mimeTypeForUrl(url).name();
+ // don't convert "content:/" like URLs to an empty path
+ const auto filePath = url.isLocalFile() ? url.toLocalFile() : url.toString();
+ const auto mimeType = m_mimeDb.mimeTypeForFile(filePath, QMimeDatabase::MatchContent).name();
return m_validImageMimeTypes.contains(mimeType);
}
qint64 UrlUtils::getFileSize(const QUrl& url)
{
- if (url.isLocalFile())
- return QFile(url.toLocalFile()).size();
-
- return 0;
+ // don't convert "content:/" like URLs to an empty path
+ const auto filePath = url.isLocalFile() ? url.toLocalFile() : url.toString();
+ return QFile(filePath).size(); // will return 0 for unknown file paths
}
QString UrlUtils::convertUrlToLocalPath(const QString &url) const {
+#ifdef Q_OS_ANDROID
+ return resolveAndroidContentUrl(url);
+#endif
+
const auto localFileOrUrl = urlFromUserInput(url); // accept both "file:/foo/bar" and "/foo/bar"
if (localFileOrUrl.isLocalFile()) {
- #ifdef Q_OS_IOS
+#ifdef Q_OS_IOS
return resolveIOSPhotoAsset(localFileOrUrl.toLocalFile());
- #endif // Q_OS_IOS
+#endif
return localFileOrUrl.toLocalFile();
}
return {};
}
+#ifdef Q_OS_ANDROID
+QString UrlUtils::resolveAndroidContentUrl(const QString& urlPath) const {
+ // test if we already have a real (resolved) path
+ if (urlPath.startsWith('/'))
+ return urlPath;
+
+ // test if we already have a real (resolved) path in URL form
+ if (urlPath.startsWith(QStringLiteral("file:/")))
+ return urlFromUserInput(urlPath).toLocalFile();
+
+ QDir dir(urlPath);
+ if (urlPath.endsWith('/') || dir.exists())
+ return urlPath; // a directory, just return it
+
+ QFile fileIn(urlPath);
+ if (!fileIn.open(QIODevice::ReadOnly))
+ return urlPath;
+
+ // save to a temp file, and return the filepath
+ const auto newFilePath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + '/' + QUuid::createUuid().toString(QUuid::WithoutBraces);
+ QFile fileOut(newFilePath);
+ if (!fileOut.open(QIODevice::WriteOnly | QIODevice::Truncate))
+ return urlPath;
+ if (fileOut.write(fileIn.readAll()) != -1)
+ return fileOut.fileName(); // return the real filename, not the virtual "content:/" URI
+
+ return urlPath;
+}
+#endif
+
QStringList UrlUtils::convertUrlsToLocalPaths(const QStringList &urls) const {
QStringList result;
for (const auto& url: urls) {
diff --git a/ui/app/AppLayouts/HomePage/delegates/HomePageGridItem.qml b/ui/app/AppLayouts/HomePage/delegates/HomePageGridItem.qml
index 713ba74de71..48a5857d329 100644
--- a/ui/app/AppLayouts/HomePage/delegates/HomePageGridItem.qml
+++ b/ui/app/AppLayouts/HomePage/delegates/HomePageGridItem.qml
@@ -1,7 +1,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
-import Qt5Compat.GraphicalEffects
+import QtQuick.Effects
import StatusQ.Core
import StatusQ.Controls
@@ -43,9 +43,12 @@ AbstractButton {
signal pinRequested
- layer.enabled: true
- layer.effect: Glow {
- samples: 33
+ RectangularShadow {
+ anchors.fill: background
+ z: background.z - 1
+ offset.x: 5
+ offset.y: 10
+ radius: Theme.defaultPadding
spread: 0.1
color: root.hovered ? Theme.palette.backdropColor : Theme.palette.dropShadow
Behavior on color { ColorAnimation { duration: Theme.AnimationDuration.Fast } }
diff --git a/ui/app/AppLayouts/Onboarding/OnboardingLayout.qml b/ui/app/AppLayouts/Onboarding/OnboardingLayout.qml
index d2a887bdc75..e39f633badc 100644
--- a/ui/app/AppLayouts/Onboarding/OnboardingLayout.qml
+++ b/ui/app/AppLayouts/Onboarding/OnboardingLayout.qml
@@ -257,6 +257,13 @@ Page {
onClicked: onboardingFlow.popTopLevelItem()
}
+ Keys.onPressed: function(e) {
+ if (e.key === Qt.Key_Back && backButton.visible) {
+ e.accepted = true
+ onboardingFlow.popTopLevelItem()
+ }
+ }
+
Connections {
target: onboardingFlow.topLevelItem
ignoreUnknownSignals: true
diff --git a/ui/app/AppLayouts/Onboarding/controls/LoginUserSelector.qml b/ui/app/AppLayouts/Onboarding/controls/LoginUserSelector.qml
index 5a36dd0d142..dd93d8c1865 100644
--- a/ui/app/AppLayouts/Onboarding/controls/LoginUserSelector.qml
+++ b/ui/app/AppLayouts/Onboarding/controls/LoginUserSelector.qml
@@ -90,7 +90,7 @@ Control {
id: dropdown
objectName: "dropdown"
- closePolicy: Popup.CloseOnPressOutsideParent | Popup.CloseOnEscape
+ bottomSheetAllowed: false
directParent: root
relativeY: root.height + 2
diff --git a/ui/app/AppLayouts/Profile/ProfileLayout.qml b/ui/app/AppLayouts/Profile/ProfileLayout.qml
index 7cc7d47f765..cce271b725c 100644
--- a/ui/app/AppLayouts/Profile/ProfileLayout.qml
+++ b/ui/app/AppLayouts/Profile/ProfileLayout.qml
@@ -150,7 +150,7 @@ StatusSectionLayout {
id: settingsEntriesModel
showWalletEntries: root.walletStore.isWalletEnabled
- showBrowserEntries: root.isBrowserEnabled
+ showBrowserEntries: root.isBrowserEnabled && localAccountSensitiveSettings.isBrowserEnabled
showBackUpSeed: !root.privacyStore.mnemonicBackedUp
backUpSeedBadgeCount: root.profileStore.userDeclinedBackupBanner ? 0 : showBackUpSeed
isKeycardEnabled: root.isKeycardEnabled
@@ -455,6 +455,7 @@ StatusSectionLayout {
advancedStore: root.advancedStore
walletStore: root.walletStore
isFleetSelectionEnabled: fleetSelectionEnabled
+ isBrowserEnabled: root.isBrowserEnabled
sectionTitle: settingsEntriesModel.getNameForSubsection(Constants.settingsSubsection.advanced)
contentWidth: d.contentWidth
}
diff --git a/ui/app/AppLayouts/Profile/helpers/SettingsEntriesModel.qml b/ui/app/AppLayouts/Profile/helpers/SettingsEntriesModel.qml
index fe2971b9b19..6fb951dfd5b 100644
--- a/ui/app/AppLayouts/Profile/helpers/SettingsEntriesModel.qml
+++ b/ui/app/AppLayouts/Profile/helpers/SettingsEntriesModel.qml
@@ -217,7 +217,7 @@ SortFilterProxyModel {
case Constants.settingsSubsection.ensUsernames:
case Constants.settingsSubsection.wallet:
return root.showWalletEntries
- case Constants.settingsSubsection.browser:
+ case Constants.settingsSubsection.browserSettings:
return root.showBrowserEntries
case Constants.settingsSubsection.backUpSeed:
return root.showBackUpSeed
diff --git a/ui/app/AppLayouts/Profile/panels/SupportedTokenListsPanel.qml b/ui/app/AppLayouts/Profile/panels/SupportedTokenListsPanel.qml
index 83fa36f10fc..d422bac371b 100644
--- a/ui/app/AppLayouts/Profile/panels/SupportedTokenListsPanel.qml
+++ b/ui/app/AppLayouts/Profile/panels/SupportedTokenListsPanel.qml
@@ -6,7 +6,6 @@ import StatusQ.Core
import StatusQ.Components
import StatusQ.Controls
import StatusQ.Core.Theme
-import StatusQ.Core.Utils as SQUtils
import SortFilterProxyModel
import shared.controls
@@ -20,11 +19,10 @@ StatusListView {
required property var sourcesOfTokensModel // Expected roles: key, name, updatedAt, source, version, tokensCount, image
required property var tokensListModel // Expected roles: name, symbol, image, chainName, explorerUrl
- signal itemClicked(string key)
-
implicitHeight: contentHeight
model: root.sourcesOfTokensModel
spacing: Theme.halfPadding
+
delegate: StatusListItem {
height: ProfileUtils.defaultDelegateHeight
width: ListView.view.width
@@ -65,36 +63,39 @@ StatusListView {
required property string name
required property string image
required property string source
- required property int updatedAt
+ required property double updatedAt
required property string version
required property int tokensCount
-
- Component.onCompleted: popup.open()
+ }
+ onObjectAdded: function(index, delegate) {
+ popupComp.createObject(root, {
+ title: delegate.name,
+ sourceImage: delegate.image,
+ sourceUrl: delegate.source,
+ sourceVersion: delegate.version,
+ updatedAt: delegate.updatedAt,
+ tokensCount: delegate.tokensCount
+ }).open()
}
}
- TokenListPopup {
- id: popup
+ Component {
+ id: popupComp
+ TokenListPopup {
+ destroyOnClose: true
- sourceImage: delegate.image
- sourceUrl: delegate.source
- sourceVersion: delegate.version
- updatedAt: delegate.updatedAt
- tokensCount: delegate.tokensCount
+ tokensListModel: SortFilterProxyModel {
+ sourceModel: root.tokensListModel
- title: delegate.name
-
- tokensListModel: SortFilterProxyModel {
- sourceModel: root.tokensListModel
-
- // Filter by source
- filters: RegExpFilter {
- roleName: "sources"
- pattern: "\;" + keyFilter.value + "\;"
+ // Filter by source
+ filters: RegExpFilter {
+ roleName: "sources"
+ pattern: "\;" + keyFilter.value + "\;"
+ }
}
- }
- onLinkClicked: (link) => Global.openLink(link)
- onClosed: keyFilter.value = ""
+ onLinkClicked: (link) => Global.openLink(link)
+ onClosed: keyFilter.value = ""
+ }
}
}
diff --git a/ui/app/AppLayouts/Profile/views/AdvancedView.qml b/ui/app/AppLayouts/Profile/views/AdvancedView.qml
index 8515535ce49..3c593a721d5 100644
--- a/ui/app/AppLayouts/Profile/views/AdvancedView.qml
+++ b/ui/app/AppLayouts/Profile/views/AdvancedView.qml
@@ -11,6 +11,7 @@ import shared.popups
import shared.status
import shared.controls
+import StatusQ
import StatusQ.Components
import StatusQ.Controls
import StatusQ.Controls.Validators
@@ -36,6 +37,7 @@ SettingsContentBase {
property WalletStore walletStore
property bool isFleetSelectionEnabled
+ property bool isBrowserEnabled: true
Item {
id: advancedContainer
@@ -54,6 +56,7 @@ SettingsContentBase {
width: root.contentWidth
StatusSettingsLineButton {
+ width: parent.width
text: qsTr("Fleet")
currentValue: root.advancedStore.fleet
onClicked: fleetModal.open()
@@ -62,12 +65,14 @@ SettingsContentBase {
StatusSettingsLineButton {
id: labelScrolling
+ width: parent.width
text: qsTr("Chat scrolling")
currentValue: root.advancedStore.isCustomScrollingEnabled ? qsTr("Custom") : qsTr("System")
onClicked: scrollingModal.open()
}
StatusSettingsLineButton {
+ width: parent.width
text: qsTr("Minimize on close")
isSwitch: true
checked: !localAccountSensitiveSettings.quitOnClose
@@ -75,6 +80,7 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ width: parent.width
text: qsTr("Mainnet data verified by Nimbus")
isSwitch: true
checked: root.advancedStore.isNimbusProxyEnabled
@@ -88,22 +94,28 @@ SettingsContentBase {
anchors.right: parent.right
anchors.leftMargin: Theme.padding
anchors.rightMargin: Theme.padding
- text: qsTr("Application Logs")
+ text: qsTr("Application Logs") + " (" + root.advancedStore.logDir() + ")"
font.underline: mouseArea.containsMouse
color: Theme.palette.primaryColor1
topPadding: 23
+ wrapMode: Text.Wrap
+ elide: Text.ElideRight
StatusMouseArea {
id: mouseArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
- onClicked: {
- Qt.openUrlExternally(root.advancedStore.logDir())
- }
+ onClicked: logsFolderDialog.open()
}
}
+ StatusFolderDialog {
+ id: logsFolderDialog
+ title: qsTr("Application Logs")
+ currentFolder: root.advancedStore.logDir()
+ }
+
Item {
id: spacer1
height: Theme.bigPadding
@@ -125,9 +137,11 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ visible: root.isBrowserEnabled // feature flag
+ width: parent.width
text: qsTr("Web/dApp Browser")
isSwitch: true
- checked: localAccountSensitiveSettings.isBrowserEnabled
+ checked: localAccountSensitiveSettings.isBrowserEnabled // user setting
onToggled: {
if (checked) {
confirmationPopup.experimentalFeature = root.advancedStore.experimentalFeatures.browser
@@ -139,6 +153,7 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ width: parent.width
text: qsTr("Node Management")
isSwitch: true
checked: localAccountSensitiveSettings.nodeManagementEnabled
@@ -153,6 +168,7 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ width: parent.width
text: qsTr("Archive Protocol Enabled")
visible: !SQUtils.Utils.isMobile
isSwitch: true
@@ -163,6 +179,7 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ width: parent.width
text: qsTr("ENS Community Permissions Enabled")
isSwitch: true
checked: root.advancedStore.ensCommunityPermissionsEnabled
@@ -186,6 +203,8 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ visible: !SQUtils.Utils.isMobile
+ width: parent.width
text: qsTr("Enable creation of sharded communities")
isSwitch: true
checked: root.advancedStore.isWakuV2ShardedCommunitiesEnabled
@@ -287,6 +306,7 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ width: parent.width
text: qsTr("Full developer mode")
enabled: {
return !localAccountSensitiveSettings.downloadChannelMessagesEnabled ||
@@ -303,6 +323,7 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ width: parent.width
text: qsTr("Enable translations")
isSwitch: true
checked: localAppSettings.translationsEnabled
@@ -325,6 +346,7 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ width: parent.width
text: qsTr("Download messages")
isSwitch: true
checked: localAccountSensitiveSettings.downloadChannelMessagesEnabled
@@ -334,6 +356,7 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ width: parent.width
text: qsTr("Debug")
isSwitch: true
enabled: !root.advancedStore.isRuntimeLogLevelSet
@@ -359,6 +382,7 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ width: parent.width
text: qsTr("Auto message")
isSwitch: true
checked: root.advancedStore.isAutoMessageEnabled
@@ -368,6 +392,7 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ width: parent.width
objectName: "manageCommunitiesOnTestnetButton"
text: qsTr("Manage communities on testnet")
isSwitch: true
@@ -378,6 +403,7 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ width: parent.width
text: qsTr("Enable community tokens refreshing")
isSwitch: true
checked: root.advancedStore.refreshTokenEnabled
@@ -387,6 +413,7 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ width: parent.width
text: qsTr("How many log files to keep archived")
currentValue: root.advancedStore.logMaxBackups.toString()
onClicked: {
@@ -395,6 +422,7 @@ SettingsContentBase {
}
StatusSettingsLineButton {
+ width: parent.width
id: rpcStatsButton
text: qsTr("RPC statistics")
onClicked: rpcStatsModal.open()
diff --git a/ui/app/AppLayouts/Profile/views/BrowserView.qml b/ui/app/AppLayouts/Profile/views/BrowserView.qml
index 50f3e1a4a2a..df1515630e2 100644
--- a/ui/app/AppLayouts/Profile/views/BrowserView.qml
+++ b/ui/app/AppLayouts/Profile/views/BrowserView.qml
@@ -45,10 +45,8 @@ SettingsContentBase {
anchors.rightMargin: Theme.padding
}
- // TODO: Replace with StatusQ StatusListItem component
StatusSettingsLineButton {
- anchors.leftMargin: 0
- anchors.rightMargin: 0
+ width: parent.width
text: qsTr("Search engine used in the address bar")
currentValue: {
switch (accountSettings.shouldShowBrowserSearchEngine) {
diff --git a/ui/app/AppLayouts/Profile/views/wallet/MainView.qml b/ui/app/AppLayouts/Profile/views/wallet/MainView.qml
index d1c5aec1be3..fcc9b42cfeb 100644
--- a/ui/app/AppLayouts/Profile/views/wallet/MainView.qml
+++ b/ui/app/AppLayouts/Profile/views/wallet/MainView.qml
@@ -150,7 +150,7 @@ Column {
id: accountOrderBetaTag
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
- anchors.leftMargin: accountOrderItem.statusListItemTitle.width + Theme.bigPadding
+ anchors.leftMargin: accountOrderItem.statusListItemTitle.width + parent.leftPadding + Theme.bigPadding
tooltipText: qsTr("Under construction, you might experience some minor issues")
cursorShape: Qt.PointingHandCursor
}
@@ -177,7 +177,7 @@ Column {
id: manageTokensBetaTag
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
- anchors.leftMargin: manageTokensItem.statusListItemTitle.width + Theme.bigPadding
+ anchors.leftMargin: manageTokensItem.statusListItemTitle.width + parent.leftPadding + Theme.bigPadding
tooltipText: qsTr("Under construction, you might experience some minor issues")
cursorShape: Qt.PointingHandCursor
}
diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml
index 0d9561d973f..1a2d31d67df 100644
--- a/ui/app/mainui/AppMain.qml
+++ b/ui/app/mainui/AppMain.qml
@@ -827,7 +827,7 @@ Item {
id: d
readonly property int activeSectionType: appMain.rootStore.activeSectionType
- readonly property bool isBrowserEnabled: featureFlagsStore.browserEnabled && localAccountSensitiveSettings.isBrowserEnabled
+ readonly property bool isBrowserEnabled: appMain.featureFlagsStore.browserEnabled && localAccountSensitiveSettings.isBrowserEnabled
function openHomePage() {
appMain.rootStore.setActiveSectionBySectionType(Constants.appSection.homePage)
@@ -1239,6 +1239,11 @@ Item {
value: Constants.appSection.browser
enabled: d.isBrowserEnabled
}
+ ValueFilter {
+ roleName: "sectionType"
+ value: Constants.appSection.node
+ enabled: localAccountSensitiveSettings.nodeManagementEnabled
+ }
},
ValueFilter {
roleName: "enabled"
diff --git a/ui/app/mainui/Popups.qml b/ui/app/mainui/Popups.qml
index a2f4a27dfcd..e206cd82cc8 100644
--- a/ui/app/mainui/Popups.qml
+++ b/ui/app/mainui/Popups.qml
@@ -1,3 +1,4 @@
+import QtCore
import QtQuick
import QtQuick.Layouts
import QtQuick.Window
@@ -933,6 +934,7 @@ QtObject {
title: qsTr("Please choose a directory")
modality: Qt.NonModal
+ currentFolder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
onAccepted: {
SystemUtils.downloadImageByUrl(imageSource, selectedFolder)
diff --git a/ui/i18n/CMakeLists.txt b/ui/i18n/CMakeLists.txt
index fd8a8dedb0f..dcc625d67ab 100644
--- a/ui/i18n/CMakeLists.txt
+++ b/ui/i18n/CMakeLists.txt
@@ -13,6 +13,7 @@ file(GLOB_RECURSE
COLLECTED_SOURCE_FILES
${CMAKE_SOURCE_DIR}/../*.qml
)
+list(FILTER COLLECTED_SOURCE_FILES EXCLUDE REGEX "${CMAKE_SOURCE_DIR}/../StatusQ/build/.*" )
qt6_add_lupdate(
LUPDATE_TARGET update_application_translations # name of the cmake target
diff --git a/ui/i18n/qml_base_en.ts b/ui/i18n/qml_base_en.ts
index 7662ee17ec9..73dabe34f1c 100644
--- a/ui/i18n/qml_base_en.ts
+++ b/ui/i18n/qml_base_en.ts
@@ -7691,6 +7691,13 @@ Please add it and try again.
+
+ FeeRow
+
+ Max.
+
+
+
FeesBox
@@ -8439,7 +8446,7 @@ L2 fee: %2
- Format of the image you chose is not supported. Most probably you picked a file that is invalid, corrupted or has a wrong file extension.
+ Format of the image you chose is not supported. Most probably you picked a file that is invalid, corrupted or has a wrong file extension. The requested file was: %1
@@ -8974,6 +8981,10 @@ Are you sure you want to do this?
PIN correct
+
+ Keycard blocked
+
+
%n attempt(s) remaining
@@ -15904,6 +15915,10 @@ to load
Format not supported.
+
+ Format not supported. File: %1
+
+
Upload %1 only
diff --git a/ui/i18n/qml_base_lokalise_en.ts b/ui/i18n/qml_base_lokalise_en.ts
index 4a89e0ecd27..10838ed3ecb 100644
--- a/ui/i18n/qml_base_lokalise_en.ts
+++ b/ui/i18n/qml_base_lokalise_en.ts
@@ -9394,6 +9394,14 @@
Remove
+
+ FeeRow
+
+ Max.
+ FeeRow
+ Max.
+
+
FeesBox
@@ -10303,9 +10311,9 @@
Image format not supported
- Format of the image you chose is not supported. Most probably you picked a file that is invalid, corrupted or has a wrong file extension.
+ Format of the image you chose is not supported. Most probably you picked a file that is invalid, corrupted or has a wrong file extension. The requested file was: %1
ImageCropWorkflow
- Format of the image you chose is not supported. Most probably you picked a file that is invalid, corrupted or has a wrong file extension.
+ Format of the image you chose is not supported. Most probably you picked a file that is invalid, corrupted or has a wrong file extension. The requested file was: %1
Supported image extensions: %1
@@ -10944,6 +10952,11 @@
KeycardEnterPinPage
PIN correct
+
+ Keycard blocked
+ KeycardEnterPinPage
+ Keycard blocked
+
%n attempt(s) remaining
KeycardEnterPinPage
@@ -19360,6 +19373,11 @@
StatusChatImageExtensionValidator
Format not supported.
+
+ Format not supported. File: %1
+ StatusChatImageExtensionValidator
+ Format not supported. File: %1
+
Upload %1 only
StatusChatImageExtensionValidator
diff --git a/ui/i18n/qml_cs.ts b/ui/i18n/qml_cs.ts
index 82a790d59b0..8429c2b50aa 100644
--- a/ui/i18n/qml_cs.ts
+++ b/ui/i18n/qml_cs.ts
@@ -7718,6 +7718,13 @@ Please add it and try again.
Odstranit
+
+ FeeRow
+
+ Max.
+
+
+
FeesBox
@@ -8467,7 +8474,7 @@ L2 poplatek: %2
- Format of the image you chose is not supported. Most probably you picked a file that is invalid, corrupted or has a wrong file extension.
+ Format of the image you chose is not supported. Most probably you picked a file that is invalid, corrupted or has a wrong file extension. The requested file was: %1
@@ -9010,6 +9017,10 @@ Are you sure you want to do this?
PIN correct
+
+ Keycard blocked
+
+
%n attempt(s) remaining
@@ -15972,6 +15983,10 @@ to load
Format not supported.
+
+ Format not supported. File: %1
+
+
Upload %1 only
diff --git a/ui/i18n/qml_ko.ts b/ui/i18n/qml_ko.ts
index 589856af3ab..5946a99d50e 100644
--- a/ui/i18n/qml_ko.ts
+++ b/ui/i18n/qml_ko.ts
@@ -1205,7 +1205,7 @@
Application Logs
- 애플리케이션 로그
+
Experimental features
@@ -7664,6 +7664,13 @@ Please add it and try again.
제거
+
+ FeeRow
+
+ Max.
+
+
+
FeesBox
@@ -8412,8 +8419,8 @@ L2 fee: %2
지원하지 않는 이미지 형식입니다
- Format of the image you chose is not supported. Most probably you picked a file that is invalid, corrupted or has a wrong file extension.
- 선택한 이미지의 형식은 지원되지 않습니다. 아마도 잘못된 파일, 손상된 파일이거나 파일 확장자가 올바르지 않은 경우일 수 있습니다.
+ Format of the image you chose is not supported. Most probably you picked a file that is invalid, corrupted or has a wrong file extension. The requested file was: %1
+
Supported image extensions: %1
@@ -8939,6 +8946,10 @@ Are you sure you want to do this?
PIN correct
PIN이 올바릅니다
+
+ Keycard blocked
+ Keycard가 차단됨
+
%n attempt(s) remaining
@@ -15840,6 +15851,10 @@ to load
Format not supported.
지원되지 않는 형식입니다.
+
+ Format not supported. File: %1
+
+
Upload %1 only
%1만 업로드
diff --git a/ui/imports/shared/controls/chat/menuItems/MuteChatMenuItem.qml b/ui/imports/shared/controls/chat/menuItems/MuteChatMenuItem.qml
index 3cc6c7304b4..1d857aba76d 100644
--- a/ui/imports/shared/controls/chat/menuItems/MuteChatMenuItem.qml
+++ b/ui/imports/shared/controls/chat/menuItems/MuteChatMenuItem.qml
@@ -4,8 +4,6 @@ import QtQuick.Controls
import utils
import StatusQ.Popups
-import shared.controls.chat.menuItems
-
StatusMenu {
property bool isCommunityChat: false
diff --git a/ui/imports/shared/popups/ImageCropWorkflow.qml b/ui/imports/shared/popups/ImageCropWorkflow.qml
index 17f4f28625e..56ff5fce9ff 100644
--- a/ui/imports/shared/popups/ImageCropWorkflow.qml
+++ b/ui/imports/shared/popups/ImageCropWorkflow.qml
@@ -44,24 +44,29 @@ Item {
nameFilters: [qsTr("Supported image formats (%1)").arg(UrlUtils.validImageNameFilters)]
onAccepted: {
if (fileDialog.selectedFiles.length > 0) {
- const url = fileDialog.selectedFiles[0]
+ const url = fileDialog.selectedFile
if (Utils.isValidDragNDropImage(url))
cropImage(url)
- else
+ else {
+ errorDialog.fileOpened = url
errorDialog.open()
+ }
}
}
} // FileDialog
StatusDialog {
id: errorDialog
+
+ property string fileOpened
+
title: qsTr("Image format not supported")
width: 480
contentItem: ColumnLayout {
StatusBaseText {
Layout.fillWidth: true
wrapMode: Text.WordWrap
- text: qsTr("Format of the image you chose is not supported. Most probably you picked a file that is invalid, corrupted or has a wrong file extension.")
+ text: qsTr("Format of the image you chose is not supported. Most probably you picked a file that is invalid, corrupted or has a wrong file extension. The requested file was: %1").arg(errorDialog.fileOpened)
}
StatusBaseText {
Layout.fillWidth: true
diff --git a/ui/imports/shared/status/StatusChatImageExtensionValidator.qml b/ui/imports/shared/status/StatusChatImageExtensionValidator.qml
index 9a6bf558741..676fefe3f49 100644
--- a/ui/imports/shared/status/StatusChatImageExtensionValidator.qml
+++ b/ui/imports/shared/status/StatusChatImageExtensionValidator.qml
@@ -7,13 +7,19 @@ import utils
StatusChatImageValidator {
id: root
- errorMessage: qsTr("Format not supported.")
+ errorMessage: !!lastFailedImgPath ? qsTr("Format not supported. File: %1").arg(lastFailedImgPath) : qsTr("Format not supported.")
secondaryErrorMessage: qsTr("Upload %1 only").arg(UrlUtils.validPreferredImageExtensions.map(ext => ext.toUpperCase() + "s").join(", "))
+ property string lastFailedImgPath
+
onImagesChanged: {
let isValid = true
root.validImages = images.filter(img => {
const isImage = Utils.isValidDragNDropImage(img)
+ if (!isImage)
+ root.lastFailedImgPath = img
+ else
+ root.lastFailedImgPath = ""
isValid = isValid && isImage
return isImage
})
diff --git a/ui/imports/shared/status/StatusChatInput.qml b/ui/imports/shared/status/StatusChatInput.qml
index d98c18ee981..f97ad161231 100644
--- a/ui/imports/shared/status/StatusChatInput.qml
+++ b/ui/imports/shared/status/StatusChatInput.qml
@@ -21,6 +21,8 @@ import StatusQ.Core.Utils as StatusQUtils
import StatusQ.Components
import StatusQ.Controls as StatusQ
+import QtModelsToolkit
+
Rectangle {
id: control
objectName: "statusChatInput"
@@ -1440,7 +1442,8 @@ Rectangle {
implicitWidth: 32
icon.name: "send"
type: StatusQ.StatusFlatRoundButton.Type.Tertiary
- visible: messageInputField.length > 0 || control.fileUrlsAndSources.length > 0
+ visible: messageInputField.length > 0 || control.fileUrlsAndSources.length > 0 ||
+ (!!control.paymentRequestModel && control.paymentRequestModel.ModelCount.count > 0)
onClicked: {
control.onKeyPress({modifiers: d.kbdModifierToSendMessage, key: Qt.Key_Return})
}
diff --git a/ui/imports/shared/status/StatusSettingsLineButton.qml b/ui/imports/shared/status/StatusSettingsLineButton.qml
index 3994ff395a0..ace26133aef 100644
--- a/ui/imports/shared/status/StatusSettingsLineButton.qml
+++ b/ui/imports/shared/status/StatusSettingsLineButton.qml
@@ -15,6 +15,8 @@ ItemDelegate {
property int badgeValue
property int badgeRadius: 9
+ implicitHeight: 64
+
horizontalPadding: Theme.padding
spacing: Theme.padding
diff --git a/ui/main.qml b/ui/main.qml
index 21c868ceaa0..5b89401d3d2 100644
--- a/ui/main.qml
+++ b/ui/main.qml
@@ -441,6 +441,7 @@ StatusWindow {
anchors.topMargin: Qt.platform.os === SQUtils.Utils.mac ? 0 : parent.SafeArea.margins.top
anchors.leftMargin: parent.SafeArea.margins.left
anchors.rightMargin: parent.SafeArea.margins.right
+ anchors.bottomMargin: parent.SafeArea.margins.bottom
sourceComponent: onboardingV2
}
@@ -479,8 +480,6 @@ StatusWindow {
id: onboardingLayout
objectName: "startupOnboardingLayout"
- bottomPadding: applicationWindow.SafeArea.margins.bottom
-
isKeycardEnabled: featureFlagsStore.keycardEnabled
networkChecksEnabled: true
diff --git a/vendor/DOtherSide b/vendor/DOtherSide
index dcbe0cc7584..51f6e4c3411 160000
--- a/vendor/DOtherSide
+++ b/vendor/DOtherSide
@@ -1 +1 @@
-Subproject commit dcbe0cc75846b183b45bfbf13f1c6c54325d4306
+Subproject commit 51f6e4c34112e27fbebfb1a77557cd4fd93f2c48
diff --git a/vendor/nimqml b/vendor/nimqml
index 3567d72d3f1..629898020d5 160000
--- a/vendor/nimqml
+++ b/vendor/nimqml
@@ -1 +1 @@
-Subproject commit 3567d72d3f13bd1c333fbdcacea4e8ca00016f02
+Subproject commit 629898020d590ba01b223db15623b6dd90f4c8ef