forked from mirrors/Fedilab
Add support for Nyastodon-style emoji reactions
This commit is contained in:
parent
97ba87aba7
commit
a06fb1657d
5 changed files with 199 additions and 51 deletions
|
@ -318,4 +318,18 @@ public interface MastodonStatusesService {
|
||||||
@Header("Authorization") String token,
|
@Header("Authorization") String token,
|
||||||
@Path("id") String id
|
@Path("id") String id
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@POST("statuses/{id}/react/{name}")
|
||||||
|
Call<Void> addReaction(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Path("name") String name
|
||||||
|
);
|
||||||
|
|
||||||
|
@POST("statuses/{id}/unreact/{name}")
|
||||||
|
Call<Void> removeReaction(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Path("name") String name
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,6 +109,8 @@ public class Status implements Serializable, Cloneable {
|
||||||
public boolean cached = false;
|
public boolean cached = false;
|
||||||
@SerializedName("is_maths")
|
@SerializedName("is_maths")
|
||||||
public Boolean isMaths;
|
public Boolean isMaths;
|
||||||
|
@SerializedName("reactions")
|
||||||
|
public List<Reaction> reactions;
|
||||||
|
|
||||||
public Attachment art_attachment;
|
public Attachment art_attachment;
|
||||||
public boolean isExpended = false;
|
public boolean isExpended = false;
|
||||||
|
|
|
@ -34,6 +34,7 @@ import app.fedilab.android.mastodon.client.entities.api.Reaction;
|
||||||
import app.fedilab.android.mastodon.helper.Helper;
|
import app.fedilab.android.mastodon.helper.Helper;
|
||||||
import app.fedilab.android.mastodon.helper.ThemeHelper;
|
import app.fedilab.android.mastodon.helper.ThemeHelper;
|
||||||
import app.fedilab.android.mastodon.viewmodel.mastodon.AnnouncementsVM;
|
import app.fedilab.android.mastodon.viewmodel.mastodon.AnnouncementsVM;
|
||||||
|
import app.fedilab.android.mastodon.viewmodel.mastodon.StatusesVM;
|
||||||
import app.fedilab.android.mastodon.viewmodel.pleroma.ActionsVM;
|
import app.fedilab.android.mastodon.viewmodel.pleroma.ActionsVM;
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,18 +47,29 @@ public class ReactionAdapter extends RecyclerView.Adapter<ReactionAdapter.Reacti
|
||||||
private final List<Reaction> reactions;
|
private final List<Reaction> reactions;
|
||||||
private final String announcementId;
|
private final String announcementId;
|
||||||
private final boolean statusReaction;
|
private final boolean statusReaction;
|
||||||
|
private final boolean isPleroma;
|
||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
|
|
||||||
|
ReactionAdapter(String announcementId, List<Reaction> reactions, boolean statusReaction, boolean isPleroma) {
|
||||||
|
this.reactions = reactions;
|
||||||
|
this.announcementId = announcementId;
|
||||||
|
this.statusReaction = statusReaction;
|
||||||
|
this.isPleroma = isPleroma;
|
||||||
|
}
|
||||||
|
|
||||||
ReactionAdapter(String announcementId, List<Reaction> reactions, boolean statusReaction) {
|
ReactionAdapter(String announcementId, List<Reaction> reactions, boolean statusReaction) {
|
||||||
this.reactions = reactions;
|
this.reactions = reactions;
|
||||||
this.announcementId = announcementId;
|
this.announcementId = announcementId;
|
||||||
this.statusReaction = statusReaction;
|
this.statusReaction = statusReaction;
|
||||||
|
this.isPleroma = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReactionAdapter(String announcementId, List<Reaction> reactions) {
|
ReactionAdapter(String announcementId, List<Reaction> reactions) {
|
||||||
this.reactions = reactions;
|
this.reactions = reactions;
|
||||||
this.announcementId = announcementId;
|
this.announcementId = announcementId;
|
||||||
this.statusReaction = false;
|
this.statusReaction = false;
|
||||||
|
this.isPleroma = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -101,7 +113,7 @@ public class ReactionAdapter extends RecyclerView.Adapter<ReactionAdapter.Reacti
|
||||||
}
|
}
|
||||||
notifyItemChanged(position);
|
notifyItemChanged(position);
|
||||||
});
|
});
|
||||||
} else {
|
} else if (isPleroma) {
|
||||||
ActionsVM actionVM = new ViewModelProvider((ViewModelStoreOwner) context).get(ActionsVM.class);
|
ActionsVM actionVM = new ViewModelProvider((ViewModelStoreOwner) context).get(ActionsVM.class);
|
||||||
holder.binding.reactionContainer.setOnClickListener(v -> {
|
holder.binding.reactionContainer.setOnClickListener(v -> {
|
||||||
if (reaction.me) {
|
if (reaction.me) {
|
||||||
|
@ -115,6 +127,20 @@ public class ReactionAdapter extends RecyclerView.Adapter<ReactionAdapter.Reacti
|
||||||
}
|
}
|
||||||
notifyItemChanged(position);
|
notifyItemChanged(position);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
StatusesVM statusesVM = new ViewModelProvider((ViewModelStoreOwner) context).get(StatusesVM.class);
|
||||||
|
holder.binding.reactionContainer.setOnClickListener(v -> {
|
||||||
|
if (reaction.me) {
|
||||||
|
statusesVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, announcementId, reaction.name);
|
||||||
|
reaction.me = false;
|
||||||
|
reaction.count -= 1;
|
||||||
|
} else {
|
||||||
|
statusesVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, announcementId, reaction.name);
|
||||||
|
reaction.me = true;
|
||||||
|
reaction.count += 1;
|
||||||
|
}
|
||||||
|
notifyItemChanged(position);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ import android.os.Looper;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
@ -519,7 +520,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
holder.binding.quotedMessage.cardviewContainer.setVisibility(View.GONE);
|
holder.binding.quotedMessage.cardviewContainer.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentAccount != null && currentAccount.api == Account.API.PLEROMA) {
|
if (currentAccount != null && currentAccount.api == Account.API.PLEROMA || status.reactions != null) {
|
||||||
if (status.pleroma != null && status.pleroma.emoji_reactions != null && status.pleroma.emoji_reactions.size() > 0) {
|
if (status.pleroma != null && status.pleroma.emoji_reactions != null && status.pleroma.emoji_reactions.size() > 0) {
|
||||||
holder.binding.layoutReactions.getRoot().setVisibility(View.VISIBLE);
|
holder.binding.layoutReactions.getRoot().setVisibility(View.VISIBLE);
|
||||||
ReactionAdapter reactionAdapter = new ReactionAdapter(status.id, status.pleroma.emoji_reactions, true);
|
ReactionAdapter reactionAdapter = new ReactionAdapter(status.id, status.pleroma.emoji_reactions, true);
|
||||||
|
@ -527,6 +528,13 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
LinearLayoutManager layoutManager
|
LinearLayoutManager layoutManager
|
||||||
= new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
|
= new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
|
||||||
holder.binding.layoutReactions.reactionsView.setLayoutManager(layoutManager);
|
holder.binding.layoutReactions.reactionsView.setLayoutManager(layoutManager);
|
||||||
|
} else if (status.reactions != null && status.reactions.size() > 0) {
|
||||||
|
holder.binding.layoutReactions.getRoot().setVisibility(View.VISIBLE);
|
||||||
|
ReactionAdapter reactionAdapter = new ReactionAdapter(status.id, status.reactions, true, false);
|
||||||
|
holder.binding.layoutReactions.reactionsView.setAdapter(reactionAdapter);
|
||||||
|
LinearLayoutManager layoutManager
|
||||||
|
= new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
|
||||||
|
holder.binding.layoutReactions.reactionsView.setLayoutManager(layoutManager);
|
||||||
} else {
|
} else {
|
||||||
holder.binding.layoutReactions.getRoot().setVisibility(View.GONE);
|
holder.binding.layoutReactions.getRoot().setVisibility(View.GONE);
|
||||||
holder.binding.layoutReactions.reactionsView.setAdapter(null);
|
holder.binding.layoutReactions.reactionsView.setAdapter(null);
|
||||||
|
@ -539,33 +547,57 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
}).setOnEmojiClickListener((emoji, imageView) -> {
|
}).setOnEmojiClickListener((emoji, imageView) -> {
|
||||||
String emojiStr = imageView.getUnicode();
|
String emojiStr = imageView.getUnicode();
|
||||||
boolean alreadyAdded = false;
|
boolean alreadyAdded = false;
|
||||||
if (status.pleroma == null || status.pleroma.emoji_reactions == null) {
|
if (status.pleroma != null && status.pleroma.emoji_reactions != null) {
|
||||||
return;
|
for (Reaction reaction : status.pleroma.emoji_reactions) {
|
||||||
}
|
if (reaction.name.compareTo(emojiStr) == 0 && reaction.me) {
|
||||||
for (Reaction reaction : status.pleroma.emoji_reactions) {
|
alreadyAdded = true;
|
||||||
if (reaction.name.compareTo(emojiStr) == 0 && reaction.me) {
|
reaction.count = (reaction.count - 1);
|
||||||
alreadyAdded = true;
|
if (reaction.count == 0) {
|
||||||
reaction.count = (reaction.count - 1);
|
status.pleroma.emoji_reactions.remove(reaction);
|
||||||
if (reaction.count == 0) {
|
}
|
||||||
status.pleroma.emoji_reactions.remove(reaction);
|
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
if (!alreadyAdded) {
|
||||||
if (!alreadyAdded) {
|
Reaction reaction = new Reaction();
|
||||||
Reaction reaction = new Reaction();
|
reaction.me = true;
|
||||||
reaction.me = true;
|
reaction.count = 1;
|
||||||
reaction.count = 1;
|
reaction.name = emojiStr;
|
||||||
reaction.name = emojiStr;
|
status.pleroma.emoji_reactions.add(0, reaction);
|
||||||
status.pleroma.emoji_reactions.add(0, reaction);
|
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
||||||
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
}
|
||||||
}
|
ActionsVM actionVM = new ViewModelProvider((ViewModelStoreOwner) context).get(ActionsVM.class);
|
||||||
ActionsVM actionVM = new ViewModelProvider((ViewModelStoreOwner) context).get(ActionsVM.class);
|
if (alreadyAdded) {
|
||||||
if (alreadyAdded) {
|
actionVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
|
||||||
actionVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
|
} else {
|
||||||
} else {
|
actionVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
|
||||||
actionVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
|
}
|
||||||
|
} else if (status.reactions != null) {
|
||||||
|
for (Reaction reaction : status.reactions) {
|
||||||
|
if (reaction.name.compareTo(emojiStr) == 0 && reaction.me) {
|
||||||
|
alreadyAdded = true;
|
||||||
|
reaction.count = (reaction.count - 1);
|
||||||
|
if (reaction.count == 0) {
|
||||||
|
status.reactions.remove(reaction);
|
||||||
|
}
|
||||||
|
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!alreadyAdded) {
|
||||||
|
Reaction reaction = new Reaction();
|
||||||
|
reaction.me = true;
|
||||||
|
reaction.count = 1;
|
||||||
|
reaction.name = emojiStr;
|
||||||
|
status.reactions.add(0, reaction);
|
||||||
|
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
||||||
|
}
|
||||||
|
if (alreadyAdded) {
|
||||||
|
statusesVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
|
||||||
|
} else {
|
||||||
|
statusesVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build(holder.binding.layoutReactions.fakeEdittext);
|
.build(holder.binding.layoutReactions.fakeEdittext);
|
||||||
|
@ -591,32 +623,61 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
String url = emojis.get(BaseMainActivity.currentInstance).get(index).url;
|
String url = emojis.get(BaseMainActivity.currentInstance).get(index).url;
|
||||||
String static_url = emojis.get(BaseMainActivity.currentInstance).get(index).static_url;
|
String static_url = emojis.get(BaseMainActivity.currentInstance).get(index).static_url;
|
||||||
boolean alreadyAdded = false;
|
boolean alreadyAdded = false;
|
||||||
for (Reaction reaction : status.pleroma.emoji_reactions) {
|
if (status.pleroma != null && status.pleroma.emoji_reactions != null) {
|
||||||
if (reaction.name.compareTo(emojiStr) == 0 && reaction.me) {
|
for (Reaction reaction : status.pleroma.emoji_reactions) {
|
||||||
alreadyAdded = true;
|
if (reaction.name.compareTo(emojiStr) == 0 && reaction.me) {
|
||||||
reaction.count = (reaction.count - 1);
|
alreadyAdded = true;
|
||||||
if (reaction.count == 0) {
|
reaction.count = (reaction.count - 1);
|
||||||
status.pleroma.emoji_reactions.remove(reaction);
|
if (reaction.count == 0) {
|
||||||
|
status.pleroma.emoji_reactions.remove(reaction);
|
||||||
|
}
|
||||||
|
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
if (!alreadyAdded) {
|
||||||
if (!alreadyAdded) {
|
Reaction reaction = new Reaction();
|
||||||
Reaction reaction = new Reaction();
|
reaction.me = true;
|
||||||
reaction.me = true;
|
reaction.count = 1;
|
||||||
reaction.count = 1;
|
reaction.name = emojiStr;
|
||||||
reaction.name = emojiStr;
|
reaction.url = url;
|
||||||
reaction.url = url;
|
reaction.static_url = static_url;
|
||||||
reaction.static_url = static_url;
|
status.pleroma.emoji_reactions.add(0, reaction);
|
||||||
status.pleroma.emoji_reactions.add(0, reaction);
|
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
||||||
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
}
|
||||||
}
|
ActionsVM actionsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(ActionsVM.class);
|
||||||
ActionsVM actionsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(ActionsVM.class);
|
if (alreadyAdded) {
|
||||||
if (alreadyAdded) {
|
actionsVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
|
||||||
actionsVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
|
} else {
|
||||||
} else {
|
actionsVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
|
||||||
actionsVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
|
}
|
||||||
|
} else if (status.reactions != null) {
|
||||||
|
for (Reaction reaction : status.reactions) {
|
||||||
|
if (reaction.name.compareTo(emojiStr) == 0 && reaction.me) {
|
||||||
|
alreadyAdded = true;
|
||||||
|
reaction.count = (reaction.count - 1);
|
||||||
|
if (reaction.count == 0) {
|
||||||
|
status.reactions.remove(reaction);
|
||||||
|
}
|
||||||
|
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!alreadyAdded) {
|
||||||
|
Reaction reaction = new Reaction();
|
||||||
|
reaction.me = true;
|
||||||
|
reaction.count = 1;
|
||||||
|
reaction.name = emojiStr;
|
||||||
|
reaction.url = url;
|
||||||
|
reaction.static_url = static_url;
|
||||||
|
status.reactions.add(0, reaction);
|
||||||
|
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
||||||
|
}
|
||||||
|
if (alreadyAdded) {
|
||||||
|
statusesVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
|
||||||
|
} else {
|
||||||
|
statusesVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
gridView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp);
|
gridView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp);
|
||||||
|
|
|
@ -30,6 +30,7 @@ import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import app.fedilab.android.R;
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.mastodon.client.endpoints.MastodonAnnouncementsService;
|
||||||
import app.fedilab.android.mastodon.client.endpoints.MastodonStatusesService;
|
import app.fedilab.android.mastodon.client.endpoints.MastodonStatusesService;
|
||||||
import app.fedilab.android.mastodon.client.entities.api.Account;
|
import app.fedilab.android.mastodon.client.entities.api.Account;
|
||||||
import app.fedilab.android.mastodon.client.entities.api.Accounts;
|
import app.fedilab.android.mastodon.client.entities.api.Accounts;
|
||||||
|
@ -1292,4 +1293,48 @@ public class StatusesVM extends AndroidViewModel {
|
||||||
}).start();
|
}).start();
|
||||||
return voidMutableLiveData;
|
return voidMutableLiveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React to a status with an emoji.
|
||||||
|
*
|
||||||
|
* @param instance Instance domain of the active account
|
||||||
|
* @param token Access token of the active account
|
||||||
|
* @param id Local ID of an announcement
|
||||||
|
* @param name Unicode emoji, or shortcode of custom emoji
|
||||||
|
*/
|
||||||
|
public void addReaction(@NonNull String instance, String token, @NonNull String id, @NonNull String name) {
|
||||||
|
MastodonStatusesService mastodonStatusesService = init(instance);
|
||||||
|
new Thread(() -> {
|
||||||
|
Call<Void> addReactionCall = mastodonStatusesService.addReaction(token, id, name);
|
||||||
|
if (addReactionCall != null) {
|
||||||
|
try {
|
||||||
|
addReactionCall.execute();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo a react emoji to a status.
|
||||||
|
*
|
||||||
|
* @param instance Instance domain of the active account
|
||||||
|
* @param token Access token of the active account
|
||||||
|
* @param id Local ID of an announcement
|
||||||
|
* @param name Unicode emoji, or shortcode of custom emoji
|
||||||
|
*/
|
||||||
|
public void removeReaction(@NonNull String instance, String token, @NonNull String id, @NonNull String name) {
|
||||||
|
MastodonStatusesService mastodonStatusesService = init(instance);
|
||||||
|
new Thread(() -> {
|
||||||
|
Call<Void> removeReactionCall = mastodonStatusesService.removeReaction(token, id, name);
|
||||||
|
if (removeReactionCall != null) {
|
||||||
|
try {
|
||||||
|
removeReactionCall.execute();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue