From 6b97197c8097ab81244f46bd7870892a8265fa3f Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 24 Nov 2022 14:43:20 +0100 Subject: [PATCH] react to given reactions inside message Signed-off-by: Marcel Hibbe --- .../messages/CommonMessageInterface.kt | 3 +- .../IncomingLinkPreviewMessageViewHolder.kt | 17 +- .../IncomingLocationMessageViewHolder.kt | 17 +- .../messages/IncomingPollMessageViewHolder.kt | 17 +- .../IncomingPreviewMessageViewHolder.java | 8 +- .../IncomingVoiceMessageViewHolder.kt | 17 +- .../MagicIncomingTextMessageViewHolder.kt | 17 +- .../MagicOutcomingTextMessageViewHolder.kt | 25 +- .../MagicPreviewMessageViewHolder.java | 375 ------------------ .../OutcomingLinkPreviewMessageViewHolder.kt | 17 +- .../OutcomingLocationMessageViewHolder.kt | 17 +- .../OutcomingPollMessageViewHolder.kt | 17 +- .../OutcomingPreviewMessageViewHolder.java | 7 +- .../OutcomingVoiceMessageViewHolder.kt | 17 +- .../messages/PreviewMessageViewHolder.kt | 341 ++++++++++++++++ .../talk/adapters/messages/Reaction.kt | 32 +- .../messages/TalkMessagesListAdapter.java | 6 +- .../talk/controllers/ChatController.kt | 96 ++++- .../talk/dagger/modules/RepositoryModule.kt | 9 + .../talk/models/domain/ReactionAddedModel.kt | 29 ++ .../models/domain/ReactionDeletedModel.kt | 29 ++ .../reactions/ReactionsRepository.kt | 42 ++ .../reactions/ReactionsRepositoryImpl.kt | 102 +++++ .../talk/ui/dialog/MessageActionsDialog.kt | 135 +++---- .../nextcloud/talk/utils/FileViewerUtils.kt | 10 +- .../res/layout/reactions_inside_message.xml | 28 +- 26 files changed, 849 insertions(+), 581 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java create mode 100644 app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/domain/ReactionAddedModel.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/domain/ReactionDeletedModel.kt create mode 100644 app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepository.kt create mode 100644 app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepositoryImpl.kt diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/CommonMessageInterface.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/CommonMessageInterface.kt index a40509b30..34116479b 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/CommonMessageInterface.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/CommonMessageInterface.kt @@ -3,6 +3,7 @@ package com.nextcloud.talk.adapters.messages import com.nextcloud.talk.models.json.chat.ChatMessage interface CommonMessageInterface { - fun onClickReactions(chatMessage: ChatMessage) + fun onLongClickReactions(chatMessage: ChatMessage) + fun onClickReaction(chatMessage: ChatMessage, emoji: String) fun onOpenMessageActionsDialog(chatMessage: ChatMessage) } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt index 14f96025c..26c510020 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt @@ -98,18 +98,21 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) : M Reaction().showReactions( message, + ::clickOnReaction, + ::longClickOnReaction, binding.reactions, binding.messageTime.context, false, viewThemeUtils ) - binding.reactions.reactionsEmojiWrapper.setOnClickListener { - commonMessageInterface.onClickReactions(message) - } - binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? -> - commonMessageInterface.onOpenMessageActionsDialog(message) - true - } + } + + private fun longClickOnReaction(chatMessage: ChatMessage) { + commonMessageInterface.onLongClickReactions(chatMessage) + } + + private fun clickOnReaction(chatMessage: ChatMessage, emoji: String) { + commonMessageInterface.onClickReaction(chatMessage, emoji) } private fun setAvatarAndAuthorOnMessageItem(message: ChatMessage) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt index 64b489216..8da197c1d 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt @@ -103,18 +103,21 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) : Mess Reaction().showReactions( message, + ::clickOnReaction, + ::longClickOnReaction, binding.reactions, binding.messageText.context, false, viewThemeUtils ) - binding.reactions.reactionsEmojiWrapper.setOnClickListener { - commonMessageInterface.onClickReactions(message) - } - binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? -> - commonMessageInterface.onOpenMessageActionsDialog(message) - true - } + } + + private fun longClickOnReaction(chatMessage: ChatMessage) { + commonMessageInterface.onLongClickReactions(chatMessage) + } + + private fun clickOnReaction(chatMessage: ChatMessage, emoji: String) { + commonMessageInterface.onClickReaction(chatMessage, emoji) } private fun setAvatarAndAuthorOnMessageItem(message: ChatMessage) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index d3e7dcd78..5466c5a8e 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -89,18 +89,21 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH Reaction().showReactions( message, + ::clickOnReaction, + ::longClickOnReaction, binding.reactions, binding.messageTime.context, false, viewThemeUtils ) - binding.reactions.reactionsEmojiWrapper.setOnClickListener { - commonMessageInterface.onClickReactions(message) - } - binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? -> - commonMessageInterface.onOpenMessageActionsDialog(message) - true - } + } + + private fun longClickOnReaction(chatMessage: ChatMessage) { + commonMessageInterface.onLongClickReactions(chatMessage) + } + + private fun clickOnReaction(chatMessage: ChatMessage, emoji: String) { + commonMessageInterface.onClickReaction(chatMessage, emoji) } private fun setPollPreview(message: ChatMessage) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPreviewMessageViewHolder.java index 687fdc750..a60f089f1 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPreviewMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPreviewMessageViewHolder.java @@ -35,7 +35,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage; import androidx.core.content.ContextCompat; import androidx.emoji.widget.EmojiTextView; -public class IncomingPreviewMessageViewHolder extends MagicPreviewMessageViewHolder { +public class IncomingPreviewMessageViewHolder extends PreviewMessageViewHolder { private final ItemCustomIncomingPreviewMessageBinding binding; public IncomingPreviewMessageViewHolder(View itemView, Object payload) { @@ -63,11 +63,6 @@ public class IncomingPreviewMessageViewHolder extends MagicPreviewMessageViewHol return binding.progressBar; } - @Override - public SimpleDraweeView getImage() { - return binding.image; - } - @Override public View getPreviewContainer() { return binding.previewContainer; @@ -95,4 +90,5 @@ public class IncomingPreviewMessageViewHolder extends MagicPreviewMessageViewHol @Override public ReactionsInsideMessageBinding getReactionsBinding(){ return binding.reactions; } + } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt index 10f6284a9..2cc1419aa 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt @@ -147,18 +147,21 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : Message Reaction().showReactions( message, + ::clickOnReaction, + ::longClickOnReaction, binding.reactions, binding.messageTime.context, false, viewThemeUtils ) - binding.reactions.reactionsEmojiWrapper.setOnClickListener { - commonMessageInterface.onClickReactions(message) - } - binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? -> - commonMessageInterface.onOpenMessageActionsDialog(message) - true - } + } + + private fun longClickOnReaction(chatMessage: ChatMessage) { + commonMessageInterface.onLongClickReactions(chatMessage) + } + + private fun clickOnReaction(chatMessage: ChatMessage, emoji: String) { + commonMessageInterface.onClickReaction(chatMessage, emoji) } private fun updateDownloadState(message: ChatMessage) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt index c8f2a7a2e..e578cbb30 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt @@ -119,18 +119,21 @@ class MagicIncomingTextMessageViewHolder(itemView: View, payload: Any) : Message Reaction().showReactions( message, + ::clickOnReaction, + ::longClickOnReaction, binding.reactions, binding.messageText.context, false, viewThemeUtils ) - binding.reactions.reactionsEmojiWrapper.setOnClickListener { - commonMessageInterface.onClickReactions(message) - } - binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? -> - commonMessageInterface.onOpenMessageActionsDialog(message) - true - } + } + + private fun longClickOnReaction(chatMessage: ChatMessage) { + commonMessageInterface.onLongClickReactions(chatMessage) + } + + private fun clickOnReaction(chatMessage: ChatMessage, emoji: String) { + commonMessageInterface.onClickReaction(chatMessage, emoji) } private fun processAuthor(message: ChatMessage) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.kt index 66a0d4f3f..d35e9a9e0 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.kt @@ -121,14 +121,23 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage itemView.setTag(MessageSwipeCallback.REPLYABLE_VIEW_TAG, message.replyable) - Reaction().showReactions(message, binding.reactions, context, true, viewThemeUtils) - binding.reactions.reactionsEmojiWrapper.setOnClickListener { - commonMessageInterface.onClickReactions(message) - } - binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? -> - commonMessageInterface.onOpenMessageActionsDialog(message) - true - } + Reaction().showReactions( + message, + ::clickOnReaction, + ::longClickOnReaction, + binding.reactions, + context, + true, + viewThemeUtils + ) + } + + private fun longClickOnReaction(chatMessage: ChatMessage) { + commonMessageInterface.onLongClickReactions(chatMessage) + } + + private fun clickOnReaction(chatMessage: ChatMessage, emoji: String) { + commonMessageInterface.onClickReaction(chatMessage, emoji) } private fun processParentMessage(message: ChatMessage) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java deleted file mode 100644 index ca7394341..000000000 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * @author Marcel Hibbe - * @author Andy Scherzinger - * @author Tim Krüger - * Copyright (C) 2021 Tim Krüger - * Copyright (C) 2021 Andy Scherzinger - * Copyright (C) 2021 Marcel Hibbe - * Copyright (C) 2017-2018 Mario Danic - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.nextcloud.talk.adapters.messages; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; -import android.net.Uri; -import android.os.Handler; -import android.util.Base64; -import android.util.Log; -import android.view.Gravity; -import android.view.View; -import android.widget.PopupMenu; -import android.widget.ProgressBar; - -import com.facebook.drawee.view.SimpleDraweeView; -import com.google.android.material.card.MaterialCardView; -import com.nextcloud.talk.R; -import com.nextcloud.talk.application.NextcloudTalkApplication; -import com.nextcloud.talk.components.filebrowser.models.BrowserFile; -import com.nextcloud.talk.components.filebrowser.models.DavResponse; -import com.nextcloud.talk.components.filebrowser.webdav.ReadFilesystemOperation; -import com.nextcloud.talk.data.user.model.User; -import com.nextcloud.talk.databinding.ReactionsInsideMessageBinding; -import com.nextcloud.talk.models.json.chat.ChatMessage; -import com.nextcloud.talk.ui.theme.ViewThemeUtils; -import com.nextcloud.talk.utils.DisplayUtils; -import com.nextcloud.talk.utils.DrawableUtils; -import com.nextcloud.talk.utils.FileViewerUtils; -import com.stfalcon.chatkit.messages.MessageHolders; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.Callable; - -import javax.inject.Inject; - -import androidx.appcompat.view.ContextThemeWrapper; -import androidx.core.content.ContextCompat; -import androidx.emoji.widget.EmojiTextView; -import autodagger.AutoInjector; -import io.reactivex.Single; -import io.reactivex.SingleObserver; -import io.reactivex.annotations.NonNull; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; -import okhttp3.OkHttpClient; - -import static com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback.REPLYABLE_VIEW_TAG; - -@AutoInjector(NextcloudTalkApplication.class) -public abstract class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageMessageViewHolder { - - private static final String TAG = "PreviewMsgViewHolder"; - public static final String KEY_CONTACT_NAME = "contact-name"; - public static final String KEY_CONTACT_PHOTO = "contact-photo"; - public static final String KEY_MIMETYPE = "mimetype"; - public static final String KEY_ID = "id"; - public static final String KEY_PATH = "path"; - public static final String ACTOR_TYPE_BOTS = "bots"; - public static final String ACTOR_ID_CHANGELOG = "changelog"; - public static final String KEY_NAME = "name"; - - @Inject - Context context; - - @Inject - ViewThemeUtils viewThemeUtils; - - @Inject - OkHttpClient okHttpClient; - - ProgressBar progressBar; - - ReactionsInsideMessageBinding reactionsBinding; - - FileViewerUtils fileViewerUtils; - - View clickView; - - CommonMessageInterface commonMessageInterface; - PreviewMessageInterface previewMessageInterface; - - public MagicPreviewMessageViewHolder(View itemView, Object payload) { - super(itemView, payload); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - } - - @SuppressLint("SetTextI18n") - @Override - public void onBind(ChatMessage message) { - super.onBind(message); - if (userAvatar != null) { - if (message.isGrouped() || message.isOneToOneConversation()) { - if (message.isOneToOneConversation()) { - userAvatar.setVisibility(View.GONE); - } else { - userAvatar.setVisibility(View.INVISIBLE); - } - } else { - userAvatar.setVisibility(View.VISIBLE); - userAvatar.setOnClickListener(v -> { - if (payload instanceof MessagePayload) { - ((MessagePayload) payload).getProfileBottomSheet().showFor(message.getActorId(), - v.getContext()); - } - }); - - if (ACTOR_TYPE_BOTS.equals(message.getActorType()) && ACTOR_ID_CHANGELOG.equals(message.getActorId())) { - if (context != null) { - Drawable[] layers = new Drawable[2]; - layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background); - layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground); - LayerDrawable layerDrawable = new LayerDrawable(layers); - - userAvatar.getHierarchy().setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable)); - } - } - } - } - - progressBar = getProgressBar(); - viewThemeUtils.platform.colorCircularProgressBar(getProgressBar()); - image = getImage(); - clickView = getImage(); - getMessageText().setVisibility(View.VISIBLE); - - if (message.getCalculateMessageType() == ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE) { - - fileViewerUtils = new FileViewerUtils(context, message.getActiveUser()); - - String fileName = message.getSelectedIndividualHashMap().get(KEY_NAME); - getMessageText().setText(fileName); - if (message.getSelectedIndividualHashMap().containsKey(KEY_CONTACT_NAME)) { - getPreviewContainer().setVisibility(View.GONE); - getPreviewContactName().setText(message.getSelectedIndividualHashMap().get(KEY_CONTACT_NAME)); - progressBar = getPreviewContactProgressBar(); - getMessageText().setVisibility(View.INVISIBLE); - clickView = getPreviewContactContainer(); - viewThemeUtils.talk.colorContactChatItemBackground(getPreviewContactContainer()); - viewThemeUtils.talk.colorContactChatItemName(getPreviewContactName()); - viewThemeUtils.platform.colorCircularProgressBarOnPrimaryContainer(getPreviewContactProgressBar()); - } else { - getPreviewContainer().setVisibility(View.VISIBLE); - getPreviewContactContainer().setVisibility(View.GONE); - } - - if (message.getSelectedIndividualHashMap().containsKey(KEY_CONTACT_PHOTO)) { - image = getPreviewContactPhoto(); - Drawable drawable = getDrawableFromContactDetails( - context, - message.getSelectedIndividualHashMap().get(KEY_CONTACT_PHOTO)); - image.getHierarchy().setPlaceholderImage(drawable); - } else if (message.getSelectedIndividualHashMap().containsKey(KEY_MIMETYPE)) { - String mimetype = message.getSelectedIndividualHashMap().get(KEY_MIMETYPE); - int drawableResourceId = DrawableUtils.INSTANCE.getDrawableResourceIdForMimeType(mimetype); - Drawable drawable = ContextCompat.getDrawable(context, drawableResourceId); - - if (drawable != null && - (drawableResourceId == R.drawable.ic_mimetype_folder || - drawableResourceId == R.drawable.ic_mimetype_package_x_generic)) { - drawable.setColorFilter(viewThemeUtils.getScheme(image.getContext()).getPrimary(), - PorterDuff.Mode.SRC_ATOP); - } - - image.getHierarchy().setPlaceholderImage(drawable); - } else { - fetchFileInformation("/" + message.getSelectedIndividualHashMap().get(KEY_PATH), - message.getActiveUser()); - } - - if (message.getActiveUser() != null && - message.getActiveUser().getUsername() != null && - message.getActiveUser().getBaseUrl() != null) { - clickView.setOnClickListener(v -> - fileViewerUtils.openFile( - message, - new FileViewerUtils.ProgressUi(progressBar, getMessageText(), image) - ) - ); - - clickView.setOnLongClickListener(l -> { - onMessageViewLongClick(message); - return true; - }); - } else { - Log.e(TAG, "failed to set click listener because activeUser, username or baseUrl were null"); - } - - fileViewerUtils.resumeToUpdateViewsByProgress( - Objects.requireNonNull(message.getSelectedIndividualHashMap().get(MagicPreviewMessageViewHolder.KEY_NAME)), - Objects.requireNonNull(message.getSelectedIndividualHashMap().get(MagicPreviewMessageViewHolder.KEY_ID)), - message.getSelectedIndividualHashMap().get(MagicPreviewMessageViewHolder.KEY_MIMETYPE), - new FileViewerUtils.ProgressUi(progressBar, getMessageText(), image)); - - } else if (message.getCalculateMessageType() == ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE) { - getMessageText().setText("GIPHY"); - DisplayUtils.setClickableString("GIPHY", "https://giphy.com", getMessageText()); - } else if (message.getCalculateMessageType() == ChatMessage.MessageType.SINGLE_LINK_TENOR_MESSAGE) { - getMessageText().setText("Tenor"); - DisplayUtils.setClickableString("Tenor", "https://tenor.com", getMessageText()); - } else { - if (message.getMessageType().equals(ChatMessage.MessageType.SINGLE_LINK_IMAGE_MESSAGE)) { - clickView.setOnClickListener(v -> { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(message.getImageUrl())); - browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(browserIntent); - }); - } else { - clickView.setOnClickListener(null); - } - getMessageText().setText(""); - } - - itemView.setTag(REPLYABLE_VIEW_TAG, message.getReplyable()); - - reactionsBinding = getReactionsBinding(); - new Reaction().showReactions(message, - reactionsBinding, - getMessageText().getContext(), - true, - viewThemeUtils); - reactionsBinding.reactionsEmojiWrapper.setOnClickListener(l -> { - commonMessageInterface.onClickReactions(message); - }); - reactionsBinding.reactionsEmojiWrapper.setOnLongClickListener(l -> { - commonMessageInterface.onOpenMessageActionsDialog(message); - return true; - }); - } - - private Drawable getDrawableFromContactDetails(Context context, String base64) { - Drawable drawable = null; - if (!base64.equals("")) { - ByteArrayInputStream inputStream = new ByteArrayInputStream( - Base64.decode(base64.getBytes(), Base64.DEFAULT)); - drawable = Drawable.createFromResourceStream(context.getResources(), - null, inputStream, null, null); - try { - inputStream.close(); - } catch (IOException e) { - int drawableResourceId = DrawableUtils.INSTANCE.getDrawableResourceIdForMimeType("text/vcard"); - drawable = ContextCompat.getDrawable(context, drawableResourceId); - } - } - - return drawable; - } - - private void onMessageViewLongClick(ChatMessage message) { - if (fileViewerUtils.isSupportedForInternalViewer(message.getSelectedIndividualHashMap().get(KEY_MIMETYPE))) { - previewMessageInterface.onPreviewMessageLongClick(message); - return; - } - - Context viewContext; - - if (itemView != null && itemView.getContext() != null) { - viewContext = itemView.getContext(); - } else { - viewContext = this.context; - } - - PopupMenu popupMenu = new PopupMenu( - new ContextThemeWrapper(viewContext, R.style.appActionBarPopupMenu), - itemView, - Gravity.START - ); - popupMenu.inflate(R.menu.chat_preview_message_menu); - - popupMenu.setOnMenuItemClickListener(item -> { - if (item.getItemId()== R.id.openInFiles){ - String keyID = message.getSelectedIndividualHashMap().get(KEY_ID); - String link = message.getSelectedIndividualHashMap().get("link"); - fileViewerUtils.openFileInFilesApp(link, keyID); - } - return true; - }); - - popupMenu.show(); - } - - private void fetchFileInformation(String url, User activeUser) { - Single.fromCallable(new Callable() { - @Override - public ReadFilesystemOperation call() { - return new ReadFilesystemOperation(okHttpClient, activeUser, url, 0); - } - }).observeOn(Schedulers.io()) - .subscribe(new SingleObserver() { - @Override - public void onSubscribe(@NonNull Disposable d) { - // unused atm - } - - @Override - public void onSuccess(@NonNull ReadFilesystemOperation readFilesystemOperation) { - DavResponse davResponse = readFilesystemOperation.readRemotePath(); - if (davResponse.data != null) { - List browserFileList = (List) davResponse.data; - if (!browserFileList.isEmpty()) { - new Handler(context.getMainLooper()).post(() -> { - int resourceId = DrawableUtils - .INSTANCE - .getDrawableResourceIdForMimeType(browserFileList.get(0).getMimeType()); - Drawable drawable = ContextCompat.getDrawable(context, resourceId); - image.getHierarchy().setPlaceholderImage(drawable); - }); - } - } - } - - @Override - public void onError(@NonNull Throwable e) { - Log.e(TAG, "Error reading file information", e); - } - }); - } - - public void assignCommonMessageInterface(CommonMessageInterface commonMessageInterface) { - this.commonMessageInterface = commonMessageInterface; - } - - public void assignPreviewMessageInterface(PreviewMessageInterface previewMessageInterface) { - this.previewMessageInterface = previewMessageInterface; - } - - public abstract EmojiTextView getMessageText(); - - public abstract ProgressBar getProgressBar(); - - public abstract SimpleDraweeView getImage(); - - public abstract View getPreviewContainer(); - - public abstract MaterialCardView getPreviewContactContainer(); - - public abstract SimpleDraweeView getPreviewContactPhoto(); - - public abstract EmojiTextView getPreviewContactName(); - - public abstract ProgressBar getPreviewContactProgressBar(); - - public abstract ReactionsInsideMessageBinding getReactionsBinding(); -} diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt index 7d501e646..6d5197b10 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt @@ -116,18 +116,21 @@ class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) : Reaction().showReactions( message, + ::clickOnReaction, + ::longClickOnReaction, binding.reactions, binding.messageTime.context, true, viewThemeUtils ) - binding.reactions.reactionsEmojiWrapper.setOnClickListener { - commonMessageInterface.onClickReactions(message) - } - binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? -> - commonMessageInterface.onOpenMessageActionsDialog(message) - true - } + } + + private fun longClickOnReaction(chatMessage: ChatMessage) { + commonMessageInterface.onLongClickReactions(chatMessage) + } + + private fun clickOnReaction(chatMessage: ChatMessage, emoji: String) { + commonMessageInterface.onClickReaction(chatMessage, emoji) } private fun setParentMessageDataOnMessageItem(message: ChatMessage) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt index 148c45a4d..3802e54dd 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt @@ -122,18 +122,21 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders Reaction().showReactions( message, + ::clickOnReaction, + ::longClickOnReaction, binding.reactions, binding.messageText.context, true, viewThemeUtils ) - binding.reactions.reactionsEmojiWrapper.setOnClickListener { - commonMessageInterface.onClickReactions(message) - } - binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? -> - commonMessageInterface.onOpenMessageActionsDialog(message) - true - } + } + + private fun longClickOnReaction(chatMessage: ChatMessage) { + commonMessageInterface.onLongClickReactions(chatMessage) + } + + private fun clickOnReaction(chatMessage: ChatMessage, emoji: String) { + commonMessageInterface.onClickReaction(chatMessage, emoji) } @SuppressLint("SetJavaScriptEnabled", "ClickableViewAccessibility") diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt index 0fb552edd..4788ba2f5 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt @@ -108,18 +108,21 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag Reaction().showReactions( message, + ::clickOnReaction, + ::longClickOnReaction, binding.reactions, binding.messageTime.context, true, viewThemeUtils ) - binding.reactions.reactionsEmojiWrapper.setOnClickListener { - commonMessageInterface.onClickReactions(message) - } - binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? -> - commonMessageInterface.onOpenMessageActionsDialog(message) - true - } + } + + private fun longClickOnReaction(chatMessage: ChatMessage) { + commonMessageInterface.onLongClickReactions(chatMessage) + } + + private fun clickOnReaction(chatMessage: ChatMessage, emoji: String) { + commonMessageInterface.onClickReaction(chatMessage, emoji) } private fun setPollPreview(message: ChatMessage) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPreviewMessageViewHolder.java index 9426cfab0..f6860c9da 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPreviewMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPreviewMessageViewHolder.java @@ -35,7 +35,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage; import androidx.core.content.ContextCompat; import androidx.emoji.widget.EmojiTextView; -public class OutcomingPreviewMessageViewHolder extends MagicPreviewMessageViewHolder { +public class OutcomingPreviewMessageViewHolder extends PreviewMessageViewHolder { private final ItemCustomOutcomingPreviewMessageBinding binding; @@ -64,11 +64,6 @@ public class OutcomingPreviewMessageViewHolder extends MagicPreviewMessageViewHo return binding.progressBar; } - @Override - public SimpleDraweeView getImage() { - return binding.image; - } - @Override public View getPreviewContainer() { return binding.previewContainer; diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt index 845aa20c5..76ece745e 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt @@ -140,18 +140,21 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders Reaction().showReactions( message, + ::clickOnReaction, + ::longClickOnReaction, binding.reactions, binding.messageTime.context, true, viewThemeUtils ) - binding.reactions.reactionsEmojiWrapper.setOnClickListener { - commonMessageInterface.onClickReactions(message) - } - binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? -> - commonMessageInterface.onOpenMessageActionsDialog(message) - true - } + } + + private fun longClickOnReaction(chatMessage: ChatMessage) { + commonMessageInterface.onLongClickReactions(chatMessage) + } + + private fun clickOnReaction(chatMessage: ChatMessage, emoji: String) { + commonMessageInterface.onClickReaction(chatMessage, emoji) } private fun handleResetVoiceMessageState(message: ChatMessage) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt new file mode 100644 index 000000000..c66e7788b --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt @@ -0,0 +1,341 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * @author Marcel Hibbe + * @author Andy Scherzinger + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * Copyright (C) 2021 Andy Scherzinger + * Copyright (C) 2021 Marcel Hibbe + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.adapters.messages + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.graphics.PorterDuff +import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable +import android.net.Uri +import android.os.Handler +import android.util.Base64 +import android.util.Log +import android.view.Gravity +import android.view.MenuItem +import android.view.View +import android.widget.PopupMenu +import android.widget.ProgressBar +import androidx.appcompat.view.ContextThemeWrapper +import androidx.core.content.ContextCompat +import androidx.emoji.widget.EmojiTextView +import autodagger.AutoInjector +import com.facebook.drawee.view.SimpleDraweeView +import com.google.android.material.card.MaterialCardView +import com.nextcloud.talk.R +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication +import com.nextcloud.talk.components.filebrowser.models.BrowserFile +import com.nextcloud.talk.components.filebrowser.webdav.ReadFilesystemOperation +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.databinding.ReactionsInsideMessageBinding +import com.nextcloud.talk.models.json.chat.ChatMessage +import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback +import com.nextcloud.talk.ui.theme.ViewThemeUtils +import com.nextcloud.talk.utils.DisplayUtils +import com.nextcloud.talk.utils.DrawableUtils.getDrawableResourceIdForMimeType +import com.nextcloud.talk.utils.FileViewerUtils +import com.nextcloud.talk.utils.FileViewerUtils.ProgressUi +import com.stfalcon.chatkit.messages.MessageHolders.IncomingImageMessageViewHolder +import io.reactivex.Single +import io.reactivex.SingleObserver +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import okhttp3.OkHttpClient +import java.io.ByteArrayInputStream +import java.io.IOException +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) : + IncomingImageMessageViewHolder(itemView, payload) { + @JvmField + @Inject + var context: Context? = null + + @JvmField + @Inject + var viewThemeUtils: ViewThemeUtils? = null + + @JvmField + @Inject + var okHttpClient: OkHttpClient? = null + open var progressBar: ProgressBar? = null + open var reactionsBinding: ReactionsInsideMessageBinding? = null + var fileViewerUtils: FileViewerUtils? = null + var clickView: View? = null + + lateinit var commonMessageInterface: CommonMessageInterface + var previewMessageInterface: PreviewMessageInterface? = null + + init { + sharedApplication!!.componentApplication.inject(this) + } + + @SuppressLint("SetTextI18n") + override fun onBind(message: ChatMessage) { + super.onBind(message) + if (userAvatar != null) { + if (message.isGrouped || message.isOneToOneConversation) { + if (message.isOneToOneConversation) { + userAvatar.visibility = View.GONE + } else { + userAvatar.visibility = View.INVISIBLE + } + } else { + userAvatar.visibility = View.VISIBLE + userAvatar.setOnClickListener { v: View -> + if (payload is MessagePayload) { + (payload as MessagePayload).profileBottomSheet.showFor( + message.actorId!!, + v.context + ) + } + } + if (ACTOR_TYPE_BOTS == message.actorType && ACTOR_ID_CHANGELOG == message.actorId) { + if (context != null) { + val layers = arrayOfNulls(2) + layers[0] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_background) + layers[1] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_foreground) + val layerDrawable = LayerDrawable(layers) + userAvatar.hierarchy.setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable)) + } + } + } + } + viewThemeUtils!!.platform.colorCircularProgressBar(progressBar!!) + clickView = image + messageText.visibility = View.VISIBLE + if (message.getCalculateMessageType() === ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE) { + fileViewerUtils = FileViewerUtils(context!!, message.activeUser!!) + val fileName = message.selectedIndividualHashMap!![KEY_NAME] + messageText.text = fileName + if (message.selectedIndividualHashMap!!.containsKey(KEY_CONTACT_NAME)) { + previewContainer.visibility = View.GONE + previewContactName.text = message.selectedIndividualHashMap!![KEY_CONTACT_NAME] + progressBar = previewContactProgressBar + messageText.visibility = View.INVISIBLE + clickView = previewContactContainer + viewThemeUtils!!.talk.colorContactChatItemBackground(previewContactContainer) + viewThemeUtils!!.talk.colorContactChatItemName(previewContactName) + viewThemeUtils!!.platform.colorCircularProgressBarOnPrimaryContainer(previewContactProgressBar!!) + } else { + previewContainer.visibility = View.VISIBLE + previewContactContainer.visibility = View.GONE + } + if (message.selectedIndividualHashMap!!.containsKey(KEY_CONTACT_PHOTO)) { + image = previewContactPhoto + val drawable = getDrawableFromContactDetails( + context, + message.selectedIndividualHashMap!![KEY_CONTACT_PHOTO] + ) + image.hierarchy.setPlaceholderImage(drawable) + } else if (message.selectedIndividualHashMap!!.containsKey(KEY_MIMETYPE)) { + val mimetype = message.selectedIndividualHashMap!![KEY_MIMETYPE] + val drawableResourceId = getDrawableResourceIdForMimeType(mimetype) + val drawable = ContextCompat.getDrawable(context!!, drawableResourceId) + if (drawable != null && + ( + drawableResourceId == R.drawable.ic_mimetype_folder || + drawableResourceId == R.drawable.ic_mimetype_package_x_generic + ) + ) { + drawable.setColorFilter( + viewThemeUtils!!.getScheme(image.context).primary, + PorterDuff.Mode.SRC_ATOP + ) + } + image.hierarchy.setPlaceholderImage(drawable) + } else { + fetchFileInformation( + "/" + message.selectedIndividualHashMap!![KEY_PATH], + message.activeUser + ) + } + if (message.activeUser != null && + message.activeUser!!.username != null && + message.activeUser!!.baseUrl != null + ) { + clickView!!.setOnClickListener { v: View? -> + fileViewerUtils!!.openFile( + message, + ProgressUi(progressBar, messageText, image) + ) + } + clickView!!.setOnLongClickListener { l: View? -> + onMessageViewLongClick(message) + true + } + } else { + Log.e(TAG, "failed to set click listener because activeUser, username or baseUrl were null") + } + fileViewerUtils!!.resumeToUpdateViewsByProgress( + message.selectedIndividualHashMap!![KEY_NAME]!!, + message.selectedIndividualHashMap!![KEY_ID]!!, + message.selectedIndividualHashMap!![KEY_MIMETYPE], + ProgressUi(progressBar, messageText, image) + ) + } else if (message.getCalculateMessageType() === ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE) { + messageText.text = "GIPHY" + DisplayUtils.setClickableString("GIPHY", "https://giphy.com", messageText) + } else if (message.getCalculateMessageType() === ChatMessage.MessageType.SINGLE_LINK_TENOR_MESSAGE) { + messageText.text = "Tenor" + DisplayUtils.setClickableString("Tenor", "https://tenor.com", messageText) + } else { + if (message.messageType == ChatMessage.MessageType.SINGLE_LINK_IMAGE_MESSAGE.name) { + (clickView as SimpleDraweeView?)?.setOnClickListener { + val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(message.imageUrl)) + browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context!!.startActivity(browserIntent) + } + } else { + (clickView as SimpleDraweeView?)?.setOnClickListener(null) + } + messageText.text = "" + } + itemView.setTag(MessageSwipeCallback.REPLYABLE_VIEW_TAG, message.replyable) + Reaction().showReactions( + message, + ::clickOnReaction, + ::longClickOnReaction, + reactionsBinding!!, + messageText.context, + true, + viewThemeUtils!! + ) + } + + private fun longClickOnReaction(chatMessage: ChatMessage) { + commonMessageInterface.onLongClickReactions(chatMessage) + } + + private fun clickOnReaction(chatMessage: ChatMessage, emoji: String) { + commonMessageInterface.onClickReaction(chatMessage, emoji) + } + + private fun getDrawableFromContactDetails(context: Context?, base64: String?): Drawable? { + var drawable: Drawable? = null + if (base64 != "") { + val inputStream = ByteArrayInputStream( + Base64.decode(base64!!.toByteArray(), Base64.DEFAULT) + ) + drawable = Drawable.createFromResourceStream( + context!!.resources, + null, inputStream, null, null + ) + try { + inputStream.close() + } catch (e: IOException) { + val drawableResourceId = getDrawableResourceIdForMimeType("text/vcard") + drawable = ContextCompat.getDrawable(context, drawableResourceId) + } + } + return drawable + } + + private fun onMessageViewLongClick(message: ChatMessage) { + if (fileViewerUtils!!.isSupportedForInternalViewer(message.selectedIndividualHashMap!![KEY_MIMETYPE])) { + previewMessageInterface!!.onPreviewMessageLongClick(message) + return + } + val viewContext: Context? = if (itemView.context != null) { + itemView.context + } else { + context + } + val popupMenu = PopupMenu( + ContextThemeWrapper(viewContext, R.style.appActionBarPopupMenu), + itemView, + Gravity.START + ) + popupMenu.inflate(R.menu.chat_preview_message_menu) + popupMenu.setOnMenuItemClickListener { item: MenuItem -> + if (item.itemId == R.id.openInFiles) { + val keyID = message.selectedIndividualHashMap!![KEY_ID] + val link = message.selectedIndividualHashMap!!["link"] + fileViewerUtils!!.openFileInFilesApp(link!!, keyID!!) + } + true + } + popupMenu.show() + } + + private fun fetchFileInformation(url: String, activeUser: User?) { + Single.fromCallable { ReadFilesystemOperation(okHttpClient, activeUser, url, 0) } + .observeOn(Schedulers.io()) + .subscribe(object : SingleObserver { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onSuccess(readFilesystemOperation: ReadFilesystemOperation) { + val davResponse = readFilesystemOperation.readRemotePath() + if (davResponse.data != null) { + val browserFileList = davResponse.data as List + if (browserFileList.isNotEmpty()) { + Handler(context!!.mainLooper).post { + val resourceId = getDrawableResourceIdForMimeType(browserFileList[0].mimeType) + val drawable = ContextCompat.getDrawable(context!!, resourceId) + image.hierarchy.setPlaceholderImage(drawable) + } + } + } + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Error reading file information", e) + } + }) + } + + fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) { + this.commonMessageInterface = commonMessageInterface + } + + fun assignPreviewMessageInterface(previewMessageInterface: PreviewMessageInterface?) { + this.previewMessageInterface = previewMessageInterface + } + + abstract val messageText: EmojiTextView + abstract val previewContainer: View + abstract val previewContactContainer: MaterialCardView + abstract val previewContactPhoto: SimpleDraweeView + abstract val previewContactName: EmojiTextView + abstract val previewContactProgressBar: ProgressBar? + + companion object { + private const val TAG = "PreviewMsgViewHolder" + const val KEY_CONTACT_NAME = "contact-name" + const val KEY_CONTACT_PHOTO = "contact-photo" + const val KEY_MIMETYPE = "mimetype" + const val KEY_ID = "id" + const val KEY_PATH = "path" + const val ACTOR_TYPE_BOTS = "bots" + const val ACTOR_ID_CHANGELOG = "changelog" + const val KEY_NAME = "name" + } +} diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/Reaction.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/Reaction.kt index dd31d98ed..481c26b56 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/Reaction.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/Reaction.kt @@ -37,17 +37,22 @@ class Reaction { fun showReactions( message: ChatMessage, + clickOnReaction: (message: ChatMessage, emoji: String) -> Unit, + longClickOnReaction: (message: ChatMessage) -> Unit, binding: ReactionsInsideMessageBinding, context: Context, isOutgoingMessage: Boolean, viewThemeUtils: ViewThemeUtils ) { binding.reactionsEmojiWrapper.removeAllViews() + if (message.reactions != null && message.reactions!!.isNotEmpty()) { binding.reactionsEmojiWrapper.visibility = View.VISIBLE - var remainingEmojisToDisplay = MAX_EMOJIS_TO_DISPLAY - val showInfoAboutMoreEmojis = message.reactions!!.size > MAX_EMOJIS_TO_DISPLAY + binding.reactionsEmojiWrapper.setOnLongClickListener { + longClickOnReaction(message) + true + } val amountParams = getAmountLayoutParams(context) val wrapperParams = getWrapperLayoutParams(context) @@ -78,13 +83,15 @@ class Reaction { ), ) - binding.reactionsEmojiWrapper.addView(emojiWithAmountWrapper) - - remainingEmojisToDisplay-- - if (remainingEmojisToDisplay == 0 && showInfoAboutMoreEmojis) { - binding.reactionsEmojiWrapper.addView(getMoreReactionsTextView(context, textColor)) - break + emojiWithAmountWrapper.setOnClickListener { + clickOnReaction(message, emoji) } + emojiWithAmountWrapper.setOnLongClickListener { + longClickOnReaction(message) + false + } + + binding.reactionsEmojiWrapper.addView(emojiWithAmountWrapper) } } else { binding.reactionsEmojiWrapper.visibility = View.GONE @@ -132,13 +139,6 @@ class Reaction { return emojiWithAmountWrapper } - private fun getMoreReactionsTextView(context: Context, textColor: Int): TextView { - val infoAboutMoreEmojis = TextView(context) - infoAboutMoreEmojis.setTextColor(textColor) - infoAboutMoreEmojis.text = EMOJI_MORE - return infoAboutMoreEmojis - } - private fun getEmojiTextView(context: Context, emoji: String): EmojiTextView { val reactionEmoji = EmojiTextView(context) reactionEmoji.text = emoji @@ -202,12 +202,10 @@ class Reaction { ) companion object { - const val MAX_EMOJIS_TO_DISPLAY = 4 const val AMOUNT_START_MARGIN: Float = 2F const val EMOJI_END_MARGIN: Float = 6F const val EMOJI_AND_AMOUNT_PADDING_SIDE: Float = 4F const val WRAPPER_PADDING_TOP: Float = 2F const val WRAPPER_PADDING_BOTTOM: Float = 3F - const val EMOJI_MORE = "…" } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/TalkMessagesListAdapter.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/TalkMessagesListAdapter.java index 2947a2faa..f410c671e 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/TalkMessagesListAdapter.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/TalkMessagesListAdapter.java @@ -71,9 +71,9 @@ public class TalkMessagesListAdapter extends MessagesListAda ((OutcomingVoiceMessageViewHolder) holder).assignVoiceMessageInterface(chatController); ((OutcomingVoiceMessageViewHolder) holder).assignCommonMessageInterface(chatController); - } else if (holder instanceof MagicPreviewMessageViewHolder) { - ((MagicPreviewMessageViewHolder) holder).assignPreviewMessageInterface(chatController); - ((MagicPreviewMessageViewHolder) holder).assignCommonMessageInterface(chatController); + } else if (holder instanceof PreviewMessageViewHolder) { + ((PreviewMessageViewHolder) holder).assignPreviewMessageInterface(chatController); + ((PreviewMessageViewHolder) holder).assignCommonMessageInterface(chatController); } } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index 1e55d37dc..6e80094a7 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -7,7 +7,7 @@ * @author Tim Krüger * Copyright (C) 2021 Tim Krüger * Copyright (C) 2021 Andy Scherzinger - * Copyright (C) 2021 Marcel Hibbe + * Copyright (C) 2021-2022 Marcel Hibbe * Copyright (C) 2017-2019 Mario Danic * * This program is free software: you can redistribute it and/or modify @@ -138,6 +138,8 @@ import com.nextcloud.talk.jobs.DownloadFileToCacheWorker import com.nextcloud.talk.jobs.ShareOperationWorker import com.nextcloud.talk.jobs.UploadAndShareFilesWorker import com.nextcloud.talk.messagesearch.MessageSearchActivity +import com.nextcloud.talk.models.domain.ReactionAddedModel +import com.nextcloud.talk.models.domain.ReactionDeletedModel import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ChatOverall import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage @@ -150,6 +152,7 @@ import com.nextcloud.talk.models.json.mention.Mention import com.nextcloud.talk.polls.ui.PollCreateDialogFragment import com.nextcloud.talk.presenters.MentionAutocompletePresenter import com.nextcloud.talk.remotefilebrowser.activities.RemoteFileBrowserActivity +import com.nextcloud.talk.repositories.reactions.ReactionsRepository import com.nextcloud.talk.shareditems.activities.SharedItemsActivity import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet import com.nextcloud.talk.ui.dialog.AttachmentDialog @@ -235,6 +238,9 @@ class ChatController(args: Bundle) : @Inject lateinit var eventBus: EventBus + @Inject + lateinit var reactionsRepository: ReactionsRepository + @Inject lateinit var permissionUtil: PlatformPermissionUtil @@ -1225,7 +1231,7 @@ class ChatController(args: Bundle) : } } - fun vibrate() { + private fun vibrate() { val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator if (Build.VERSION.SDK_INT >= O) { vibrator.vibrate(VibrationEffect.createOneShot(SHORT_VIBRATE, VibrationEffect.DEFAULT_AMPLITUDE)) @@ -2788,7 +2794,22 @@ class ChatController(args: Bundle) : } } - override fun onClickReactions(chatMessage: ChatMessage) { + override fun onClickReaction(chatMessage: ChatMessage, emoji: String) { + vibrate() + if (chatMessage.reactionsSelf?.contains(emoji) == true) { + reactionsRepository.deleteReaction(currentConversation!!, chatMessage, emoji) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(ReactionDeletedObserver()) + } else { + reactionsRepository.addReaction(currentConversation!!, chatMessage, emoji) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(ReactionAddedObserver()) + } + } + + override fun onLongClickReactions(chatMessage: ChatMessage) { activity?.let { ShowReactionsDialog( activity!!, @@ -2801,6 +2822,52 @@ class ChatController(args: Bundle) : } } + inner class ReactionAddedObserver : Observer { + override fun onSubscribe(d: Disposable) { + } + + override fun onNext(reactionAddedModel: ReactionAddedModel) { + Log.d(TAG, "onNext") + if (reactionAddedModel.success) { + updateUiToAddReaction( + reactionAddedModel.chatMessage, + reactionAddedModel.emoji + ) + } + } + + override fun onError(e: Throwable) { + Log.d(TAG, "onError") + } + + override fun onComplete() { + Log.d(TAG, "onComplete") + } + } + + inner class ReactionDeletedObserver : Observer { + override fun onSubscribe(d: Disposable) { + } + + override fun onNext(reactionDeletedModel: ReactionDeletedModel) { + Log.d(TAG, "onNext") + if (reactionDeletedModel.success) { + updateUiToDeleteReaction( + reactionDeletedModel.chatMessage, + reactionDeletedModel.emoji + ) + } + } + + override fun onError(e: Throwable) { + Log.d(TAG, "onError") + } + + override fun onComplete() { + Log.d(TAG, "onComplete") + } + } + override fun onOpenMessageActionsDialog(chatMessage: ChatMessage) { openMessageActionsDialog(chatMessage) } @@ -2823,8 +2890,7 @@ class ChatController(args: Bundle) : conversationUser, currentConversation, isShowMessageDeletionButton(message), - participantPermissions.hasChatPermission(), - ncApi + participantPermissions.hasChatPermission() ).show() } } @@ -3126,7 +3192,7 @@ class ChatController(args: Bundle) : adapter?.update(messageTemp) } - fun updateAdapterAfterSendReaction(message: ChatMessage, emoji: String) { + fun updateUiToAddReaction(message: ChatMessage, emoji: String) { if (message.reactions == null) { message.reactions = LinkedHashMap() } @@ -3144,6 +3210,24 @@ class ChatController(args: Bundle) : adapter?.update(message) } + fun updateUiToDeleteReaction(message: ChatMessage, emoji: String) { + if (message.reactions == null) { + message.reactions = LinkedHashMap() + } + + if (message.reactionsSelf == null) { + message.reactionsSelf = ArrayList() + } + + var amount = message.reactions!![emoji] + if (amount == null) { + amount = 0 + } + message.reactions!![emoji] = amount - 1 + message.reactionsSelf!!.remove(emoji) + adapter?.update(message) + } + private fun isShowMessageDeletionButton(message: ChatMessage): Boolean { if (conversationUser == null) return false diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt index 06021c07e..1c73844c2 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt @@ -3,6 +3,8 @@ * * @author Álvaro Brey * @author Andy Scherzinger + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe * Copyright (C) 2022 Andy Scherzinger * Copyright (C) 2022 Álvaro Brey * Copyright (C) 2022 Nextcloud GmbH @@ -35,6 +37,8 @@ import com.nextcloud.talk.remotefilebrowser.repositories.RemoteFileBrowserItemsR import com.nextcloud.talk.remotefilebrowser.repositories.RemoteFileBrowserItemsRepositoryImpl import com.nextcloud.talk.repositories.conversations.ConversationsRepository import com.nextcloud.talk.repositories.conversations.ConversationsRepositoryImpl +import com.nextcloud.talk.repositories.reactions.ReactionsRepository +import com.nextcloud.talk.repositories.reactions.ReactionsRepositoryImpl import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepository import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepositoryImpl import com.nextcloud.talk.shareditems.repositories.SharedItemsRepository @@ -82,4 +86,9 @@ class RepositoryModule { fun provideArbitraryStoragesRepository(database: TalkDatabase): ArbitraryStoragesRepository { return ArbitraryStoragesRepositoryImpl(database.arbitraryStoragesDao()) } + + @Provides + fun provideReactionsRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew): ReactionsRepository { + return ReactionsRepositoryImpl(ncApi, userProvider) + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/domain/ReactionAddedModel.kt b/app/src/main/java/com/nextcloud/talk/models/domain/ReactionAddedModel.kt new file mode 100644 index 000000000..004814d71 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/domain/ReactionAddedModel.kt @@ -0,0 +1,29 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.models.domain + +import com.nextcloud.talk.models.json.chat.ChatMessage + +data class ReactionAddedModel( + var chatMessage: ChatMessage, + var emoji: String, + var success: Boolean +) diff --git a/app/src/main/java/com/nextcloud/talk/models/domain/ReactionDeletedModel.kt b/app/src/main/java/com/nextcloud/talk/models/domain/ReactionDeletedModel.kt new file mode 100644 index 000000000..ca396f52f --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/domain/ReactionDeletedModel.kt @@ -0,0 +1,29 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.models.domain + +import com.nextcloud.talk.models.json.chat.ChatMessage + +data class ReactionDeletedModel( + var chatMessage: ChatMessage, + var emoji: String, + var success: Boolean +) diff --git a/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepository.kt new file mode 100644 index 000000000..912b8cc61 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepository.kt @@ -0,0 +1,42 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.repositories.reactions + +import com.nextcloud.talk.models.domain.ReactionAddedModel +import com.nextcloud.talk.models.domain.ReactionDeletedModel +import com.nextcloud.talk.models.json.chat.ChatMessage +import com.nextcloud.talk.models.json.conversations.Conversation +import io.reactivex.Observable + +interface ReactionsRepository { + + fun addReaction( + currentConversation: Conversation, + message: ChatMessage, + emoji: String + ): Observable + + fun deleteReaction( + currentConversation: Conversation, + message: ChatMessage, + emoji: String + ): Observable +} diff --git a/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepositoryImpl.kt new file mode 100644 index 000000000..17eff5b01 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepositoryImpl.kt @@ -0,0 +1,102 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.repositories.reactions + +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.models.domain.ReactionAddedModel +import com.nextcloud.talk.models.domain.ReactionDeletedModel +import com.nextcloud.talk.models.json.chat.ChatMessage +import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.json.generic.GenericMeta +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew +import io.reactivex.Observable + +class ReactionsRepositoryImpl(private val ncApi: NcApi, currentUserProvider: CurrentUserProviderNew) : + ReactionsRepository { + + val currentUser: User = currentUserProvider.currentUser.blockingGet() + val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) + + override fun addReaction( + currentConversation: Conversation, + message: ChatMessage, + emoji: String + ): Observable { + return ncApi.sendReaction( + credentials, + ApiUtils.getUrlForMessageReaction( + currentUser.baseUrl, + currentConversation.token, + message.id + ), + emoji + ).map { mapToReactionAddedModel(message, emoji, it.ocs?.meta!!) } + } + + override fun deleteReaction( + currentConversation: Conversation, + message: ChatMessage, + emoji: String + ): Observable { + return ncApi.deleteReaction( + credentials, + ApiUtils.getUrlForMessageReaction( + currentUser.baseUrl, + currentConversation.token, + message.id + ), + emoji + ).map { mapToReactionDeletedModel(message, emoji, it.ocs?.meta!!) } + } + + private fun mapToReactionAddedModel( + message: ChatMessage, + emoji: String, + reactionResponse: GenericMeta + ): ReactionAddedModel { + val success = reactionResponse.statusCode == HTTP_CREATED + return ReactionAddedModel( + message, + emoji, + success + ) + } + + private fun mapToReactionDeletedModel( + message: ChatMessage, + emoji: String, + reactionResponse: GenericMeta + ): ReactionDeletedModel { + val success = reactionResponse.statusCode == HTTP_OK + return ReactionDeletedModel( + message, + emoji, + success + ) + } + + companion object { + private const val HTTP_OK: Int = 200 + private const val HTTP_CREATED: Int = 201 + } +} diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt index 51d392aee..22335ad06 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt @@ -2,6 +2,8 @@ * Nextcloud Talk application * * @author Andy Scherzinger + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe * Copyright (C) 2022 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify @@ -25,7 +27,6 @@ import android.content.Context import android.os.Bundle import android.os.Handler import android.os.Looper -import android.util.Log import android.view.MotionEvent import android.view.View import android.view.ViewGroup @@ -35,16 +36,16 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.nextcloud.talk.BuildConfig import com.nextcloud.talk.R -import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.controllers.ChatController import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.DialogMessageActionsBinding +import com.nextcloud.talk.models.domain.ReactionAddedModel +import com.nextcloud.talk.models.domain.ReactionDeletedModel import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.conversations.Conversation -import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.repositories.reactions.ReactionsRepository import com.nextcloud.talk.ui.theme.ViewThemeUtils -import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew import com.vanniktech.emoji.EmojiPopup import com.vanniktech.emoji.EmojiTextView @@ -63,13 +64,15 @@ class MessageActionsDialog( private val user: User?, private val currentConversation: Conversation?, private val showMessageDeletionButton: Boolean, - private val hasChatPermission: Boolean, - private val ncApi: NcApi + private val hasChatPermission: Boolean ) : BottomSheetDialog(chatController.activity!!) { @Inject lateinit var viewThemeUtils: ViewThemeUtils + @Inject + lateinit var reactionsRepository: ReactionsRepository + private lateinit var dialogMessageActionsBinding: DialogMessageActionsBinding private lateinit var popup: EmojiPopup @@ -138,7 +141,7 @@ class MessageActionsDialog( }, onEmojiClickListener = { popup.dismiss() - sendReaction(message, it.unicode) + clickOnEmoji(message, it.unicode) }, onEmojiPopupDismissListener = { dialogMessageActionsBinding.emojiMore.clearFocus() @@ -180,27 +183,27 @@ class MessageActionsDialog( ) { checkAndSetEmojiSelfReaction(dialogMessageActionsBinding.emojiThumbsUp) dialogMessageActionsBinding.emojiThumbsUp.setOnClickListener { - sendReaction(message, dialogMessageActionsBinding.emojiThumbsUp.text.toString()) + clickOnEmoji(message, dialogMessageActionsBinding.emojiThumbsUp.text.toString()) } checkAndSetEmojiSelfReaction(dialogMessageActionsBinding.emojiThumbsDown) dialogMessageActionsBinding.emojiThumbsDown.setOnClickListener { - sendReaction(message, dialogMessageActionsBinding.emojiThumbsDown.text.toString()) + clickOnEmoji(message, dialogMessageActionsBinding.emojiThumbsDown.text.toString()) } checkAndSetEmojiSelfReaction(dialogMessageActionsBinding.emojiLaugh) dialogMessageActionsBinding.emojiLaugh.setOnClickListener { - sendReaction(message, dialogMessageActionsBinding.emojiLaugh.text.toString()) + clickOnEmoji(message, dialogMessageActionsBinding.emojiLaugh.text.toString()) } checkAndSetEmojiSelfReaction(dialogMessageActionsBinding.emojiHeart) dialogMessageActionsBinding.emojiHeart.setOnClickListener { - sendReaction(message, dialogMessageActionsBinding.emojiHeart.text.toString()) + clickOnEmoji(message, dialogMessageActionsBinding.emojiHeart.text.toString()) } checkAndSetEmojiSelfReaction(dialogMessageActionsBinding.emojiConfused) dialogMessageActionsBinding.emojiConfused.setOnClickListener { - sendReaction(message, dialogMessageActionsBinding.emojiConfused.text.toString()) + clickOnEmoji(message, dialogMessageActionsBinding.emojiConfused.text.toString()) } checkAndSetEmojiSelfReaction(dialogMessageActionsBinding.emojiSad) dialogMessageActionsBinding.emojiSad.setOnClickListener { - sendReaction(message, dialogMessageActionsBinding.emojiSad.text.toString()) + clickOnEmoji(message, dialogMessageActionsBinding.emojiSad.text.toString()) } dialogMessageActionsBinding.emojiMore.setOnClickListener { @@ -302,88 +305,66 @@ class MessageActionsDialog( } } - private fun sendReaction(message: ChatMessage, emoji: String) { + private fun clickOnEmoji(message: ChatMessage, emoji: String) { if (message.reactionsSelf?.contains(emoji) == true) { - deleteReaction(message, emoji) + reactionsRepository.deleteReaction(currentConversation!!, message, emoji) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(ReactionDeletedObserver()) } else { - addReaction(message, emoji) + reactionsRepository.addReaction(currentConversation!!, message, emoji) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(ReactionAddedObserver()) } } - private fun addReaction(message: ChatMessage, emoji: String) { - val credentials = ApiUtils.getCredentials(user?.username, user?.token) + inner class ReactionAddedObserver : Observer { + override fun onSubscribe(d: Disposable) { + } - ncApi.sendReaction( - credentials, - ApiUtils.getUrlForMessageReaction( - user?.baseUrl, - currentConversation!!.token, - message.id - ), - emoji - ) - ?.subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + override fun onNext(reactionAddedModel: ReactionAddedModel) { + if (reactionAddedModel.success) { + chatController.updateUiToAddReaction( + reactionAddedModel.chatMessage, + reactionAddedModel.emoji + ) + } + } - override fun onNext(genericOverall: GenericOverall) { - val statusCode = genericOverall.ocs?.meta?.statusCode - if (statusCode == HTTP_CREATED) { - chatController.updateAdapterAfterSendReaction(message, emoji) - } - } + override fun onError(e: Throwable) { + } - override fun onError(e: Throwable) { - Log.e(TAG, "error while sending reaction: $emoji") - } - - override fun onComplete() { - dismiss() - } - }) + override fun onComplete() { + dismiss() + } } - private fun deleteReaction(message: ChatMessage, emoji: String) { - val credentials = ApiUtils.getCredentials(user?.username, user?.token) + inner class ReactionDeletedObserver : Observer { + override fun onSubscribe(d: Disposable) { + } - ncApi.deleteReaction( - credentials, - ApiUtils.getUrlForMessageReaction( - user?.baseUrl, - currentConversation!!.token, - message.id - ), - emoji - ) - ?.subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + override fun onNext(reactionDeletedModel: ReactionDeletedModel) { + if (reactionDeletedModel.success) { + chatController.updateUiToDeleteReaction( + reactionDeletedModel.chatMessage, + reactionDeletedModel.emoji + ) + } + } - override fun onNext(genericOverall: GenericOverall) { - Log.d(TAG, "deleted reaction: $emoji") - } + override fun onError(e: Throwable) { + } - override fun onError(e: Throwable) { - Log.e(TAG, "error while deleting reaction: $emoji") - } - - override fun onComplete() { - dismiss() - } - }) + override fun onComplete() { + dismiss() + } } companion object { - private const val TAG = "MessageActionsDialog" + private val TAG = MessageActionsDialog::class.java.simpleName private const val ACTOR_LENGTH = 6 private const val NO_PREVIOUS_MESSAGE_ID: Int = -1 - private const val HTTP_CREATED: Int = 201 private const val DELAY: Long = 200 } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt index fb9840b33..35d1f2998 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt @@ -41,7 +41,7 @@ import com.nextcloud.talk.R import com.nextcloud.talk.activities.FullScreenImageActivity import com.nextcloud.talk.activities.FullScreenMediaActivity import com.nextcloud.talk.activities.FullScreenTextViewerActivity -import com.nextcloud.talk.adapters.messages.MagicPreviewMessageViewHolder +import com.nextcloud.talk.adapters.messages.PreviewMessageViewHolder import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.jobs.DownloadFileToCacheWorker import com.nextcloud.talk.models.json.chat.ChatMessage @@ -78,12 +78,12 @@ class FileViewerUtils(private val context: Context, private val user: User) { message: ChatMessage, progressUi: ProgressUi ) { - val fileName = message.selectedIndividualHashMap!![MagicPreviewMessageViewHolder.KEY_NAME]!! - val mimetype = message.selectedIndividualHashMap!![MagicPreviewMessageViewHolder.KEY_MIMETYPE]!! + val fileName = message.selectedIndividualHashMap!![PreviewMessageViewHolder.KEY_NAME]!! + val mimetype = message.selectedIndividualHashMap!![PreviewMessageViewHolder.KEY_MIMETYPE]!! val link = message.selectedIndividualHashMap!!["link"]!! - val fileId = message.selectedIndividualHashMap!![MagicPreviewMessageViewHolder.KEY_ID]!! - val path = message.selectedIndividualHashMap!![MagicPreviewMessageViewHolder.KEY_PATH]!! + val fileId = message.selectedIndividualHashMap!![PreviewMessageViewHolder.KEY_ID]!! + val path = message.selectedIndividualHashMap!![PreviewMessageViewHolder.KEY_PATH]!! var size = message.selectedIndividualHashMap!!["size"] if (size == null) { diff --git a/app/src/main/res/layout/reactions_inside_message.xml b/app/src/main/res/layout/reactions_inside_message.xml index d4cb662d9..0378a086b 100644 --- a/app/src/main/res/layout/reactions_inside_message.xml +++ b/app/src/main/res/layout/reactions_inside_message.xml @@ -17,20 +17,20 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> - - + - - \ No newline at end of file + android:gravity="center_vertical" + android:orientation="horizontal" > + + \ No newline at end of file