mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-22 22:57:29 +00:00
Allow removing like/reaction
This commit is contained in:
parent
255c3c84b4
commit
6a163454f4
@ -389,7 +389,9 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
|
||||
|
||||
void onStoryClick(DirectItemStoryShare storyShare);
|
||||
|
||||
void onReaction(final DirectItem item, Emoji emoji);
|
||||
void onReaction(DirectItem item, Emoji emoji);
|
||||
|
||||
void onReactionClick(DirectItem item, int position);
|
||||
}
|
||||
|
||||
public interface DirectItemInternalLongClickListener {
|
||||
|
@ -10,7 +10,7 @@ import androidx.recyclerview.widget.ListAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.adapters.viewholder.DirectInboxItemViewHolder;
|
||||
import awais.instagrabber.adapters.viewholder.directmessages.DirectInboxItemViewHolder;
|
||||
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
|
@ -0,0 +1,81 @@
|
||||
package awais.instagrabber.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.adapters.viewholder.directmessages.DirectReactionViewHolder;
|
||||
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
|
||||
|
||||
public final class DirectReactionsAdapter extends ListAdapter<DirectItemEmojiReaction, DirectReactionViewHolder> {
|
||||
|
||||
private static final DiffUtil.ItemCallback<DirectItemEmojiReaction> DIFF_CALLBACK = new DiffUtil.ItemCallback<DirectItemEmojiReaction>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull final DirectItemEmojiReaction oldItem, @NonNull final DirectItemEmojiReaction newItem) {
|
||||
return oldItem.getSenderId() == newItem.getSenderId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull final DirectItemEmojiReaction oldItem, @NonNull final DirectItemEmojiReaction newItem) {
|
||||
return oldItem.getEmoji().equals(newItem.getEmoji());
|
||||
}
|
||||
};
|
||||
|
||||
private final long viewerId;
|
||||
private final List<User> users;
|
||||
private final String itemId;
|
||||
private final OnReactionClickListener onReactionClickListener;
|
||||
|
||||
public DirectReactionsAdapter(final long viewerId,
|
||||
final List<User> users,
|
||||
final String itemId,
|
||||
final OnReactionClickListener onReactionClickListener) {
|
||||
super(DIFF_CALLBACK);
|
||||
this.viewerId = viewerId;
|
||||
this.users = users;
|
||||
this.itemId = itemId;
|
||||
this.onReactionClickListener = onReactionClickListener;
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DirectReactionViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
||||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
||||
final LayoutDmUserItemBinding binding = LayoutDmUserItemBinding.inflate(layoutInflater, parent, false);
|
||||
return new DirectReactionViewHolder(binding, viewerId, itemId, onReactionClickListener);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final DirectReactionViewHolder holder, final int position) {
|
||||
final DirectItemEmojiReaction reaction = getItem(position);
|
||||
if (reaction == null) return;
|
||||
holder.bind(reaction, getUser(reaction.getSenderId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(final int position) {
|
||||
return getItem(position).getSenderId();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private User getUser(final long pk) {
|
||||
return users.stream()
|
||||
.filter(user -> user.getPk() == pk)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public interface OnReactionClickListener {
|
||||
void onReactionClick(String itemId, DirectItemEmojiReaction reaction);
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.viewholder.DirectUserViewHolder;
|
||||
import awais.instagrabber.adapters.viewholder.directmessages.DirectUserViewHolder;
|
||||
import awais.instagrabber.databinding.ItemFavSectionHeaderBinding;
|
||||
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
|
@ -12,7 +12,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import awais.instagrabber.adapters.DirectUsersAdapter.OnDirectUserClickListener;
|
||||
import awais.instagrabber.adapters.viewholder.DirectUserViewHolder;
|
||||
import awais.instagrabber.adapters.viewholder.directmessages.DirectUserViewHolder;
|
||||
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
package awais.instagrabber.adapters.viewholder;
|
||||
package awais.instagrabber.adapters.viewholder.directmessages;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
import android.view.View;
|
@ -24,7 +24,6 @@ import androidx.transition.TransitionManager;
|
||||
import com.google.android.material.transition.MaterialFade;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
@ -109,7 +108,7 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder {
|
||||
public void bind(final int position, final DirectItem item) {
|
||||
this.item = item;
|
||||
final MessageDirection messageDirection = isSelf(item) ? MessageDirection.OUTGOING : MessageDirection.INCOMING;
|
||||
itemView.post(() -> bindBase(item, messageDirection));
|
||||
itemView.post(() -> bindBase(item, messageDirection, position));
|
||||
itemView.post(() -> bindItem(item, messageDirection));
|
||||
itemView.post(() -> setupLongClickListener(position));
|
||||
// bindBase(item, messageDirection);
|
||||
@ -117,7 +116,7 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder {
|
||||
// setupLongClickListener(position);
|
||||
}
|
||||
|
||||
private void bindBase(final DirectItem item, final MessageDirection messageDirection) {
|
||||
private void bindBase(final DirectItem item, final MessageDirection messageDirection, final int position) {
|
||||
final FrameLayout.LayoutParams containerLayoutParams = (FrameLayout.LayoutParams) binding.container.getLayoutParams();
|
||||
final DirectItemType itemType = item.getItemType();
|
||||
setMessageDirectionGravity(messageDirection, containerLayoutParams);
|
||||
@ -134,7 +133,7 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder {
|
||||
binding.messageInfo.setPadding(0, 0, messageInfoPaddingSmall, dmRadiusSmall);
|
||||
}
|
||||
setupReply(item, messageDirection);
|
||||
setReactions(item);
|
||||
setReactions(item, position);
|
||||
}
|
||||
|
||||
private void setBackground(final MessageDirection messageDirection) {
|
||||
@ -334,7 +333,7 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder {
|
||||
replyInfoLayoutParams.endToStart = isIncoming ? ConstraintLayout.LayoutParams.UNSET : quoteLineId;
|
||||
}
|
||||
|
||||
private void setReactions(final DirectItem item) {
|
||||
private void setReactions(final DirectItem item, final int position) {
|
||||
binding.getRoot().post(() -> {
|
||||
MaterialFade materialFade = new MaterialFade();
|
||||
materialFade.addTarget(binding.emojis);
|
||||
@ -343,17 +342,27 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder {
|
||||
final List<DirectItemEmojiReaction> emojis = reactions != null ? reactions.getEmojis() : null;
|
||||
if (emojis == null || emojis.isEmpty()) {
|
||||
binding.container.setPadding(messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall);
|
||||
binding.emojis.setVisibility(View.GONE);
|
||||
binding.reactionsWrapper.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
binding.emojis.setVisibility(View.VISIBLE);
|
||||
binding.emojis.setTranslationY(getReactionsTranslationY());
|
||||
binding.reactionsWrapper.setVisibility(View.VISIBLE);
|
||||
binding.reactionsWrapper.setTranslationY(getReactionsTranslationY());
|
||||
binding.container.setPadding(messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall, reactionAdjustMargin);
|
||||
final String emojisJoined = emojis.stream()
|
||||
binding.emojis.setEmojis(emojis.stream()
|
||||
.map(DirectItemEmojiReaction::getEmoji)
|
||||
.collect(Collectors.joining());
|
||||
final String text = String.format(Locale.ENGLISH, "%s %d", emojisJoined, emojis.size());
|
||||
binding.emojis.setText(text);
|
||||
.collect(Collectors.toList()));
|
||||
// binding.emojis.setEmojis(ImmutableList.of("😣",
|
||||
// "😖",
|
||||
// "😫",
|
||||
// "😩",
|
||||
// "🥺",
|
||||
// "😢",
|
||||
// "😭",
|
||||
// "😤",
|
||||
// "😠",
|
||||
// "😡",
|
||||
// "🤬"));
|
||||
binding.emojis.setOnClickListener(v -> callback.onReactionClick(item, position));
|
||||
// final List<DirectUser> reactedUsers = emojis.stream()
|
||||
// .map(DirectItemEmojiReaction::getSenderId)
|
||||
// .distinct()
|
||||
|
@ -0,0 +1,71 @@
|
||||
package awais.instagrabber.adapters.viewholder.directmessages;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.DirectReactionsAdapter.OnReactionClickListener;
|
||||
import awais.instagrabber.customviews.emoji.Emoji;
|
||||
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
|
||||
import awais.instagrabber.utils.emoji.EmojiParser;
|
||||
|
||||
public class DirectReactionViewHolder extends RecyclerView.ViewHolder {
|
||||
private final LayoutDmUserItemBinding binding;
|
||||
private final long viewerId;
|
||||
private final String itemId;
|
||||
private final OnReactionClickListener onReactionClickListener;
|
||||
private final EmojiParser emojiParser;
|
||||
|
||||
public DirectReactionViewHolder(final LayoutDmUserItemBinding binding,
|
||||
final long viewerId,
|
||||
final String itemId,
|
||||
final OnReactionClickListener onReactionClickListener) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
this.viewerId = viewerId;
|
||||
this.itemId = itemId;
|
||||
this.onReactionClickListener = onReactionClickListener;
|
||||
binding.info.setVisibility(View.GONE);
|
||||
binding.secondaryImage.setVisibility(View.VISIBLE);
|
||||
emojiParser = EmojiParser.getInstance();
|
||||
}
|
||||
|
||||
public void bind(final DirectItemEmojiReaction reaction,
|
||||
@Nullable final User user) {
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (onReactionClickListener == null) return;
|
||||
onReactionClickListener.onReactionClick(itemId, reaction);
|
||||
});
|
||||
setUser(user);
|
||||
setReaction(reaction);
|
||||
}
|
||||
|
||||
private void setReaction(final DirectItemEmojiReaction reaction) {
|
||||
final Emoji emoji = emojiParser.getEmoji(reaction.getEmoji());
|
||||
if (emoji == null) {
|
||||
binding.secondaryImage.setImageDrawable(null);
|
||||
return;
|
||||
}
|
||||
binding.secondaryImage.setImageDrawable(emoji.getDrawable());
|
||||
}
|
||||
|
||||
private void setUser(final User user) {
|
||||
if (user == null) {
|
||||
binding.fullName.setText("");
|
||||
binding.username.setText("");
|
||||
binding.profilePic.setImageURI((String) null);
|
||||
return;
|
||||
}
|
||||
binding.fullName.setText(user.getFullName());
|
||||
if (user.getPk() == viewerId) {
|
||||
binding.username.setText(R.string.tap_to_remove);
|
||||
} else {
|
||||
binding.username.setText(user.getUsername());
|
||||
}
|
||||
binding.profilePic.setImageURI(user.getProfilePicUrl());
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package awais.instagrabber.adapters.viewholder;
|
||||
package awais.instagrabber.adapters.viewholder.directmessages;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.SpannableStringBuilder;
|
@ -0,0 +1,82 @@
|
||||
package awais.instagrabber.customviews;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.emoji.widget.EmojiAppCompatTextView;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ReactionEmojiTextView extends EmojiAppCompatTextView {
|
||||
private static final String TAG = ReactionEmojiTextView.class.getSimpleName();
|
||||
|
||||
private final SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
|
||||
|
||||
private String count = "";
|
||||
private SpannableString ellipsisSpannable;
|
||||
private String distinctEmojis;
|
||||
|
||||
public ReactionEmojiTextView(final Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public ReactionEmojiTextView(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public ReactionEmojiTextView(final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
ellipsisSpannable = new SpannableString(count);
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
public void setEmojis(@NonNull final List<String> emojis) {
|
||||
count = String.valueOf(emojis.size());
|
||||
distinctEmojis = emojis.stream()
|
||||
.distinct()
|
||||
.collect(Collectors.joining());
|
||||
ellipsisSpannable = new SpannableString(count);
|
||||
setText(distinctEmojis + (emojis.size() > 1 ? count : ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
final CharSequence text = getText();
|
||||
if (text == null) return;
|
||||
final int measuredWidth = getMeasuredWidth();
|
||||
float availableTextWidth = measuredWidth - getCompoundPaddingLeft() - getCompoundPaddingRight();
|
||||
CharSequence ellipsizedText = TextUtils.ellipsize(text, getPaint(), availableTextWidth, getEllipsize());
|
||||
if (!ellipsizedText.toString().equals(text.toString())) {
|
||||
// If the ellipsizedText is different than the original text, this means that it didn't fit and got indeed ellipsized.
|
||||
// Calculate the new availableTextWidth by taking into consideration the size of the custom ellipsis, too.
|
||||
availableTextWidth = (availableTextWidth - getPaint().measureText(count));
|
||||
ellipsizedText = TextUtils.ellipsize(text, getPaint(), availableTextWidth, getEllipsize());
|
||||
final int defaultEllipsisStart = ellipsizedText.toString().indexOf(getDefaultEllipsis());
|
||||
final int defaultEllipsisEnd = defaultEllipsisStart + 1;
|
||||
spannableStringBuilder.clear();
|
||||
// Update the text with the ellipsized version and replace the default ellipsis with the custom one.
|
||||
final SpannableStringBuilder replace = spannableStringBuilder.append(ellipsizedText)
|
||||
.replace(defaultEllipsisStart, defaultEllipsisEnd, ellipsisSpannable);
|
||||
setText(replace);
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
private char getDefaultEllipsis() {
|
||||
return '…';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
package awais.instagrabber.dialogs;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.DirectReactionsAdapter;
|
||||
import awais.instagrabber.adapters.DirectReactionsAdapter.OnReactionClickListener;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemReactions;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
|
||||
public class DirectItemReactionDialogFragment extends BottomSheetDialogFragment {
|
||||
|
||||
private static final String ARG_VIEWER_ID = "viewerId";
|
||||
private static final String ARG_ITEM_ID = "itemId";
|
||||
private static final String ARG_USERS = "users";
|
||||
private static final String ARG_REACTIONS = "reactions";
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
private OnReactionClickListener onReactionClickListener;
|
||||
|
||||
public static DirectItemReactionDialogFragment newInstance(final long viewerId,
|
||||
@NonNull final ArrayList<User> users,
|
||||
@NonNull final String itemId,
|
||||
@NonNull final DirectItemReactions reactions) {
|
||||
Bundle args = new Bundle();
|
||||
args.putLong(ARG_VIEWER_ID, viewerId);
|
||||
args.putSerializable(ARG_USERS, users);
|
||||
args.putString(ARG_ITEM_ID, itemId);
|
||||
args.putSerializable(ARG_REACTIONS, reactions);
|
||||
DirectItemReactionDialogFragment fragment = new DirectItemReactionDialogFragment();
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public DirectItemReactionDialogFragment() {}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.ThemeOverlay_Rounded_BottomSheetDialog);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
|
||||
final Context context = getContext();
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
recyclerView = new RecyclerView(context);
|
||||
return recyclerView;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull final Context context) {
|
||||
super.onAttach(context);
|
||||
try {
|
||||
onReactionClickListener = (OnReactionClickListener) getParentFragment();
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException("Calling fragment must implement DirectReactionsAdapter.OnReactionClickListener interface");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
final Dialog dialog = getDialog();
|
||||
if (dialog == null) return;
|
||||
final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialog;
|
||||
final View bottomSheetInternal = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);
|
||||
if (bottomSheetInternal == null) return;
|
||||
bottomSheetInternal.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
bottomSheetInternal.requestLayout();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final Bundle arguments = getArguments();
|
||||
if (arguments == null) return;
|
||||
final long viewerId = arguments.getLong(ARG_VIEWER_ID);
|
||||
final Serializable usersSerializable = arguments.getSerializable(ARG_USERS);
|
||||
if (!(usersSerializable instanceof ArrayList)) return;
|
||||
//noinspection unchecked
|
||||
final List<User> users = (ArrayList<User>) usersSerializable;
|
||||
final Serializable reactionsSerializable = arguments.getSerializable(ARG_REACTIONS);
|
||||
if (!(reactionsSerializable instanceof DirectItemReactions)) return;
|
||||
final DirectItemReactions reactions = (DirectItemReactions) reactionsSerializable;
|
||||
final String itemId = arguments.getString(ARG_ITEM_ID);
|
||||
if (TextUtils.isEmpty(itemId)) return;
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(context));
|
||||
final DirectReactionsAdapter adapter = new DirectReactionsAdapter(viewerId, users, itemId, onReactionClickListener);
|
||||
recyclerView.setAdapter(adapter);
|
||||
adapter.submitList(reactions.getEmojis());
|
||||
}
|
||||
}
|
@ -50,6 +50,7 @@ import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -60,6 +61,7 @@ import awais.instagrabber.adapters.DirectItemsAdapter;
|
||||
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
|
||||
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemLongClickListener;
|
||||
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemOrHeader;
|
||||
import awais.instagrabber.adapters.DirectReactionsAdapter;
|
||||
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemViewHolder;
|
||||
import awais.instagrabber.animations.CubicBezierInterpolator;
|
||||
import awais.instagrabber.customviews.RecordView;
|
||||
@ -70,6 +72,7 @@ import awais.instagrabber.customviews.helpers.HeightProvider;
|
||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge;
|
||||
import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
|
||||
import awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding;
|
||||
import awais.instagrabber.dialogs.DirectItemReactionDialogFragment;
|
||||
import awais.instagrabber.dialogs.MediaPickerBottomDialogFragment;
|
||||
import awais.instagrabber.fragments.PostViewV2Fragment;
|
||||
import awais.instagrabber.models.Resource;
|
||||
@ -77,6 +80,7 @@ import awais.instagrabber.repositories.requests.StoryViewerOptions;
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
import awais.instagrabber.utils.AppExecutors;
|
||||
@ -90,7 +94,7 @@ import awais.instagrabber.viewmodels.DirectThreadViewModel;
|
||||
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
|
||||
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
|
||||
|
||||
public class DirectMessageThreadFragment extends Fragment {
|
||||
public class DirectMessageThreadFragment extends Fragment implements DirectReactionsAdapter.OnReactionClickListener {
|
||||
private static final String TAG = DirectMessageThreadFragment.class.getSimpleName();
|
||||
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||
private static final int AUDIO_RECORD_PERM_REQUEST_CODE = 1000;
|
||||
@ -145,9 +149,7 @@ public class DirectMessageThreadFragment extends Fragment {
|
||||
|
||||
@Override
|
||||
public void onMentionClick(final String mention) {
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putString("username", "@" + mention);
|
||||
NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(R.id.action_global_profileFragment, bundle);
|
||||
navigateToUser(mention);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -215,10 +217,17 @@ public class DirectMessageThreadFragment extends Fragment {
|
||||
resourceLiveData.observe(getViewLifecycleOwner(), directItemResource -> handleSentMessage(resourceLiveData));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReactionClick(final DirectItem item, final int position) {
|
||||
showReactionsDialog(item);
|
||||
}
|
||||
};
|
||||
|
||||
private final DirectItemLongClickListener directItemLongClickListener = position -> {
|
||||
// viewModel.setSelectedPosition(position);
|
||||
};
|
||||
private DirectItemReactionDialogFragment reactionDialogFragment;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
@ -1132,6 +1141,50 @@ public class DirectMessageThreadFragment extends Fragment {
|
||||
|
||||
}
|
||||
|
||||
private void showReactionsDialog(final DirectItem item) {
|
||||
final LiveData<List<User>> users = viewModel.getUsers();
|
||||
final LiveData<List<User>> leftUsers = viewModel.getLeftUsers();
|
||||
final ArrayList<User> allUsers = new ArrayList<>();
|
||||
allUsers.add(viewModel.getCurrentUser());
|
||||
if (users != null && users.getValue() != null) {
|
||||
allUsers.addAll(users.getValue());
|
||||
}
|
||||
if (leftUsers != null && leftUsers.getValue() != null) {
|
||||
allUsers.addAll(leftUsers.getValue());
|
||||
}
|
||||
reactionDialogFragment = DirectItemReactionDialogFragment
|
||||
.newInstance(viewModel.getViewerId(),
|
||||
allUsers,
|
||||
item.getItemId(),
|
||||
item.getReactions());
|
||||
reactionDialogFragment.show(getChildFragmentManager(), "reactions_dialog");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReactionClick(final String itemId, final DirectItemEmojiReaction reaction) {
|
||||
if (reactionDialogFragment != null) {
|
||||
reactionDialogFragment.dismiss();
|
||||
}
|
||||
if (reaction == null) return;
|
||||
if (reaction.getSenderId() == viewModel.getViewerId()) {
|
||||
final LiveData<Resource<DirectItem>> resourceLiveData = viewModel.sendDeleteReaction(itemId);
|
||||
if (resourceLiveData != null) {
|
||||
resourceLiveData.observe(getViewLifecycleOwner(), directItemResource -> handleSentMessage(resourceLiveData));
|
||||
}
|
||||
return;
|
||||
}
|
||||
// navigate to user
|
||||
final User user = viewModel.getUser(reaction.getSenderId());
|
||||
if (user == null) return;
|
||||
navigateToUser(user.getUsername());
|
||||
}
|
||||
|
||||
private void navigateToUser(@NonNull final String username) {
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putString("username", "@" + username);
|
||||
NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(R.id.action_global_profileFragment, bundle);
|
||||
}
|
||||
|
||||
public static class ItemsAdapterDataMerger extends MediatorLiveData<Pair<User, DirectThread>> {
|
||||
private User user;
|
||||
private DirectThread thread;
|
||||
|
@ -1,8 +1,9 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class DirectItemEmojiReaction {
|
||||
public class DirectItemEmojiReaction implements Serializable {
|
||||
private final long senderId;
|
||||
private final long timestamp;
|
||||
private final String emoji;
|
||||
|
@ -2,10 +2,11 @@ package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class DirectItemReactions implements Cloneable {
|
||||
public class DirectItemReactions implements Cloneable, Serializable {
|
||||
private List<DirectItemEmojiReaction> emojis;
|
||||
private List<DirectItemEmojiReaction> likes;
|
||||
|
||||
|
@ -270,6 +270,13 @@ public final class EmojiParser {
|
||||
return ALL_EMOJIS;
|
||||
}
|
||||
|
||||
public Emoji getEmoji(final String emoji) {
|
||||
if (emoji == null) {
|
||||
return null;
|
||||
}
|
||||
return ALL_EMOJIS.get(emoji);
|
||||
}
|
||||
|
||||
// public String getMinorCategory(String emoji) {
|
||||
// String minorCat = emojiToMinorCategory.get(emoji);
|
||||
// if (minorCat == null) {
|
||||
|
@ -77,6 +77,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
private final MutableLiveData<String> threadTitle = new MutableLiveData<>("");
|
||||
private final MutableLiveData<Boolean> fetching = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<List<User>> users = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<User>> leftUsers = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
private final DirectMessagesService service;
|
||||
private final ContentResolver contentResolver;
|
||||
@ -92,18 +93,19 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
private User currentUser;
|
||||
private Call<DirectThreadFeedResponse> chatsRequest;
|
||||
private VoiceRecorder voiceRecorder;
|
||||
private final long viewerId;
|
||||
|
||||
public DirectThreadViewModel(@NonNull final Application application) {
|
||||
super(application);
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final long userId = CookieUtils.getUserIdFromCookie(cookie);
|
||||
viewerId = CookieUtils.getUserIdFromCookie(cookie);
|
||||
final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID);
|
||||
csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
if (TextUtils.isEmpty(csrfToken) || userId <= 0 || TextUtils.isEmpty(deviceUuid)) {
|
||||
if (TextUtils.isEmpty(csrfToken) || viewerId <= 0 || TextUtils.isEmpty(deviceUuid)) {
|
||||
throw new IllegalArgumentException("User is not logged in!");
|
||||
}
|
||||
service = DirectMessagesService.getInstance(csrfToken, userId, deviceUuid);
|
||||
mediaService = MediaService.getInstance(deviceUuid, csrfToken, userId);
|
||||
service = DirectMessagesService.getInstance(csrfToken, viewerId, deviceUuid);
|
||||
mediaService = MediaService.getInstance(deviceUuid, csrfToken, viewerId);
|
||||
contentResolver = application.getContentResolver();
|
||||
recordingsDir = DirectoryUtils.getOutputMediaDirectory(application, "Recordings");
|
||||
this.application = application;
|
||||
@ -138,6 +140,10 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
return items;
|
||||
}
|
||||
|
||||
public long getViewerId() {
|
||||
return viewerId;
|
||||
}
|
||||
|
||||
public void setItems(final List<DirectItem> items) {
|
||||
this.items.postValue(items);
|
||||
}
|
||||
@ -219,13 +225,53 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
"none"
|
||||
);
|
||||
if (index < 0) {
|
||||
temp.add(reaction);
|
||||
temp.add(0, reaction);
|
||||
} else if (shouldReplaceIfAlreadyReacted) {
|
||||
temp.set(index, reaction);
|
||||
temp.add(0, reaction);
|
||||
temp.remove(index);
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
private void removeReaction(final DirectItem item) {
|
||||
try {
|
||||
final DirectItem itemClone = (DirectItem) item.clone();
|
||||
final DirectItemReactions reactions = itemClone.getReactions();
|
||||
final DirectItemReactions reactionsClone = (DirectItemReactions) reactions.clone();
|
||||
final List<DirectItemEmojiReaction> likes = reactionsClone.getLikes();
|
||||
if (likes != null) {
|
||||
final List<DirectItemEmojiReaction> updatedLikes = likes.stream()
|
||||
.filter(like -> like.getSenderId() != viewerId)
|
||||
.collect(Collectors.toList());
|
||||
reactionsClone.setLikes(updatedLikes);
|
||||
}
|
||||
final List<DirectItemEmojiReaction> emojis = reactionsClone.getEmojis();
|
||||
if (emojis != null) {
|
||||
final List<DirectItemEmojiReaction> updatedEmojis = emojis.stream()
|
||||
.filter(emoji -> emoji.getSenderId() != viewerId)
|
||||
.collect(Collectors.toList());
|
||||
reactionsClone.setEmojis(updatedEmojis);
|
||||
}
|
||||
itemClone.setReactions(reactionsClone);
|
||||
List<DirectItem> list = this.items.getValue();
|
||||
list = list == null ? new LinkedList<>() : new LinkedList<>(list);
|
||||
int index = -1;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
final DirectItem directItem = list.get(i);
|
||||
if (directItem.getItemId().equals(item.getItemId())) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index >= 0) {
|
||||
list.set(index, itemClone);
|
||||
}
|
||||
this.items.postValue(list);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "removeReaction: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateItemSent(final String clientContext, final long timestamp) {
|
||||
if (clientContext == null) return;
|
||||
List<DirectItem> list = this.items.getValue();
|
||||
@ -259,6 +305,10 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
return users;
|
||||
}
|
||||
|
||||
public LiveData<List<User>> getLeftUsers() {
|
||||
return leftUsers;
|
||||
}
|
||||
|
||||
public void fetchChats() {
|
||||
final Boolean isFetching = fetching.getValue();
|
||||
if ((isFetching != null && isFetching) || !hasOlder) return;
|
||||
@ -319,9 +369,8 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
threadTitle.postValue(thread.getThreadTitle());
|
||||
cursor = thread.getOldestCursor();
|
||||
hasOlder = thread.hasOlder();
|
||||
if (users.getValue() == null || users.getValue().isEmpty()) {
|
||||
users.postValue(thread.getUsers());
|
||||
}
|
||||
leftUsers.postValue(thread.getLeftUsers());
|
||||
fetching.postValue(false);
|
||||
}
|
||||
|
||||
@ -637,6 +686,33 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
}
|
||||
final Call<DirectThreadBroadcastResponse> request = service.broadcastReaction(
|
||||
clientContext, threadIdOrUserIds, item.getItemId(), emojiUnicode, false);
|
||||
handleBroadcastReactionRequest(data, item, request);
|
||||
return data;
|
||||
}
|
||||
|
||||
public LiveData<Resource<DirectItem>> sendDeleteReaction(final String itemId) {
|
||||
final MutableLiveData<Resource<DirectItem>> data = new MutableLiveData<>();
|
||||
final DirectItem item = getItem(itemId);
|
||||
if (item == null) {
|
||||
data.postValue(Resource.error("Invalid item", null));
|
||||
return data;
|
||||
}
|
||||
final DirectItemReactions reactions = item.getReactions();
|
||||
if (reactions == null) {
|
||||
// already removed?
|
||||
data.postValue(Resource.success(item));
|
||||
return data;
|
||||
}
|
||||
removeReaction(item);
|
||||
final String clientContext = UUID.randomUUID().toString();
|
||||
final Call<DirectThreadBroadcastResponse> request = service.broadcastReaction(clientContext, threadIdOrUserIds, item.getItemId(), null, true);
|
||||
handleBroadcastReactionRequest(data, item, request);
|
||||
return data;
|
||||
}
|
||||
|
||||
private void handleBroadcastReactionRequest(final MutableLiveData<Resource<DirectItem>> data,
|
||||
final DirectItem item,
|
||||
@NonNull final Call<DirectThreadBroadcastResponse> request) {
|
||||
request.enqueue(new Callback<DirectThreadBroadcastResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<DirectThreadBroadcastResponse> call,
|
||||
@ -662,13 +738,51 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
Log.e(TAG, "enqueueRequest: onFailure: ", t);
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DirectItem getItem(final String itemId) {
|
||||
if (itemId == null) return null;
|
||||
final List<DirectItem> items = this.items.getValue();
|
||||
if (items == null) return null;
|
||||
return items.stream()
|
||||
.filter(directItem -> directItem.getItemId().equals(itemId))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public User getCurrentUser() {
|
||||
return currentUser;
|
||||
}
|
||||
|
||||
public void setCurrentUser(final User currentUser) {
|
||||
this.currentUser = currentUser;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public User getUser(final long userId) {
|
||||
final LiveData<List<User>> users = getUsers();
|
||||
User match = null;
|
||||
if (users != null && users.getValue() != null) {
|
||||
final List<User> userList = users.getValue();
|
||||
match = userList.stream()
|
||||
.filter(user -> user.getPk() == userId)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
if (match == null) {
|
||||
final LiveData<List<User>> leftUsers = getLeftUsers();
|
||||
if (leftUsers != null && leftUsers.getValue() != null) {
|
||||
final List<User> userList = leftUsers.getValue();
|
||||
match = userList.stream()
|
||||
.filter(user -> user.getPk() == userId)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
private void enqueueRequest(@NonNull final Call<DirectThreadBroadcastResponse> request,
|
||||
@NonNull final MutableLiveData<Resource<DirectItem>> data,
|
||||
@NonNull final DirectItem directItem) {
|
||||
|
@ -173,28 +173,34 @@
|
||||
|
||||
</awais.instagrabber.customviews.ChatMessageLayout>
|
||||
|
||||
<androidx.emoji.widget.EmojiAppCompatTextView
|
||||
android:id="@+id/emojis"
|
||||
<FrameLayout
|
||||
android:id="@+id/reactions_wrapper"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:background="@drawable/bg_rounded_corner"
|
||||
android:elevation="1dp"
|
||||
android:maxLines="1"
|
||||
android:padding="4dp"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="18sp"
|
||||
android:paddingBottom="2dp"
|
||||
android:translationY="@dimen/dm_reaction_translation_y_type_1"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="@id/chat_message_layout"
|
||||
app:layout_constraintTop_toBottomOf="parent"
|
||||
app:layout_constraintWidth_max="wrap"
|
||||
tools:text="😀"
|
||||
tools:visibility="visible" />
|
||||
tools:visibility="visible">
|
||||
|
||||
<awais.instagrabber.customviews.ReactionEmojiTextView
|
||||
android:id="@+id/emojis"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_rounded_corner"
|
||||
android:elevation="1dp"
|
||||
android:ellipsize="end"
|
||||
android:padding="4dp"
|
||||
android:singleLine="true"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="18sp"
|
||||
tools:text="😀😀😀😀😀😀😀" />
|
||||
</FrameLayout>
|
||||
|
||||
<!--<FrameLayout-->
|
||||
<!-- android:id="@+id/reactions"-->
|
||||
|
@ -78,12 +78,26 @@
|
||||
android:duplicateParentState="true"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/profile_pic"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/secondary_image"
|
||||
app:layout_constraintStart_toEndOf="@id/info"
|
||||
app:layout_constraintTop_toTopOf="@id/profile_pic"
|
||||
app:srcCompat="@drawable/ic_circle_check"
|
||||
app:tint="@color/ic_circle_check_tint"
|
||||
tools:visibility="visible" />
|
||||
tools:visibility="gone" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/secondary_image"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:duplicateParentState="true"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/profile_pic"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/select"
|
||||
app:layout_constraintTop_toTopOf="@id/profile_pic"
|
||||
tools:srcCompat="@mipmap/ic_launcher"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<include
|
||||
layout="@layout/item_pref_divider"
|
||||
|
@ -32,7 +32,7 @@
|
||||
<dimen name="dm_message_item_margin">80dp</dimen>
|
||||
<dimen name="dm_message_item_avatar_size">48dp</dimen>
|
||||
<dimen name="dm_message_info_padding_small">4dp</dimen>
|
||||
<dimen name="dm_reaction_adjust_margin">22dp</dimen>
|
||||
<dimen name="dm_reaction_adjust_margin">24dp</dimen>
|
||||
<dimen name="dm_reaction_translation_y_type_1">6dp</dimen>
|
||||
<dimen name="dm_reaction_translation_y_type_2">-12dp</dimen>
|
||||
|
||||
|
@ -397,4 +397,5 @@
|
||||
<string name="edit_unsuccessful">Edit was unsuccessful</string>
|
||||
<string name="message">Message</string>
|
||||
<string name="reply">Reply</string>
|
||||
<string name="tap_to_remove">Tap to remove</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user