Compare commits

...

4 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] f0e95a59d1 Simplify sendComplete: remove chatId param and dead refreshFragment logic
sendComplete is only ever called from the message-send path, never the
draft path, so the passed chatId always equals this.chatId. The
refreshFragment branch and this.chatId = chatId assignment were dead
code. Removing them also eliminates the class of bug where an old
chatId could accidentally revert the activity's current chat.

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-03-13 20:09:26 +00:00
copilot-swe-agent[bot] d44ad7de0c Fix introduced bug: restore sendComplete to use dcChat.getId()
The previous fix captured `currentChatId` to stop the setDraft race
condition, but also changed `sendComplete(dcChat.getId())` to
`sendComplete(currentChatId)`. Java lambdas access instance fields via
`this`, so `dcChat.getId()` in the lambda reads the current value when
it runs — after `initializeResources()` updates `dcChat` to the private
chat, `sendComplete` naturally received the private chat ID. Passing
`currentChatId` (old group chat ID) instead caused `sendComplete` to
write `this.chatId = GROUP_CHAT_ID`, reverting the activity to the old
chat and triggering an unwanted fragment reload.

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-03-13 20:00:04 +00:00
copilot-swe-agent[bot] 3e0a283633 Fix: reply privately sometimes doesn't quote the message
Two fixes for the race condition that caused "reply privately" to
sometimes open the 1:1 chat without the quoted message:

1. In processComposeControls(), capture dcChat.getId() as currentChatId
   before the background thread starts. Without this, initializeResources()
   can change dcChat to the new private chat before the background thread
   runs, causing it to overwrite the quote draft with an empty/wrong draft.

2. In InputPanel.setQuote(), if getMeasuredHeight() returns 0 after
   measure(0,0) (view not yet laid out), fall back to WRAP_CONTENT instead
   of animating to 0 height which would keep the quote view at 1px height.

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-03-13 18:00:02 +00:00
copilot-swe-agent[bot] ac01395482 Initial plan 2026-03-13 17:36:33 +00:00
2 changed files with 26 additions and 29 deletions
@@ -1182,6 +1182,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
playbackViewModel.stopNonMessageAudioPlayback();
DcContext dcContext = DcHelper.getContext(context);
final int currentChatId = dcChat.getId();
Util.runOnAnyBackgroundThread(() -> {
DcMsg msg = null;
int recompress = 0;
@@ -1191,9 +1192,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (action == ACTION_SEND_OUT) {
dcContext.sendEditRequest(msgId, body);
} else {
dcContext.setDraft(chatId, null);
dcContext.setDraft(currentChatId, null);
}
future.set(chatId);
future.set(currentChatId);
return;
}
@@ -1204,7 +1205,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
try {
if (slideDeck.getWebxdctDraftId() != 0) {
msg = dcContext.getDraft(chatId);
msg = dcContext.getDraft(currentChatId);
} else {
List<Attachment> attachments = slideDeck.asAttachments();
for (Attachment attachment : attachments) {
@@ -1253,7 +1254,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
// for WEBXDC, drafts are just sent out as is.
// for preparations and other cases, cleanup draft soon.
if (msg == null || msg.getType() != DcMsg.DC_MSG_WEBXDC) {
dcContext.setDraft(dcChat.getId(), null);
dcContext.setDraft(currentChatId, null);
}
if(msg!=null) {
@@ -1269,7 +1270,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
false
);
});
doSend = VideoRecoder.prepareVideo(ConversationActivity.this, dcChat.getId(), msg);
doSend = VideoRecoder.prepareVideo(ConversationActivity.this, currentChatId, msg);
Util.runOnMain(() -> {
try {
if (progressDialog != null) progressDialog.dismiss();
@@ -1280,48 +1281,34 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
if (doSend) {
if (dcContext.sendMsg(dcChat.getId(), msg) == 0) {
if (dcContext.sendMsg(currentChatId, msg) == 0) {
String lastError = dcContext.getLastError();
if (!"".equals(lastError)) {
Util.runOnMain(() -> Toast.makeText(ConversationActivity.this, lastError, Toast.LENGTH_LONG).show());
}
future.set(chatId);
future.set(currentChatId);
return;
}
}
Util.runOnMain(() -> sendComplete(dcChat.getId()));
Util.runOnMain(() -> sendComplete());
}
} else {
dcContext.setDraft(dcChat.getId(), msg);
dcContext.setDraft(currentChatId, msg);
}
future.set(chatId);
future.set(currentChatId);
});
return future;
}
protected void sendComplete(int chatId) {
boolean refreshFragment = (chatId != this.chatId);
this.chatId = chatId;
protected void sendComplete() {
if (fragment == null || !fragment.isVisible() || isFinishing()) {
return;
}
fragment.setLastSeen(-1);
if (refreshFragment) {
fragment.reload(recipient, chatId);
try {
int accId = rpc.getSelectedAccountId();
DcHelper.getNotificationCenter(this).updateVisibleChat(accId, chatId);
} catch (RpcException e) {
Log.e(TAG, "rpc.getSelectedAccountId() failed", e);
}
}
fragment.scrollToBottom();
attachmentManager.cleanup();
}
@@ -132,10 +132,20 @@ public class InputPanel extends ConstraintLayout
quoteAnimator.cancel();
}
quoteAnimator =
createHeightAnimator(quoteView, originalHeight, this.quoteView.getMeasuredHeight(), null);
quoteAnimator.start();
int finalHeight = this.quoteView.getMeasuredHeight();
if (finalHeight > 0) {
quoteAnimator =
createHeightAnimator(quoteView, originalHeight, finalHeight, null);
quoteAnimator.start();
} else {
// Fallback when getMeasuredHeight() returns 0 (view not yet laid out).
// Setting WRAP_CONTENT ensures the quote is visible instead of animating to 0 height.
ViewGroup.LayoutParams params = quoteView.getLayoutParams();
if (params != null) {
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
quoteView.setLayoutParams(params);
}
}
}
public void clearQuoteWithoutAnimation() {