Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
1297852
Release 2.38.0
alaibe Apr 28, 2026
35a6c3b
Merge branch 'master' into release/2.38.x
alaibe Apr 30, 2026
1f444fc
fix(browser): hide sidebar when tapping outside (#20624)
friofry Apr 30, 2026
835d78f
fix: payment modal padding (#20702)
alaibe May 4, 2026
72e3ce1
fix(Browser): open URLs in a new tab from Downloads view
caybro May 4, 2026
0caa703
fix(RenameAccountModal): responsive layout
caybro May 4, 2026
5083cc4
feat(StatusTextField): provide a standard edit context menu
caybro May 4, 2026
a228e3d
fix(Portal): fix the community promo icon color
caybro May 4, 2026
60ef537
fix: cannot login via keycard
saledjenic May 4, 2026
883beb7
chore: remove Polygon zkEVM chain
saledjenic May 4, 2026
f58c7f7
chore: display "Unknown token" for tx in History tab if token details…
saledjenic May 5, 2026
eb5d0a5
fix: amount colors of the token breakdown pills make the amounts hard…
saledjenic May 5, 2026
a81e657
fix(Settings): fix overflow in Appearance view
caybro May 5, 2026
e520f0b
fix(Desktop) Increasing app height triggers portrait mode
caybro Apr 24, 2026
098d6ac
chore(@e2e): text input
anastasiyaig Apr 28, 2026
1329fe0
chore(@qml): object name for send button in chat
anastasiyaig Apr 28, 2026
e430c3a
chore(@e2e): temp exclude password change from PRs
anastasiyaig Apr 29, 2026
3775888
fix: don't try to (re)store window geometry/visiblity on mobile
caybro Apr 30, 2026
137004f
fix(AppMain): reapply font size and padding when the orientation changes
caybro Apr 30, 2026
f372409
fix(StatusSectionLayout): explicitly state the breakpoint
caybro May 4, 2026
e96d153
fix(main): restore visibility on mobile
caybro May 4, 2026
c6d5e8b
fix(browser): android connector fixes (#20623)
friofry May 6, 2026
0a7f0d7
fix(Syncing): Ensure mobile network sync is enabled by default
noeliaSD May 6, 2026
c1c57ba
fix(CommunitiesPortal): community cards are cut on the right
caybro May 5, 2026
aa18bf6
chore: remove status l2 sepolia (#20748)
alaibe May 7, 2026
62a3c64
ios: upgrade to SDK 26
siddarthkay Jan 4, 2026
b05ee34
fix: avoid sync getAllTokens on every token refresh (#20786)
friofry May 7, 2026
4714b1a
fix(StatusChatInfoButton): don't open Profile dialog with pinned message
caybro May 6, 2026
c423c3f
ConfirmationDialog: refactor to derive from StatusDialog
micieslak May 5, 2026
d6e3965
StatusDialog: delayed bindings to ensure proper height in bottom shee…
micieslak May 5, 2026
86d6adf
Settings: prevent navigating to settings on sign out request in portr…
micieslak May 5, 2026
48d83f5
AdvancdedView: prevent options switching before confirmation
micieslak May 5, 2026
fce8d6c
fix(StatusBaseInput): no placeholder with preeditText
caybro May 7, 2026
4f21b7b
fix(browser): android snapshot colors (#20791)
friofry May 8, 2026
850c352
fix(connector): parallel dapp requests race (#20703)
friofry May 8, 2026
24fbe69
chore: hide data verified by Nimbus (#20811)
alaibe May 11, 2026
512d5aa
feat(connector): ephemeral dApp records (#20808)
friofry May 11, 2026
ad66a95
ChatColumnView: workaround for QTBUG-146653
micieslak May 7, 2026
8b06869
StatusChatInputTextArea: take preedit text into account when filterin…
micieslak May 7, 2026
52c1deb
StatusChatInputTextArea: take preedit text into account when filterin…
micieslak May 8, 2026
4e52a0c
fix: swapped ui in history (#20818)
alaibe May 12, 2026
86f7dab
fix(iOS/FileDialog): Use native pickers for image selection
noeliaSD May 7, 2026
d1003a9
chore: a huge delay between balance/history update (#20812)
saledjenic May 12, 2026
98cfe45
fix(wc): reconnect broken pipe (#20809)
friofry May 12, 2026
9d83507
fix(Search): focus the user/channel/chat search fields
caybro May 7, 2026
e1716c0
chore(@e2e): fix locators for search boxes
anastasiyaig May 12, 2026
1bf26ab
chore(@e2e): fix for seed phrase
anastasiyaig May 12, 2026
af9268b
fix(ThirdpartyServicesPopup): prevent eliding the instructions
caybro May 12, 2026
b7251ca
chore: don't show unknown token for nft name in history view
saledjenic May 12, 2026
ecf5b2d
fix(Onboarding): Make sync pairing page scrollable
noeliaSD May 12, 2026
0c3665b
fix(StatusSectionLayout): Fix SwipeView panel positioning
noeliaSD May 13, 2026
74b38c0
Android: workaround for triggering full-screen mode after screenshot
micieslak May 14, 2026
1bc6b5d
fix: GifPopup and StickersPopup bottom-sheet layout improved
micieslak May 11, 2026
ca35d6d
StatusStickersPopup: layout improved
micieslak May 13, 2026
bf7b3f1
emoji/gif/stickers popups sizing ad-hoc patches
micieslak May 13, 2026
12844a6
Perf/cherry pick v1 (#20802)
alexjba May 15, 2026
1fccaa2
fix: handle shutdown process to avoid race conditions
saledjenic May 13, 2026
a6294f5
fix(nft): disable more chains (#20866)
friofry May 15, 2026
f9c7e79
fix(dapps): dapp name html injection (#20881)
friofry May 15, 2026
9c86b54
fix(Portal): do not autofocus the Search field
caybro May 15, 2026
61e6958
fix: Community members in community settins
alexjba May 17, 2026
f8e1a01
fix(android): async webview construction (#20916)
friofry May 18, 2026
81e5e5c
fix(chat): restore iOS paste menu behavior
noeliaSD May 15, 2026
59c15c3
fix(StatusTextField): Use native paste menu for text fields on iOS
noeliaSD May 18, 2026
490e30f
fix(android): battery usage while the app is in the background
alexjba May 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,11 @@ run-statusq-tests: statusq-tests
STORYBOOK_SOURCE_PATH := storybook
STORYBOOK_BUILD_PATH := $(STORYBOOK_SOURCE_PATH)/build/Qt$(QT_VERSION)
STORYBOOK_CMAKE_CACHE := $(STORYBOOK_BUILD_PATH)/CMakeCache.txt
ifeq ($(mkspecs),macx)
STORYBOOK_BINARY := $(STORYBOOK_BUILD_PATH)/bin/Storybook.app/Contents/MacOS/Storybook
else
STORYBOOK_BINARY := $(STORYBOOK_BUILD_PATH)/bin/Storybook
endif

$(STORYBOOK_CMAKE_CACHE): | check-qt-dir
echo -e "\033[92mConfiguring:\033[39m Storybook"
Expand All @@ -418,7 +423,7 @@ storybook-build: | storybook-configure

run-storybook: storybook-build
echo -e "\033[92mRunning:\033[39m Storybook"
$(STORYBOOK_BUILD_PATH)/bin/Storybook ${ARGS}
$(STORYBOOK_BINARY) ${ARGS}

run-storybook-tests: storybook-build
echo -e "\033[92mRunning:\033[39m Storybook Tests"
Expand Down
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2.38.0-rc.1
2 changes: 1 addition & 1 deletion ci/Jenkinsfile.ios
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def isPRBuild = utils.isPRBuild()

pipeline {

agent { label "macos && arm64 && nix-2.24 && xcode-16.2 && qt-${QT_VERSION}" }
agent { label "macos && arm64 && nix-2.24 && xcode-26.4 && qt-${QT_VERSION}" }

parameters {
booleanParam(
Expand Down
2 changes: 1 addition & 1 deletion ci/Jenkinsfile.macos
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def isPRBuild = utils.isPRBuild()
pipeline {
/* This way we run the same Jenkinsfile on different platforms. */
agent {
label "${getAgentLabels().join(' && ')} && qt-${QT_VERSION} && go-1.24 && xcode-16.2"
label "${getAgentLabels().join(' && ')} && qt-${QT_VERSION} && go-1.24 && xcode-26.4"
}

parameters {
Expand Down
4 changes: 2 additions & 2 deletions mobile/DEV_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,12 @@ The build system uses several environment variables to control the build process

#### iOS-specific Variables
- `IPHONE_SDK`: iOS SDK to use (`iphoneos` or `iphonesimulator`)
- `IOS_TARGET`: Minimum iOS version (16 for Qt6)
- `IOS_TARGET`: Minimum iOS version (26 for Qt6)

### Qt Version Compatibility

#### Qt6
- iOS minimum deployment target: iOS 16
- iOS minimum deployment target: iOS 26
- iOS simulator: iPad Pro
- Android target: Android 35
- Android NDK: 27.2.12479018
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package app.status.mobile.ipc;

import app.status.mobile.ipc.IStatusGoSignalListener;
import app.status.mobile.ipc.RpcResponse;

interface IStatusGoService {
/** Generic call into status-go exports (method name is the C export name). */
String call(String method, String argsJson);

/**
* Same as call(), but writes the response to a file in the service's cache dir
* and returns the absolute file path. This avoids Binder size limits for large JSON.
* Hybrid status-go RPC.
*
* Returns the response inline in the Binder reply Parcel when small enough, otherwise
* via an ashmem-backed SharedMemory region carried by the returned RpcResponse. The
* server picks the path based on response size; the client must release the
* RpcResponse via close() (try-with-resources).
*/
String callToFile(String method, String argsJson);
RpcResponse rpcCall(String method, String argsJson);

/** Register a signal listener. */
void registerSignalListener(IStatusGoSignalListener listener);
Expand All @@ -26,4 +28,3 @@ interface IStatusGoService {
*/
void setUiVisible(boolean visible);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package app.status.mobile.ipc;

// Hybrid carrier for status-go RPC responses: inline UTF-8 bytes for small payloads,
// SharedMemory fd for large ones. Layout and ownership rules in RpcResponse.java.
parcelable RpcResponse;
2 changes: 2 additions & 0 deletions mobile/android/qt6/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
//noinspection GradleDependency
implementation 'androidx.core:core:1.16.0'
// Required for WebViewFeature / addDocumentStartJavaScript
implementation 'androidx.webkit:webkit:1.12.1'
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.core:core-splashscreen:1.0.1'
implementation 'com.google.android.material:material:1.12.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public class NativeSwipeHandlerHelper {
private boolean swiping = false;
private int touchSlopPx = 0;
private View passthroughTarget;
private boolean dismissTapMode = false;
private float startRawY = 0.0f;

// Handler rect in parent pixels (contentView coordinates).
private float handlerX = 0.0f;
Expand All @@ -38,6 +40,7 @@ public class NativeSwipeHandlerHelper {
private static native void nativeOnSwipeBegan(long ptr, float velocityX);
private static native void nativeOnSwipeChanged(long ptr, float deltaX, float velocityX);
private static native void nativeOnSwipeEnded(long ptr, float deltaX, float velocityX, boolean canceled);
private static native void nativeOnTapToDismiss(long ptr);

public NativeSwipeHandlerHelper(long ptr, Activity activity) {
this.nativePtr = ptr;
Expand Down Expand Up @@ -72,17 +75,19 @@ private void createTouchOverlay() {

if (action == MotionEvent.ACTION_DOWN) {
// The overlay view is sized/positioned to the swipe rect, so any DOWN here is in-bounds.
final float x = event.getX();
final float rawX = event.getRawX();
final float rawY = event.getRawY();

active = true;
swiping = false;
activePointerId = event.getPointerId(0);
startRawX = rawX;
startRawY = rawY;
lastRawX = rawX;
lastEventTimeMs = event.getEventTime();
lastVx = 0.0f;
passthroughTarget = findPassthroughTarget();
// Full-window dismiss mode must not forward to WebView, or taps never reach Qt.
passthroughTarget = dismissTapMode ? null : findPassthroughTarget();

if (velocityTracker != null) {
velocityTracker.recycle();
Expand Down Expand Up @@ -113,8 +118,8 @@ private void createTouchOverlay() {
}

final int idx = activePointerId >= 0 ? event.findPointerIndex(activePointerId) : 0;
final float x = idx >= 0 ? event.getX(idx) : event.getX();
final float rawX = idx >= 0 ? event.getRawX(idx) : event.getRawX();
final float rawY = idx >= 0 ? event.getRawY(idx) : event.getRawY();
final long t = event.getEventTime();
final long dt = Math.max(1, t - lastEventTimeMs);
final float vx = ((rawX - lastRawX) / (float) dt) * 1000.0f;
Expand Down Expand Up @@ -142,9 +147,19 @@ private void createTouchOverlay() {

if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
final float dx = rawX - startRawX;
final float dy = rawY - startRawY;
// Slightly looser than touchSlop so small jitter still counts as a tap.
final int tapSlop = Math.max(touchSlopPx * 2, touchSlopPx + 16);
final boolean tapLike = (dx * dx + dy * dy) <= (float) tapSlop * tapSlop;
final boolean tapToDismiss = dismissTapMode
&& action == MotionEvent.ACTION_UP
&& tapLike;

// Use the last MOVE velocity; UP often has vx≈0 because there's no delta.
if (swiping) {
nativeOnSwipeEnded(nativePtr, dx, lastVx, action == MotionEvent.ACTION_CANCEL);
} else if (tapToDismiss) {
nativeOnTapToDismiss(nativePtr);
} else if (passthroughTarget != null) {
dispatchToTarget(passthroughTarget, event, action);
}
Expand All @@ -168,34 +183,42 @@ private void createTouchOverlay() {
});
}

public void updateTouchOverlayBounds(float xPx, float yPx, float widthPx, float heightPx) {
handlerX = xPx;
handlerY = yPx;
handlerWidth = widthPx;
handlerHeight = heightPx;

if (activity != null) {
activity.runOnUiThread(() -> {
if (touchOverlayView == null) return;
// Re-attach if rotation/activity changes detached the view.
if (touchOverlayView.getParent() == null) {
ViewGroup contentView = (ViewGroup) activity.getWindow().getDecorView().findViewById(android.R.id.content);
if (contentView != null) {
contentView.addView(touchOverlayView);
}
}
/** Applies dismiss mode and overlay geometry on the UI thread (posted from Qt/JNI). */
public void applyTouchOverlayState(boolean dismissMode, float xPx, float yPx, float widthPx, float heightPx) {
if (activity == null)
return;

ViewGroup.LayoutParams lp = touchOverlayView.getLayoutParams();
if (lp != null) {
lp.width = (int) Math.max(1, handlerWidth);
lp.height = (int) Math.max(1, handlerHeight);
touchOverlayView.setLayoutParams(lp);
}
touchOverlayView.setX(handlerX);
touchOverlayView.setY(handlerY);
updateGestureExclusion();
});
}
activity.runOnUiThread(() -> {
dismissTapMode = dismissMode;
handlerX = xPx;
handlerY = yPx;
handlerWidth = widthPx;
handlerHeight = heightPx;

if (touchOverlayView == null)
return;

ViewGroup contentView = (ViewGroup) activity.getWindow().getDecorView().findViewById(android.R.id.content);
if (touchOverlayView.getParent() == null && contentView != null) {
contentView.addView(touchOverlayView);
}

ViewGroup.LayoutParams lp = touchOverlayView.getLayoutParams();
if (lp != null) {
lp.width = (int) Math.max(1, handlerWidth);
lp.height = (int) Math.max(1, handlerHeight);
touchOverlayView.setLayoutParams(lp);
}
touchOverlayView.setX(handlerX);
touchOverlayView.setY(handlerY);
touchOverlayView.setElevation(dismissMode ? 48f : 1f);

ViewGroup parent = (ViewGroup) touchOverlayView.getParent();
if (parent != null && dismissMode) {
parent.bringChildToFront(touchOverlayView);
}
updateGestureExclusion();
});
}

private void updateGestureExclusion() {
Expand Down Expand Up @@ -272,4 +295,3 @@ public void cleanup() {
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ public final class NotificationReplyReceiver extends BroadcastReceiver {
return t;
});

/**
* Un-pauses the "messaging" service so a chat message sent while the app is backgrounded
* actually transmits (the mvds outbound loop is parked while messaging is paused). The
* subsequent CallPrivateRPC persists the message regardless, so a failure here only delays
* delivery; the send path schedules a re-pause via StatusGoService.scheduleMessagingRepause().
*/
private static void resumeMessagingForBackgroundSend() {
try {
final String resp = StatusGoService.callRpc("ResumeService", "[\"messaging\"]");
} catch (Throwable t) {
Log.w(TAG, "failed to resume messaging for background send", t);
}
}

@Override
public void onReceive(Context context, Intent intent) {
if (intent == null) return;
Expand Down Expand Up @@ -71,6 +85,7 @@ public void onReceive(Context context, Intent intent) {

REPLY_EXECUTOR.execute(() -> {
try {
resumeMessagingForBackgroundSend();
// Build sendChatMessage params (same format as Nim backend)
JSONObject msg = new JSONObject();
msg.put("chatId", conversationId);
Expand Down Expand Up @@ -135,6 +150,9 @@ public void onReceive(Context context, Intent intent) {
NotificationManagerCompat.from(appContext).cancel(androidNotificationId);
}
} finally {
// We resumed "messaging" above so the send transmits; ask the service to
// re-pause after a flush window (a real fg/bg transition supersedes it).
StatusGoService.scheduleMessagingRepause();
pendingResult.finish();
}
});
Expand All @@ -157,6 +175,7 @@ private void handleContactRequestAction(Context context, Intent intent, String a

REPLY_EXECUTOR.execute(() -> {
try {
resumeMessagingForBackgroundSend();
JSONObject params = new JSONObject();
params.put("id", contactId);

Expand Down Expand Up @@ -208,6 +227,9 @@ private void handleContactRequestAction(Context context, Intent intent, String a
NotificationManagerCompat.from(appContext).cancel(androidNotificationId);
}
} finally {
// We resumed "messaging" above so the send transmits; ask the service to
// re-pause after a flush window (a real fg/bg transition supersedes it).
StatusGoService.scheduleMessagingRepause();
pendingResult.finish();
}
});
Expand Down
Loading