mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-20 12:09:45 +01:00
Merge pull request #985 from nextcloud/feature/322/deleteMessage
add ability to delete messages
This commit is contained in:
commit
5a4c17e240
@ -160,7 +160,11 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
|
|||||||
|
|
||||||
val resources = itemView.resources
|
val resources = itemView.resources
|
||||||
|
|
||||||
val bg_bubble_color = resources.getColor(R.color.bg_message_list_incoming_bubble)
|
val bg_bubble_color = if (message.isDeleted) {
|
||||||
|
resources.getColor(R.color.bg_message_list_incoming_bubble_deleted)
|
||||||
|
} else {
|
||||||
|
resources.getColor(R.color.bg_message_list_incoming_bubble)
|
||||||
|
}
|
||||||
|
|
||||||
var bubbleResource = R.drawable.shape_incoming_message
|
var bubbleResource = R.drawable.shape_incoming_message
|
||||||
|
|
||||||
@ -229,13 +233,14 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
|
|||||||
|
|
||||||
// parent message handling
|
// parent message handling
|
||||||
|
|
||||||
message.parentMessage?.let { parentChatMessage ->
|
if (!message.isDeleted && message.parentMessage != null) {
|
||||||
|
var parentChatMessage = message.parentMessage
|
||||||
parentChatMessage.activeUser = message.activeUser
|
parentChatMessage.activeUser = message.activeUser
|
||||||
quotedUserAvatar?.load(parentChatMessage.user.avatar) {
|
quotedUserAvatar?.load(parentChatMessage.user.avatar) {
|
||||||
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
|
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
|
||||||
transformations(CircleCropTransformation())
|
transformations(CircleCropTransformation())
|
||||||
}
|
}
|
||||||
parentChatMessage.imageUrl?.let{
|
parentChatMessage.imageUrl?.let {
|
||||||
quotedMessagePreview?.visibility = View.VISIBLE
|
quotedMessagePreview?.visibility = View.VISIBLE
|
||||||
quotedMessagePreview?.load(it) {
|
quotedMessagePreview?.load(it) {
|
||||||
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
|
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
|
||||||
@ -253,7 +258,7 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
|
|||||||
quotedMessageTime?.setTextColor(context!!.resources.getColor(R.color.warm_grey_four))
|
quotedMessageTime?.setTextColor(context!!.resources.getColor(R.color.warm_grey_four))
|
||||||
quoteColoredView?.setBackgroundResource(R.color.textColorMaxContrast)
|
quoteColoredView?.setBackgroundResource(R.color.textColorMaxContrast)
|
||||||
quotedChatMessageView?.visibility = View.VISIBLE
|
quotedChatMessageView?.visibility = View.VISIBLE
|
||||||
} ?: run {
|
} else {
|
||||||
quotedChatMessageView?.visibility = View.GONE
|
quotedChatMessageView?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,18 +136,23 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
|
|||||||
realView.isSelected = true
|
realView.isSelected = true
|
||||||
}
|
}
|
||||||
val resources = sharedApplication!!.resources
|
val resources = sharedApplication!!.resources
|
||||||
|
val bg_bubble_color = if (message.isDeleted) {
|
||||||
|
resources.getColor(R.color.bg_message_list_outcoming_bubble_deleted)
|
||||||
|
} else {
|
||||||
|
resources.getColor(R.color.bg_message_list_outcoming_bubble)
|
||||||
|
}
|
||||||
if (message.isGrouped) {
|
if (message.isGrouped) {
|
||||||
val bubbleDrawable = getMessageSelector(
|
val bubbleDrawable = getMessageSelector(
|
||||||
resources.getColor(R.color.colorPrimary),
|
bg_bubble_color,
|
||||||
resources.getColor(R.color.transparent),
|
resources.getColor(R.color.transparent),
|
||||||
resources.getColor(R.color.colorPrimary),
|
bg_bubble_color,
|
||||||
R.drawable.shape_grouped_outcoming_message)
|
R.drawable.shape_grouped_outcoming_message)
|
||||||
ViewCompat.setBackground(bubble, bubbleDrawable)
|
ViewCompat.setBackground(bubble, bubbleDrawable)
|
||||||
} else {
|
} else {
|
||||||
val bubbleDrawable = getMessageSelector(
|
val bubbleDrawable = getMessageSelector(
|
||||||
resources.getColor(R.color.colorPrimary),
|
bg_bubble_color,
|
||||||
resources.getColor(R.color.transparent),
|
resources.getColor(R.color.transparent),
|
||||||
resources.getColor(R.color.colorPrimary),
|
bg_bubble_color,
|
||||||
R.drawable.shape_outcoming_message)
|
R.drawable.shape_outcoming_message)
|
||||||
ViewCompat.setBackground(bubble, bubbleDrawable)
|
ViewCompat.setBackground(bubble, bubbleDrawable)
|
||||||
}
|
}
|
||||||
@ -157,13 +162,14 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
|
|||||||
|
|
||||||
// parent message handling
|
// parent message handling
|
||||||
|
|
||||||
message.parentMessage?.let { parentChatMessage ->
|
if (!message.isDeleted && message.parentMessage != null) {
|
||||||
|
var parentChatMessage = message.parentMessage
|
||||||
parentChatMessage.activeUser = message.activeUser
|
parentChatMessage.activeUser = message.activeUser
|
||||||
quotedUserAvatar?.load(parentChatMessage.user.avatar) {
|
quotedUserAvatar?.load(parentChatMessage.user.avatar) {
|
||||||
transformations(CircleCropTransformation())
|
transformations(CircleCropTransformation())
|
||||||
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
|
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
|
||||||
}
|
}
|
||||||
parentChatMessage.imageUrl?.let{
|
parentChatMessage.imageUrl?.let {
|
||||||
quotedMessagePreview?.visibility = View.VISIBLE
|
quotedMessagePreview?.visibility = View.VISIBLE
|
||||||
quotedMessagePreview?.load(it) {
|
quotedMessagePreview?.load(it) {
|
||||||
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
|
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
|
||||||
@ -182,7 +188,7 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
|
|||||||
quoteColoredView?.setBackgroundResource(R.color.white)
|
quoteColoredView?.setBackgroundResource(R.color.white)
|
||||||
|
|
||||||
quotedChatMessageView?.visibility = View.VISIBLE
|
quotedChatMessageView?.visibility = View.VISIBLE
|
||||||
} ?: run {
|
} else {
|
||||||
quotedChatMessageView?.visibility = View.GONE
|
quotedChatMessageView?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall;
|
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall;
|
||||||
import com.nextcloud.talk.models.json.chat.ChatOverall;
|
import com.nextcloud.talk.models.json.chat.ChatOverall;
|
||||||
|
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage;
|
||||||
import com.nextcloud.talk.models.json.conversations.RoomOverall;
|
import com.nextcloud.talk.models.json.conversations.RoomOverall;
|
||||||
import com.nextcloud.talk.models.json.conversations.RoomsOverall;
|
import com.nextcloud.talk.models.json.conversations.RoomsOverall;
|
||||||
import com.nextcloud.talk.models.json.generic.GenericOverall;
|
import com.nextcloud.talk.models.json.generic.GenericOverall;
|
||||||
@ -296,7 +297,8 @@ public interface NcApi {
|
|||||||
|
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
@POST
|
@POST
|
||||||
Observable<GenericOverall> sendChatMessage(@Header("Authorization") String authorization, @Url String url,
|
Observable<GenericOverall> sendChatMessage(@Header("Authorization") String authorization,
|
||||||
|
@Url String url,
|
||||||
@Field("message") CharSequence message,
|
@Field("message") CharSequence message,
|
||||||
@Field("actorDisplayName") String actorDisplayName,
|
@Field("actorDisplayName") String actorDisplayName,
|
||||||
@Field("replyTo") Integer replyTo);
|
@Field("replyTo") Integer replyTo);
|
||||||
@ -355,4 +357,8 @@ public interface NcApi {
|
|||||||
Observable<Response<GenericOverall>> uploadFile(@Header("Authorization") String authorization,
|
Observable<Response<GenericOverall>> uploadFile(@Header("Authorization") String authorization,
|
||||||
@Url String url,
|
@Url String url,
|
||||||
@Body RequestBody body);
|
@Body RequestBody body);
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
Observable<ChatOverallSingleMessage> deleteChatMessage(@Header("Authorization") String authorization,
|
||||||
|
@Url String url);
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,7 @@ import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
|
|||||||
import com.nextcloud.talk.models.database.UserEntity
|
import com.nextcloud.talk.models.database.UserEntity
|
||||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||||
import com.nextcloud.talk.models.json.chat.ChatOverall
|
import com.nextcloud.talk.models.json.chat.ChatOverall
|
||||||
|
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
|
||||||
import com.nextcloud.talk.models.json.chat.ReadStatus
|
import com.nextcloud.talk.models.json.chat.ReadStatus
|
||||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||||
@ -114,6 +115,7 @@ import org.greenrobot.eventbus.ThreadMode
|
|||||||
import org.parceler.Parcels
|
import org.parceler.Parcels
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
import java.net.HttpURLConnection
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -1049,7 +1051,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
|
|||||||
if (!wasDetached) {
|
if (!wasDetached) {
|
||||||
if (lookIntoFuture > 0) {
|
if (lookIntoFuture > 0) {
|
||||||
val finalTimeout = timeout
|
val finalTimeout = timeout
|
||||||
ncApi?.pullChatMessages(credentials, ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap)
|
ncApi?.pullChatMessages(credentials,
|
||||||
|
ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap)
|
||||||
?.subscribeOn(Schedulers.io())
|
?.subscribeOn(Schedulers.io())
|
||||||
?.observeOn(AndroidSchedulers.mainThread())
|
?.observeOn(AndroidSchedulers.mainThread())
|
||||||
?.takeWhile { observable -> inConversation && !wasDetached }
|
?.takeWhile { observable -> inConversation && !wasDetached }
|
||||||
@ -1129,7 +1132,7 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
|
|||||||
if (response.code() == 200) {
|
if (response.code() == 200) {
|
||||||
|
|
||||||
val chatOverall = response.body() as ChatOverall?
|
val chatOverall = response.body() as ChatOverall?
|
||||||
val chatMessageList = chatOverall?.ocs!!.data
|
val chatMessageList = setDeletionFlagsAndRemoveInfomessages(chatOverall?.ocs!!.data)
|
||||||
|
|
||||||
if (isFirstMessagesProcessing) {
|
if (isFirstMessagesProcessing) {
|
||||||
cancelNotificationsForCurrentConversation()
|
cancelNotificationsForCurrentConversation()
|
||||||
@ -1331,6 +1334,31 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setDeletionFlagsAndRemoveInfomessages(chatMessageList: List<ChatMessage>): List<ChatMessage> {
|
||||||
|
val chatMessageMap = chatMessageList.map { it.id to it }.toMap().toMutableMap()
|
||||||
|
val chatMessageIterator = chatMessageMap.iterator()
|
||||||
|
while (chatMessageIterator.hasNext()) {
|
||||||
|
val currentMessage = chatMessageIterator.next()
|
||||||
|
if (isInfoMessageAboutDeletion(currentMessage)) {
|
||||||
|
if (!chatMessageMap.containsKey(currentMessage.value.parentMessage.id)) {
|
||||||
|
// if chatMessageMap doesnt't contain message to delete (this happens when lookingIntoFuture),
|
||||||
|
// the message to delete has to be modified directly inside the adapter
|
||||||
|
setMessageAsDeleted(currentMessage.value.parentMessage)
|
||||||
|
} else {
|
||||||
|
chatMessageMap[currentMessage.value.parentMessage.id]!!.isDeleted = true
|
||||||
|
}
|
||||||
|
chatMessageIterator.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chatMessageMap.values.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isInfoMessageAboutDeletion(currentMessage: MutableMap.MutableEntry<String, ChatMessage>): Boolean {
|
||||||
|
return currentMessage.value.parentMessage != null && currentMessage.value.systemMessageType == ChatMessage
|
||||||
|
.SystemMessageType.PARENT_MESSAGE_DELETED
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun startACall(isVoiceOnlyCall: Boolean) {
|
private fun startACall(isVoiceOnlyCall: Boolean) {
|
||||||
isLeavingForConversation = true
|
isLeavingForConversation = true
|
||||||
if (!isVoiceOnlyCall) {
|
if (!isVoiceOnlyCall) {
|
||||||
@ -1432,15 +1460,82 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
R.id.action_delete_message -> {
|
||||||
|
ncApi?.deleteChatMessage(
|
||||||
|
credentials,
|
||||||
|
ApiUtils.getUrlForMessageDeletion(conversationUser?.baseUrl, roomToken, message?.id)
|
||||||
|
)?.subscribeOn(Schedulers.io())
|
||||||
|
?.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
?.subscribe(object : Observer<ChatOverallSingleMessage> {
|
||||||
|
override fun onSubscribe(d: Disposable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNext(t: ChatOverallSingleMessage) {
|
||||||
|
if (t.ocs.meta.statusCode == HttpURLConnection.HTTP_ACCEPTED) {
|
||||||
|
Toast.makeText(context, R.string.nc_delete_message_leaked_to_matterbridge,
|
||||||
|
Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: Throwable) {
|
||||||
|
Log.e(TAG, "Something went wrong when trying to delete message with id " +
|
||||||
|
message?.id, e)
|
||||||
|
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onComplete() {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
true
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inflate(R.menu.chat_message_menu)
|
inflate(R.menu.chat_message_menu)
|
||||||
|
menu.findItem(R.id.action_copy_message).isVisible = !(message as ChatMessage).isDeleted
|
||||||
menu.findItem(R.id.action_reply_to_message).isVisible = (message as ChatMessage).replyable
|
menu.findItem(R.id.action_reply_to_message).isVisible = (message as ChatMessage).replyable
|
||||||
show()
|
menu.findItem(R.id.action_delete_message).isVisible = isShowMessageDeletionButton(message)
|
||||||
|
if(menu.hasVisibleItems()){
|
||||||
|
show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setMessageAsDeleted(message: IMessage?) {
|
||||||
|
val messageTemp = message as ChatMessage
|
||||||
|
messageTemp.isDeleted = true
|
||||||
|
|
||||||
|
messageTemp.isOneToOneConversation = currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||||
|
messageTemp.isLinkPreviewAllowed = isLinkPreviewAllowed
|
||||||
|
messageTemp.activeUser = conversationUser
|
||||||
|
|
||||||
|
adapter?.update(messageTemp)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isShowMessageDeletionButton(message: ChatMessage): Boolean {
|
||||||
|
if (conversationUser == null) return false
|
||||||
|
|
||||||
|
if(message.systemMessageType != ChatMessage.SystemMessageType.DUMMY) return false
|
||||||
|
|
||||||
|
if(message.isDeleted) return false
|
||||||
|
|
||||||
|
val sixHoursInMillis = 6 * 3600 * 1000
|
||||||
|
val isOlderThanSixHours = message.createdAt?.before(Date(System.currentTimeMillis() - sixHoursInMillis)) == true
|
||||||
|
if(isOlderThanSixHours) return false
|
||||||
|
|
||||||
|
val isUserAllowedByPrivileges = if (message.actorId == conversationUser.userId) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
currentConversation!!.isParticipantOwnerOrModerator
|
||||||
|
}
|
||||||
|
if(!isUserAllowedByPrivileges) return false
|
||||||
|
|
||||||
|
if(!conversationUser.hasSpreedFeatureCapability("delete-messages")) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun hasContentFor(message: IMessage, type: Byte): Boolean {
|
override fun hasContentFor(message: IMessage, type: Byte): Boolean {
|
||||||
when (type) {
|
when (type) {
|
||||||
CONTENT_TYPE_SYSTEM_MESSAGE -> return !TextUtils.isEmpty(message.systemMessage)
|
CONTENT_TYPE_SYSTEM_MESSAGE -> return !TextUtils.isEmpty(message.systemMessage)
|
||||||
|
@ -21,6 +21,8 @@ package com.nextcloud.talk.models.json.chat;
|
|||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||||
import com.bluelinelabs.logansquare.annotation.JsonIgnore;
|
import com.bluelinelabs.logansquare.annotation.JsonIgnore;
|
||||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||||
@ -42,7 +44,6 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
@ -59,6 +60,8 @@ public class ChatMessage implements IMessage, MessageContentType, MessageContent
|
|||||||
public Map<String, String> selectedIndividualHashMap;
|
public Map<String, String> selectedIndividualHashMap;
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public boolean isLinkPreviewAllowed;
|
public boolean isLinkPreviewAllowed;
|
||||||
|
@JsonIgnore
|
||||||
|
public boolean isDeleted;
|
||||||
@JsonField(name = "id")
|
@JsonField(name = "id")
|
||||||
public int jsonMessageId;
|
public int jsonMessageId;
|
||||||
@JsonField(name = "token")
|
@JsonField(name = "token")
|
||||||
@ -283,6 +286,7 @@ public class ChatMessage implements IMessage, MessageContentType, MessageContent
|
|||||||
FILE_SHARED,
|
FILE_SHARED,
|
||||||
LOBBY_NONE,
|
LOBBY_NONE,
|
||||||
LOBBY_NON_MODERATORS,
|
LOBBY_NON_MODERATORS,
|
||||||
LOBBY_OPEN_TO_EVERYONE
|
LOBBY_OPEN_TO_EVERYONE,
|
||||||
|
PARENT_MESSAGE_DELETED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Marcel Hibbe
|
||||||
|
* Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.nextcloud.talk.models.json.chat;
|
||||||
|
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||||
|
import com.nextcloud.talk.models.json.generic.GenericOCS;
|
||||||
|
|
||||||
|
import org.parceler.Parcel;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Parcel
|
||||||
|
@JsonObject
|
||||||
|
public class ChatOCSSingleMessage extends GenericOCS {
|
||||||
|
@JsonField(name = "data")
|
||||||
|
public ChatMessage data;
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Marcel Hibbe
|
||||||
|
* Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.models.json.chat;
|
||||||
|
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||||
|
|
||||||
|
import org.parceler.Parcel;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Parcel
|
||||||
|
@JsonObject
|
||||||
|
public class ChatOverallSingleMessage {
|
||||||
|
@JsonField(name = "ocs")
|
||||||
|
public ChatOCSSingleMessage ocs;
|
||||||
|
}
|
@ -110,8 +110,12 @@ public class Conversation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean canModerate(UserEntity conversationUser) {
|
public boolean canModerate(UserEntity conversationUser) {
|
||||||
return ((Participant.ParticipantType.OWNER.equals(participantType)
|
return (isParticipantOwnerOrModerator() && !isLockedOneToOne(conversationUser));
|
||||||
|| Participant.ParticipantType.MODERATOR.equals(participantType)) && !isLockedOneToOne(conversationUser));
|
}
|
||||||
|
|
||||||
|
public boolean isParticipantOwnerOrModerator() {
|
||||||
|
return Participant.ParticipantType.OWNER.equals(participantType)
|
||||||
|
|| Participant.ParticipantType.MODERATOR.equals(participantType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean shouldShowLobby(UserEntity conversationUser) {
|
public boolean shouldShowLobby(UserEntity conversationUser) {
|
||||||
@ -121,6 +125,7 @@ public class Conversation {
|
|||||||
public boolean isLobbyViewApplicable(UserEntity conversationUser) {
|
public boolean isLobbyViewApplicable(UserEntity conversationUser) {
|
||||||
return !canModerate(conversationUser) && (getType() == ConversationType.ROOM_GROUP_CALL || getType() == ConversationType.ROOM_PUBLIC_CALL);
|
return !canModerate(conversationUser) && (getType() == ConversationType.ROOM_GROUP_CALL || getType() == ConversationType.ROOM_PUBLIC_CALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNameEditable(UserEntity conversationUser) {
|
public boolean isNameEditable(UserEntity conversationUser) {
|
||||||
return (canModerate(conversationUser) && !ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL.equals(type));
|
return (canModerate(conversationUser) && !ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL.equals(type));
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.Syst
|
|||||||
"lobby_none" -> return LOBBY_NONE
|
"lobby_none" -> return LOBBY_NONE
|
||||||
"lobby_non_moderators" -> return LOBBY_NON_MODERATORS
|
"lobby_non_moderators" -> return LOBBY_NON_MODERATORS
|
||||||
"lobby_timer_reached" -> return LOBBY_OPEN_TO_EVERYONE
|
"lobby_timer_reached" -> return LOBBY_OPEN_TO_EVERYONE
|
||||||
|
"message_deleted" -> return PARENT_MESSAGE_DELETED
|
||||||
else -> return DUMMY
|
else -> return DUMMY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,4 +292,8 @@ public class ApiUtils {
|
|||||||
public static String getUrlForFileUpload(String baseUrl, String user, String attachmentFolder, String filename) {
|
public static String getUrlForFileUpload(String baseUrl, String user, String attachmentFolder, String filename) {
|
||||||
return baseUrl + "/remote.php/dav/files/" + user + attachmentFolder + "/" + filename;
|
return baseUrl + "/remote.php/dav/files/" + user + attachmentFolder + "/" + filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getUrlForMessageDeletion(String baseUrl, String token, String messageId) {
|
||||||
|
return baseUrl + ocsApiVersion + spreedApiVersion + "/chat/" + token + "/" + messageId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
25
app/src/main/res/drawable/ic_delete_white_24dp.xml
Normal file
25
app/src/main/res/drawable/ic_delete_white_24dp.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!--
|
||||||
|
~ Nextcloud Talk application
|
||||||
|
~
|
||||||
|
~ @author Mario Danic
|
||||||
|
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector android:autoMirrored="true" android:height="24dp"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FFFFFF" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||||
|
</vector>
|
@ -6,12 +6,17 @@
|
|||||||
android:id="@+id/action_copy_message"
|
android:id="@+id/action_copy_message"
|
||||||
android:icon="@drawable/ic_content_copy_white_24dp"
|
android:icon="@drawable/ic_content_copy_white_24dp"
|
||||||
android:title="@string/nc_copy_message"
|
android:title="@string/nc_copy_message"
|
||||||
app:showAsAction="always"/>
|
app:showAsAction="always" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_reply_to_message"
|
android:id="@+id/action_reply_to_message"
|
||||||
android:icon="@drawable/ic_reply_white_24dp"
|
android:icon="@drawable/ic_reply_white_24dp"
|
||||||
android:title="@string/nc_reply"
|
android:title="@string/nc_reply"
|
||||||
app:showAsAction="always"
|
app:showAsAction="always" />
|
||||||
/>
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_delete_message"
|
||||||
|
android:icon="@drawable/ic_delete_white_24dp"
|
||||||
|
android:title="@string/nc_delete_message"
|
||||||
|
app:showAsAction="always" />
|
||||||
</menu>
|
</menu>
|
@ -32,6 +32,7 @@
|
|||||||
<color name="nc_grey">@android:color/holo_purple</color>
|
<color name="nc_grey">@android:color/holo_purple</color>
|
||||||
<color name="bg_bottom_sheet">#222222</color>
|
<color name="bg_bottom_sheet">#222222</color>
|
||||||
<color name="bg_message_list_incoming_bubble">#484848</color>
|
<color name="bg_message_list_incoming_bubble">#484848</color>
|
||||||
|
<color name="bg_message_list_incoming_bubble_deleted">#66484848</color>
|
||||||
|
|
||||||
<color name="textColorMaxContrast">#8c8c8c</color>
|
<color name="textColorMaxContrast">#8c8c8c</color>
|
||||||
|
|
||||||
|
@ -59,6 +59,9 @@
|
|||||||
<color name="bg_dark_mention_chips">#333333</color>
|
<color name="bg_dark_mention_chips">#333333</color>
|
||||||
|
|
||||||
<color name="bg_message_list_incoming_bubble">#EFEFEF</color>
|
<color name="bg_message_list_incoming_bubble">#EFEFEF</color>
|
||||||
|
<color name="bg_message_list_incoming_bubble_deleted">#66EFEFEF</color>
|
||||||
|
<color name="bg_message_list_outcoming_bubble">@color/colorPrimary</color>
|
||||||
|
<color name="bg_message_list_outcoming_bubble_deleted">#800082C9</color>
|
||||||
|
|
||||||
<color name="bg_bottom_sheet">#46ffffff</color>
|
<color name="bg_bottom_sheet">#46ffffff</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Common -->
|
||||||
|
<string name="nc_common_error_sorry">Sorry, something went wrong!</string>
|
||||||
|
|
||||||
<!-- Bottom Navigation -->
|
<!-- Bottom Navigation -->
|
||||||
<string name="nc_settings">Settings</string>
|
<string name="nc_settings">Settings</string>
|
||||||
|
|
||||||
@ -320,6 +323,8 @@
|
|||||||
<string name="nc_99_plus">99+</string>
|
<string name="nc_99_plus">99+</string>
|
||||||
<string name="nc_copy_message">Copy</string>
|
<string name="nc_copy_message">Copy</string>
|
||||||
<string name="nc_reply">Reply</string>
|
<string name="nc_reply">Reply</string>
|
||||||
|
<string name="nc_delete_message">Delete</string>
|
||||||
|
<string name="nc_delete_message_leaked_to_matterbridge">Message deleted successfully, but it might have been leaked to other services</string>
|
||||||
|
|
||||||
<!-- Upload -->
|
<!-- Upload -->
|
||||||
<string name="nc_upload_local_file">Upload local file</string>
|
<string name="nc_upload_local_file">Upload local file</string>
|
||||||
@ -327,12 +332,13 @@
|
|||||||
<string name="nc_upload_failed">Failed to upload file</string>
|
<string name="nc_upload_failed">Failed to upload file</string>
|
||||||
<string name="nc_upload_choose_local_files">Choose files</string>
|
<string name="nc_upload_choose_local_files">Choose files</string>
|
||||||
|
|
||||||
<!-- Non-translatable strings -->
|
<!-- Phonebook Integration -->
|
||||||
|
|
||||||
<string name="path_password_strike_through" translatable="false"
|
|
||||||
tools:override="true">M3.27,4.27L19.74,20.74</string>
|
|
||||||
<string name="nc_settings_phone_book_integration_key" translatable="false">phone_book_integration</string>
|
<string name="nc_settings_phone_book_integration_key" translatable="false">phone_book_integration</string>
|
||||||
<string name="nc_settings_phone_book_integration_desc">Match contacts based on phone number to integrate Talk shortcut in phone book</string>
|
<string name="nc_settings_phone_book_integration_desc">Match contacts based on phone number to integrate Talk shortcut in phone book</string>
|
||||||
<string name="nc_settings_phone_book_integration_title">Phone book integration</string>
|
<string name="nc_settings_phone_book_integration_title">Phone book integration</string>
|
||||||
<string name="no_phone_book_integration_due_to_permissions">No phone book integration due to missing permissions</string>
|
<string name="no_phone_book_integration_due_to_permissions">No phone book integration due to missing permissions</string>
|
||||||
|
|
||||||
|
<!-- Non-translatable strings -->
|
||||||
|
<string name="path_password_strike_through" translatable="false"
|
||||||
|
tools:override="true">M3.27,4.27L19.74,20.74</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user