emoji_reactions: implement adding and removing on existing reactions(LOW PERFORMANCE)

This commit is contained in:
Alibek Omarov 2020-03-06 22:33:25 +03:00
parent 6a4215363e
commit c2ded20df8
8 changed files with 100 additions and 21 deletions

View File

@ -57,7 +57,7 @@ public class EmojiReactionsAdapter extends RecyclerView.Adapter<EmojiReactionVie
holder.emojiReaction.setText(str);
holder.emojiReaction.setActivated(reaction.getMe());
holder.emojiReaction.setOnClickListener(v -> {
listener.onEmojiReactMenu(v, reaction, statusId, position);
listener.onEmojiReactMenu(v, reaction, statusId);
});
}

View File

@ -9,7 +9,7 @@ data class FavoriteEvent(val statusId: String, val favourite: Boolean) : Dispatc
data class ReblogEvent(val statusId: String, val reblog: Boolean) : Dispatchable
data class BookmarkEvent(val statusId: String, val bookmark: Boolean) : Dispatchable
data class MuteStatusEvent(val statusId: String, val mute: Boolean) : Dispatchable
data class EmojiReactEvent(val statusId: String, val reacted: Boolean, val emoji: String) : Dispatchable
data class EmojiReactEvent(val newStatus: Status) : Dispatchable
data class UnfollowEvent(val accountId: String) : Dispatchable
data class BlockEvent(val accountId: String) : Dispatchable
data class MuteEvent(val accountId: String) : Dispatchable

View File

