update fork #128

Manually merged
tristan merged 181 commits from mirror/monocles_chat_clean:master into master 2026-01-23 14:02:38 +01:00
3 changed files with 92 additions and 29 deletions
Showing only changes of commit be5aa295b0 - Show all commits

Fix story publishing and display relative time of a story

Arne 2025-12-31 15:48:14 +01:00

View file

@ -7771,8 +7771,8 @@ public class XmppConnectionService extends Service {
}
return;
}
final Bundle options = retry ? IqGenerator.defaultStoriesConfiguration() : null;
final Iq packet = getIqGenerator().publishStory(url, type, title, options);
// This is the corrected publish request. It sends NO configuration options.
final Iq packet = getIqGenerator().publishStory(url, type, title, null);
sendIqPacket(account, packet, response -> {
if (response.getType() == Iq.Type.RESULT) {
if (callback != null) {
@ -7780,18 +7780,22 @@ public class XmppConnectionService extends Service {
}
} else if (retry && PublishOptions.preconditionNotMet(response)) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": stories node does not exist. creating it");
// The node does not exist. Now we create it with the correct configuration.
final Iq createRequest = getIqGenerator().createStoriesNode();
createRequest.setTo(account.getJid().asBareJid());
sendIqPacket(account, createRequest, createResponse -> {
if (createResponse.getType() == Iq.Type.RESULT) {
// After successfully creating the node, retry publishing without the config options
// Node created. Now, retry publishing the story ONE more time, without the retry/create logic.
publishStory(account, url, type, title, false, callback);
} else {
Log.e(Config.LOGTAG, "Failed to create stories node: " + createResponse);
if (callback != null) {
callback.error(R.string.error_publish_avatar_server_reject, null);
}
}
});
} else {
Log.e(Config.LOGTAG, "Failed to publish story: " + response);
if (callback != null) {
callback.error(R.string.error_publish_avatar_server_reject, null);
}
@ -7845,7 +7849,6 @@ public class XmppConnectionService extends Service {
});
};
FILE_ATTACHMENT_EXECUTOR.execute(runnable);
deleteMessage(message);
}
private final List<eu.siacs.conversations.entities.Story> stories = new java.util.concurrent.CopyOnWriteArrayList<>();

View file

@ -1,6 +1,7 @@
package eu.siacs.conversations.ui.adapter;
import android.content.Intent;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -8,6 +9,8 @@ import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Handler;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.bumptech.glide.Glide;
@ -26,9 +29,30 @@ public class StoryAdapter extends RecyclerView.Adapter<StoryAdapter.StoryViewHol
private final XmppActivity activity;
private final List<Story> stories;
private final Handler handler = new Handler();
private final Runnable refreshRunnable;
private RecyclerView recyclerView;
public StoryAdapter(XmppActivity activity, List<Story> stories) {
this.activity = activity;
this.stories = stories;
this.refreshRunnable = new Runnable() {
@Override
public void run() {
if (recyclerView != null) {
// This is a more efficient way to refresh just the visible items
final LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
if (layoutManager != null) {
final int first = layoutManager.findFirstVisibleItemPosition();
final int last = layoutManager.findLastVisibleItemPosition();
if (first != RecyclerView.NO_POSITION) {
notifyItemRangeChanged(first, (last - first) + 1, "payload_time");
}
}
}
handler.postDelayed(this, 60000); // Run again in 1 minute
}
};
}
@NonNull
@ -38,30 +62,35 @@ public class StoryAdapter extends RecyclerView.Adapter<StoryAdapter.StoryViewHol
return new StoryViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull StoryViewHolder holder, int position, @NonNull List<Object> payloads) {
if (payloads.contains("payload_time")) {
final Story story = stories.get(position);
holder.storyTime.setText(DateUtils.getRelativeTimeSpanString(story.getPublished(), System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS));
} else {
super.onBindViewHolder(holder, position, payloads);
}
}
@Override
public void onBindViewHolder(@NonNull StoryViewHolder holder, int position) {
final Story story = stories.get(position);
final Jid jid = story.getContact();
Contact contact = null;
Account storyAccount = null;
// Check if the story author is one of our own accounts
storyAccount = activity.xmppConnectionService.findAccountByJid(jid);
if (storyAccount != null) {
// It's our own story
contact = storyAccount.getSelfContact();
} else {
// It's from someone else. Find which of our accounts knows them.
for (Account account : activity.xmppConnectionService.getAccounts()) {
contact = account.getRoster().getContact(jid);
if (contact != null) {
storyAccount = account; // The account that has this contact in its roster
storyAccount = account;
break;
}
}
if (contact == null) {
storyAccount = activity.xmppConnectionService.findAccountByJid(jid);
if (storyAccount != null) {
contact = storyAccount.getSelfContact();
}
}
if (contact != null) {
holder.storyTitle.setText(contact.getDisplayName());
holder.storyImage.setImageDrawable(activity.xmppConnectionService.getAvatarService().get(contact, activity.getResources().getDimensionPixelSize(R.dimen.avatar_story_size)));
@ -70,6 +99,8 @@ public class StoryAdapter extends RecyclerView.Adapter<StoryAdapter.StoryViewHol
holder.storyImage.setImageResource(R.drawable.ic_person_black_48dp);
}
holder.storyTime.setText(DateUtils.getRelativeTimeSpanString(story.getPublished(), System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS));
Glide.with(activity).load(story.getUrl()).into(holder.storyPreview);
final Account finalStoryAccount = storyAccount;
@ -78,8 +109,6 @@ public class StoryAdapter extends RecyclerView.Adapter<StoryAdapter.StoryViewHol
ArrayList<String> urls = new ArrayList<>();
ArrayList<String> titles = new ArrayList<>();
ArrayList<String> storyIds = new ArrayList<>();
// This is the corrected logic: Get the FULL list from the service
for (Story s : activity.xmppConnectionService.getStories()) {
if (s.getContact().asBareJid().equals(story.getContact().asBareJid())) {
urls.add(s.getUrl());
@ -107,13 +136,29 @@ public class StoryAdapter extends RecyclerView.Adapter<StoryAdapter.StoryViewHol
final ImageView storyImage;
final TextView storyTitle;
final TextView storyTime;
final ImageView storyPreview;
StoryViewHolder(@NonNull View itemView) {
super(itemView);
storyImage = itemView.findViewById(R.id.story_image);
storyTitle = itemView.findViewById(R.id.story_title);
storyTime = itemView.findViewById(R.id.story_time);
storyPreview = itemView.findViewById(R.id.story_preview);
}
}
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
this.recyclerView = recyclerView;
handler.post(refreshRunnable);
}
@Override
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
handler.removeCallbacks(refreshRunnable);
this.recyclerView = null;
}
}

View file

@ -3,8 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:padding="12dp">
android:padding="12dp"
android:minHeight="?android:attr/listPreferredItemHeight">
<eu.siacs.conversations.ui.widget.AvatarView
android:id="@+id/story_image"
@ -14,8 +14,7 @@
android:layout_centerVertical="true"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/story_title"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
@ -23,10 +22,26 @@
android:layout_marginEnd="16dp"
android:layout_toStartOf="@id/story_preview_card"
android:layout_toEndOf="@id/story_image"
android:orientation="vertical">
<TextView
android:id="@+id/story_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" />
<TextView
android:id="@+id/story_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" />
</LinearLayout>
<com.google.android.material.card.MaterialCardView
android:id="@+id/story_preview_card"
android:layout_width="56dp"