forked from mirror/monocles_chat_clean
update fork #128
12 changed files with 134 additions and 39 deletions
Live handling of posts comments
commit
a7bcce88ff
|
|
@ -34,6 +34,7 @@ public abstract class AbstractGenerator {
|
|||
Namespace.OOB,
|
||||
"http://jabber.org/protocol/caps",
|
||||
"http://jabber.org/protocol/disco#info",
|
||||
Namespace.PUBSUB,
|
||||
"urn:xmpp:avatar:metadata+notify",
|
||||
Namespace.NICK + "+notify",
|
||||
"urn:xmpp:ping",
|
||||
|
|
@ -41,6 +42,12 @@ public abstract class AbstractGenerator {
|
|||
"http://jabber.org/protocol/chatstates",
|
||||
Namespace.REACTIONS,
|
||||
Namespace.USER_TUNE + "+notify",
|
||||
Namespace.PUBSUB_SOCIAL_FEED,
|
||||
Namespace.PUBSUB_SOCIAL_FEED + "+notify",
|
||||
Namespace.MICROBLOG,
|
||||
Namespace.MICROBLOG + "+notify",
|
||||
Namespace.PUBSUB_STORIES,
|
||||
Namespace.PUBSUB_STORIES + "+notify",
|
||||
};
|
||||
private final String[] MESSAGE_CONFIRMATION_FEATURES = {
|
||||
"urn:xmpp:chat-markers:0", "urn:xmpp:receipts"
|
||||
|
|
@ -116,12 +123,6 @@ public abstract class AbstractGenerator {
|
|||
final ArrayList<String> features = new ArrayList<>(Arrays.asList(STATIC_FEATURES));
|
||||
features.add("http://jabber.org/protocol/xhtml-im");
|
||||
features.add("urn:xmpp:bob");
|
||||
features.add(Namespace.PUBSUB_SOCIAL_FEED);
|
||||
features.add(Namespace.PUBSUB_SOCIAL_FEED + "+notify");
|
||||
features.add(Namespace.PUBSUB_MICROBLOG);
|
||||
features.add(Namespace.PUBSUB_MICROBLOG + "+notify");
|
||||
features.add(Namespace.PUBSUB_STORIES);
|
||||
features.add(Namespace.PUBSUB_STORIES + "+notify");
|
||||
if (Config.MESSAGE_DISPLAYED_SYNCHRONIZATION) {
|
||||
features.add(Namespace.MDS_DISPLAYED + "+notify");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,15 +248,12 @@ public class IqGenerator extends AbstractGenerator {
|
|||
|
||||
public Iq publishStory(final Account account, final String url, final String type, final String title, Bundle options) {
|
||||
final Element item = new Element("item");
|
||||
// This is the fix: Generate a single ID for both the pubsub item and the atom entry.
|
||||
final String storyId = UUID.randomUUID().toString();
|
||||
item.setAttribute("id", storyId);
|
||||
final Element entry = item.addChild("entry", Namespace.ATOM);
|
||||
|
||||
// atom:id is a mandatory element for the entry, must be a unique and permanent URI
|
||||
entry.addChild("id").setContent("urn:uuid:" + storyId);
|
||||
|
||||
// atom:title is mandatory
|
||||
String effectiveTitle = title;
|
||||
if (Strings.isNullOrEmpty(effectiveTitle)) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
|
||||
|
|
@ -264,7 +261,6 @@ public class IqGenerator extends AbstractGenerator {
|
|||
}
|
||||
entry.addChild("title").setContent(effectiveTitle);
|
||||
|
||||
// atom:updated is mandatory
|
||||
final String timestamp = getTimestamp(System.currentTimeMillis());
|
||||
entry.addChild("updated").setContent(timestamp);
|
||||
entry.addChild("published").setContent(timestamp);
|
||||
|
|
@ -273,7 +269,6 @@ public class IqGenerator extends AbstractGenerator {
|
|||
entry.addChild("author").addChild("uri").setContent("xmpp:" + account.getJid().asBareJid());
|
||||
}
|
||||
|
||||
// The <link> element as specified by the XEP
|
||||
final Element link = entry.addChild("link");
|
||||
link.setAttribute("rel", "enclosure");
|
||||
link.setAttribute("href", url);
|
||||
|
|
@ -841,7 +836,7 @@ public class IqGenerator extends AbstractGenerator {
|
|||
item.setAttribute("id", postId);
|
||||
final Element entry = item.addChild("entry", Namespace.ATOM);
|
||||
|
||||
entry.addChild("id").setContent(postId);
|
||||
entry.addChild("id").setContent("urn:uuid:" + postId);
|
||||
|
||||
entry.addChild("link")
|
||||
.setAttribute("rel", "replies")
|
||||
|
|
@ -880,11 +875,11 @@ public class IqGenerator extends AbstractGenerator {
|
|||
options.putString("pubsub#type", Namespace.PUBSUB_SOCIAL_FEED);
|
||||
options.putString("pubsub#access_model", "roster");
|
||||
options.putString("pubsub#persist_items", "1");
|
||||
options.putString("pubsub#deliver_payloads", "0");
|
||||
options.putString("pubsub#send_last_published_item", "on_sub");
|
||||
options.putString("pubsub#max_items", "max");
|
||||
options.putString("pubsub#notify_retract", "1");
|
||||
options.putString("pubsub#deliver_notifications", "1");
|
||||
options.putString("pubsub#deliver_payloads", "1");
|
||||
options.putString("pubsub#send_last_published_item", "never");
|
||||
options.putString("pubsub#publish_model", "publishers");
|
||||
return options;
|
||||
}
|
||||
|
|
@ -895,11 +890,11 @@ public class IqGenerator extends AbstractGenerator {
|
|||
options.putString("pubsub#type", "urn:xmpp:microblog:0:comments");
|
||||
options.putString("pubsub#access_model", "roster");
|
||||
options.putString("pubsub#persist_items", "1");
|
||||
options.putString("pubsub#max_items", "1000");
|
||||
options.putString("pubsub#max_items", "max");
|
||||
options.putString("pubsub#notify_retract", "1");
|
||||
options.putString("pubsub#deliver_notifications", "1");
|
||||
options.putString("pubsub#deliver_payloads", "1");
|
||||
options.putString("pubsub#send_last_published_item", "never");
|
||||
options.putString("pubsub#deliver_payloads", "0");
|
||||
options.putString("pubsub#send_last_published_item", "on_sub");
|
||||
options.putString("pubsub#publish_model", "open");
|
||||
options.putString("pubsub#itemreply", "publisher");
|
||||
return options;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.google.common.io.BaseEncoding;
|
|||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
||||
import eu.siacs.conversations.entities.Account;
|
||||
import eu.siacs.conversations.entities.Comment;
|
||||
import eu.siacs.conversations.entities.Contact;
|
||||
import eu.siacs.conversations.entities.Post;
|
||||
import eu.siacs.conversations.entities.Room;
|
||||
|
|
@ -457,17 +458,27 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (node != null && node.startsWith("urn:xmpp:microblog:0") || node != null && node.startsWith(Namespace.PUBSUB_SOCIAL_FEED)) {
|
||||
} else if (node != null && node.equals(Namespace.ATOM) || node != null && node.startsWith("urn:xmpp:microblog:0") || node != null && node.startsWith(Namespace.PUBSUB_SOCIAL_FEED)) {
|
||||
for (Element child : items.getChildren()) {
|
||||
if ("item".equals(child.getName())) {
|
||||
final String postId = child.getAttribute("id");
|
||||
Element entry = child.findChild("entry", Namespace.ATOM);
|
||||
if (entry != null) {
|
||||
try {
|
||||
Post post = Post.fromElement(entry);
|
||||
mXmppConnectionService.onPostReceived(post, account);
|
||||
Element inReplyTo = entry.findChild("in-reply-to", "http://purl.org/syndication/thread/1.0");
|
||||
if (inReplyTo != null) {
|
||||
Comment comment = Comment.fromElement(entry);
|
||||
String originalPostUuid = inReplyTo.getAttribute("ref");
|
||||
if (originalPostUuid != null && originalPostUuid.startsWith("urn:uuid:")) {
|
||||
originalPostUuid = originalPostUuid.substring(9);
|
||||
}
|
||||
mXmppConnectionService.notifyOnCommentReceived(originalPostUuid, comment);
|
||||
} else {
|
||||
Post post = Post.fromElement(entry);
|
||||
mXmppConnectionService.onPostReceived(post, account);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(Config.LOGTAG, "error creating post from pubsub item in iq", e);
|
||||
Log.d(Config.LOGTAG, "error creating post/comment from pubsub item in iq", e);
|
||||
}
|
||||
} else if (postId != null) {
|
||||
mXmppConnectionService.onPostRetracted(postId);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import java.util.function.Consumer;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import eu.siacs.conversations.crypto.OtrService;
|
||||
import eu.siacs.conversations.entities.Comment;
|
||||
import eu.siacs.conversations.entities.Post;
|
||||
import eu.siacs.conversations.entities.Presence;
|
||||
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
|
||||
|
|
@ -1759,26 +1760,31 @@ public class MessageParser extends AbstractParser
|
|||
final Element items = event.findChild("items");
|
||||
if (items != null) {
|
||||
final String node = items.getAttribute("node");
|
||||
if (node != null && node.startsWith("urn:xmpp:microblog:0") || node != null && node.startsWith(Namespace.PUBSUB_SOCIAL_FEED)) {
|
||||
if (node != null && node.equals(Namespace.ATOM) || node != null && node.startsWith("urn:xmpp:microblog:0") || node != null && node.startsWith(Namespace.PUBSUB_SOCIAL_FEED)) {
|
||||
for (Element child : items.getChildren()) {
|
||||
if ("item".equals(child.getName())) {
|
||||
final String postId = child.getAttribute("id");
|
||||
Element entry = child.findChild("entry", Namespace.ATOM);
|
||||
if (entry != null) {
|
||||
try {
|
||||
Post post = Post.fromElement(entry);
|
||||
mXmppConnectionService.onPostReceived(post, account);
|
||||
Element inReplyTo = entry.findChild("in-reply-to", "http://purl.org/syndication/thread/1.0");
|
||||
if (inReplyTo != null) {
|
||||
Comment comment = Comment.fromElement(entry);
|
||||
String originalPostUuid = inReplyTo.getAttribute("ref");
|
||||
if (originalPostUuid != null && originalPostUuid.startsWith("urn:uuid:")) {
|
||||
originalPostUuid = originalPostUuid.substring(9);
|
||||
}
|
||||
mXmppConnectionService.notifyOnCommentReceived(originalPostUuid, comment);
|
||||
} else {
|
||||
Post post = Post.fromElement(entry);
|
||||
mXmppConnectionService.onPostReceived(post, account);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.d(Config.LOGTAG, "error creating post from pubsub item in message", e);
|
||||
Log.d(Config.LOGTAG, "error creating post/comment from pubsub item in message", e);
|
||||
}
|
||||
} else if (postId != null) {
|
||||
mXmppConnectionService.onPostRetracted(postId);
|
||||
}
|
||||
} else if ("retract".equals(child.getName())) {
|
||||
final String postId = child.getAttribute("id");
|
||||
if (postId != null) {
|
||||
mXmppConnectionService.onPostRetracted(postId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
import eu.siacs.conversations.Conversations;
|
||||
import eu.siacs.conversations.entities.Comment;
|
||||
import eu.siacs.conversations.entities.Post;
|
||||
import eu.siacs.conversations.entities.Story;
|
||||
import eu.siacs.conversations.entities.StubConversation;
|
||||
|
|
@ -8383,12 +8384,44 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
|
||||
public void subscribeTo(final Account account, final Jid to, final String node, final Consumer<Iq> callback) {
|
||||
final Iq iq = getIqGenerator().generateSubscriptionIq(to, node, account.getJid());
|
||||
final Iq iq = getIqGenerator().generateSubscriptionIq(to.asBareJid(), node, account.getJid().asBareJid());
|
||||
sendIqPacket(account, iq, callback);
|
||||
}
|
||||
|
||||
public void unsubscribeFrom(final Account account, final Jid to, final String node, final Consumer<Iq> callback) {
|
||||
final Iq iq = getIqGenerator().generateUnsubscriptionIq(to, node, account.getJid());
|
||||
final Iq iq = getIqGenerator().generateUnsubscriptionIq(to.asBareJid(), node, account.getJid().asBareJid());
|
||||
sendIqPacket(account, iq, callback);
|
||||
}
|
||||
|
||||
public interface OnCommentReceived {
|
||||
void onCommentReceived(String originalPostUuid, Comment comment);
|
||||
}
|
||||
|
||||
private final List<OnCommentReceived> mOnCommentReceivedListeners = new ArrayList<>();
|
||||
|
||||
public void addOnCommentReceivedListener(OnCommentReceived listener) {
|
||||
synchronized (mOnCommentReceivedListeners) {
|
||||
if (!mOnCommentReceivedListeners.contains(listener)) {
|
||||
mOnCommentReceivedListeners.add(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeOnCommentReceivedListener(OnCommentReceived listener) {
|
||||
synchronized (mOnCommentReceivedListeners) {
|
||||
mOnCommentReceivedListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyOnCommentReceived(String originalPostUuid, Comment comment) {
|
||||
synchronized (mOnCommentReceivedListeners) {
|
||||
for (OnCommentReceived listener : mOnCommentReceivedListeners) {
|
||||
try {
|
||||
listener.onCommentReceived(originalPostUuid, comment);
|
||||
} catch (Exception e) {
|
||||
Log.d(Config.LOGTAG, "safe to ignore, listener has been removed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ public class ContactDetailsActivity extends OmemoActivity
|
|||
xmppConnectionService.subscribeTo(
|
||||
contact.getAccount(),
|
||||
contact.getJid(),
|
||||
"urn:xmpp:microblog:0",
|
||||
Namespace.MICROBLOG,
|
||||
packet -> {
|
||||
runOnUiThread(
|
||||
() -> {
|
||||
|
|
@ -359,7 +359,7 @@ public class ContactDetailsActivity extends OmemoActivity
|
|||
xmppConnectionService.unsubscribeFrom(
|
||||
contact.getAccount(),
|
||||
contact.getJid(),
|
||||
"urn:xmpp:microblog:0",
|
||||
Namespace.MICROBLOG,
|
||||
packet -> {
|
||||
runOnUiThread(
|
||||
() -> {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import eu.siacs.conversations.R;
|
|||
import eu.siacs.conversations.databinding.ActivityCreatePostBinding;
|
||||
import eu.siacs.conversations.entities.Account;
|
||||
import eu.siacs.conversations.services.XmppConnectionService;
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
|
||||
public class CreatePostActivity extends XmppActivity {
|
||||
|
||||
|
|
@ -319,7 +320,7 @@ public class CreatePostActivity extends XmppActivity {
|
|||
}
|
||||
|
||||
private void publish(Account account, String title, String content, String attachmentUrl, String attachmentType) {
|
||||
xmppConnectionService.publishPost(account, "urn:xmpp:microblog:0", title, content, attachmentUrl, attachmentType, postId, new XmppConnectionService.OnPostPublished() {
|
||||
xmppConnectionService.publishPost(account, Namespace.MICROBLOG, title, content, attachmentUrl, attachmentType, postId, new XmppConnectionService.OnPostPublished() {
|
||||
@Override
|
||||
public void onPostPublished() {
|
||||
runOnUiThread(() -> {
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ public class PostsActivity extends XmppActivity implements XmppConnectionService
|
|||
}
|
||||
|
||||
for (Jid source : sourcesToFetch) {
|
||||
xmppConnectionService.fetchPubsubItems(source, "urn:xmpp:microblog:0", new XmppConnectionService.OnPubsubItemsFetched() {
|
||||
xmppConnectionService.fetchPubsubItems(source, Namespace.MICROBLOG, new XmppConnectionService.OnPubsubItemsFetched() {
|
||||
@Override
|
||||
public void onPubsubItemsFetched(String feedXml) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
|
|
@ -34,12 +36,56 @@ public class CommentsAdapter extends RecyclerView.Adapter<CommentsAdapter.Commen
|
|||
private final List<Comment> comments;
|
||||
private final XmppActivity mActivity;
|
||||
|
||||
private XmppConnectionService.OnCommentReceived mOnCommentReceived;
|
||||
|
||||
public CommentsAdapter(XmppActivity activity, Post post, List<Comment> comments) {
|
||||
this.mActivity = activity;
|
||||
this.mPost = post;
|
||||
this.comments = comments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
|
||||
super.onAttachedToRecyclerView(recyclerView);
|
||||
if (mActivity.xmppConnectionService != null) {
|
||||
this.mOnCommentReceived = (postUuid, comment) -> {
|
||||
if (mPost.getId().equals(postUuid)) {
|
||||
mActivity.runOnUiThread(() -> {
|
||||
boolean found = false;
|
||||
for (Comment c : comments) {
|
||||
if (c.getId().equals(comment.getId())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
comments.add(comment);
|
||||
Collections.sort(comments, (c1, c2) -> {
|
||||
Date d1 = c1.getPublished();
|
||||
Date d2 = c2.getPublished();
|
||||
if (d1 == null && d2 == null) return 0;
|
||||
if (d1 == null) return -1;
|
||||
if (d2 == null) return 1;
|
||||
return d1.compareTo(d2);
|
||||
});
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
mActivity.xmppConnectionService.addOnCommentReceivedListener(this.mOnCommentReceived);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
|
||||
super.onDetachedFromRecyclerView(recyclerView);
|
||||
if (mActivity.xmppConnectionService != null && this.mOnCommentReceived != null) {
|
||||
mActivity.xmppConnectionService.removeOnCommentReceivedListener(this.mOnCommentReceived);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public CommentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import eu.siacs.conversations.entities.Contact;
|
|||
import eu.siacs.conversations.services.XmppConnectionService;
|
||||
import eu.siacs.conversations.ui.PostsActivity;
|
||||
import eu.siacs.conversations.ui.util.AvatarWorkerTask;
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import im.conversations.android.xmpp.model.stanza.Iq;
|
||||
|
||||
public class FollowSuggestionAdapter extends RecyclerView.Adapter<FollowSuggestionAdapter.ViewHolder> {
|
||||
|
|
@ -49,7 +50,7 @@ public class FollowSuggestionAdapter extends RecyclerView.Adapter<FollowSuggesti
|
|||
holder.mAvatar.setOnClickListener(v -> mPostsActivity.switchToContactDetails(contact));
|
||||
holder.mFollowButton.setOnClickListener(v -> {
|
||||
final Account account = contact.getAccount();
|
||||
mXmppConnectionService.subscribeTo(account, contact.getJid(), "urn:xmpp:microblog:0", packet -> {
|
||||
mXmppConnectionService.subscribeTo(account, contact.getJid(), Namespace.MICROBLOG, packet -> {
|
||||
mPostsActivity.runOnUiThread(() -> {
|
||||
if (packet.getType() == Iq.Type.RESULT) {
|
||||
contact.setFollowed(true);
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ import eu.siacs.conversations.ui.util.AvatarWorkerTask;
|
|||
import eu.siacs.conversations.utils.AccountUtils;
|
||||
import eu.siacs.conversations.utils.XmppUri;
|
||||
import eu.siacs.conversations.xml.Element;
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import eu.siacs.conversations.xml.XmlReader;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
import io.noties.markwon.Markwon;
|
||||
|
|
@ -309,7 +310,7 @@ public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHold
|
|||
.setTitle(R.string.retract_post)
|
||||
.setMessage(R.string.retract_post_confirm)
|
||||
.setPositiveButton(R.string.retract, (dialog, which) -> {
|
||||
mActivity.xmppConnectionService.retractPost(ownAccount, "urn:xmpp:microblog:0", post.getId(),
|
||||
mActivity.xmppConnectionService.retractPost(ownAccount, Namespace.MICROBLOG, post.getId(),
|
||||
new XmppConnectionService.OnPostRetracted() {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ public final class Namespace {
|
|||
public static final String MDS_DISPLAYED = "urn:xmpp:mds:displayed:0";
|
||||
public static final String MDS_SERVER_ASSIST = "urn:xmpp:mds:server-assist:0";
|
||||
public static final String PUBSUB_SOCIAL_FEED = "urn:xmpp:pubsub-social-feed:1";
|
||||
public static final String PUBSUB_MICROBLOG = "urn:xmpp:microblog:0";
|
||||
public static final String MICROBLOG = "urn:xmpp:microblog:0";
|
||||
public static final String PUBSUB_STORIES = "urn:xmpp:pubsub-social-feed:stories:0";
|
||||
public static final String ATOM = "http://www.w3.org/2005/Atom";
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue