mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-19 19:49:33 +01:00
Merge pull request #4826 from nextcloud/feature/4712/addParticipantTo1to1
Feature/4712/add participant to1to1
This commit is contained in:
commit
a9090d4e71
@ -186,12 +186,10 @@ class MainActivity : BaseActivity(), ActionBarProvider {
|
|||||||
val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.API_V4, 1))
|
val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.API_V4, 1))
|
||||||
val credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token)
|
val credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token)
|
||||||
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||||
apiVersion,
|
version = apiVersion,
|
||||||
currentUser?.baseUrl!!,
|
baseUrl = currentUser?.baseUrl!!,
|
||||||
roomType,
|
roomType = roomType,
|
||||||
null,
|
invite = userId
|
||||||
userId,
|
|
||||||
null
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ncApi.createRoom(
|
ncApi.createRoom(
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
* Nextcloud Talk - Android Client
|
* Nextcloud Talk - Android Client
|
||||||
*
|
*
|
||||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||||
|
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.nextcloud.talk.api
|
package com.nextcloud.talk.api
|
||||||
|
|
||||||
|
import com.nextcloud.talk.conversationinfo.CreateRoomRequest
|
||||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
|
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
|
||||||
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.ChatOverallSingleMessage
|
||||||
@ -57,6 +59,13 @@ interface NcApiCoroutines {
|
|||||||
@QueryMap options: Map<String, String>?
|
@QueryMap options: Map<String, String>?
|
||||||
): RoomOverall
|
): RoomOverall
|
||||||
|
|
||||||
|
@POST
|
||||||
|
suspend fun createRoomWithBody(
|
||||||
|
@Header("Authorization") authorization: String?,
|
||||||
|
@Url url: String?,
|
||||||
|
@Body roomRequest: CreateRoomRequest
|
||||||
|
): RoomOverall
|
||||||
|
|
||||||
/*
|
/*
|
||||||
QueryMap items are as follows:
|
QueryMap items are as follows:
|
||||||
- "roomName" : "newName"
|
- "roomName" : "newName"
|
||||||
|
@ -45,6 +45,7 @@ import android.widget.ImageView
|
|||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.PopupMenu
|
import android.widget.PopupMenu
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
@ -1597,19 +1598,17 @@ class ChatActivity :
|
|||||||
private fun switchToRoom(token: String, startCallAfterRoomSwitch: Boolean, isVoiceOnlyCall: Boolean) {
|
private fun switchToRoom(token: String, startCallAfterRoomSwitch: Boolean, isVoiceOnlyCall: Boolean) {
|
||||||
if (conversationUser != null) {
|
if (conversationUser != null) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
if (currentConversation?.objectType == ConversationEnums.ObjectType.ROOM) {
|
val toastInfo = if (currentConversation?.objectType == ConversationEnums.ObjectType.ROOM) {
|
||||||
Snackbar.make(
|
context.resources.getString(R.string.switch_to_main_room)
|
||||||
binding.root,
|
|
||||||
context.resources.getString(R.string.switch_to_main_room),
|
|
||||||
Snackbar.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
} else {
|
} else {
|
||||||
Snackbar.make(
|
context.resources.getString(R.string.switch_to_breakout_room)
|
||||||
binding.root,
|
|
||||||
context.resources.getString(R.string.switch_to_breakout_room),
|
|
||||||
Snackbar.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
|
// do not replace with snackbar, as it would disappear with the activity switch
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
toastInfo,
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
@ -3167,12 +3166,10 @@ class ChatActivity :
|
|||||||
val apiVersion =
|
val apiVersion =
|
||||||
ApiUtils.getConversationApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V4, 1))
|
ApiUtils.getConversationApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V4, 1))
|
||||||
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||||
apiVersion,
|
version = apiVersion,
|
||||||
conversationUser?.baseUrl!!,
|
baseUrl = conversationUser?.baseUrl!!,
|
||||||
"1",
|
roomType = "1",
|
||||||
null,
|
invite = message?.user?.id?.substring(INVITE_LENGTH)
|
||||||
message?.user?.id?.substring(INVITE_LENGTH),
|
|
||||||
null
|
|
||||||
)
|
)
|
||||||
chatViewModel.createRoom(
|
chatViewModel.createRoom(
|
||||||
credentials!!,
|
credentials!!,
|
||||||
@ -3600,12 +3597,10 @@ class ChatActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||||
apiVersion,
|
version = apiVersion,
|
||||||
conversationUser?.baseUrl!!,
|
baseUrl = conversationUser?.baseUrl!!,
|
||||||
"1",
|
roomType = "1",
|
||||||
null,
|
invite = userMentionClickEvent.userId
|
||||||
userMentionClickEvent.userId,
|
|
||||||
null
|
|
||||||
)
|
)
|
||||||
|
|
||||||
chatViewModel.createRoom(
|
chatViewModel.createRoom(
|
||||||
@ -3712,12 +3707,11 @@ class ChatActivity :
|
|||||||
val apiVersion =
|
val apiVersion =
|
||||||
ApiUtils.getConversationApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V4, 1))
|
ApiUtils.getConversationApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V4, 1))
|
||||||
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||||
apiVersion,
|
version = apiVersion,
|
||||||
conversationUser?.baseUrl!!,
|
baseUrl = conversationUser?.baseUrl!!,
|
||||||
ROOM_TYPE_ONE_TO_ONE,
|
roomType = ROOM_TYPE_ONE_TO_ONE,
|
||||||
ACTOR_TYPE,
|
source = ACTOR_TYPE,
|
||||||
userId,
|
invite = userId
|
||||||
null
|
|
||||||
)
|
)
|
||||||
chatViewModel.createRoom(
|
chatViewModel.createRoom(
|
||||||
credentials!!,
|
credentials!!,
|
||||||
|
@ -71,12 +71,12 @@ class ContactsRepositoryImpl @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||||
apiVersion,
|
version = apiVersion,
|
||||||
_currentUser.baseUrl,
|
baseUrl = _currentUser.baseUrl,
|
||||||
roomType,
|
roomType = roomType,
|
||||||
sourceType,
|
source = sourceType,
|
||||||
userId,
|
invite = userId,
|
||||||
conversationName
|
conversationName = conversationName
|
||||||
)
|
)
|
||||||
val response = ncApiCoroutines.createRoom(
|
val response = ncApiCoroutines.createRoom(
|
||||||
credentials,
|
credentials,
|
||||||
|
@ -1,307 +0,0 @@
|
|||||||
/*
|
|
||||||
* Nextcloud Talk - Android Client
|
|
||||||
*
|
|
||||||
* SPDX-FileCopyrightText: 2023 Marcel Hibbe <dev@mhibbe.de>
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
package com.nextcloud.talk.conversation
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.os.Parcelable
|
|
||||||
import android.text.Editable
|
|
||||||
import android.text.TextUtils
|
|
||||||
import android.text.TextWatcher
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.core.content.res.ResourcesCompat
|
|
||||||
import androidx.fragment.app.DialogFragment
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.work.Data
|
|
||||||
import androidx.work.OneTimeWorkRequest
|
|
||||||
import androidx.work.WorkInfo
|
|
||||||
import androidx.work.WorkManager
|
|
||||||
import autodagger.AutoInjector
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
|
||||||
import com.nextcloud.talk.R
|
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
|
||||||
import com.nextcloud.talk.chat.ChatActivity
|
|
||||||
import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel
|
|
||||||
import com.nextcloud.talk.databinding.DialogCreateConversationBinding
|
|
||||||
import com.nextcloud.talk.jobs.AddParticipantsToConversation
|
|
||||||
import com.nextcloud.talk.models.json.conversations.ConversationEnums
|
|
||||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
|
||||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
|
||||||
import com.vanniktech.emoji.EmojiPopup
|
|
||||||
import org.greenrobot.eventbus.EventBus
|
|
||||||
import org.parceler.Parcels
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AutoInjector(NextcloudTalkApplication::class)
|
|
||||||
class CreateConversationDialogFragment : DialogFragment() {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var viewModelFactory: ViewModelProvider.Factory
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var viewThemeUtils: ViewThemeUtils
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var eventBus: EventBus
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var currentUserProvider: CurrentUserProviderNew
|
|
||||||
|
|
||||||
private lateinit var binding: DialogCreateConversationBinding
|
|
||||||
private lateinit var viewModel: ConversationViewModel
|
|
||||||
|
|
||||||
private var emojiPopup: EmojiPopup? = null
|
|
||||||
|
|
||||||
private var conversationType: ConversationEnums.ConversationType? = null
|
|
||||||
private var usersToInvite: ArrayList<String> = ArrayList()
|
|
||||||
private var groupsToInvite: ArrayList<String> = ArrayList()
|
|
||||||
private var emailsToInvite: ArrayList<String> = ArrayList()
|
|
||||||
private var circlesToInvite: ArrayList<String> = ArrayList()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this, viewModelFactory)[ConversationViewModel::class.java]
|
|
||||||
|
|
||||||
if (arguments?.containsKey(USERS_TO_INVITE) == true) {
|
|
||||||
usersToInvite = arguments?.getStringArrayList(USERS_TO_INVITE)!!
|
|
||||||
}
|
|
||||||
if (arguments?.containsKey(GROUPS_TO_INVITE) == true) {
|
|
||||||
groupsToInvite = arguments?.getStringArrayList(GROUPS_TO_INVITE)!!
|
|
||||||
}
|
|
||||||
if (arguments?.containsKey(EMAILS_TO_INVITE) == true) {
|
|
||||||
emailsToInvite = arguments?.getStringArrayList(EMAILS_TO_INVITE)!!
|
|
||||||
}
|
|
||||||
if (arguments?.containsKey(CIRCLES_TO_INVITE) == true) {
|
|
||||||
circlesToInvite = arguments?.getStringArrayList(CIRCLES_TO_INVITE)!!
|
|
||||||
}
|
|
||||||
if (arguments?.containsKey(KEY_CONVERSATION_TYPE) == true) {
|
|
||||||
conversationType = Parcels.unwrap(arguments?.getParcelable(KEY_CONVERSATION_TYPE))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("InflateParams")
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
||||||
binding = DialogCreateConversationBinding.inflate(layoutInflater)
|
|
||||||
|
|
||||||
val dialogBuilder = MaterialAlertDialogBuilder(binding.root.context)
|
|
||||||
.setTitle(resources.getString(R.string.create_conversation))
|
|
||||||
// listener is null for now to avoid closing after button was clicked.
|
|
||||||
// listener is set later in onStart
|
|
||||||
.setPositiveButton(R.string.nc_common_create, null)
|
|
||||||
.setNegativeButton(R.string.nc_common_dismiss, null)
|
|
||||||
.setView(binding.root)
|
|
||||||
viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.root.context, dialogBuilder)
|
|
||||||
|
|
||||||
return dialogBuilder.create()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
|
|
||||||
setupListeners()
|
|
||||||
setupStateObserver()
|
|
||||||
|
|
||||||
setupEmojiPopup()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStart() {
|
|
||||||
super.onStart()
|
|
||||||
|
|
||||||
val positiveButton = (dialog as AlertDialog).getButton(AlertDialog.BUTTON_POSITIVE)
|
|
||||||
positiveButton.isEnabled = false
|
|
||||||
positiveButton.setOnClickListener {
|
|
||||||
viewModel.createConversation(
|
|
||||||
binding.textEdit.text.toString(),
|
|
||||||
conversationType
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
themeDialog()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun themeDialog() {
|
|
||||||
viewThemeUtils.platform.themeDialog(binding.root)
|
|
||||||
viewThemeUtils.platform.colorTextButtons((dialog as AlertDialog).getButton(AlertDialog.BUTTON_POSITIVE))
|
|
||||||
viewThemeUtils.platform.colorTextButtons((dialog as AlertDialog).getButton(AlertDialog.BUTTON_NEGATIVE))
|
|
||||||
viewThemeUtils.material.colorTextInputLayout(binding.textInputLayout)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupEmojiPopup() {
|
|
||||||
emojiPopup = binding.let {
|
|
||||||
EmojiPopup(
|
|
||||||
rootView = requireView(),
|
|
||||||
editText = it.textEdit,
|
|
||||||
onEmojiPopupShownListener = {
|
|
||||||
viewThemeUtils.platform.colorImageView(it.smileyButton, ColorRole.PRIMARY)
|
|
||||||
},
|
|
||||||
onEmojiPopupDismissListener = {
|
|
||||||
it.smileyButton.imageTintList = ColorStateList.valueOf(
|
|
||||||
ResourcesCompat.getColor(
|
|
||||||
resources,
|
|
||||||
R.color.medium_emphasis_text,
|
|
||||||
context?.theme
|
|
||||||
)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onEmojiClickListener = {
|
|
||||||
binding.textEdit.editableText?.append(" ")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupListeners() {
|
|
||||||
binding.smileyButton.setOnClickListener { emojiPopup?.toggle() }
|
|
||||||
binding.textEdit.addTextChangedListener(object : TextWatcher {
|
|
||||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
|
||||||
// unused atm
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
|
||||||
// unused atm
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun afterTextChanged(s: Editable) {
|
|
||||||
val positiveButton = (dialog as AlertDialog).getButton(AlertDialog.BUTTON_POSITIVE)
|
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(s)) {
|
|
||||||
if (!positiveButton.isEnabled) {
|
|
||||||
positiveButton.isEnabled = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (positiveButton.isEnabled) {
|
|
||||||
positiveButton.isEnabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupStateObserver() {
|
|
||||||
viewModel.viewState.observe(viewLifecycleOwner) { state ->
|
|
||||||
when (state) {
|
|
||||||
is ConversationViewModel.InitialState -> {}
|
|
||||||
is ConversationViewModel.CreatingState -> {}
|
|
||||||
is ConversationViewModel.CreatingSuccessState -> addParticipants(state.roomToken)
|
|
||||||
is ConversationViewModel.CreatingFailedState -> {
|
|
||||||
Log.e(TAG, "Failed to create conversation")
|
|
||||||
showError()
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addParticipants(roomToken: String) {
|
|
||||||
val data = Data.Builder()
|
|
||||||
data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, currentUserProvider.currentUser.blockingGet().id!!)
|
|
||||||
data.putString(BundleKeys.KEY_TOKEN, roomToken)
|
|
||||||
data.putStringArray(BundleKeys.KEY_SELECTED_USERS, usersToInvite.toTypedArray())
|
|
||||||
data.putStringArray(BundleKeys.KEY_SELECTED_GROUPS, groupsToInvite.toTypedArray())
|
|
||||||
data.putStringArray(BundleKeys.KEY_SELECTED_EMAILS, emailsToInvite.toTypedArray())
|
|
||||||
data.putStringArray(BundleKeys.KEY_SELECTED_CIRCLES, circlesToInvite.toTypedArray())
|
|
||||||
|
|
||||||
val addParticipantsToConversationWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(
|
|
||||||
AddParticipantsToConversation::class.java
|
|
||||||
)
|
|
||||||
.setInputData(data.build())
|
|
||||||
.build()
|
|
||||||
|
|
||||||
WorkManager.getInstance(requireContext()).enqueue(addParticipantsToConversationWorker)
|
|
||||||
|
|
||||||
WorkManager.getInstance(requireContext()).getWorkInfoByIdLiveData(addParticipantsToConversationWorker.id)
|
|
||||||
.observeForever { workInfo: WorkInfo? ->
|
|
||||||
if (workInfo != null) {
|
|
||||||
when (workInfo.state) {
|
|
||||||
WorkInfo.State.RUNNING -> {
|
|
||||||
Log.d(TAG, "running AddParticipantsToConversation")
|
|
||||||
}
|
|
||||||
|
|
||||||
WorkInfo.State.SUCCEEDED -> {
|
|
||||||
Log.d(TAG, "success AddParticipantsToConversation")
|
|
||||||
initiateConversation(roomToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
WorkInfo.State.FAILED -> {
|
|
||||||
Log.e(TAG, "failed to AddParticipantsToConversation")
|
|
||||||
showError()
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initiateConversation(roomToken: String) {
|
|
||||||
activity?.let {
|
|
||||||
val bundle = Bundle()
|
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
|
||||||
val chatIntent = Intent(it, ChatActivity::class.java)
|
|
||||||
chatIntent.putExtras(bundle)
|
|
||||||
chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
|
||||||
startActivity(chatIntent)
|
|
||||||
}
|
|
||||||
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showError() {
|
|
||||||
dismiss()
|
|
||||||
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fragment creator
|
|
||||||
*/
|
|
||||||
companion object {
|
|
||||||
private val TAG = CreateConversationDialogFragment::class.java.simpleName
|
|
||||||
private const val USERS_TO_INVITE = "usersToInvite"
|
|
||||||
private const val GROUPS_TO_INVITE = "groupsToInvite"
|
|
||||||
private const val EMAILS_TO_INVITE = "emailsToInvite"
|
|
||||||
private const val CIRCLES_TO_INVITE = "circlesToInvite"
|
|
||||||
private const val KEY_CONVERSATION_TYPE = "keyConversationType"
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun newInstance(
|
|
||||||
usersToInvite: ArrayList<String>?,
|
|
||||||
groupsToInvite: ArrayList<String>?,
|
|
||||||
emailsToInvite: ArrayList<String>?,
|
|
||||||
circlesToInvite: ArrayList<String>?,
|
|
||||||
conversationType: Parcelable
|
|
||||||
): CreateConversationDialogFragment {
|
|
||||||
val args = Bundle()
|
|
||||||
args.putStringArrayList(USERS_TO_INVITE, usersToInvite)
|
|
||||||
args.putStringArrayList(GROUPS_TO_INVITE, groupsToInvite)
|
|
||||||
args.putStringArrayList(EMAILS_TO_INVITE, emailsToInvite)
|
|
||||||
args.putStringArrayList(CIRCLES_TO_INVITE, circlesToInvite)
|
|
||||||
args.putParcelable(KEY_CONVERSATION_TYPE, conversationType)
|
|
||||||
val fragment = CreateConversationDialogFragment()
|
|
||||||
fragment.arguments = args
|
|
||||||
return fragment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
/*
|
|
||||||
* Nextcloud Talk - Android Client
|
|
||||||
*
|
|
||||||
* SPDX-FileCopyrightText: 2023 Marcel Hibbe <dev@mhibbe.de>
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
package com.nextcloud.talk.conversation.repository
|
|
||||||
|
|
||||||
import com.nextcloud.talk.models.json.conversations.ConversationEnums
|
|
||||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
|
||||||
import io.reactivex.Observable
|
|
||||||
|
|
||||||
interface ConversationRepository {
|
|
||||||
|
|
||||||
fun createConversation(
|
|
||||||
roomName: String,
|
|
||||||
conversationType: ConversationEnums.ConversationType?
|
|
||||||
): Observable<RoomOverall>
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* Nextcloud Talk - Android Client
|
|
||||||
*
|
|
||||||
* SPDX-FileCopyrightText: 2023 Marcel Hibbe <dev@mhibbe.de>
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
package com.nextcloud.talk.conversation.repository
|
|
||||||
|
|
||||||
import com.nextcloud.talk.api.NcApi
|
|
||||||
import com.nextcloud.talk.data.user.model.User
|
|
||||||
import com.nextcloud.talk.models.RetrofitBucket
|
|
||||||
import com.nextcloud.talk.models.json.conversations.ConversationEnums
|
|
||||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
|
||||||
import com.nextcloud.talk.utils.ApiUtils
|
|
||||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
|
||||||
import io.reactivex.Observable
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
|
||||||
import io.reactivex.schedulers.Schedulers
|
|
||||||
|
|
||||||
class ConversationRepositoryImpl(private val ncApi: NcApi, currentUserProvider: CurrentUserProviderNew) :
|
|
||||||
ConversationRepository {
|
|
||||||
|
|
||||||
val currentUser: User = currentUserProvider.currentUser.blockingGet()
|
|
||||||
val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)!!
|
|
||||||
|
|
||||||
override fun createConversation(
|
|
||||||
roomName: String,
|
|
||||||
conversationType: ConversationEnums.ConversationType?
|
|
||||||
): Observable<RoomOverall> {
|
|
||||||
val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1))
|
|
||||||
|
|
||||||
val retrofitBucket: RetrofitBucket =
|
|
||||||
if (conversationType == ConversationEnums.ConversationType.ROOM_PUBLIC_CALL) {
|
|
||||||
ApiUtils.getRetrofitBucketForCreateRoom(
|
|
||||||
apiVersion,
|
|
||||||
currentUser.baseUrl!!,
|
|
||||||
ROOM_TYPE_PUBLIC,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
roomName
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
ApiUtils.getRetrofitBucketForCreateRoom(
|
|
||||||
apiVersion,
|
|
||||||
currentUser.baseUrl!!,
|
|
||||||
ROOM_TYPE_GROUP,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
roomName
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return ncApi.createRoom(credentials, retrofitBucket.url, retrofitBucket.queryMap)
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.retry(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val ROOM_TYPE_PUBLIC = "3"
|
|
||||||
private const val ROOM_TYPE_GROUP = "2"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* Nextcloud Talk - Android Client
|
|
||||||
*
|
|
||||||
* SPDX-FileCopyrightText: 2023 Marcel Hibbe <dev@mhibbe.de>
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
package com.nextcloud.talk.conversation.viewmodel
|
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import com.nextcloud.talk.conversation.repository.ConversationRepository
|
|
||||||
import com.nextcloud.talk.models.json.conversations.ConversationEnums
|
|
||||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
|
||||||
import io.reactivex.Observer
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
|
||||||
import io.reactivex.disposables.Disposable
|
|
||||||
import io.reactivex.schedulers.Schedulers
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class ConversationViewModel @Inject constructor(private val repository: ConversationRepository) : ViewModel() {
|
|
||||||
|
|
||||||
sealed class ViewState
|
|
||||||
object InitialState : ViewState()
|
|
||||||
|
|
||||||
object CreatingState : ViewState()
|
|
||||||
class CreatingSuccessState(val roomToken: String) : ViewState()
|
|
||||||
object CreatingFailedState : ViewState()
|
|
||||||
|
|
||||||
private val _viewState: MutableLiveData<ViewState> = MutableLiveData(
|
|
||||||
InitialState
|
|
||||||
)
|
|
||||||
val viewState: LiveData<ViewState>
|
|
||||||
get() = _viewState
|
|
||||||
|
|
||||||
private var disposable: Disposable? = null
|
|
||||||
|
|
||||||
override fun onCleared() {
|
|
||||||
super.onCleared()
|
|
||||||
disposable?.dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createConversation(roomName: String, conversationType: ConversationEnums.ConversationType?) {
|
|
||||||
_viewState.value = CreatingState
|
|
||||||
|
|
||||||
repository.createConversation(
|
|
||||||
roomName,
|
|
||||||
conversationType
|
|
||||||
)
|
|
||||||
.doOnSubscribe { disposable = it }
|
|
||||||
?.subscribeOn(Schedulers.io())
|
|
||||||
?.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
?.subscribe(CreateConversationObserver())
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class CreateConversationObserver : Observer<RoomOverall> {
|
|
||||||
override fun onSubscribe(d: Disposable) {
|
|
||||||
// unused atm
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNext(roomOverall: RoomOverall) {
|
|
||||||
val conversation = roomOverall.ocs!!.data
|
|
||||||
_viewState.value = CreatingSuccessState(conversation?.token!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
// dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onComplete() {
|
|
||||||
// dispose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val TAG = ConversationViewModel::class.java.simpleName
|
|
||||||
}
|
|
||||||
}
|
|
@ -97,12 +97,10 @@ class ConversationCreationRepositoryImpl @Inject constructor(
|
|||||||
|
|
||||||
override suspend fun createRoom(roomType: String, conversationName: String?): RoomOverall {
|
override suspend fun createRoom(roomType: String, conversationName: String?): RoomOverall {
|
||||||
val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||||
apiVersion,
|
version = apiVersion,
|
||||||
_currentUser.baseUrl,
|
baseUrl = _currentUser.baseUrl,
|
||||||
roomType,
|
roomType = roomType,
|
||||||
null,
|
conversationName = conversationName
|
||||||
null,
|
|
||||||
conversationName
|
|
||||||
)
|
)
|
||||||
val response = ncApiCoroutines.createRoom(
|
val response = ncApiCoroutines.createRoom(
|
||||||
credentials,
|
credentials,
|
||||||
|
@ -142,23 +142,6 @@ class ConversationCreationViewModel @Inject constructor(
|
|||||||
fun getImageUri(avatarId: String, requestBigSize: Boolean): String {
|
fun getImageUri(avatarId: String, requestBigSize: Boolean): String {
|
||||||
return repository.getImageUri(avatarId, requestBigSize)
|
return repository.getImageUri(avatarId, requestBigSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
|
||||||
fun createRoom(roomType: String, conversationName: String?) {
|
|
||||||
viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
val room = repository.createRoom(
|
|
||||||
roomType,
|
|
||||||
conversationName
|
|
||||||
)
|
|
||||||
|
|
||||||
val conversation: Conversation? = room.ocs?.data
|
|
||||||
roomViewState.value = RoomUIState.Success(conversation)
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
roomViewState.value = RoomUIState.Error(exception.message ?: "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class AllowGuestsUiState {
|
sealed class AllowGuestsUiState {
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
package com.nextcloud.talk.conversationinfo
|
package com.nextcloud.talk.conversationinfo
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
@ -66,7 +65,7 @@ import com.nextcloud.talk.extensions.loadConversationAvatar
|
|||||||
import com.nextcloud.talk.extensions.loadNoteToSelfAvatar
|
import com.nextcloud.talk.extensions.loadNoteToSelfAvatar
|
||||||
import com.nextcloud.talk.extensions.loadSystemAvatar
|
import com.nextcloud.talk.extensions.loadSystemAvatar
|
||||||
import com.nextcloud.talk.extensions.loadUserAvatar
|
import com.nextcloud.talk.extensions.loadUserAvatar
|
||||||
import com.nextcloud.talk.jobs.AddParticipantsToConversation
|
import com.nextcloud.talk.jobs.AddParticipantsToConversationWorker
|
||||||
import com.nextcloud.talk.jobs.DeleteConversationWorker
|
import com.nextcloud.talk.jobs.DeleteConversationWorker
|
||||||
import com.nextcloud.talk.jobs.LeaveConversationWorker
|
import com.nextcloud.talk.jobs.LeaveConversationWorker
|
||||||
import com.nextcloud.talk.models.domain.ConversationModel
|
import com.nextcloud.talk.models.domain.ConversationModel
|
||||||
@ -93,6 +92,7 @@ import com.nextcloud.talk.utils.DateUtils
|
|||||||
import com.nextcloud.talk.utils.ShareUtils
|
import com.nextcloud.talk.utils.ShareUtils
|
||||||
import com.nextcloud.talk.utils.SpreedFeatures
|
import com.nextcloud.talk.utils.SpreedFeatures
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||||
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||||
import com.nextcloud.talk.utils.preferences.preferencestorage.DatabaseStorageModule
|
import com.nextcloud.talk.utils.preferences.preferencestorage.DatabaseStorageModule
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||||
@ -142,6 +142,8 @@ class ConversationInfoActivity :
|
|||||||
private var adapter: FlexibleAdapter<ParticipantItem>? = null
|
private var adapter: FlexibleAdapter<ParticipantItem>? = null
|
||||||
private var userItems: MutableList<ParticipantItem> = ArrayList()
|
private var userItems: MutableList<ParticipantItem> = ArrayList()
|
||||||
|
|
||||||
|
private var startGroupChat: Boolean = false
|
||||||
|
|
||||||
private lateinit var optionsMenu: Menu
|
private lateinit var optionsMenu: Menu
|
||||||
|
|
||||||
private val workerData: Data?
|
private val workerData: Data?
|
||||||
@ -157,13 +159,22 @@ class ConversationInfoActivity :
|
|||||||
|
|
||||||
private val addParticipantsResult = registerForActivityResult(
|
private val addParticipantsResult = registerForActivityResult(
|
||||||
ActivityResultContracts.StartActivityForResult()
|
ActivityResultContracts.StartActivityForResult()
|
||||||
) {
|
) { it ->
|
||||||
executeIfResultOk(it) { intent ->
|
executeIfResultOk(it) { intent ->
|
||||||
val selectedParticipants =
|
val selectedAutocompleteUsers =
|
||||||
intent?.getParcelableArrayListExtraProvider<AutocompleteUser>("selectedParticipants")
|
intent?.getParcelableArrayListExtraProvider<AutocompleteUser>("selectedParticipants")
|
||||||
?: emptyList()
|
?: emptyList()
|
||||||
val participants = selectedParticipants.toMutableList()
|
|
||||||
addParticipantsToConversation(participants)
|
if (startGroupChat) {
|
||||||
|
viewModel.createRoomFromOneToOne(
|
||||||
|
conversationUser,
|
||||||
|
userItems.map { it.model },
|
||||||
|
selectedAutocompleteUsers,
|
||||||
|
conversationToken
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
addParticipantsToConversation(selectedAutocompleteUsers)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +217,14 @@ class ConversationInfoActivity :
|
|||||||
binding.deleteConversationAction.setOnClickListener { showDeleteConversationDialog() }
|
binding.deleteConversationAction.setOnClickListener { showDeleteConversationDialog() }
|
||||||
binding.leaveConversationAction.setOnClickListener { leaveConversation() }
|
binding.leaveConversationAction.setOnClickListener { leaveConversation() }
|
||||||
binding.clearConversationHistory.setOnClickListener { showClearHistoryDialog() }
|
binding.clearConversationHistory.setOnClickListener { showClearHistoryDialog() }
|
||||||
binding.addParticipantsAction.setOnClickListener { selectParticipantsToAdd() }
|
binding.addParticipantsAction.setOnClickListener {
|
||||||
|
startGroupChat = false
|
||||||
|
selectParticipantsToAdd()
|
||||||
|
}
|
||||||
|
binding.startGroupChat.setOnClickListener {
|
||||||
|
startGroupChat = true
|
||||||
|
selectParticipantsToAdd()
|
||||||
|
}
|
||||||
binding.listBansButton.setOnClickListener { listBans() }
|
binding.listBansButton.setOnClickListener { listBans() }
|
||||||
|
|
||||||
viewModel.getRoom(conversationUser, conversationToken)
|
viewModel.getRoom(conversationUser, conversationToken)
|
||||||
@ -222,47 +240,14 @@ class ConversationInfoActivity :
|
|||||||
|
|
||||||
private fun initObservers() {
|
private fun initObservers() {
|
||||||
initViewStateObserver()
|
initViewStateObserver()
|
||||||
|
initCapabilitiesObersver()
|
||||||
|
initRoomOberserver()
|
||||||
|
initBanActorObserver()
|
||||||
|
initConversationReadOnlyObserver()
|
||||||
|
initClearChatHistoryObserver()
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.getCapabilitiesViewState.observe(this) { state ->
|
private fun initClearChatHistoryObserver() {
|
||||||
when (state) {
|
|
||||||
is ConversationInfoViewModel.GetCapabilitiesSuccessState -> {
|
|
||||||
spreedCapabilities = state.spreedCapabilities
|
|
||||||
|
|
||||||
handleConversation()
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel.getBanActorState.observe(this) { state ->
|
|
||||||
when (state) {
|
|
||||||
is ConversationInfoViewModel.BanActorSuccessState -> {
|
|
||||||
getListOfParticipants() // Refresh the list of participants
|
|
||||||
}
|
|
||||||
|
|
||||||
ConversationInfoViewModel.BanActorErrorState -> {
|
|
||||||
Snackbar.make(binding.root, "Error banning actor", Snackbar.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel.getConversationReadOnlyState.observe(this) { state ->
|
|
||||||
when (state) {
|
|
||||||
is ConversationInfoViewModel.SetConversationReadOnlyViewState.Success -> {
|
|
||||||
}
|
|
||||||
|
|
||||||
is ConversationInfoViewModel.SetConversationReadOnlyViewState.Error -> {
|
|
||||||
Snackbar.make(binding.root, R.string.conversation_read_only_failed, Snackbar.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
is ConversationInfoViewModel.SetConversationReadOnlyViewState.None -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel.clearChatHistoryViewState.observe(this) { uiState ->
|
viewModel.clearChatHistoryViewState.observe(this) { uiState ->
|
||||||
when (uiState) {
|
when (uiState) {
|
||||||
is ConversationInfoViewModel.ClearChatHistoryViewState.None -> {
|
is ConversationInfoViewModel.ClearChatHistoryViewState.None -> {
|
||||||
@ -284,6 +269,73 @@ class ConversationInfoActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun initConversationReadOnlyObserver() {
|
||||||
|
viewModel.getConversationReadOnlyState.observe(this) { state ->
|
||||||
|
when (state) {
|
||||||
|
is ConversationInfoViewModel.SetConversationReadOnlyViewState.Success -> {
|
||||||
|
}
|
||||||
|
|
||||||
|
is ConversationInfoViewModel.SetConversationReadOnlyViewState.Error -> {
|
||||||
|
Snackbar.make(binding.root, R.string.conversation_read_only_failed, Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
is ConversationInfoViewModel.SetConversationReadOnlyViewState.None -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initBanActorObserver() {
|
||||||
|
viewModel.getBanActorState.observe(this) { state ->
|
||||||
|
when (state) {
|
||||||
|
is ConversationInfoViewModel.BanActorSuccessState -> {
|
||||||
|
getListOfParticipants() // Refresh the list of participants
|
||||||
|
}
|
||||||
|
|
||||||
|
ConversationInfoViewModel.BanActorErrorState -> {
|
||||||
|
Snackbar.make(binding.root, "Error banning actor", Snackbar.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initRoomOberserver() {
|
||||||
|
viewModel.createRoomViewState.observe(this) { state ->
|
||||||
|
when (state) {
|
||||||
|
is ConversationInfoViewModel.CreateRoomUIState.Success -> {
|
||||||
|
state.room.ocs?.data?.token?.let { token ->
|
||||||
|
val chatIntent = Intent(context, ChatActivity::class.java).apply {
|
||||||
|
putExtra(KEY_ROOM_TOKEN, token)
|
||||||
|
}
|
||||||
|
startActivity(chatIntent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is ConversationInfoViewModel.CreateRoomUIState.Error -> {
|
||||||
|
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initCapabilitiesObersver() {
|
||||||
|
viewModel.getCapabilitiesViewState.observe(this) { state ->
|
||||||
|
when (state) {
|
||||||
|
is ConversationInfoViewModel.GetCapabilitiesSuccessState -> {
|
||||||
|
spreedCapabilities = state.spreedCapabilities
|
||||||
|
|
||||||
|
handleConversation()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun initViewStateObserver() {
|
private fun initViewStateObserver() {
|
||||||
viewModel.viewState.observe(this) { state ->
|
viewModel.viewState.observe(this) { state ->
|
||||||
when (state) {
|
when (state) {
|
||||||
@ -673,7 +725,7 @@ class ConversationInfoActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun executeIfResultOk(result: ActivityResult, onResult: (intent: Intent?) -> Unit) {
|
private fun executeIfResultOk(result: ActivityResult, onResult: (intent: Intent?) -> Unit) {
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
if (result.resultCode == RESULT_OK) {
|
||||||
onResult(result.data)
|
onResult(result.data)
|
||||||
} else {
|
} else {
|
||||||
Log.e(ChatActivity.TAG, "resultCode for received intent was != ok")
|
Log.e(ChatActivity.TAG, "resultCode for received intent was != ok")
|
||||||
@ -706,17 +758,17 @@ class ConversationInfoActivity :
|
|||||||
addParticipantsResult.launch(intent)
|
addParticipantsResult.launch(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addParticipantsToConversation(participants: List<AutocompleteUser>) {
|
private fun addParticipantsToConversation(autocompleteUsers: List<AutocompleteUser>) {
|
||||||
val groupIdsArray: MutableSet<String> = HashSet()
|
val groupIdsArray: MutableSet<String> = HashSet()
|
||||||
val emailIdsArray: MutableSet<String> = HashSet()
|
val emailIdsArray: MutableSet<String> = HashSet()
|
||||||
val circleIdsArray: MutableSet<String> = HashSet()
|
val circleIdsArray: MutableSet<String> = HashSet()
|
||||||
val userIdsArray: MutableSet<String> = HashSet()
|
val userIdsArray: MutableSet<String> = HashSet()
|
||||||
|
|
||||||
participants.forEach { participant ->
|
autocompleteUsers.forEach { participant ->
|
||||||
when (participant.source) {
|
when (participant.source) {
|
||||||
Participant.ActorType.GROUPS.name.lowercase() -> groupIdsArray.add(participant.id!!)
|
GROUPS.name.lowercase() -> groupIdsArray.add(participant.id!!)
|
||||||
Participant.ActorType.EMAILS.name.lowercase() -> emailIdsArray.add(participant.id!!)
|
Participant.ActorType.EMAILS.name.lowercase() -> emailIdsArray.add(participant.id!!)
|
||||||
Participant.ActorType.CIRCLES.name.lowercase() -> circleIdsArray.add(participant.id!!)
|
CIRCLES.name.lowercase() -> circleIdsArray.add(participant.id!!)
|
||||||
else -> userIdsArray.add(participant.id!!)
|
else -> userIdsArray.add(participant.id!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -729,7 +781,7 @@ class ConversationInfoActivity :
|
|||||||
data.putStringArray(BundleKeys.KEY_SELECTED_EMAILS, emailIdsArray.toTypedArray())
|
data.putStringArray(BundleKeys.KEY_SELECTED_EMAILS, emailIdsArray.toTypedArray())
|
||||||
data.putStringArray(BundleKeys.KEY_SELECTED_CIRCLES, circleIdsArray.toTypedArray())
|
data.putStringArray(BundleKeys.KEY_SELECTED_CIRCLES, circleIdsArray.toTypedArray())
|
||||||
val addParticipantsToConversationWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(
|
val addParticipantsToConversationWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(
|
||||||
AddParticipantsToConversation::class.java
|
AddParticipantsToConversationWorker::class.java
|
||||||
).setInputData(data.build()).build()
|
).setInputData(data.build()).build()
|
||||||
WorkManager.getInstance().enqueue(addParticipantsToConversationWorker)
|
WorkManager.getInstance().enqueue(addParticipantsToConversationWorker)
|
||||||
|
|
||||||
@ -865,7 +917,12 @@ class ConversationInfoActivity :
|
|||||||
binding.sharedItems.visibility = GONE
|
binding.sharedItems.visibility = GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConversationUtils.canModerate(conversationCopy, spreedCapabilities)) {
|
if (conversation!!.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL &&
|
||||||
|
hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.CONVERSATION_CREATION_ALL)
|
||||||
|
) {
|
||||||
|
binding.addParticipantsAction.visibility = GONE
|
||||||
|
binding.startGroupChat.visibility = VISIBLE
|
||||||
|
} else if (ConversationUtils.canModerate(conversationCopy, spreedCapabilities)) {
|
||||||
binding.addParticipantsAction.visibility = VISIBLE
|
binding.addParticipantsAction.visibility = VISIBLE
|
||||||
if (hasSpreedFeatureCapability(
|
if (hasSpreedFeatureCapability(
|
||||||
spreedCapabilities,
|
spreedCapabilities,
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.conversationinfo
|
||||||
|
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||||
|
|
||||||
|
@JsonObject
|
||||||
|
data class CreateRoomRequest(
|
||||||
|
@JsonField(name = ["roomType"])
|
||||||
|
var roomType: String,
|
||||||
|
@JsonField(name = ["roomName"])
|
||||||
|
var roomName: String? = null,
|
||||||
|
@JsonField(name = ["objectType"])
|
||||||
|
var objectType: String? = null,
|
||||||
|
@JsonField(name = ["objectId"])
|
||||||
|
var objectId: String? = null,
|
||||||
|
@JsonField(name = ["password"])
|
||||||
|
var password: String? = null,
|
||||||
|
@JsonField(name = ["readOnly"])
|
||||||
|
var readOnly: Int,
|
||||||
|
@JsonField(name = ["listable"])
|
||||||
|
var listable: Int,
|
||||||
|
@JsonField(name = ["messageExpiration"])
|
||||||
|
var messageExpiration: Int? = null,
|
||||||
|
@JsonField(name = ["lobbyState"])
|
||||||
|
var lobbyState: Int? = null,
|
||||||
|
@JsonField(name = ["lobbyTimer"])
|
||||||
|
var lobbyTimer: Int,
|
||||||
|
@JsonField(name = ["sipEnabled"])
|
||||||
|
var sipEnabled: Int,
|
||||||
|
@JsonField(name = ["permissions"])
|
||||||
|
var permissions: Int,
|
||||||
|
@JsonField(name = ["recordingConsent"])
|
||||||
|
var recordingConsent: Int,
|
||||||
|
@JsonField(name = ["mentionPermissions"])
|
||||||
|
var mentionPermissions: Int,
|
||||||
|
@JsonField(name = ["description"])
|
||||||
|
var description: String? = null,
|
||||||
|
@JsonField(name = ["emoji"])
|
||||||
|
var emoji: String? = null,
|
||||||
|
@JsonField(name = ["avatarColor"])
|
||||||
|
var avatarColor: String? = null,
|
||||||
|
@JsonField(name = ["participants"])
|
||||||
|
var participants: Participants? = null
|
||||||
|
) {
|
||||||
|
constructor() : this(
|
||||||
|
0.toString(),
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
Participants()
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.conversationinfo
|
||||||
|
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||||
|
|
||||||
|
@JsonObject
|
||||||
|
data class Participants(
|
||||||
|
@JsonField(name = ["users"])
|
||||||
|
var users: MutableList<String> = arrayListOf(),
|
||||||
|
@JsonField(name = ["federated_users"])
|
||||||
|
var federatedUsers: MutableList<String> = arrayListOf(),
|
||||||
|
@JsonField(name = ["groups"])
|
||||||
|
var groups: MutableList<String> = arrayListOf(),
|
||||||
|
@JsonField(name = ["emails"])
|
||||||
|
var emails: MutableList<String> = arrayListOf(),
|
||||||
|
@JsonField(name = ["phones"])
|
||||||
|
var phones: MutableList<String> = arrayListOf(),
|
||||||
|
@JsonField(name = ["teams"])
|
||||||
|
var teams: MutableList<String> = arrayListOf()
|
||||||
|
)
|
@ -15,12 +15,23 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.nextcloud.talk.chat.data.network.ChatNetworkDataSource
|
import com.nextcloud.talk.chat.data.network.ChatNetworkDataSource
|
||||||
|
import com.nextcloud.talk.conversationinfo.CreateRoomRequest
|
||||||
|
import com.nextcloud.talk.conversationinfo.Participants
|
||||||
import com.nextcloud.talk.data.user.model.User
|
import com.nextcloud.talk.data.user.model.User
|
||||||
import com.nextcloud.talk.models.domain.ConversationModel
|
import com.nextcloud.talk.models.domain.ConversationModel
|
||||||
|
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
|
||||||
import com.nextcloud.talk.models.json.capabilities.SpreedCapability
|
import com.nextcloud.talk.models.json.capabilities.SpreedCapability
|
||||||
|
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||||
|
import com.nextcloud.talk.models.json.participants.Participant
|
||||||
|
import com.nextcloud.talk.models.json.participants.Participant.ActorType.CIRCLES
|
||||||
|
import com.nextcloud.talk.models.json.participants.Participant.ActorType.EMAILS
|
||||||
|
import com.nextcloud.talk.models.json.participants.Participant.ActorType.FEDERATED
|
||||||
|
import com.nextcloud.talk.models.json.participants.Participant.ActorType.GROUPS
|
||||||
import com.nextcloud.talk.models.json.participants.TalkBan
|
import com.nextcloud.talk.models.json.participants.TalkBan
|
||||||
import com.nextcloud.talk.repositories.conversations.ConversationsRepository
|
import com.nextcloud.talk.repositories.conversations.ConversationsRepository
|
||||||
import com.nextcloud.talk.utils.ApiUtils
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
|
import com.nextcloud.talk.utils.ApiUtils.getUrlForRooms
|
||||||
|
import com.nextcloud.talk.utils.DisplayUtils
|
||||||
import io.reactivex.Observer
|
import io.reactivex.Observer
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
@ -112,6 +123,10 @@ class ConversationInfoViewModel @Inject constructor(
|
|||||||
val getConversationReadOnlyState: LiveData<SetConversationReadOnlyViewState>
|
val getConversationReadOnlyState: LiveData<SetConversationReadOnlyViewState>
|
||||||
get() = _getConversationReadOnlyState
|
get() = _getConversationReadOnlyState
|
||||||
|
|
||||||
|
private val _createRoomViewState = MutableLiveData<CreateRoomUIState>(CreateRoomUIState.None)
|
||||||
|
val createRoomViewState: LiveData<CreateRoomUIState>
|
||||||
|
get() = _createRoomViewState
|
||||||
|
|
||||||
fun getRoom(user: User, token: String) {
|
fun getRoom(user: User, token: String) {
|
||||||
_viewState.value = GetRoomStartState
|
_viewState.value = GetRoomStartState
|
||||||
chatNetworkDataSource.getRoom(user, token)
|
chatNetworkDataSource.getRoom(user, token)
|
||||||
@ -120,6 +135,69 @@ class ConversationInfoViewModel @Inject constructor(
|
|||||||
?.subscribe(GetRoomObserver())
|
?.subscribe(GetRoomObserver())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||||
|
fun createRoomFromOneToOne(
|
||||||
|
user: User,
|
||||||
|
userItems: List<Participant>,
|
||||||
|
autocompleteUsers: List<AutocompleteUser>,
|
||||||
|
roomToken: String
|
||||||
|
) {
|
||||||
|
val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, 1))
|
||||||
|
val url = getUrlForRooms(apiVersion, user.baseUrl!!)
|
||||||
|
val credentials = ApiUtils.getCredentials(user.username, user.token)!!
|
||||||
|
|
||||||
|
val participantsBody = convertAutocompleteUserToParticipant(autocompleteUsers)
|
||||||
|
|
||||||
|
val body = CreateRoomRequest(
|
||||||
|
roomName = createConversationNameByParticipants(
|
||||||
|
userItems.map { it.displayName },
|
||||||
|
autocompleteUsers.map { it.label }
|
||||||
|
),
|
||||||
|
roomType = GROUP_CONVERSATION_TYPE,
|
||||||
|
readOnly = 0,
|
||||||
|
listable = 1,
|
||||||
|
lobbyTimer = 0,
|
||||||
|
sipEnabled = 0,
|
||||||
|
permissions = 0,
|
||||||
|
recordingConsent = 0,
|
||||||
|
mentionPermissions = 0,
|
||||||
|
participants = participantsBody,
|
||||||
|
objectType = EXTENDED_CONVERSATION,
|
||||||
|
objectId = roomToken
|
||||||
|
)
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val roomOverall = conversationsRepository.createRoom(
|
||||||
|
credentials,
|
||||||
|
url,
|
||||||
|
body
|
||||||
|
)
|
||||||
|
_createRoomViewState.value = CreateRoomUIState.Success(roomOverall)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to create room", e)
|
||||||
|
_createRoomViewState.value = CreateRoomUIState.Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun convertAutocompleteUserToParticipant(autocompleteUsers: List<AutocompleteUser>): Participants {
|
||||||
|
val participants = Participants()
|
||||||
|
|
||||||
|
autocompleteUsers.forEach { autocompleteUser ->
|
||||||
|
when (autocompleteUser.source) {
|
||||||
|
GROUPS.name.lowercase() -> participants.groups.add(autocompleteUser.id!!)
|
||||||
|
EMAILS.name.lowercase() -> participants.emails.add(autocompleteUser.id!!)
|
||||||
|
CIRCLES.name.lowercase() -> participants.teams.add(autocompleteUser.id!!)
|
||||||
|
FEDERATED.name.lowercase() -> participants.federatedUsers.add(autocompleteUser.id!!)
|
||||||
|
"phones".lowercase() -> participants.phones.add(autocompleteUser.id!!)
|
||||||
|
else -> participants.users.add(autocompleteUser.id!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return participants
|
||||||
|
}
|
||||||
|
|
||||||
fun getCapabilities(user: User, token: String, conversationModel: ConversationModel) {
|
fun getCapabilities(user: User, token: String, conversationModel: ConversationModel) {
|
||||||
_getCapabilitiesViewState.value = GetCapabilitiesStartState
|
_getCapabilitiesViewState.value = GetCapabilitiesStartState
|
||||||
|
|
||||||
@ -280,6 +358,26 @@ class ConversationInfoViewModel @Inject constructor(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = ConversationInfoViewModel::class.simpleName
|
private val TAG = ConversationInfoViewModel::class.simpleName
|
||||||
|
private const val NEW_CONVERSATION_PARTICIPANTS_SEPARATOR = ", "
|
||||||
|
private const val EXTENDED_CONVERSATION = "extended_conversation"
|
||||||
|
private const val GROUP_CONVERSATION_TYPE = "2"
|
||||||
|
private const val MAX_ROOM_NAME_LENGTH = 255
|
||||||
|
|
||||||
|
fun createConversationNameByParticipants(
|
||||||
|
originalParticipants: List<String?>,
|
||||||
|
allParticipants: List<String?>
|
||||||
|
): String {
|
||||||
|
fun List<String?>.sortedJoined() =
|
||||||
|
sortedBy { it?.lowercase() }
|
||||||
|
.joinToString(NEW_CONVERSATION_PARTICIPANTS_SEPARATOR)
|
||||||
|
|
||||||
|
val addedParticipants = allParticipants - originalParticipants.toSet()
|
||||||
|
val conversationName = originalParticipants.mapNotNull { it }.sortedJoined() +
|
||||||
|
NEW_CONVERSATION_PARTICIPANTS_SEPARATOR +
|
||||||
|
addedParticipants.mapNotNull { it }.sortedJoined()
|
||||||
|
|
||||||
|
return DisplayUtils.ellipsize(conversationName, MAX_ROOM_NAME_LENGTH)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class ClearChatHistoryViewState {
|
sealed class ClearChatHistoryViewState {
|
||||||
@ -300,6 +398,12 @@ class ConversationInfoViewModel @Inject constructor(
|
|||||||
data class Error(val exception: Exception) : AllowGuestsUIState()
|
data class Error(val exception: Exception) : AllowGuestsUIState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class CreateRoomUIState {
|
||||||
|
data object None : CreateRoomUIState()
|
||||||
|
data class Success(val room: RoomOverall) : CreateRoomUIState()
|
||||||
|
data class Error(val exception: Exception) : CreateRoomUIState()
|
||||||
|
}
|
||||||
|
|
||||||
sealed class PasswordUiState {
|
sealed class PasswordUiState {
|
||||||
data object None : PasswordUiState()
|
data object None : PasswordUiState()
|
||||||
data object Success : PasswordUiState()
|
data object Success : PasswordUiState()
|
||||||
|
@ -17,8 +17,6 @@ import com.nextcloud.talk.chat.data.network.OfflineFirstChatRepository
|
|||||||
import com.nextcloud.talk.chat.data.network.RetrofitChatNetwork
|
import com.nextcloud.talk.chat.data.network.RetrofitChatNetwork
|
||||||
import com.nextcloud.talk.contacts.ContactsRepository
|
import com.nextcloud.talk.contacts.ContactsRepository
|
||||||
import com.nextcloud.talk.contacts.ContactsRepositoryImpl
|
import com.nextcloud.talk.contacts.ContactsRepositoryImpl
|
||||||
import com.nextcloud.talk.conversation.repository.ConversationRepository
|
|
||||||
import com.nextcloud.talk.conversation.repository.ConversationRepositoryImpl
|
|
||||||
import com.nextcloud.talk.conversationcreation.ConversationCreationRepository
|
import com.nextcloud.talk.conversationcreation.ConversationCreationRepository
|
||||||
import com.nextcloud.talk.conversationcreation.ConversationCreationRepositoryImpl
|
import com.nextcloud.talk.conversationcreation.ConversationCreationRepositoryImpl
|
||||||
import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepository
|
import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepository
|
||||||
@ -142,10 +140,6 @@ class RepositoryModule {
|
|||||||
return ConversationInfoEditRepositoryImpl(ncApi, ncApiCoroutines, userProvider)
|
return ConversationInfoEditRepositoryImpl(ncApi, ncApiCoroutines, userProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
fun provideConversationRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew): ConversationRepository =
|
|
||||||
ConversationRepositoryImpl(ncApi, userProvider)
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
fun provideInvitationsRepository(ncApi: NcApi): InvitationsRepository = InvitationsRepositoryImpl(ncApi)
|
fun provideInvitationsRepository(ncApi: NcApi): InvitationsRepository = InvitationsRepositoryImpl(ncApi)
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ import androidx.lifecycle.ViewModelProvider
|
|||||||
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
|
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
|
||||||
import com.nextcloud.talk.chat.viewmodels.MessageInputViewModel
|
import com.nextcloud.talk.chat.viewmodels.MessageInputViewModel
|
||||||
import com.nextcloud.talk.contacts.ContactsViewModel
|
import com.nextcloud.talk.contacts.ContactsViewModel
|
||||||
import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel
|
|
||||||
import com.nextcloud.talk.conversationcreation.ConversationCreationViewModel
|
import com.nextcloud.talk.conversationcreation.ConversationCreationViewModel
|
||||||
import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel
|
import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel
|
||||||
import com.nextcloud.talk.conversationinfoedit.viewmodel.ConversationInfoEditViewModel
|
import com.nextcloud.talk.conversationinfoedit.viewmodel.ConversationInfoEditViewModel
|
||||||
@ -135,11 +134,6 @@ abstract class ViewModelModule {
|
|||||||
@ViewModelKey(ConversationInfoEditViewModel::class)
|
@ViewModelKey(ConversationInfoEditViewModel::class)
|
||||||
abstract fun conversationInfoEditViewModel(viewModel: ConversationInfoEditViewModel): ViewModel
|
abstract fun conversationInfoEditViewModel(viewModel: ConversationInfoEditViewModel): ViewModel
|
||||||
|
|
||||||
@Binds
|
|
||||||
@IntoMap
|
|
||||||
@ViewModelKey(ConversationViewModel::class)
|
|
||||||
abstract fun conversationViewModel(viewModel: ConversationViewModel): ViewModel
|
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@ViewModelKey(InvitationsViewModel::class)
|
@ViewModelKey(InvitationsViewModel::class)
|
||||||
|
@ -28,7 +28,7 @@ import autodagger.AutoInjector;
|
|||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
@AutoInjector(NextcloudTalkApplication.class)
|
@AutoInjector(NextcloudTalkApplication.class)
|
||||||
public class AddParticipantsToConversation extends Worker {
|
public class AddParticipantsToConversationWorker extends Worker {
|
||||||
@Inject
|
@Inject
|
||||||
NcApi ncApi;
|
NcApi ncApi;
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ public class AddParticipantsToConversation extends Worker {
|
|||||||
@Inject
|
@Inject
|
||||||
EventBus eventBus;
|
EventBus eventBus;
|
||||||
|
|
||||||
public AddParticipantsToConversation(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
public AddParticipantsToConversationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
||||||
super(context, workerParams);
|
super(context, workerParams);
|
||||||
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
|
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
|
||||||
}
|
}
|
@ -7,6 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.nextcloud.talk.repositories.conversations
|
package com.nextcloud.talk.repositories.conversations
|
||||||
|
|
||||||
|
import com.nextcloud.talk.conversationinfo.CreateRoomRequest
|
||||||
|
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||||
import com.nextcloud.talk.models.json.participants.TalkBan
|
import com.nextcloud.talk.models.json.participants.TalkBan
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
@ -42,4 +44,6 @@ interface ConversationsRepository {
|
|||||||
suspend fun setConversationReadOnly(roomToken: String, state: Int): GenericOverall
|
suspend fun setConversationReadOnly(roomToken: String, state: Int): GenericOverall
|
||||||
|
|
||||||
suspend fun clearChatHistory(apiVersion: Int, roomToken: String): GenericOverall
|
suspend fun clearChatHistory(apiVersion: Int, roomToken: String): GenericOverall
|
||||||
|
|
||||||
|
suspend fun createRoom(credentials: String, url: String, body: CreateRoomRequest): RoomOverall
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,9 @@ package com.nextcloud.talk.repositories.conversations
|
|||||||
|
|
||||||
import com.nextcloud.talk.api.NcApi
|
import com.nextcloud.talk.api.NcApi
|
||||||
import com.nextcloud.talk.api.NcApiCoroutines
|
import com.nextcloud.talk.api.NcApiCoroutines
|
||||||
|
import com.nextcloud.talk.conversationinfo.CreateRoomRequest
|
||||||
import com.nextcloud.talk.data.user.model.User
|
import com.nextcloud.talk.data.user.model.User
|
||||||
|
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||||
import com.nextcloud.talk.models.json.participants.TalkBan
|
import com.nextcloud.talk.models.json.participants.TalkBan
|
||||||
import com.nextcloud.talk.repositories.conversations.ConversationsRepository.ResendInvitationsResult
|
import com.nextcloud.talk.repositories.conversations.ConversationsRepository.ResendInvitationsResult
|
||||||
@ -105,6 +107,15 @@ class ConversationsRepositoryImpl(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun createRoom(credentials: String, url: String, body: CreateRoomRequest): RoomOverall {
|
||||||
|
val response = coroutineApi.createRoomWithBody(
|
||||||
|
credentials,
|
||||||
|
url,
|
||||||
|
body
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun banActor(
|
override suspend fun banActor(
|
||||||
credentials: String,
|
credentials: String,
|
||||||
url: String,
|
url: String,
|
||||||
|
@ -118,12 +118,10 @@ class ProfileBottomSheet(val ncApi: NcApi, val userModel: User, val viewThemeUti
|
|||||||
val apiVersion =
|
val apiVersion =
|
||||||
ApiUtils.getConversationApiVersion(userModel, intArrayOf(ApiUtils.API_V4, 1))
|
ApiUtils.getConversationApiVersion(userModel, intArrayOf(ApiUtils.API_V4, 1))
|
||||||
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||||
apiVersion,
|
version = apiVersion,
|
||||||
userModel.baseUrl!!,
|
baseUrl = userModel.baseUrl!!,
|
||||||
"1",
|
roomType = "1",
|
||||||
null,
|
invite = userId
|
||||||
userId,
|
|
||||||
null
|
|
||||||
)
|
)
|
||||||
val credentials = ApiUtils.getCredentials(userModel.username, userModel.token)
|
val credentials = ApiUtils.getCredentials(userModel.username, userModel.token)
|
||||||
ncApi.createRoom(
|
ncApi.createRoom(
|
||||||
|
@ -298,25 +298,19 @@ object ApiUtils {
|
|||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
fun getRetrofitBucketForCreateRoom(
|
fun getRetrofitBucketForCreateRoom(
|
||||||
version: Int,
|
version: Int,
|
||||||
baseUrl: String?,
|
|
||||||
roomType: String,
|
roomType: String,
|
||||||
source: String?,
|
baseUrl: String? = null,
|
||||||
invite: String?,
|
source: String? = null,
|
||||||
conversationName: String?
|
invite: String? = null,
|
||||||
|
conversationName: String? = null
|
||||||
): RetrofitBucket {
|
): RetrofitBucket {
|
||||||
val retrofitBucket = RetrofitBucket()
|
val retrofitBucket = RetrofitBucket()
|
||||||
retrofitBucket.url = getUrlForRooms(version, baseUrl)
|
retrofitBucket.url = getUrlForRooms(version, baseUrl)
|
||||||
val queryMap: MutableMap<String, String> = HashMap()
|
val queryMap: MutableMap<String, String> = HashMap()
|
||||||
queryMap["roomType"] = roomType
|
queryMap["roomType"] = roomType
|
||||||
if (invite != null) {
|
invite?.let { queryMap["invite"] = it }
|
||||||
queryMap["invite"] = invite
|
source?.let { queryMap["source"] = it }
|
||||||
}
|
conversationName?.let { queryMap["roomName"] = it }
|
||||||
if (source != null) {
|
|
||||||
queryMap["source"] = source
|
|
||||||
}
|
|
||||||
if (conversationName != null) {
|
|
||||||
queryMap["roomName"] = conversationName
|
|
||||||
}
|
|
||||||
retrofitBucket.queryMap = queryMap
|
retrofitBucket.queryMap = queryMap
|
||||||
return retrofitBucket
|
return retrofitBucket
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,8 @@ enum class SpreedFeatures(val value: String) {
|
|||||||
DELETE_MESSAGES_UNLIMITED("delete-messages-unlimited"),
|
DELETE_MESSAGES_UNLIMITED("delete-messages-unlimited"),
|
||||||
BAN_V1("ban-v1"),
|
BAN_V1("ban-v1"),
|
||||||
EDIT_MESSAGES_NOTE_TO_SELF("edit-messages-note-to-self"),
|
EDIT_MESSAGES_NOTE_TO_SELF("edit-messages-note-to-self"),
|
||||||
ARCHIVE_CONVERSATIONS("archived-conversations-v2")
|
ARCHIVE_CONVERSATIONS("archived-conversations-v2"),
|
||||||
|
CONVERSATION_CREATION_ALL("conversation-creation-all")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("TooManyFunctions")
|
@Suppress("TooManyFunctions")
|
||||||
|
@ -392,6 +392,34 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/startGroupChat"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/standard_quarter_margin"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="@dimen/standard_padding"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginEnd="@dimen/standard_margin"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:src="@drawable/ic_people_group_black_24px"
|
||||||
|
app:tint="@color/grey_600" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:text="@string/nc_start_group_chat"
|
||||||
|
android:textSize="@dimen/two_line_primary_text_size" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recycler_view"
|
android:id="@+id/recycler_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.conversationinfo.viewmodel
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
|
||||||
|
class ConversationInfoViewModelTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `createConversationNameByParticipants should combine names correctly`() {
|
||||||
|
val original = listOf("Dave", null, "Charlie")
|
||||||
|
val all = listOf("Bob", "Charlie", "Dave", "Alice", null, "Simon")
|
||||||
|
|
||||||
|
val expectedName = "Charlie, Dave, Alice, Bob, Simon"
|
||||||
|
val result = ConversationInfoViewModel.createConversationNameByParticipants(original, all)
|
||||||
|
|
||||||
|
assertEquals(expectedName, result)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user