@ -145,8 +145,8 @@ data class Status(
fun getEmojiReactions(): List<EmojiReaction>? {
return pleroma?.emojiReactions;
}
}
private fun getEditableText(): String {
val builder = SpannableStringBuilder(content)
for (span in content.getSpans(0, content.length, URLSpan::class.java)) {

View File

@ -62,6 +62,7 @@ import com.keylesspalace.tusky.entity.EmojiReaction;
import com.keylesspalace.tusky.network.MastodonApi;
import com.keylesspalace.tusky.network.TimelineCases;
import com.keylesspalace.tusky.viewdata.AttachmentViewData;
import com.keylesspalace.tusky.interfaces.StatusActionListener;
import java.util.ArrayList;
import java.util.LinkedHashSet;
@ -175,7 +176,7 @@ public abstract class SFragment extends BaseFragment implements Injectable {
getActivity().startActivity(intent);
}
protected void emojiReactMenu(@NonNull final String statusId, @NonNull final EmojiReaction reaction, View view, final int position) {
protected void emojiReactMenu(@NonNull final String statusId, @NonNull final EmojiReaction reaction, View view, final StatusActionListener listener) {
PopupMenu popup = new PopupMenu(getContext(), view);
popup.inflate(R.menu.emoji_reaction_more);
@ -186,10 +187,10 @@ public abstract class SFragment extends BaseFragment implements Injectable {
popup.setOnMenuItemClickListener(item -> {
switch (item.getItemId()) {
case R.id.emoji_react:
// TODO
listener.onEmojiReact(true, reaction.getName(), statusId);
return true;
case R.id.emoji_unreact:
// TODO
listener.onEmojiReact(false, reaction.getName(), statusId);
return true;
case R.id.emoji_reacted_by:
Intent intent = AccountListActivity.newIntent(getContext(), AccountListActivity.Type.REACTED, statusId, reaction.getName());

View File

@ -531,6 +531,8 @@ public class TimelineFragment extends SFragment implements
handleStatusComposeEvent(status);
} else if (event instanceof PreferenceChangedEvent) {
onPreferenceChanged(((PreferenceChangedEvent) event).getPreferenceKey());
} else if (event instanceof EmojiReactEvent) {
handleEmojiReactEvent((EmojiReactEvent)event);
}
});
eventRegistered = true;
@ -1497,9 +1499,47 @@ public class TimelineFragment extends SFragment implements
isNeedRefresh = true;
}
private void setEmojiReactionForStatus(int position, Status newStatus) {
StatusViewData newViewData = ViewDataUtils.statusToViewData(newStatus, false, false);
statuses.setPairedItem(position, newViewData);
updateAdapter();
}
private void setEmojiReactForStatus(int position, Status status, Status newStatus) {
Pair<StatusViewData.Concrete, Integer> actual =
findStatusAndPosition(position, status);
if (actual == null) return;
setEmojiReactionForStatus(actual.second, newStatus);
}
public void handleEmojiReactEvent(EmojiReactEvent event) {
int pos = findStatusOrReblogPositionById(event.getNewStatus().getActionableId());
if (pos < 0) return;
Status status = statuses.get(pos).asRight();
setEmojiReactForStatus(pos, status, event.getNewStatus());
}
@Override
public void onEmojiReactMenu(@NonNull View view, final EmojiReaction emoji, final String statusId, final int position) {
super.emojiReactMenu(statusId, emoji, view, position);
public void onEmojiReact(final boolean react, final String emoji, final String statusId) {
int position = findStatusOrReblogPositionById(statusId);
if (position < 0) return;
timelineCases.react(emoji, statusId, react)
.observeOn(AndroidSchedulers.mainThread())
.as(autoDisposable(from(this)))
.subscribe(
(newStatus) -> setEmojiReactionForStatus(position, newStatus),
(t) -> Log.d(TAG,
"Failed to react with " + emoji + " on status: " + statusId, t)
);
}
@Override
public void onEmojiReactMenu(@NonNull View view, final EmojiReaction emoji, final String statusId) {
super.emojiReactMenu(statusId, emoji, view, this);
}
}

View File

@ -44,13 +44,7 @@ import com.keylesspalace.tusky.BuildConfig;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.ViewThreadActivity;
import com.keylesspalace.tusky.adapter.ThreadAdapter;
import com.keylesspalace.tusky.appstore.BlockEvent;
import com.keylesspalace.tusky.appstore.BookmarkEvent;
import com.keylesspalace.tusky.appstore.EventHub;
import com.keylesspalace.tusky.appstore.FavoriteEvent;
import com.keylesspalace.tusky.appstore.ReblogEvent;
import com.keylesspalace.tusky.appstore.StatusComposedEvent;
import com.keylesspalace.tusky.appstore.StatusDeletedEvent;
import com.keylesspalace.tusky.appstore.*;
import com.keylesspalace.tusky.di.Injectable;
import com.keylesspalace.tusky.entity.*;
import com.keylesspalace.tusky.interfaces.StatusActionListener;
@ -196,6 +190,8 @@ public final class ViewThreadFragment extends SFragment implements
handleStatusComposedEvent((StatusComposedEvent) event);
} else if (event instanceof StatusDeletedEvent) {
handleStatusDeletedEvent((StatusDeletedEvent) event);
} else if (event instanceof EmojiReactEvent) {
handleEmojiReactEvent((EmojiReactEvent)event);
}
});
@ -745,8 +741,40 @@ public final class ViewThreadFragment extends SFragment implements
onRefresh();
}
private void setEmojiReactionForStatus(int position, Status status) {
StatusViewData.Concrete newViewData = ViewDataUtils.statusToViewData(status, false, false);
statuses.setPairedItem(position, newViewData);
adapter.setItem(position, newViewData, true);
}
public void handleEmojiReactEvent(EmojiReactEvent event) {
Pair<Integer, Status> posAndStatus = findStatusAndPos(event.getNewStatus().getActionableId());
if (posAndStatus == null) return;
setEmojiReactionForStatus(posAndStatus.first, event.getNewStatus());
}
@Override
public void onEmojiReactMenu(@NonNull View view, final EmojiReaction emoji, final String statusId, final int position) {
super.emojiReactMenu(statusId, emoji, view, position);
public void onEmojiReact(final boolean react, final String emoji, final String statusId) {
Pair<Integer, Status> statusAndPos = findStatusAndPos(statusId);
if(statusAndPos == null)
return;
int position = statusAndPos.first;
timelineCases.react(emoji, statusId, react)
.observeOn(AndroidSchedulers.mainThread())
.as(autoDisposable(from(this)))
.subscribe(
(newStatus) -> setEmojiReactionForStatus(position, newStatus),
(t) -> Log.d(TAG,
"Failed to react with " + emoji + " on status: " + statusId, t)
);
}
@Override
public void onEmojiReactMenu(@NonNull View view, final EmojiReaction emoji, final String statusId) {
super.emojiReactMenu(statusId, emoji, view, this);
}
}

View File

@ -65,7 +65,7 @@ public interface StatusActionListener extends LinkListener {
void onVoteInPoll(int position, @NonNull List<Integer> choices);
default void onMute(int position, boolean isMuted) {}
// default void onEmojiReact(final boolean react, final String emoji, final int position) {};
default void onEmojiReactMenu(@NonNull View view, final EmojiReaction emoji, final String statusId, final int position) {};
default void onEmojiReact(final boolean react, final String emoji, final String statusId) {};
default void onEmojiReactMenu(@NonNull View view, final EmojiReaction emoji, final String statusId) {};
}

View File

@ -43,7 +43,7 @@ interface TimelineCases {
fun delete(id: String): Single<DeletedStatus>
fun pin(status: Status, pin: Boolean)
fun voteInPoll(status: Status, choices: List<Int>): Single<Poll>
fun react(emoji: String, id: String, react: Boolean) : Single<Status>
}
class TimelineCasesImpl(
@ -157,4 +157,14 @@ class TimelineCasesImpl(
}
}
override fun react(emoji: String, id: String, react: Boolean): Single<Status> {
val call = if (react) {
mastodonApi.reactWithEmoji(id, emoji)
} else {
mastodonApi.unreactWithEmoji(id, emoji)
}
return call.doAfterSuccess { status ->
eventHub.dispatch(EmojiReactEvent(status))
}
}
}