emoji reactions for announcements!
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.announcements;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
|
public class AddAnnouncementReaction extends MastodonAPIRequest<Object> {
|
||||||
|
public AddAnnouncementReaction(String id, String emoji) {
|
||||||
|
super(HttpMethod.PUT, "/announcements/" + id + "/reactions/" + emoji, Object.class);
|
||||||
|
setRequestBody(new Object());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.announcements;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
|
||||||
|
public class DeleteAnnouncementReaction extends MastodonAPIRequest<Object> {
|
||||||
|
public DeleteAnnouncementReaction(String id, String emoji) {
|
||||||
|
super(HttpMethod.DELETE, "/announcements/" + id + "/reactions/" + emoji, Object.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,8 @@ import org.joinmastodon.android.model.HeaderPaginationList;
|
|||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.ScheduledStatus;
|
import org.joinmastodon.android.model.ScheduledStatus;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.DummyStatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.EmojiReactionsStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
||||||
@@ -68,11 +70,12 @@ public class AnnouncementsFragment extends BaseStatusListFragment<Announcement>
|
|||||||
instanceUser.emojis = List.of();
|
instanceUser.emojis = List.of();
|
||||||
Status fakeStatus = a.toStatus();
|
Status fakeStatus = a.toStatus();
|
||||||
TextStatusDisplayItem textItem = new TextStatusDisplayItem(a.id, HtmlParser.parse(a.content, a.emojis, a.mentions, a.tags, accountID), this, fakeStatus, true);
|
TextStatusDisplayItem textItem = new TextStatusDisplayItem(a.id, HtmlParser.parse(a.content, a.emojis, a.mentions, a.tags, accountID), this, fakeStatus, true);
|
||||||
// TODO: emoji reactions!
|
|
||||||
textItem.textSelectable = true;
|
textItem.textSelectable = true;
|
||||||
return List.of(
|
return List.of(
|
||||||
HeaderStatusDisplayItem.fromAnnouncement(a, fakeStatus, instanceUser, this, accountID, this::onMarkAsRead),
|
HeaderStatusDisplayItem.fromAnnouncement(a, fakeStatus, instanceUser, this, accountID, this::onMarkAsRead),
|
||||||
textItem
|
textItem,
|
||||||
|
new EmojiReactionsStatusDisplayItem(a.id, this, fakeStatus, accountID, false, true)
|
||||||
|
// new DummyStatusDisplayItem(a.id, this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package org.joinmastodon.android.model;
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.ObjectValidationException;
|
||||||
import org.joinmastodon.android.api.RequiredField;
|
import org.joinmastodon.android.api.RequiredField;
|
||||||
import org.parceler.Parcel;
|
import org.parceler.Parcel;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
@@ -20,6 +22,7 @@ public class Announcement extends BaseModel implements DisplayItemsParent {
|
|||||||
public Instant updatedAt;
|
public Instant updatedAt;
|
||||||
public boolean read;
|
public boolean read;
|
||||||
public List<Emoji> emojis;
|
public List<Emoji> emojis;
|
||||||
|
public List<EmojiReaction> reactions;
|
||||||
public List<Mention> mentions;
|
public List<Mention> mentions;
|
||||||
public List<Hashtag> tags;
|
public List<Hashtag> tags;
|
||||||
|
|
||||||
@@ -41,10 +44,17 @@ public class Announcement extends BaseModel implements DisplayItemsParent {
|
|||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postprocess() throws ObjectValidationException{
|
||||||
|
super.postprocess();
|
||||||
|
if(reactions==null) reactions=new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
public Status toStatus() {
|
public Status toStatus() {
|
||||||
Status s = Status.ofFake(id, content, publishedAt);
|
Status s=Status.ofFake(id, content, publishedAt);
|
||||||
s.createdAt = startsAt != null ? startsAt : publishedAt;
|
s.createdAt=startsAt != null ? startsAt : publishedAt;
|
||||||
if (updatedAt != null) s.editedAt = updatedAt;
|
s.reactions=reactions;
|
||||||
|
if(updatedAt != null) s.editedAt=updatedAt;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
|||||||
t.postprocess();
|
t.postprocess();
|
||||||
for(Emoji e:emojis)
|
for(Emoji e:emojis)
|
||||||
e.postprocess();
|
e.postprocess();
|
||||||
if (mediaAttachments == null) mediaAttachments = List.of();
|
if (mediaAttachments == null) mediaAttachments=List.of();
|
||||||
for(Attachment a:mediaAttachments)
|
for(Attachment a:mediaAttachments)
|
||||||
a.postprocess();
|
a.postprocess();
|
||||||
account.postprocess();
|
account.postprocess();
|
||||||
@@ -201,17 +201,18 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Status ofFake(String id, String text, Instant createdAt) {
|
public static Status ofFake(String id, String text, Instant createdAt) {
|
||||||
Status s = new Status();
|
Status s=new Status();
|
||||||
s.id = id;
|
s.id=id;
|
||||||
s.mediaAttachments = List.of();
|
s.mediaAttachments=List.of();
|
||||||
s.createdAt = createdAt;
|
s.createdAt=createdAt;
|
||||||
s.content = s.text = text;
|
s.content=s.text=text;
|
||||||
s.spoilerText = "";
|
s.spoilerText="";
|
||||||
s.visibility = StatusPrivacy.PUBLIC;
|
s.visibility=StatusPrivacy.PUBLIC;
|
||||||
s.mentions = List.of();
|
s.reactions=List.of();
|
||||||
s.tags = List.of();
|
s.mentions=List.of();
|
||||||
s.emojis = List.of();
|
s.tags =List.of();
|
||||||
s.filtered = List.of();
|
s.emojis=List.of();
|
||||||
|
s.filtered=List.of();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,21 +224,21 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
|||||||
public static class StatusDeserializer implements JsonDeserializer<Status> {
|
public static class StatusDeserializer implements JsonDeserializer<Status> {
|
||||||
@Override
|
@Override
|
||||||
public Status deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
public Status deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
JsonObject obj = json.getAsJsonObject();
|
JsonObject obj=json.getAsJsonObject();
|
||||||
|
|
||||||
Status quote = null;
|
Status quote=null;
|
||||||
if (obj.has("quote") && obj.get("quote").isJsonObject())
|
if (obj.has("quote") && obj.get("quote").isJsonObject())
|
||||||
quote = gson.fromJson(obj.get("quote"), Status.class);
|
quote=gson.fromJson(obj.get("quote"), Status.class);
|
||||||
obj.remove("quote");
|
obj.remove("quote");
|
||||||
|
|
||||||
Status reblog = null;
|
Status reblog=null;
|
||||||
if (obj.has("reblog"))
|
if (obj.has("reblog"))
|
||||||
reblog = gson.fromJson(obj.get("reblog"), Status.class);
|
reblog=gson.fromJson(obj.get("reblog"), Status.class);
|
||||||
obj.remove("reblog");
|
obj.remove("reblog");
|
||||||
|
|
||||||
Status status = gsonWithoutDeserializer.fromJson(json, Status.class);
|
Status status=gsonWithoutDeserializer.fromJson(json, Status.class);
|
||||||
status.quote = quote;
|
status.quote=quote;
|
||||||
status.reblog = reblog;
|
status.reblog=reblog;
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import android.widget.Space;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Status;
|
|
||||||
|
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.api.requests.announcements.AddAnnouncementReaction;
|
||||||
|
import org.joinmastodon.android.api.requests.announcements.DeleteAnnouncementReaction;
|
||||||
import org.joinmastodon.android.api.requests.statuses.AddStatusReaction;
|
import org.joinmastodon.android.api.requests.statuses.AddStatusReaction;
|
||||||
import org.joinmastodon.android.api.requests.statuses.DeleteStatusReaction;
|
import org.joinmastodon.android.api.requests.statuses.DeleteStatusReaction;
|
||||||
import org.joinmastodon.android.api.requests.statuses.PleromaAddStatusReaction;
|
import org.joinmastodon.android.api.requests.statuses.PleromaAddStatusReaction;
|
||||||
@@ -31,6 +33,7 @@ import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
|||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.fragments.account_list.StatusEmojiReactionsListFragment;
|
import org.joinmastodon.android.fragments.account_list.StatusEmojiReactionsListFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
|
import org.joinmastodon.android.model.Announcement;
|
||||||
import org.joinmastodon.android.model.Emoji;
|
import org.joinmastodon.android.model.Emoji;
|
||||||
import org.joinmastodon.android.model.EmojiReaction;
|
import org.joinmastodon.android.model.EmojiReaction;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
@@ -54,14 +57,15 @@ import me.grishka.appkit.views.UsableRecyclerView;
|
|||||||
public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
||||||
public final Status status;
|
public final Status status;
|
||||||
private final Drawable placeholder;
|
private final Drawable placeholder;
|
||||||
private final boolean hideAdd;
|
private final boolean hideAdd, forAnnouncement;
|
||||||
private final String accountID;
|
private final String accountID;
|
||||||
private boolean hidden;
|
private boolean hidden;
|
||||||
|
|
||||||
public EmojiReactionsStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, Status status, String accountID, boolean hideAdd) {
|
public EmojiReactionsStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, Status status, String accountID, boolean hideAdd, boolean forAnnouncement) {
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
this.status=status;
|
this.status=status;
|
||||||
this.hideAdd=hideAdd;
|
this.hideAdd=hideAdd;
|
||||||
|
this.forAnnouncement=forAnnouncement;
|
||||||
this.accountID=accountID;
|
this.accountID=accountID;
|
||||||
placeholder=parentFragment.getContext().getDrawable(R.drawable.image_placeholder).mutate();
|
placeholder=parentFragment.getContext().getDrawable(R.drawable.image_placeholder).mutate();
|
||||||
placeholder.setBounds(0, 0, V.sp(24), V.sp(24));
|
placeholder.setBounds(0, 0, V.sp(24), V.sp(24));
|
||||||
@@ -91,6 +95,31 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
|||||||
hidden=status.reactions.isEmpty() && hideAdd;
|
hidden=status.reactions.isEmpty() && hideAdd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MastodonAPIRequest<?> createRequest(String name, boolean delete, Runnable cb){
|
||||||
|
boolean ak=parentFragment.isInstanceAkkoma();
|
||||||
|
if(forAnnouncement){
|
||||||
|
MastodonAPIRequest<Object> req=delete
|
||||||
|
? new DeleteAnnouncementReaction(status.id, name)
|
||||||
|
: new AddAnnouncementReaction(status.id, name);
|
||||||
|
return req.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Object result){ cb.run(); }
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){ error.showToast(parentFragment.getContext()); }
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
MastodonAPIRequest<Status> req=delete
|
||||||
|
? (ak ? new PleromaDeleteStatusReaction(status.id, name) : new DeleteStatusReaction(status.id, name))
|
||||||
|
: (ak ? new PleromaAddStatusReaction(status.id, name) : new AddStatusReaction(status.id, name));
|
||||||
|
return req.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Status result){ cb.run(); }
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){ error.showToast(parentFragment.getContext()); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class Holder extends StatusDisplayItem.Holder<EmojiReactionsStatusDisplayItem> implements ImageLoaderViewHolder, CustomEmojiPopupKeyboard.Listener {
|
public static class Holder extends StatusDisplayItem.Holder<EmojiReactionsStatusDisplayItem> implements ImageLoaderViewHolder, CustomEmojiPopupKeyboard.Listener {
|
||||||
private final UsableRecyclerView list;
|
private final UsableRecyclerView list;
|
||||||
private final LinearLayout root, line;
|
private final LinearLayout root, line;
|
||||||
@@ -129,7 +158,12 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
|||||||
item.updateHidden();
|
item.updateHidden();
|
||||||
root.setVisibility(item.hidden ? View.GONE : View.VISIBLE);
|
root.setVisibility(item.hidden ? View.GONE : View.VISIBLE);
|
||||||
line.setVisibility(item.hidden ? View.GONE : View.VISIBLE);
|
line.setVisibility(item.hidden ? View.GONE : View.VISIBLE);
|
||||||
line.setPadding(list.getPaddingLeft(), item.hidden ? 0 : V.dp(8), list.getPaddingRight(), 0);
|
line.setPadding(
|
||||||
|
list.getPaddingLeft(),
|
||||||
|
item.hidden ? 0 : V.dp(8),
|
||||||
|
list.getPaddingRight(),
|
||||||
|
item.forAnnouncement ? V.dp(8) : 0
|
||||||
|
);
|
||||||
imgLoader.updateImages();
|
imgLoader.updateImages();
|
||||||
adapter.notifyDataSetChanged();
|
adapter.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
@@ -155,12 +189,7 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
|||||||
private void addEmojiReaction(String emoji, Emoji info) {
|
private void addEmojiReaction(String emoji, Emoji info) {
|
||||||
if(item.status.reactions.stream().filter(r->r.name.equals(emoji) && r.me).findAny().isPresent()) return;
|
if(item.status.reactions.stream().filter(r->r.name.equals(emoji) && r.me).findAny().isPresent()) return;
|
||||||
|
|
||||||
MastodonAPIRequest<Status> req = item.parentFragment.isInstanceAkkoma()
|
item.createRequest(emoji, false, ()->{
|
||||||
? new PleromaAddStatusReaction(item.status.id, emoji)
|
|
||||||
: new AddStatusReaction(item.status.id, emoji);
|
|
||||||
req.setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Status result) {
|
|
||||||
Account me=AccountSessionManager.get(item.accountID).self;
|
Account me=AccountSessionManager.get(item.accountID).self;
|
||||||
boolean found=false;
|
boolean found=false;
|
||||||
for(int i=0; i<item.status.reactions.size(); i++){
|
for(int i=0; i<item.status.reactions.size(); i++){
|
||||||
@@ -177,14 +206,7 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
|||||||
adapter.notifyItemRangeInserted(item.status.reactions.size() - 1, 1);
|
adapter.notifyItemRangeInserted(item.status.reactions.size() - 1, 1);
|
||||||
}
|
}
|
||||||
E.post(new StatusCountersUpdatedEvent(item.status, adapter.parentHolder));
|
E.post(new StatusCountersUpdatedEvent(item.status, adapter.parentHolder));
|
||||||
}
|
}).exec(item.accountID);
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(item.parentFragment.getContext());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.exec(item.accountID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -298,15 +320,9 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
|||||||
btn.setCompoundDrawablesRelative(item.first.placeholder, null, null, null);
|
btn.setCompoundDrawablesRelative(item.first.placeholder, null, null, null);
|
||||||
}
|
}
|
||||||
btn.setSelected(reaction.me);
|
btn.setSelected(reaction.me);
|
||||||
btn.setOnClickListener(e -> {
|
btn.setOnClickListener(e->{
|
||||||
boolean deleting=reaction.me;
|
boolean deleting=reaction.me;
|
||||||
boolean ak=parent.parentFragment.isInstanceAkkoma();
|
parent.createRequest(reaction.name, deleting, ()->{
|
||||||
MastodonAPIRequest<Status> req = deleting
|
|
||||||
? (ak ? new PleromaDeleteStatusReaction(parent.status.id, reaction.name) : new DeleteStatusReaction(parent.status.id, reaction.name))
|
|
||||||
: (ak ? new PleromaAddStatusReaction(parent.status.id, reaction.name) : new AddStatusReaction(parent.status.id, reaction.name));
|
|
||||||
req.setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Status result) {
|
|
||||||
EmojiReactionsAdapter adapter = (EmojiReactionsAdapter) getBindingAdapter();
|
EmojiReactionsAdapter adapter = (EmojiReactionsAdapter) getBindingAdapter();
|
||||||
|
|
||||||
for(int i=0; i<parent.status.reactions.size(); i++){
|
for(int i=0; i<parent.status.reactions.size(); i++){
|
||||||
@@ -326,14 +342,7 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
|||||||
|
|
||||||
E.post(new StatusCountersUpdatedEvent(parent.status, adapter.parentHolder));
|
E.post(new StatusCountersUpdatedEvent(parent.status, adapter.parentHolder));
|
||||||
adapter.parentHolder.imgLoader.updateImages();
|
adapter.parentHolder.imgLoader.updateImages();
|
||||||
}
|
}).exec(parent.parentFragment.getAccountID());
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(itemView.getContext());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.exec(parent.parentFragment.getAccountID());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (parent.parentFragment.isInstanceAkkoma()) {
|
if (parent.parentFragment.isInstanceAkkoma()) {
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ public abstract class StatusDisplayItem{
|
|||||||
if((flags & FLAG_NO_EMOJI_REACTIONS)==0
|
if((flags & FLAG_NO_EMOJI_REACTIONS)==0
|
||||||
&& AccountSessionManager.get(accountID).getLocalPreferences().emojiReactionsEnabled){
|
&& AccountSessionManager.get(accountID).getLocalPreferences().emojiReactionsEnabled){
|
||||||
boolean isMainStatus=fragment instanceof ThreadFragment t && t.getMainStatus().id.equals(statusForContent.id);
|
boolean isMainStatus=fragment instanceof ThreadFragment t && t.getMainStatus().id.equals(statusForContent.id);
|
||||||
items.add(new EmojiReactionsStatusDisplayItem(parentID, fragment, statusForContent, accountID, !isMainStatus));
|
items.add(new EmojiReactionsStatusDisplayItem(parentID, fragment, statusForContent, accountID, !isMainStatus, false));
|
||||||
}
|
}
|
||||||
FooterStatusDisplayItem footer=null;
|
FooterStatusDisplayItem footer=null;
|
||||||
if((flags & FLAG_NO_FOOTER)==0){
|
if((flags & FLAG_NO_FOOTER)==0){
|
||||||
|
|||||||
Reference in New Issue
Block a user