Add reaction when favoriting post on Iceshrimp
This commit is contained in:
parent
86b6adf228
commit
3266a490be
|
@ -7,14 +7,23 @@ import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.api.requests.statuses.SetStatusBookmarked;
|
import org.joinmastodon.android.api.requests.statuses.SetStatusBookmarked;
|
||||||
import org.joinmastodon.android.api.requests.statuses.SetStatusFavorited;
|
import org.joinmastodon.android.api.requests.statuses.SetStatusFavorited;
|
||||||
import org.joinmastodon.android.api.requests.statuses.SetStatusReblogged;
|
import org.joinmastodon.android.api.requests.statuses.SetStatusReblogged;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
import org.joinmastodon.android.events.EmojiReactionsUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.ReblogDeletedEvent;
|
import org.joinmastodon.android.events.ReblogDeletedEvent;
|
||||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.StatusCreatedEvent;
|
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||||
import org.joinmastodon.android.events.StatusDeletedEvent;
|
import org.joinmastodon.android.model.Emoji;
|
||||||
|
import org.joinmastodon.android.model.EmojiCategory;
|
||||||
|
import org.joinmastodon.android.model.EmojiReaction;
|
||||||
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.StatusPrivacy;
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
|
@ -40,6 +49,9 @@ public class StatusInteractionController{
|
||||||
if(!Looper.getMainLooper().isCurrentThread())
|
if(!Looper.getMainLooper().isCurrentThread())
|
||||||
throw new IllegalStateException("Can only be called from main thread");
|
throw new IllegalStateException("Can only be called from main thread");
|
||||||
|
|
||||||
|
AccountSession session=AccountSessionManager.get(accountID);
|
||||||
|
Instance instance=session.getInstance().get();
|
||||||
|
|
||||||
SetStatusFavorited current=runningFavoriteRequests.remove(status.id);
|
SetStatusFavorited current=runningFavoriteRequests.remove(status.id);
|
||||||
if(current!=null){
|
if(current!=null){
|
||||||
current.cancel();
|
current.cancel();
|
||||||
|
@ -52,6 +64,7 @@ public class StatusInteractionController{
|
||||||
result.favouritesCount = Math.max(0, status.favouritesCount + (favorited ? 1 : -1));
|
result.favouritesCount = Math.max(0, status.favouritesCount + (favorited ? 1 : -1));
|
||||||
cb.accept(result);
|
cb.accept(result);
|
||||||
if(updateCounters) E.post(new StatusCountersUpdatedEvent(result));
|
if(updateCounters) E.post(new StatusCountersUpdatedEvent(result));
|
||||||
|
if(instance.isIceshrimp()) E.post(new EmojiReactionsUpdatedEvent(status.id, result.reactions, false, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -61,12 +74,55 @@ public class StatusInteractionController{
|
||||||
status.favourited=!favorited;
|
status.favourited=!favorited;
|
||||||
cb.accept(status);
|
cb.accept(status);
|
||||||
if(updateCounters) E.post(new StatusCountersUpdatedEvent(status));
|
if(updateCounters) E.post(new StatusCountersUpdatedEvent(status));
|
||||||
|
if(instance.isIceshrimp()) E.post(new EmojiReactionsUpdatedEvent(status.id, status.reactions, false, null));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
runningFavoriteRequests.put(status.id, req);
|
runningFavoriteRequests.put(status.id, req);
|
||||||
status.favourited=favorited;
|
status.favourited=favorited;
|
||||||
if(updateCounters) E.post(new StatusCountersUpdatedEvent(status));
|
if(updateCounters) E.post(new StatusCountersUpdatedEvent(status));
|
||||||
|
|
||||||
|
String defaultReactionEmojiRaw=instance.configuration.reactions.defaultReaction;
|
||||||
|
if(!instance.isIceshrimp() || defaultReactionEmojiRaw==null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
boolean reactionIsCustom=defaultReactionEmojiRaw.startsWith(":");
|
||||||
|
String defaultReactionEmoji=reactionIsCustom ? defaultReactionEmojiRaw.substring(1, defaultReactionEmojiRaw.length()-1) : defaultReactionEmojiRaw;
|
||||||
|
ArrayList<EmojiReaction> reactions=new ArrayList<>(status.reactions.size());
|
||||||
|
for(EmojiReaction reaction:status.reactions){
|
||||||
|
reactions.add(reaction.copy());
|
||||||
|
}
|
||||||
|
Optional<EmojiReaction> existingReaction=reactions.stream().filter(r->r.me).findFirst();
|
||||||
|
Optional<EmojiReaction> existingDefaultReaction=reactions.stream().filter(r->r.name.equals(defaultReactionEmoji)).findFirst();
|
||||||
|
if(existingReaction.isPresent() && !favorited){
|
||||||
|
existingReaction.get().me=false;
|
||||||
|
existingReaction.get().count--;
|
||||||
|
existingReaction.get().pendingChange=true;
|
||||||
|
}else if(existingDefaultReaction.isPresent() && favorited){
|
||||||
|
existingDefaultReaction.get().count++;
|
||||||
|
existingDefaultReaction.get().me=true;
|
||||||
|
existingDefaultReaction.get().pendingChange=true;
|
||||||
|
}else if(favorited){
|
||||||
|
EmojiReaction reaction=null;
|
||||||
|
if(reactionIsCustom){
|
||||||
|
List<EmojiCategory> customEmojis=AccountSessionManager.getInstance().getCustomEmojis(session.domain);
|
||||||
|
for(EmojiCategory category:customEmojis){
|
||||||
|
for(Emoji emoji:category.emojis){
|
||||||
|
if(emoji.shortcode.equals(defaultReactionEmoji)){
|
||||||
|
reaction=EmojiReaction.of(emoji, session.self);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(reaction==null)
|
||||||
|
reaction=EmojiReaction.of(defaultReactionEmoji, session.self);
|
||||||
|
}else{
|
||||||
|
reaction=EmojiReaction.of(defaultReactionEmoji, session.self);
|
||||||
|
}
|
||||||
|
reaction.pendingChange=true;
|
||||||
|
reactions.add(reaction);
|
||||||
|
}
|
||||||
|
E.post(new EmojiReactionsUpdatedEvent(status.id, reactions, false, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReblogged(Status status, boolean reblogged, StatusPrivacy visibility, Consumer<Status> cb){
|
public void setReblogged(Status status, boolean reblogged, StatusPrivacy visibility, Consumer<Status> cb){
|
||||||
|
|
|
@ -275,13 +275,17 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
public void onEmojiReactionsChanged(EmojiReactionsUpdatedEvent ev){
|
public void onEmojiReactionsChanged(EmojiReactionsUpdatedEvent ev){
|
||||||
for(Notification n : data){
|
for(Notification n : data){
|
||||||
if(n.status!=null && n.status.getContentStatus().id.equals(ev.id)){
|
if(n.status!=null && n.status.getContentStatus().id.equals(ev.id)){
|
||||||
n.status.getContentStatus().update(ev);
|
|
||||||
AccountSessionManager.get(accountID).getCacheController().updateNotification(n);
|
|
||||||
for(int i=0; i<list.getChildCount(); i++){
|
for(int i=0; i<list.getChildCount(); i++){
|
||||||
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||||
if(holder instanceof EmojiReactionsStatusDisplayItem.Holder reactions && reactions.getItem().status==n.status.getContentStatus() && ev.viewHolder!=holder){
|
if(holder instanceof EmojiReactionsStatusDisplayItem.Holder reactions && reactions.getItem().status==n.status.getContentStatus() && ev.viewHolder!=holder){
|
||||||
reactions.rebind();
|
reactions.updateReactions(ev.reactions);
|
||||||
}else if(holder instanceof TextStatusDisplayItem.Holder text && text.getItem().parentID.equals(n.getID())){
|
}
|
||||||
|
}
|
||||||
|
n.status.getContentStatus().update(ev);
|
||||||
|
AccountSessionManager.get(accountID).getCacheController().updateNotification(n);
|
||||||
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
|
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||||
|
if(holder instanceof TextStatusDisplayItem.Holder text && text.getItem().parentID.equals(n.getID())){
|
||||||
text.rebind();
|
text.rebind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -286,13 +286,17 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>
|
||||||
public void onEmojiReactionsChanged(EmojiReactionsUpdatedEvent ev){
|
public void onEmojiReactionsChanged(EmojiReactionsUpdatedEvent ev){
|
||||||
for(Status s:data){
|
for(Status s:data){
|
||||||
if(s.getContentStatus().id.equals(ev.id)){
|
if(s.getContentStatus().id.equals(ev.id)){
|
||||||
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
|
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||||
|
if(holder instanceof EmojiReactionsStatusDisplayItem.Holder reactions && reactions.getItem().status==s.getContentStatus() && ev.viewHolder!=holder){
|
||||||
|
reactions.updateReactions(ev.reactions);
|
||||||
|
}
|
||||||
|
}
|
||||||
s.getContentStatus().update(ev);
|
s.getContentStatus().update(ev);
|
||||||
AccountSessionManager.get(accountID).getCacheController().updateStatus(s);
|
AccountSessionManager.get(accountID).getCacheController().updateStatus(s);
|
||||||
for(int i=0;i<list.getChildCount();i++){
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||||
if(holder instanceof EmojiReactionsStatusDisplayItem.Holder reactions && reactions.getItem().status==s.getContentStatus() && ev.viewHolder!=holder){
|
if(holder instanceof TextStatusDisplayItem.Holder text && text.getItem().parentID.equals(s.getID())){
|
||||||
reactions.rebind();
|
|
||||||
}else if(holder instanceof TextStatusDisplayItem.Holder text && text.getItem().parentID.equals(s.getID())){
|
|
||||||
text.rebind();
|
text.rebind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ public class EmojiReaction {
|
||||||
public String staticUrl;
|
public String staticUrl;
|
||||||
|
|
||||||
public transient ImageLoaderRequest request;
|
public transient ImageLoaderRequest request;
|
||||||
|
public transient boolean pendingChange=false;
|
||||||
|
|
||||||
public String getUrl(boolean playGifs){
|
public String getUrl(boolean playGifs){
|
||||||
String idealUrl=playGifs ? url : staticUrl;
|
String idealUrl=playGifs ? url : staticUrl;
|
||||||
|
@ -60,4 +61,18 @@ public class EmojiReaction {
|
||||||
accounts.add(self);
|
accounts.add(self);
|
||||||
accountIds.add(self.id);
|
accountIds.add(self.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EmojiReaction copy() {
|
||||||
|
EmojiReaction r=new EmojiReaction();
|
||||||
|
r.accounts=accounts;
|
||||||
|
r.accountIds=accountIds;
|
||||||
|
r.count=count;
|
||||||
|
r.me=me;
|
||||||
|
r.name=name;
|
||||||
|
r.url=url;
|
||||||
|
r.staticUrl=staticUrl;
|
||||||
|
r.request=request;
|
||||||
|
r.pendingChange=pendingChange;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,6 +227,7 @@ public class Instance extends BaseModel{
|
||||||
@Parcel
|
@Parcel
|
||||||
public static class ReactionsConfiguration {
|
public static class ReactionsConfiguration {
|
||||||
public int maxReactions;
|
public int maxReactions;
|
||||||
|
public String defaultReaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
|
|
|
@ -47,6 +47,8 @@ import org.joinmastodon.android.ui.utils.TextDrawable;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.EmojiReactionButton;
|
import org.joinmastodon.android.ui.views.EmojiReactionButton;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
|
@ -196,17 +198,23 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
||||||
emojiKeyboard.setListener(this);
|
emojiKeyboard.setListener(this);
|
||||||
space.setVisibility(View.GONE);
|
space.setVisibility(View.GONE);
|
||||||
root.addView(emojiKeyboard.getView());
|
root.addView(emojiKeyboard.getView());
|
||||||
boolean hidden=item.isHidden();
|
updateVisibility(item.isHidden(), true);
|
||||||
root.setVisibility(hidden ? View.GONE : View.VISIBLE);
|
imgLoader.updateImages();
|
||||||
line.setVisibility(hidden ? View.GONE : View.VISIBLE);
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateVisibility(boolean hidden, boolean force){
|
||||||
|
int visibility=hidden ? View.GONE : View.VISIBLE;
|
||||||
|
if(!force && visibility==root.getVisibility())
|
||||||
|
return;
|
||||||
|
root.setVisibility(visibility);
|
||||||
|
line.setVisibility(visibility);
|
||||||
line.setPadding(
|
line.setPadding(
|
||||||
list.getPaddingLeft(),
|
list.getPaddingLeft(),
|
||||||
hidden ? 0 : V.dp(8),
|
hidden ? 0 : V.dp(8),
|
||||||
list.getPaddingRight(),
|
list.getPaddingRight(),
|
||||||
item.forAnnouncement ? V.dp(8) : 0
|
item.forAnnouncement ? V.dp(8) : 0
|
||||||
);
|
);
|
||||||
imgLoader.updateImages();
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hideEmojiKeyboard(){
|
private void hideEmojiKeyboard(){
|
||||||
|
@ -267,7 +275,7 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
||||||
RecyclerView.SmoothScroller scroller=new LinearSmoothScroller(list.getContext());
|
RecyclerView.SmoothScroller scroller=new LinearSmoothScroller(list.getContext());
|
||||||
scroller.setTargetPosition(pos);
|
scroller.setTargetPosition(pos);
|
||||||
list.getLayoutManager().startSmoothScroll(scroller);
|
list.getLayoutManager().startSmoothScroll(scroller);
|
||||||
updateAddButtonClickable(false);
|
updateMeReactionCount(false);
|
||||||
}else{
|
}else{
|
||||||
finalExisting.add(me);
|
finalExisting.add(me);
|
||||||
adapter.notifyItemChanged(item.status.reactions.indexOf(finalExisting));
|
adapter.notifyItemChanged(item.status.reactions.indexOf(finalExisting));
|
||||||
|
@ -297,8 +305,9 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateAddButtonClickable(boolean deleting) {
|
private void updateAddButtonClickable() {
|
||||||
meReactionCount+=deleting ? -1 : 1;
|
if(instance==null || instance.configuration==null || instance.configuration.reactions==null || instance.configuration.reactions.maxReactions==0)
|
||||||
|
return;
|
||||||
boolean canReact=meReactionCount<instance.configuration.reactions.maxReactions;
|
boolean canReact=meReactionCount<instance.configuration.reactions.maxReactions;
|
||||||
addButton.setClickable(canReact);
|
addButton.setClickable(canReact);
|
||||||
|
|
||||||
|
@ -310,6 +319,68 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
||||||
anim.start();
|
anim.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateMeReactionCount(boolean deleting) {
|
||||||
|
meReactionCount=Math.max(0, meReactionCount + (deleting ? -1 : 1));
|
||||||
|
updateAddButtonClickable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateReactions(List<EmojiReaction> reactions){
|
||||||
|
for(int i=0;i<item.status.reactions.size();i++){
|
||||||
|
EmojiReaction reaction=item.status.reactions.get(i);
|
||||||
|
Optional<EmojiReaction> newReactionOptional=reactions.stream().filter(r->r.name.equals(reaction.name)).findFirst();
|
||||||
|
if(newReactionOptional.isEmpty()){ // deleted reactions
|
||||||
|
adapter.notifyItemRemoved(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// changed reactions
|
||||||
|
EmojiReaction newReaction=newReactionOptional.get();
|
||||||
|
if(reaction.count!=newReaction.count || reaction.me!=newReaction.me || reaction.pendingChange!=newReaction.pendingChange){
|
||||||
|
if(newReaction.pendingChange){
|
||||||
|
View holderView=list.getChildAt(i);
|
||||||
|
if(holderView!=null){
|
||||||
|
EmojiReactionViewHolder reactionHolder=(EmojiReactionViewHolder) list.getChildViewHolder(holderView);
|
||||||
|
item.setActionProgressVisible(reactionHolder, true);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
adapter.notifyItemChanged(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean pendingAddReaction=false;
|
||||||
|
for(EmojiReaction reaction:reactions){
|
||||||
|
if(item.status.reactions.stream().anyMatch(r->r.name.equals(reaction.name)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// new reactions
|
||||||
|
if(reaction.pendingChange){
|
||||||
|
pendingAddReaction=true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int pos=item.status.reactions.size();
|
||||||
|
item.status.reactions.add(pos, reaction);
|
||||||
|
adapter.notifyItemInserted(pos);
|
||||||
|
RecyclerView.SmoothScroller scroller=new LinearSmoothScroller(list.getContext());
|
||||||
|
scroller.setTargetPosition(pos);
|
||||||
|
list.getLayoutManager().startSmoothScroll(scroller);
|
||||||
|
}
|
||||||
|
if(pendingAddReaction){
|
||||||
|
progress.setVisibility(View.VISIBLE);
|
||||||
|
addButton.setClickable(false);
|
||||||
|
addButton.setAlpha(ALPHA_DISABLED);
|
||||||
|
}else{
|
||||||
|
progress.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int newMeReactionCount=(int) reactions.stream().filter(r->r.me || r.pendingChange).count();
|
||||||
|
if (newMeReactionCount!=meReactionCount){
|
||||||
|
meReactionCount=newMeReactionCount;
|
||||||
|
updateAddButtonClickable();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateVisibility(reactions.isEmpty() && item.hideEmpty, false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setImage(int index, Drawable image){
|
public void setImage(int index, Drawable image){
|
||||||
View child=list.getChildAt(index);
|
View child=list.getChildAt(index);
|
||||||
|
@ -386,6 +457,12 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(Pair<EmojiReactionsStatusDisplayItem, EmojiReaction> item){
|
public void onBind(Pair<EmojiReactionsStatusDisplayItem, EmojiReaction> item){
|
||||||
|
if(item.second.pendingChange){
|
||||||
|
itemView.setVisibility(View.GONE);
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
itemView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
item.first.setActionProgressVisible(this, false);
|
item.first.setActionProgressVisible(this, false);
|
||||||
EmojiReactionsStatusDisplayItem parent=item.first;
|
EmojiReactionsStatusDisplayItem parent=item.first;
|
||||||
EmojiReaction reaction=item.second;
|
EmojiReaction reaction=item.second;
|
||||||
|
@ -435,7 +512,7 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
||||||
|
|
||||||
Instance instance=parent.parentFragment.getInstance().get();
|
Instance instance=parent.parentFragment.getInstance().get();
|
||||||
if(instance.configuration!=null && instance.configuration.reactions!=null && instance.configuration.reactions.maxReactions!=0){
|
if(instance.configuration!=null && instance.configuration.reactions!=null && instance.configuration.reactions.maxReactions!=0){
|
||||||
adapter.parentHolder.updateAddButtonClickable(deleting);
|
adapter.parentHolder.updateMeReactionCount(deleting);
|
||||||
}
|
}
|
||||||
if(instance.isIceshrimp() && status!=null){
|
if(instance.isIceshrimp() && status!=null){
|
||||||
parent.parentFragment.onFavoriteChanged(status, adapter.parentHolder.getItemID());
|
parent.parentFragment.onFavoriteChanged(status, adapter.parentHolder.getItemID());
|
||||||
|
|
Loading…
Reference in New Issue