From cd01c80d7110b49a0fba46fcd3d2709679fb8ff6 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Wed, 22 May 2024 16:09:12 +0200 Subject: [PATCH 01/23] ConversationCreationActivity UI Signed-off-by: sowjanyakch --- app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 3 + .../com/nextcloud/talk/api/NcApiCoroutines.kt | 26 ++ .../talk/contacts/ContactsActivityCompose.kt | 9 +- .../contacts/ContactsActivityViewModel.kt | 104 ++++++++ .../ConversationCreationActivity.kt | 246 ++++++++++++++++++ .../ConversationCreationRepository.kt | 15 ++ .../ConversationCreationRepositoryImpl.kt | 48 ++++ .../ConversationCreationViewModel.kt | 57 ++++ .../talk/dagger/modules/RepositoryModule.kt | 10 + .../talk/dagger/modules/ViewModelModule.kt | 6 + .../res/drawable/baseline_tag_faces_24.xml | 18 ++ app/src/main/res/values/strings.xml | 1 + .../repository/FakeRepositorySuccess.kt | 4 + gradle/verification-metadata.xml | 138 +++++++++- 15 files changed, 680 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityViewModel.kt create mode 100644 app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt create mode 100644 app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt create mode 100644 app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt create mode 100644 app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt create mode 100644 app/src/main/res/drawable/baseline_tag_faces_24.xml diff --git a/app/build.gradle b/app/build.gradle index 9937940693..469f4a39ba 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -309,7 +309,7 @@ dependencies { //compose implementation(platform("androidx.compose:compose-bom:2024.09.00")) implementation("androidx.compose.ui:ui") - implementation 'androidx.compose.material3:material3' + implementation 'androidx.compose.material3:material3:1.2.1' implementation("androidx.compose.ui:ui-tooling-preview") implementation 'androidx.activity:activity-compose:1.9.2' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.5' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 410dd82da7..256e2b2b5f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -129,6 +129,9 @@ + + diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt index c330c33cb7..ccca322a76 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt +++ b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt @@ -9,9 +9,13 @@ package com.nextcloud.talk.api import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall import com.nextcloud.talk.models.json.conversations.RoomOverall +import com.nextcloud.talk.models.json.generic.GenericOverall +import retrofit2.http.Field +import retrofit2.http.FormUrlEncoded import retrofit2.http.GET import retrofit2.http.Header import retrofit2.http.POST +import retrofit2.http.PUT import retrofit2.http.Query import retrofit2.http.QueryMap import retrofit2.http.Url @@ -39,4 +43,26 @@ interface NcApiCoroutines { @Url url: String?, @QueryMap options: Map? ): RoomOverall + + /* + QueryMap items are as follows: + - "roomName" : "newName" + + Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken + */ + @FormUrlEncoded + @PUT + fun renameRoom( + @Header("Authorization") authorization: String?, + @Url url: String, + @Field("roomName") roomName: String? + ): GenericOverall + + @FormUrlEncoded + @PUT + fun setConversationDescription( + @Header("Authorization") authorization: String?, + @Url url: String, + @Field("description") description: String? + ): GenericOverall } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt index fe828a4b7e..d9234bac9c 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt @@ -64,6 +64,7 @@ import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.chat.ChatActivity +import com.nextcloud.talk.conversationcreation.ConversationCreationActivity import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser import com.nextcloud.talk.openconversations.ListOpenConversationsActivity import com.nextcloud.talk.utils.bundle.BundleKeys @@ -278,7 +279,11 @@ fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel fun ConversationCreationOptions(context: Context) { Column { Row( - modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp), + modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp) + .clickable { + val intent = Intent(context, ConversationCreationActivity::class.java) + context.startActivity(intent) + }, verticalAlignment = Alignment.CenterVertically ) { Icon( @@ -326,6 +331,8 @@ fun ConversationCreationOptions(context: Context) { } } + + class CompanionClass { companion object { internal val TAG = ContactsActivityCompose::class.simpleName diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityViewModel.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityViewModel.kt new file mode 100644 index 0000000000..6c87c66089 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityViewModel.kt @@ -0,0 +1,104 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.nextcloud.talk.contacts + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser +import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.users.UserManager +import com.nextcloud.talk.utils.ApiUtils +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +class ContactsActivityViewModel @Inject constructor( + private val repository: ContactsRepository, + private val userManager: UserManager +) : ViewModel() { + + private val _contactsViewState = MutableStateFlow(ContactsUiState.None) + val contactsViewState: StateFlow = _contactsViewState + private val _roomViewState = MutableStateFlow(RoomUiState.None) + val roomViewState: StateFlow = _roomViewState + private val _currentUser = userManager.currentUser.blockingGet() + val currentUser: User = _currentUser + private val _searchQuery = MutableStateFlow("") + val searchQuery: StateFlow = _searchQuery + private val shareTypes: MutableList = mutableListOf(ShareType.User.shareType) + val shareTypeList: List = shareTypes + + init { + getContactsFromSearchParams() + } + + fun updateSearchQuery(query: String) { + _searchQuery.value = query + } + + fun updateShareTypes(value: String) { + shareTypes.add(value) + } + + fun getContactsFromSearchParams() { + _contactsViewState.value = ContactsUiState.Loading + viewModelScope.launch { + try { + val contacts = repository.getContacts( + searchQuery.value, + shareTypeList + ) + val contactsList: List? = contacts.ocs!!.data + _contactsViewState.value = ContactsUiState.Success(contactsList) + } catch (exception: Exception) { + _contactsViewState.value = ContactsUiState.Error(exception.message ?: "") + } + } + } + + fun createRoom(roomType: String, sourceType: String, userId: String, conversationName: String?) { + viewModelScope.launch { + try { + val room = repository.createRoom( + roomType, + sourceType, + userId, + conversationName + ) + + val conversation: Conversation? = room.ocs?.data + _roomViewState.value = RoomUiState.Success(conversation) + } catch (exception: Exception) { + _roomViewState.value = RoomUiState.Error(exception.message ?: "") + } + } + } + + fun getImageUri(avatarId: String, requestBigSize: Boolean): String { + return ApiUtils.getUrlForAvatar( + _currentUser.baseUrl, + avatarId, + requestBigSize + ) + } +} + +sealed class ContactsUiState { + data object None : ContactsUiState() + data object Loading : ContactsUiState() + data class Success(val contacts: List?) : ContactsUiState() + data class Error(val message: String) : ContactsUiState() +} + +sealed class RoomUiState { + data object None : RoomUiState() + data class Success(val conversation: Conversation?) : RoomUiState() + data class Error(val message: String) : RoomUiState() +} diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt new file mode 100644 index 0000000000..29fc51956a --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -0,0 +1,246 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2024 Sowjanya Kota + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.nextcloud.talk.conversationcreation + +import android.app.Activity +import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.ViewModelProvider +import autodagger.AutoInjector +import coil.compose.AsyncImage +import com.nextcloud.talk.R +import com.nextcloud.talk.activities.BaseActivity +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.contacts.ContactsViewModel +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class ConversationCreationActivity : BaseActivity() { + @Inject + lateinit var viewModelFactory: ViewModelProvider.Factory + private lateinit var conversationCreationViewModel: ConversationCreationViewModel + private lateinit var contactsViewModel: ContactsViewModel + + @OptIn(ExperimentalMaterial3Api::class) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + conversationCreationViewModel = + ViewModelProvider(this, viewModelFactory)[ConversationCreationViewModel::class.java] + contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java] + setContent { + val colorScheme = viewThemeUtils.getColorScheme(this) + MaterialTheme( + colorScheme = colorScheme + ) { + val context = LocalContext.current + Scaffold( + topBar = { + TopAppBar( + title = { Text(text = stringResource(id = R.string.nc_new_conversation)) }, + + navigationIcon = { + IconButton(onClick = { + (context as? Activity)?.finish() + }) { + Icon( + Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringResource(R.string.back_button) + ) + } + } + ) + }, + content = { + Column(Modifier.padding(it)) { + DefaultUserAvatar() + UploadAvatar() + ConversationNameAndDescription(conversationCreationViewModel) + AddParticipants(contactsViewModel) + RoomCreationOptions(conversationCreationViewModel) + } + } + ) + } + } + } +} + +@Composable +fun DefaultUserAvatar() { + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + AsyncImage( + model = R.drawable.ic_circular_group, + contentDescription = "User Avatar", + modifier = Modifier + .size(width = 84.dp, height = 84.dp) + .padding(top = 8.dp) + ) + } +} + +@Composable +fun UploadAvatar() { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalArrangement = Arrangement.Center + ) { + IconButton(onClick = { + }) { + Icon( + painter = painterResource(id = R.drawable.ic_baseline_photo_camera_24), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + } + + IconButton(onClick = { + }) { + Icon( + painter = painterResource(id = R.drawable.ic_folder_multiple_image), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + } + + IconButton(onClick = { + }) { + Icon( + painter = painterResource(id = R.drawable.baseline_tag_faces_24), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + } + + IconButton(onClick = { + }) { + Icon( + painter = painterResource(id = R.drawable.ic_delete_grey600_24dp), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + } + } +} + +@Composable +fun ConversationNameAndDescription(conversationCreationViewModel: ConversationCreationViewModel) { + val conversationRoomName = conversationCreationViewModel.roomName.collectAsState() + val conversationDescription = conversationCreationViewModel.conversationDescription.collectAsState() + OutlinedTextField( + value = conversationRoomName.value, + onValueChange = { + conversationCreationViewModel.updateRoomName(it) + }, + label = { Text(text = stringResource(id = R.string.nc_call_name)) }, + modifier = Modifier + .padding(start = 16.dp, end = 16.dp) + .fillMaxWidth() + ) + OutlinedTextField( + value = conversationDescription.value, + onValueChange = { + conversationCreationViewModel.updateRoomName(it) + }, + label = { Text(text = stringResource(id = R.string.nc_conversation_description)) }, + modifier = Modifier + .padding(top = 8.dp, start = 16.dp, end = 16.dp) + .fillMaxWidth() + ) +} + +@Composable +fun AddParticipants(contactsViewModel: ContactsViewModel) { + Text( + text = stringResource(id = R.string.nc_participants).uppercase(), + fontSize = 14.sp, + modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp) + ) + + Row( + modifier = Modifier + .padding(start = 16.dp, end = 16.dp) + .clickable { + // Show contacts + }, + verticalAlignment = Alignment + .CenterVertically + ) { + Icon( + painter = painterResource(id = R.drawable.ic_account_plus), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + + Text(text = stringResource(id = R.string.nc_add_participants), modifier = Modifier.padding(start = 16.dp)) + } +} + +@Composable +fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewModel) { + val isChecked = conversationCreationViewModel.isGuestsAllowed.value + Text( + text = stringResource(id = R.string.nc_visible).uppercase(), + fontSize = 14.sp, + modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp) + ) + Row( + modifier = Modifier.padding(start = 16.dp, top = 16.dp, end = 16.dp).fillMaxWidth(), + verticalAlignment = Alignment + .CenterVertically + ) { + Icon( + painter = painterResource(id = R.drawable.ic_avatar_link), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + Text(text = stringResource(id = R.string.nc_guest_access_allow_title)) + + Spacer(modifier = Modifier.weight(1f)) + Switch( + checked = isChecked, + onCheckedChange = { + conversationCreationViewModel.isGuestsAllowed.value = it + } + ) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt new file mode 100644 index 0000000000..d241e89937 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt @@ -0,0 +1,15 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2024 Sowjanya Kota + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.nextcloud.talk.conversationcreation + +import com.nextcloud.talk.models.json.generic.GenericOverall + +interface ConversationCreationRepository { + suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall + suspend fun setConversationDescription(roomToken: String, description: String?): GenericOverall +} diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt new file mode 100644 index 0000000000..ab41d84f9a --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt @@ -0,0 +1,48 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2024 Sowjanya Kota + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.nextcloud.talk.conversationcreation + +import com.nextcloud.talk.api.NcApiCoroutines +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.users.UserManager +import com.nextcloud.talk.utils.ApiUtils + +class ConversationCreationRepositoryImpl( + private val ncApiCoroutines: NcApiCoroutines, + private val userManager: UserManager +) : ConversationCreationRepository { + private val _currentUser = userManager.currentUser.blockingGet() + val currentUser: User = _currentUser + val credentials = ApiUtils.getCredentials(_currentUser.username, _currentUser.token) + val apiVersion = ApiUtils.getConversationApiVersion(_currentUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1)) + + override suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall { + return ncApiCoroutines.renameRoom( + credentials, + ApiUtils.getUrlForRoom( + apiVersion, + _currentUser.baseUrl, + roomToken + ), + roomNameNew + ) + } + + override suspend fun setConversationDescription(roomToken: String, description: String?): GenericOverall { + return ncApiCoroutines.setConversationDescription( + credentials, + ApiUtils.getUrlForConversationDescription( + apiVersion, + _currentUser.baseUrl, + roomToken + ), + description + ) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt new file mode 100644 index 0000000000..9ba08cbecd --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt @@ -0,0 +1,57 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2024 Sowjanya Kota + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.nextcloud.talk.conversationcreation + +import android.util.Log +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +class ConversationCreationViewModel @Inject constructor( + private val repository: ConversationCreationRepository +) : ViewModel() { + + private val _roomName = MutableStateFlow("") + val roomName: StateFlow = _roomName + private val _conversationDescription = MutableStateFlow("") + val conversationDescription: StateFlow = _conversationDescription + var isGuestsAllowed = mutableStateOf(false) + var isConversationAvailableForRegisteredUsers = mutableStateOf(false) + var openForGuestAppUsers = mutableStateOf(false) + + fun updateRoomName(roomName: String) { + _roomName.value = roomName + } + + fun updateConversationDescription(conversationDescription: String) { + _conversationDescription.value = conversationDescription + } + + fun renameConversation(roomToken: String) { + viewModelScope.launch { + try { + repository.renameConversation(roomToken, roomName.value) + } catch (e: Exception) { + Log.d("ConversationCreationViewModel", "${e.message}") + } + } + } + fun setConversationDescription(roomToken: String) { + viewModelScope.launch { + try { + repository.setConversationDescription(roomToken, conversationDescription.value) + } catch (e: Exception) { + Log.d("ConversationCreationViewModel", "${e.message}") + } + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt index 4085d41146..f00c502ba3 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt @@ -19,6 +19,8 @@ import com.nextcloud.talk.contacts.ContactsRepository 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.ConversationCreationRepositoryImpl import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepository import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepositoryImpl import com.nextcloud.talk.conversationlist.data.OfflineConversationsRepository @@ -208,4 +210,12 @@ class RepositoryModule { fun provideContactsRepository(ncApiCoroutines: NcApiCoroutines, userManager: UserManager): ContactsRepository { return ContactsRepositoryImpl(ncApiCoroutines, userManager) } + + @Provides + fun provideConversationCreationRepository( + ncApiCoroutines: NcApiCoroutines, + userManager: UserManager + ): ConversationCreationRepository { + return ConversationCreationRepositoryImpl(ncApiCoroutines, userManager) + } } diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt index 511e6d7e23..f1ac935fe3 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt @@ -14,6 +14,7 @@ import com.nextcloud.talk.contacts.ContactsViewModel import com.nextcloud.talk.chat.viewmodels.MessageInputViewModel import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel import com.nextcloud.talk.conversation.viewmodel.RenameConversationViewModel +import com.nextcloud.talk.conversationcreation.ConversationCreationViewModel import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel import com.nextcloud.talk.conversationinfoedit.viewmodel.ConversationInfoEditViewModel import com.nextcloud.talk.conversationlist.viewmodels.ConversationsListViewModel @@ -156,4 +157,9 @@ abstract class ViewModelModule { @IntoMap @ViewModelKey(ContactsViewModel::class) abstract fun contactsViewModel(viewModel: ContactsViewModel): ViewModel + + @Binds + @IntoMap + @ViewModelKey(ConversationCreationViewModel::class) + abstract fun conversationCreationViewModel(viewModel: ConversationCreationViewModel): ViewModel } diff --git a/app/src/main/res/drawable/baseline_tag_faces_24.xml b/app/src/main/res/drawable/baseline_tag_faces_24.xml new file mode 100644 index 0000000000..3a4f458776 --- /dev/null +++ b/app/src/main/res/drawable/baseline_tag_faces_24.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6fa26e80ec..32679fbf45 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -386,6 +386,7 @@ How to translate with transifex: Close Icon Refresh Please check your internet connection + Visible Enter a message … diff --git a/app/src/test/java/com/nextcloud/talk/contacts/repository/FakeRepositorySuccess.kt b/app/src/test/java/com/nextcloud/talk/contacts/repository/FakeRepositorySuccess.kt index f5d87d9195..9022ba0598 100644 --- a/app/src/test/java/com/nextcloud/talk/contacts/repository/FakeRepositorySuccess.kt +++ b/app/src/test/java/com/nextcloud/talk/contacts/repository/FakeRepositorySuccess.kt @@ -29,4 +29,8 @@ class FakeRepositorySuccess : ContactsRepository { override fun getImageUri(avatarId: String, requestBigSize: Boolean): String { return "https://mydomain.com/index.php/avatar/$avatarId/512" } + + override fun getImageUri(avatarId: String, requestBigSize: Boolean): String { + TODO("Not yet implemented") + } } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index f1531e06f3..f550023128 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -4,13 +4,13 @@ true true - - - - + + + + @@ -145,7 +145,6 @@ - @@ -157,6 +156,7 @@ + @@ -246,6 +246,7 @@ + @@ -343,6 +344,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -359,6 +404,14 @@ + + + + + + + + @@ -372,6 +425,11 @@ + + + + + @@ -393,6 +451,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From d32bc74559ae53b2692b36c9efe394b8399c74e5 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Mon, 5 Aug 2024 21:52:54 +0200 Subject: [PATCH 02/23] ConversationCreationActivity - Refactoring Signed-off-by: sowjanyakch --- .../ConversationCreationActivity.kt | 93 +++++++++++++++---- app/src/main/res/values/strings.xml | 3 + 2 files changed, 79 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index 29fc51956a..a4a2135584 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -19,6 +19,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api @@ -193,7 +194,7 @@ fun AddParticipants(contactsViewModel: ContactsViewModel) { Text( text = stringResource(id = R.string.nc_participants).uppercase(), fontSize = 14.sp, - modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp) + modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp, bottom = 16.dp) ) Row( @@ -217,30 +218,88 @@ fun AddParticipants(contactsViewModel: ContactsViewModel) { @Composable fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewModel) { - val isChecked = conversationCreationViewModel.isGuestsAllowed.value + var isGuestsAllowed = conversationCreationViewModel.isGuestsAllowed.value + var isConversationAvailableForRegisteredUsers = conversationCreationViewModel + .isConversationAvailableForRegisteredUsers.value + var isOpenForGuestAppUsers = conversationCreationViewModel.openForGuestAppUsers.value + Text( text = stringResource(id = R.string.nc_visible).uppercase(), fontSize = 14.sp, modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp) ) - Row( - modifier = Modifier.padding(start = 16.dp, top = 16.dp, end = 16.dp).fillMaxWidth(), - verticalAlignment = Alignment - .CenterVertically - ) { - Icon( - painter = painterResource(id = R.drawable.ic_avatar_link), - contentDescription = null, - modifier = Modifier.size(24.dp) + + ConversationOptions( + icon = R.drawable.ic_avatar_link, + text = R.string.nc_guest_access_allow_title, + switch = { + Switch( + checked = isGuestsAllowed, + onCheckedChange = { + conversationCreationViewModel.isGuestsAllowed.value = it + } + ) + } + ) + + if (isGuestsAllowed) { + ConversationOptions( + icon = R.drawable.ic_lock_grey600_24px, + text = R.string.nc_set_password ) - Text(text = stringResource(id = R.string.nc_guest_access_allow_title)) + } - Spacer(modifier = Modifier.weight(1f)) - Switch( - checked = isChecked, - onCheckedChange = { - conversationCreationViewModel.isGuestsAllowed.value = it + ConversationOptions( + icon = R.drawable.baseline_format_list_bulleted_24, + text = R.string.nc_open_conversation_to_registered_users, + switch = { + Switch( + checked = isConversationAvailableForRegisteredUsers, + onCheckedChange = { + conversationCreationViewModel.isConversationAvailableForRegisteredUsers.value = it + } + ) + } + ) + + if (isConversationAvailableForRegisteredUsers) { + ConversationOptions( + text = R.string.nc_open_to_guest_app_users, + switch = { + Switch( + checked = isOpenForGuestAppUsers, + onCheckedChange = { + conversationCreationViewModel.openForGuestAppUsers.value = it + } + ) } ) } } + +@Composable +fun ConversationOptions(icon: Int? = null, text: Int, switch: @Composable (() -> Unit)? = null) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp, bottom = 8.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + if (icon != null) { + Icon( + painter = painterResource(id = icon), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(16.dp)) + } else { + Spacer(modifier = Modifier.width(40.dp)) + } + Text( + text = stringResource(id = text), + modifier = Modifier.weight(1f) + ) + switch?.invoke() + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 32679fbf45..8c3d0f3efe 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -242,6 +242,8 @@ How to translate with transifex: Remove from favorites Create a new conversation Join open conversations + Open conversation to registered users + Also open to guest app users Added conversation %1$s to favorites Removed conversation %1$s from favorites @@ -430,6 +432,7 @@ How to translate with transifex: Allow guests Allow guests to share a public link to join this conversation. Cannot enable/disable guest access. + Set Password Password protection Set a password to restrict who can use the public link. Guest access password From 9f61793c868a16186c375df72af082e34e064393 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 6 Aug 2024 23:21:36 +0200 Subject: [PATCH 03/23] add isAddParticipant flag Signed-off-by: sowjanyakch --- .../talk/contacts/ContactsActivityCompose.kt | 197 +++++------------- .../talk/contacts/ContactsViewModel.kt | 6 + .../nextcloud/talk/contacts/ContentItem.kt | 110 ++++++++++ .../talk/contacts/SearchComponent.kt | 10 +- .../ConversationCreationActivity.kt | 16 +- 5 files changed, 192 insertions(+), 147 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt index d9234bac9c..2241e7f6db 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt @@ -12,15 +12,10 @@ import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle -import android.util.Log import androidx.activity.compose.setContent -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -29,20 +24,15 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.List import androidx.compose.material.icons.filled.Search -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold -import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable @@ -54,7 +44,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.ViewModelProvider @@ -82,6 +71,8 @@ class ContactsActivityCompose : BaseActivity() { super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java] + val isAddParticipants = intent.getBooleanExtra("isAddParticipants", false) + contactsViewModel.updateIsAddParticipants(isAddParticipants) setContent { val colorScheme = viewThemeUtils.getColorScheme(this) val uiState = contactsViewModel.contactsViewState.collectAsState() @@ -99,7 +90,7 @@ class ContactsActivityCompose : BaseActivity() { }, content = { Column(Modifier.padding(it)) { - ConversationCreationOptions(context = context) + ConversationCreationOptions(context = context, contactsViewModel = contactsViewModel) ContactsList( contactsUiState = uiState.value, contactsViewModel = contactsViewModel, @@ -110,87 +101,9 @@ class ContactsActivityCompose : BaseActivity() { ) } } - setupSystemColors() } } -@Composable -fun ContactsList(contactsUiState: ContactsUiState, contactsViewModel: ContactsViewModel, context: Context) { - when (contactsUiState) { - is ContactsUiState.None -> { - } - is ContactsUiState.Loading -> { - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - CircularProgressIndicator() - } - } - is ContactsUiState.Success -> { - val contacts = contactsUiState.contacts - Log.d(CompanionClass.TAG, "Contacts:$contacts") - if (contacts != null) { - ContactsItem(contacts, contactsViewModel, context) - } - } - is ContactsUiState.Error -> { - val errorMessage = contactsUiState.message - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Text(text = "Error: $errorMessage", color = MaterialTheme.colorScheme.error) - } - } - } -} - -@OptIn(ExperimentalFoundationApi::class) -@Composable -fun ContactsItem(contacts: List, contactsViewModel: ContactsViewModel, context: Context) { - val groupedContacts: Map> = contacts.groupBy { contact -> - ( - if (contact.source == "users") { - contact.label?.first()?.uppercase() - } else { - contact.source?.replaceFirstChar { actorType -> - actorType.uppercase() - } - } - ).toString() - } - LazyColumn( - modifier = Modifier - .padding(8.dp) - .fillMaxWidth(), - contentPadding = PaddingValues(all = 10.dp), - verticalArrangement = Arrangement.spacedBy(10.dp) - ) { - groupedContacts.forEach { (initial, contactsForInitial) -> - stickyHeader { - Column { - Surface(Modifier.fillParentMaxWidth()) { - Header(initial) - } - HorizontalDivider(thickness = 1.dp, color = MaterialTheme.colorScheme.outlineVariant) - } - } - items(contactsForInitial) { contact -> - ContactItemRow(contact = contact, contactsViewModel = contactsViewModel, context = context) - Log.d(CompanionClass.TAG, "Contacts:$contact") - } - } - } -} - -@Composable -fun Header(header: String) { - Text( - text = header, - modifier = Modifier - .fillMaxSize() - .background(Color.Transparent) - .padding(start = 60.dp), - color = MaterialTheme.colorScheme.primary, - fontWeight = FontWeight.Bold - ) -} - @Composable fun ContactItemRow(contact: AutocompleteUser, contactsViewModel: ContactsViewModel, context: Context) { val roomUiState by contactsViewModel.roomViewState.collectAsState() @@ -231,7 +144,7 @@ fun ContactItemRow(contact: AutocompleteUser, contactsViewModel: ContactsViewMod is RoomUiState.Error -> { val errorMessage = (roomUiState as RoomUiState.Error).message Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Text(text = "Error: $errorMessage", color = MaterialTheme.colorScheme.error) + Text(text = "Error: $errorMessage", color = Color.Red) } } is RoomUiState.None -> {} @@ -276,57 +189,61 @@ fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel } @Composable -fun ConversationCreationOptions(context: Context) { - Column { - Row( - modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp) - .clickable { - val intent = Intent(context, ConversationCreationActivity::class.java) - context.startActivity(intent) - }, - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - painter = painterResource(id = R.drawable.baseline_chat_bubble_outline_24), - modifier = Modifier - .width(40.dp) - .height(40.dp) - .padding(8.dp), - contentDescription = null - ) - Text( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight(), - text = stringResource(R.string.nc_create_new_conversation), - maxLines = 1, - fontSize = 16.sp - ) - } - Row( - modifier = Modifier - .padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 8.dp) - .clickable { - val intent = Intent(context, ListOpenConversationsActivity::class.java) - context.startActivity(intent) - }, - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - Icons.AutoMirrored.Filled.List, +fun ConversationCreationOptions(context: Context, contactsViewModel: ContactsViewModel) { + val isAddParticipants by contactsViewModel.isAddParticipantsView.collectAsState() + if (!isAddParticipants) { + Column { + Row( modifier = Modifier - .width(40.dp) - .height(40.dp) - .padding(8.dp), - contentDescription = null - ) - Text( + .padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp) + .clickable { + val intent = Intent(context, ConversationCreationActivity::class.java) + context.startActivity(intent) + }, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + painter = painterResource(id = R.drawable.baseline_chat_bubble_outline_24), + modifier = Modifier + .width(40.dp) + .height(40.dp) + .padding(8.dp), + contentDescription = null + ) + Text( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + text = stringResource(R.string.nc_create_new_conversation), + maxLines = 1, + fontSize = 16.sp + ) + } + Row( modifier = Modifier - .fillMaxWidth() - .wrapContentHeight(), - text = stringResource(R.string.nc_join_open_conversations), - fontSize = 16.sp - ) + .padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 8.dp) + .clickable { + val intent = Intent(context, ListOpenConversationsActivity::class.java) + context.startActivity(intent) + }, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + Icons.AutoMirrored.Filled.List, + modifier = Modifier + .width(40.dp) + .height(40.dp) + .padding(8.dp), + contentDescription = null + ) + Text( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + text = stringResource(R.string.nc_join_open_conversations), + fontSize = 16.sp + ) + } } } } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt index 7b0a2e450c..31e3466c89 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt @@ -30,6 +30,8 @@ class ContactsViewModel @Inject constructor( val shareTypeList: List = shareTypes private val _searchState = MutableStateFlow(false) val searchState: StateFlow = _searchState + private val _isAddParticipantsView = MutableStateFlow(false) + val isAddParticipantsView: StateFlow = _isAddParticipantsView init { getContactsFromSearchParams() @@ -47,6 +49,10 @@ class ContactsViewModel @Inject constructor( shareTypes.add(value) } + fun updateIsAddParticipants(value: Boolean) { + _isAddParticipantsView.value = value + } + fun getContactsFromSearchParams() { _contactsViewState.value = ContactsUiState.Loading viewModelScope.launch { diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt new file mode 100644 index 0000000000..db2e01441c --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt @@ -0,0 +1,110 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.nextcloud.talk.contacts + +import android.content.Context +import android.util.Log +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser + +@Composable +fun ContactsList(contactsUiState: ContactsUiState, contactsViewModel: ContactsViewModel, context: Context) { + when (contactsUiState) { + is ContactsUiState.None -> { + } + is ContactsUiState.Loading -> { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + CircularProgressIndicator() + } + } + is ContactsUiState.Success -> { + val contacts = contactsUiState.contacts + Log.d(CompanionClass.TAG, "Contacts:$contacts") + if (contacts != null) { + ContactsItem(contacts, contactsViewModel, context) + } + } + is ContactsUiState.Error -> { + val errorMessage = contactsUiState.message + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text(text = "Error: $errorMessage", color = Color.Red) + } + } + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun ContactsItem(contacts: List, contactsViewModel: ContactsViewModel, context: Context) { + val groupedContacts: Map> = contacts.groupBy { contact -> + ( + if (contact.source == "users") { + contact.label?.first()?.uppercase() + } else { + contact.source?.replaceFirstChar { actorType -> + actorType.uppercase() + } + } + ).toString() + } + LazyColumn( + modifier = Modifier + .padding(8.dp) + .fillMaxWidth(), + contentPadding = PaddingValues(all = 10.dp), + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + groupedContacts.forEach { (initial, contactsForInitial) -> + stickyHeader { + Column { + Surface(Modifier.fillParentMaxWidth()) { + Header(initial) + } + HorizontalDivider(thickness = 0.1.dp, color = Color.Black) + } + } + items(contactsForInitial) { contact -> + ContactItemRow(contact = contact, contactsViewModel = contactsViewModel, context = context) + Log.d(CompanionClass.TAG, "Contacts:$contact") + } + } + } +} + +@Composable +fun Header(header: String) { + Text( + text = header, + modifier = Modifier + .fillMaxSize() + .background(Color.Transparent) + .padding(start = 60.dp), + color = Color.Blue, + fontWeight = FontWeight.Bold + ) +} diff --git a/app/src/main/java/com/nextcloud/talk/contacts/SearchComponent.kt b/app/src/main/java/com/nextcloud/talk/contacts/SearchComponent.kt index f7ba7d61b4..e0c1150d9e 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/SearchComponent.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/SearchComponent.kt @@ -14,11 +14,13 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource @@ -30,6 +32,7 @@ import com.nextcloud.talk.R @Composable fun DisplaySearch(text: String, onTextChange: (String) -> Unit, contactsViewModel: ContactsViewModel) { + val isAddParticipants = contactsViewModel.isAddParticipantsView.collectAsState() val keyboardController = LocalSoftwareKeyboardController.current TextField( modifier = Modifier @@ -42,7 +45,6 @@ fun DisplaySearch(text: String, onTextChange: (String) -> Unit, contactsViewMode text = stringResource(R.string.nc_search) ) }, - textStyle = TextStyle( fontSize = 16.sp ), @@ -91,4 +93,10 @@ fun DisplaySearch(text: String, onTextChange: (String) -> Unit, contactsViewMode ), maxLines = 1 ) + if (isAddParticipants.value) { + Button(onClick = { + }, modifier = Modifier.fillMaxWidth(0.2f)) { + Text(text = "Done") + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index a4a2135584..31d0c176bc 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -8,6 +8,8 @@ package com.nextcloud.talk.conversationcreation import android.app.Activity +import android.content.Context +import android.content.Intent import android.os.Bundle import androidx.activity.compose.setContent import androidx.compose.foundation.clickable @@ -46,6 +48,7 @@ import coil.compose.AsyncImage import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.contacts.ContactsActivityCompose import com.nextcloud.talk.contacts.ContactsViewModel import javax.inject.Inject @@ -54,7 +57,6 @@ class ConversationCreationActivity : BaseActivity() { @Inject lateinit var viewModelFactory: ViewModelProvider.Factory private lateinit var conversationCreationViewModel: ConversationCreationViewModel - private lateinit var contactsViewModel: ContactsViewModel @OptIn(ExperimentalMaterial3Api::class) override fun onCreate(savedInstanceState: Bundle?) { @@ -62,7 +64,8 @@ class ConversationCreationActivity : BaseActivity() { NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) conversationCreationViewModel = ViewModelProvider(this, viewModelFactory)[ConversationCreationViewModel::class.java] - contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java] + val contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java] + setContent { val colorScheme = viewThemeUtils.getColorScheme(this) MaterialTheme( @@ -73,7 +76,6 @@ class ConversationCreationActivity : BaseActivity() { topBar = { TopAppBar( title = { Text(text = stringResource(id = R.string.nc_new_conversation)) }, - navigationIcon = { IconButton(onClick = { (context as? Activity)?.finish() @@ -91,7 +93,7 @@ class ConversationCreationActivity : BaseActivity() { DefaultUserAvatar() UploadAvatar() ConversationNameAndDescription(conversationCreationViewModel) - AddParticipants(contactsViewModel) + AddParticipants(contactsViewModel, context) RoomCreationOptions(conversationCreationViewModel) } } @@ -190,7 +192,7 @@ fun ConversationNameAndDescription(conversationCreationViewModel: ConversationCr } @Composable -fun AddParticipants(contactsViewModel: ContactsViewModel) { +fun AddParticipants(contactsViewModel: ContactsViewModel, context: Context) { Text( text = stringResource(id = R.string.nc_participants).uppercase(), fontSize = 14.sp, @@ -201,7 +203,9 @@ fun AddParticipants(contactsViewModel: ContactsViewModel) { modifier = Modifier .padding(start = 16.dp, end = 16.dp) .clickable { - // Show contacts + val intent = Intent(context, ContactsActivityCompose::class.java) + intent.putExtra("isAddParticipants", true) + context.startActivity(intent) }, verticalAlignment = Alignment .CenterVertically From b5f7556b3de7546ff34d05f476925348905b0385 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Thu, 8 Aug 2024 23:57:29 +0200 Subject: [PATCH 04/23] don't create room on clicking contacts when creating a new conversation Signed-off-by: sowjanyakch --- .../talk/contacts/ContactsActivityCompose.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt index 2241e7f6db..17ac535ca6 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt @@ -111,12 +111,14 @@ fun ContactItemRow(contact: AutocompleteUser, contactsViewModel: ContactsViewMod modifier = Modifier .fillMaxWidth() .clickable { - contactsViewModel.createRoom( - CompanionClass.ROOM_TYPE_ONE_ONE, - contact.source!!, - contact.id!!, - null - ) + if (!contactsViewModel.isAddParticipantsView.value) { + contactsViewModel.createRoom( + CompanionClass.ROOM_TYPE_ONE_ONE, + contact.source!!, + contact.id!!, + null + ) + } }, verticalAlignment = Alignment.CenterVertically ) { From e57c13efedbda4ad9fd9eab53766bc47365d110f Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Mon, 12 Aug 2024 09:03:50 +0200 Subject: [PATCH 05/23] MultiSelect contacts Signed-off-by: sowjanyakch --- .../talk/contacts/ContactsActivityCompose.kt | 74 ++++++++++++++++--- .../talk/contacts/ContactsViewModel.kt | 4 +- .../nextcloud/talk/contacts/ContentItem.kt | 10 ++- .../talk/contacts/SearchComponent.kt | 7 -- .../com/nextcloud/talk/contacts/ShareType.kt | 6 +- 5 files changed, 78 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt index 17ac535ca6..9a2c1d381c 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt @@ -17,6 +17,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -41,9 +42,11 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.ViewModelProvider @@ -73,6 +76,17 @@ class ContactsActivityCompose : BaseActivity() { contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java] val isAddParticipants = intent.getBooleanExtra("isAddParticipants", false) contactsViewModel.updateIsAddParticipants(isAddParticipants) + if (isAddParticipants) { + contactsViewModel.updateShareTypes( + listOf( + ShareType.Group.shareType, + ShareType.Email.shareType, + ShareType + .Circle.shareType + ) + ) + contactsViewModel.getContactsFromSearchParams() + } setContent { val colorScheme = viewThemeUtils.getColorScheme(this) val uiState = contactsViewModel.contactsViewState.collectAsState() @@ -105,19 +119,32 @@ class ContactsActivityCompose : BaseActivity() { } @Composable -fun ContactItemRow(contact: AutocompleteUser, contactsViewModel: ContactsViewModel, context: Context) { +fun ContactItemRow( + contact: AutocompleteUser, + contactsViewModel: ContactsViewModel, + context: Context, + selectedContacts: MutableList +) { + val isSelected = contact.id?.let { it in selectedContacts } ?: false val roomUiState by contactsViewModel.roomViewState.collectAsState() + val isAddParticipants = contactsViewModel.isAddParticipantsView.value Row( modifier = Modifier .fillMaxWidth() .clickable { - if (!contactsViewModel.isAddParticipantsView.value) { + if (!isAddParticipants) { contactsViewModel.createRoom( CompanionClass.ROOM_TYPE_ONE_ONE, contact.source!!, contact.id!!, null ) + } else { + if (isSelected) { + selectedContacts.remove(contact.id!!) + } else { + selectedContacts.add(contact.id!!) + } } }, verticalAlignment = Alignment.CenterVertically @@ -131,6 +158,17 @@ fun ContactItemRow(contact: AutocompleteUser, contactsViewModel: ContactsViewMod modifier = Modifier.size(width = 45.dp, height = 45.dp) ) Text(modifier = Modifier.padding(16.dp), text = contact.label!!) + if (isAddParticipants) { + if (isSelected) { + Spacer(modifier = Modifier.weight(1f)) + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_check_circle), + contentDescription = "Selected", + tint = Color.Blue, + modifier = Modifier.padding(end = 8.dp) + ) + } + } } when (roomUiState) { is RoomUiState.Success -> { @@ -176,17 +214,33 @@ fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel }) { Icon(Icons.Filled.Search, contentDescription = stringResource(R.string.search_icon)) } + if (contactsViewModel.isAddParticipantsView.value) { + Text( + text = stringResource(id = R.string.nc_contacts_done), + modifier = Modifier.clickable { + } + ) + } } ) if (searchState.value) { - DisplaySearch( - text = searchQuery, - onTextChange = { searchQuery -> - contactsViewModel.updateSearchQuery(query = searchQuery) - contactsViewModel.getContactsFromSearchParams() - }, - contactsViewModel = contactsViewModel - ) + Row { + DisplaySearch( + text = searchQuery, + onTextChange = { searchQuery -> + contactsViewModel.updateSearchQuery(query = searchQuery) + contactsViewModel.getContactsFromSearchParams() + }, + contactsViewModel = contactsViewModel + ) + if (contactsViewModel.isAddParticipantsView.value) { + Text( + text = stringResource(id = R.string.nc_contacts_done), + modifier = Modifier.clickable { + } + ) + } + } } } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt index 31e3466c89..cfb307c0f8 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt @@ -45,8 +45,8 @@ class ContactsViewModel @Inject constructor( _searchState.value = searchState } - fun updateShareTypes(value: String) { - shareTypes.add(value) + fun updateShareTypes(value: List) { + shareTypes.addAll(value) } fun updateIsAddParticipants(value: Boolean) { diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt index db2e01441c..828af63c88 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt @@ -25,6 +25,8 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -72,6 +74,7 @@ fun ContactsItem(contacts: List, contactsViewModel: ContactsVi } ).toString() } + val selectedContacts = remember { mutableStateListOf() } LazyColumn( modifier = Modifier .padding(8.dp) @@ -89,7 +92,12 @@ fun ContactsItem(contacts: List, contactsViewModel: ContactsVi } } items(contactsForInitial) { contact -> - ContactItemRow(contact = contact, contactsViewModel = contactsViewModel, context = context) + ContactItemRow( + contact = contact, + contactsViewModel = contactsViewModel, + context = context, + selectedContacts = selectedContacts + ) Log.d(CompanionClass.TAG, "Contacts:$contact") } } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/SearchComponent.kt b/app/src/main/java/com/nextcloud/talk/contacts/SearchComponent.kt index e0c1150d9e..5a793d40e3 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/SearchComponent.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/SearchComponent.kt @@ -14,7 +14,6 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Close -import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text @@ -93,10 +92,4 @@ fun DisplaySearch(text: String, onTextChange: (String) -> Unit, contactsViewMode ), maxLines = 1 ) - if (isAddParticipants.value) { - Button(onClick = { - }, modifier = Modifier.fillMaxWidth(0.2f)) { - Text(text = "Done") - } - } } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ShareType.kt b/app/src/main/java/com/nextcloud/talk/contacts/ShareType.kt index fb8f2dc5b3..39fad325c7 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ShareType.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ShareType.kt @@ -10,7 +10,7 @@ package com.nextcloud.talk.contacts enum class ShareType(val shareType: String) { User("0"), Group("1"), - Email(""), - Circle(""), - Federated("") + Email("4"), + Remote("5"), + Circle("7") } From 0913b162b3f8150db15f7905ee0b36341f633713 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 13 Aug 2024 13:17:33 +0200 Subject: [PATCH 06/23] add participants endpoint Signed-off-by: sowjanyakch --- .../com/nextcloud/talk/api/NcApiCoroutines.kt | 8 ++ .../talk/contacts/ContactsActivityCompose.kt | 15 +-- .../contacts/ContactsActivityViewModel.kt | 104 ------------------ .../talk/contacts/ContactsRepository.kt | 2 + .../talk/contacts/ContactsRepositoryImpl.kt | 27 +++++ .../talk/contacts/ContactsViewModel.kt | 24 ++++ .../nextcloud/talk/contacts/ContentItem.kt | 2 +- 7 files changed, 68 insertions(+), 114 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityViewModel.kt diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt index ccca322a76..21b3e4a998 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt +++ b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt @@ -10,6 +10,7 @@ package com.nextcloud.talk.api import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.models.json.participants.AddParticipantOverall import retrofit2.http.Field import retrofit2.http.FormUrlEncoded import retrofit2.http.GET @@ -65,4 +66,11 @@ interface NcApiCoroutines { @Url url: String, @Field("description") description: String? ): GenericOverall + + @POST + fun addParticipant( + @Header("Authorization") authorization: String?, + @Url url: String?, + @QueryMap options: Map? + ): AddParticipantOverall } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt index 9a2c1d381c..b35ce93c5b 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt @@ -81,8 +81,7 @@ class ContactsActivityCompose : BaseActivity() { listOf( ShareType.Group.shareType, ShareType.Email.shareType, - ShareType - .Circle.shareType + ShareType.Circle.shareType ) ) contactsViewModel.getContactsFromSearchParams() @@ -123,9 +122,9 @@ fun ContactItemRow( contact: AutocompleteUser, contactsViewModel: ContactsViewModel, context: Context, - selectedContacts: MutableList + selectedContacts: MutableList ) { - val isSelected = contact.id?.let { it in selectedContacts } ?: false + val isSelected = contact in selectedContacts val roomUiState by contactsViewModel.roomViewState.collectAsState() val isAddParticipants = contactsViewModel.isAddParticipantsView.value Row( @@ -141,10 +140,11 @@ fun ContactItemRow( ) } else { if (isSelected) { - selectedContacts.remove(contact.id!!) + selectedContacts.remove(contact) } else { - selectedContacts.add(contact.id!!) + selectedContacts.add(contact) } + contactsViewModel.updateSelectedParticipants(selectedContacts) } }, verticalAlignment = Alignment.CenterVertically @@ -200,7 +200,6 @@ fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel TopAppBar( title = { Text(text = title) }, - navigationIcon = { IconButton(onClick = { (context as? Activity)?.finish() @@ -304,8 +303,6 @@ fun ConversationCreationOptions(context: Context, contactsViewModel: ContactsVie } } - - class CompanionClass { companion object { internal val TAG = ContactsActivityCompose::class.simpleName diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityViewModel.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityViewModel.kt deleted file mode 100644 index 6c87c66089..0000000000 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityViewModel.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Nextcloud Talk - Android Client - * - * SPDX-FileCopyrightText: 2024 Your Name - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -package com.nextcloud.talk.contacts - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.nextcloud.talk.data.user.model.User -import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser -import com.nextcloud.talk.models.json.conversations.Conversation -import com.nextcloud.talk.users.UserManager -import com.nextcloud.talk.utils.ApiUtils -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch -import javax.inject.Inject - -class ContactsActivityViewModel @Inject constructor( - private val repository: ContactsRepository, - private val userManager: UserManager -) : ViewModel() { - - private val _contactsViewState = MutableStateFlow(ContactsUiState.None) - val contactsViewState: StateFlow = _contactsViewState - private val _roomViewState = MutableStateFlow(RoomUiState.None) - val roomViewState: StateFlow = _roomViewState - private val _currentUser = userManager.currentUser.blockingGet() - val currentUser: User = _currentUser - private val _searchQuery = MutableStateFlow("") - val searchQuery: StateFlow = _searchQuery - private val shareTypes: MutableList = mutableListOf(ShareType.User.shareType) - val shareTypeList: List = shareTypes - - init { - getContactsFromSearchParams() - } - - fun updateSearchQuery(query: String) { - _searchQuery.value = query - } - - fun updateShareTypes(value: String) { - shareTypes.add(value) - } - - fun getContactsFromSearchParams() { - _contactsViewState.value = ContactsUiState.Loading - viewModelScope.launch { - try { - val contacts = repository.getContacts( - searchQuery.value, - shareTypeList - ) - val contactsList: List? = contacts.ocs!!.data - _contactsViewState.value = ContactsUiState.Success(contactsList) - } catch (exception: Exception) { - _contactsViewState.value = ContactsUiState.Error(exception.message ?: "") - } - } - } - - fun createRoom(roomType: String, sourceType: String, userId: String, conversationName: String?) { - viewModelScope.launch { - try { - val room = repository.createRoom( - roomType, - sourceType, - userId, - conversationName - ) - - val conversation: Conversation? = room.ocs?.data - _roomViewState.value = RoomUiState.Success(conversation) - } catch (exception: Exception) { - _roomViewState.value = RoomUiState.Error(exception.message ?: "") - } - } - } - - fun getImageUri(avatarId: String, requestBigSize: Boolean): String { - return ApiUtils.getUrlForAvatar( - _currentUser.baseUrl, - avatarId, - requestBigSize - ) - } -} - -sealed class ContactsUiState { - data object None : ContactsUiState() - data object Loading : ContactsUiState() - data class Success(val contacts: List?) : ContactsUiState() - data class Error(val message: String) : ContactsUiState() -} - -sealed class RoomUiState { - data object None : RoomUiState() - data class Success(val conversation: Conversation?) : RoomUiState() - data class Error(val message: String) : RoomUiState() -} diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepository.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepository.kt index 8655b79e13..b1e224474f 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepository.kt @@ -9,9 +9,11 @@ package com.nextcloud.talk.contacts import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall import com.nextcloud.talk.models.json.conversations.RoomOverall +import com.nextcloud.talk.models.json.participants.AddParticipantOverall interface ContactsRepository { suspend fun getContacts(searchQuery: String?, shareTypes: List): AutocompleteOverall suspend fun createRoom(roomType: String, sourceType: String, userId: String, conversationName: String?): RoomOverall fun getImageUri(avatarId: String, requestBigSize: Boolean): String + suspend fun addParticipants(conversationToken: String, userId: String, sourceType: String): AddParticipantOverall } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepositoryImpl.kt index 6fcd275124..8d0af8656b 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepositoryImpl.kt @@ -12,8 +12,11 @@ import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.RetrofitBucket import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall import com.nextcloud.talk.models.json.conversations.RoomOverall +import com.nextcloud.talk.models.json.participants.AddParticipantOverall import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.ApiUtils.getRetrofitBucketForAddParticipant +import com.nextcloud.talk.utils.ApiUtils.getRetrofitBucketForAddParticipantWithSource import com.nextcloud.talk.utils.ContactUtils class ContactsRepositoryImpl( @@ -72,4 +75,28 @@ class ContactsRepositoryImpl( requestBigSize ) } + + override suspend fun addParticipants( + conversationToken: String, + userId: String, + sourceType: String + ): AddParticipantOverall { + val retrofitBucket: RetrofitBucket = if (sourceType == "users") { + getRetrofitBucketForAddParticipant( + apiVersion, + _currentUser.baseUrl, + conversationToken, + userId + ) + } else { + getRetrofitBucketForAddParticipantWithSource( + apiVersion, + _currentUser.baseUrl, + conversationToken, + sourceType, + userId + ) + } + return ncApiCoroutines.addParticipant(credentials, retrofitBucket.url, retrofitBucket.queryMap) + } } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt index cfb307c0f8..fe553a277d 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt @@ -24,6 +24,8 @@ class ContactsViewModel @Inject constructor( val contactsViewState: StateFlow = _contactsViewState private val _roomViewState = MutableStateFlow(RoomUiState.None) val roomViewState: StateFlow = _roomViewState + private val addParticipantsViewState = MutableStateFlow(AddParticipantsUiState.None) + val addParticipantsUiState: StateFlow = addParticipantsViewState private val _searchQuery = MutableStateFlow("") val searchQuery: StateFlow = _searchQuery private val shareTypes: MutableList = mutableListOf(ShareType.User.shareType) @@ -32,6 +34,8 @@ class ContactsViewModel @Inject constructor( val searchState: StateFlow = _searchState private val _isAddParticipantsView = MutableStateFlow(false) val isAddParticipantsView: StateFlow = _isAddParticipantsView + private val selectedParticipants = mutableListOf() + val selectedParticipantsList: List = selectedParticipants init { getContactsFromSearchParams() @@ -41,6 +45,9 @@ class ContactsViewModel @Inject constructor( _searchQuery.value = query } + fun updateSelectedParticipants(participants: List) { + selectedParticipants.addAll(participants) + } fun updateSearchState(searchState: Boolean) { _searchState.value = searchState } @@ -89,6 +96,17 @@ class ContactsViewModel @Inject constructor( fun getImageUri(avatarId: String, requestBigSize: Boolean): String { return repository.getImageUri(avatarId, requestBigSize) } + fun addParticipants(conversationToken: String, userId: String, sourceType: String) { + viewModelScope.launch { + try { + val participantsOverall = repository.addParticipants(conversationToken, userId, sourceType) + val participants = participantsOverall.ocs?.data + addParticipantsViewState.value = AddParticipantsUiState.Success(participants) + } catch (exception: Exception) { + addParticipantsViewState.value = AddParticipantsUiState.Error(exception.message ?: "") + } + } + } } sealed class ContactsUiState { @@ -103,3 +121,9 @@ sealed class RoomUiState { data class Success(val conversation: Conversation?) : RoomUiState() data class Error(val message: String) : RoomUiState() } + +sealed class AddParticipantsUiState() { + data object None : AddParticipantsUiState() + data class Success(val participants: List?) : AddParticipantsUiState() + data class Error(val message: String) : AddParticipantsUiState() +} diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt index 828af63c88..e1cd47940e 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt @@ -74,7 +74,7 @@ fun ContactsItem(contacts: List, contactsViewModel: ContactsVi } ).toString() } - val selectedContacts = remember { mutableStateListOf() } + val selectedContacts = remember { mutableStateListOf() } LazyColumn( modifier = Modifier .padding(8.dp) From 9ae0d524c0f1aaa674c56ad54b8bba2ea2a1e696 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 13 Aug 2024 14:41:01 +0200 Subject: [PATCH 07/23] update ContactsViewModelTest - shareTypes Signed-off-by: sowjanyakch --- .../talk/data/database/mappers/ChatMessageMapUtils.kt | 4 ++-- .../java/com/nextcloud/talk/contacts/ContactsViewModelTest.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/data/database/mappers/ChatMessageMapUtils.kt b/app/src/main/java/com/nextcloud/talk/data/database/mappers/ChatMessageMapUtils.kt index cb34a2372d..30b856a6bf 100644 --- a/app/src/main/java/com/nextcloud/talk/data/database/mappers/ChatMessageMapUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/data/database/mappers/ChatMessageMapUtils.kt @@ -7,9 +7,9 @@ package com.nextcloud.talk.data.database.mappers -import com.nextcloud.talk.chat.data.model.ChatMessage -import com.nextcloud.talk.data.database.model.ChatMessageEntity import com.nextcloud.talk.models.json.chat.ChatMessageJson +import com.nextcloud.talk.data.database.model.ChatMessageEntity +import com.nextcloud.talk.chat.data.model.ChatMessage fun ChatMessageJson.asEntity(accountId: Long) = ChatMessageEntity( diff --git a/app/src/test/java/com/nextcloud/talk/contacts/ContactsViewModelTest.kt b/app/src/test/java/com/nextcloud/talk/contacts/ContactsViewModelTest.kt index 7bb905b431..8a9a569379 100644 --- a/app/src/test/java/com/nextcloud/talk/contacts/ContactsViewModelTest.kt +++ b/app/src/test/java/com/nextcloud/talk/contacts/ContactsViewModelTest.kt @@ -81,7 +81,7 @@ class ContactsViewModelTest { @Test fun `update shareTypes`() { - viewModel.updateShareTypes(ShareType.Group.shareType) + viewModel.updateShareTypes(listOf(ShareType.Group.shareType)) assert(viewModel.shareTypeList.contains(ShareType.Group.shareType)) } From 6a01309f2f0624cd7f116b50f653f393cdc7cc62 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Wed, 14 Aug 2024 10:57:37 +0200 Subject: [PATCH 08/23] add participants Signed-off-by: sowjanyakch --- .../talk/contacts/ContactsActivityCompose.kt | 37 +++++++++++++++---- .../talk/contacts/ContactsRepository.kt | 2 +- .../talk/contacts/ContactsRepositoryImpl.kt | 2 +- .../talk/contacts/ContactsViewModel.kt | 2 +- .../ConversationsListActivity.kt | 3 +- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt index b35ce93c5b..d8625af87d 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt @@ -12,6 +12,7 @@ import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle +import android.util.Log import androidx.activity.compose.setContent import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -197,6 +198,8 @@ fun ContactItemRow( fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel) { val searchQuery by contactsViewModel.searchQuery.collectAsState() val searchState = contactsViewModel.searchState.collectAsState() + val addParticipantsUiState = contactsViewModel.addParticipantsUiState.collectAsState() + val conversationToken:String? = null TopAppBar( title = { Text(text = title) }, @@ -217,11 +220,38 @@ fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel Text( text = stringResource(id = R.string.nc_contacts_done), modifier = Modifier.clickable { + for(contacts in contactsViewModel.selectedParticipantsList){ + contacts.let { contact -> + contactsViewModel.addParticipants( + conversationToken, + contact.id!!, + contact.source!! + ) + } + } } ) } } ) + val state = addParticipantsUiState.value + when(state){ + is AddParticipantsUiState.Error -> { + val errorMessage = state.message + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text(text = "Error: $errorMessage", color = Color.Red) + } + + } + is AddParticipantsUiState.None -> { + + + } + is AddParticipantsUiState.Success -> { + val conversation = state.participants + Log.d("ContactsActivityCompose", "$conversation") + } + } if (searchState.value) { Row { DisplaySearch( @@ -232,13 +262,6 @@ fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel }, contactsViewModel = contactsViewModel ) - if (contactsViewModel.isAddParticipantsView.value) { - Text( - text = stringResource(id = R.string.nc_contacts_done), - modifier = Modifier.clickable { - } - ) - } } } } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepository.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepository.kt index b1e224474f..e610909c2f 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepository.kt @@ -15,5 +15,5 @@ interface ContactsRepository { suspend fun getContacts(searchQuery: String?, shareTypes: List): AutocompleteOverall suspend fun createRoom(roomType: String, sourceType: String, userId: String, conversationName: String?): RoomOverall fun getImageUri(avatarId: String, requestBigSize: Boolean): String - suspend fun addParticipants(conversationToken: String, userId: String, sourceType: String): AddParticipantOverall + suspend fun addParticipants(conversationToken: String?, userId: String, sourceType: String): AddParticipantOverall } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepositoryImpl.kt index 8d0af8656b..04634ff4cc 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepositoryImpl.kt @@ -77,7 +77,7 @@ class ContactsRepositoryImpl( } override suspend fun addParticipants( - conversationToken: String, + conversationToken: String?, userId: String, sourceType: String ): AddParticipantOverall { diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt index fe553a277d..ac0c7596af 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt @@ -96,7 +96,7 @@ class ContactsViewModel @Inject constructor( fun getImageUri(avatarId: String, requestBigSize: Boolean): String { return repository.getImageUri(avatarId, requestBigSize) } - fun addParticipants(conversationToken: String, userId: String, sourceType: String) { + fun addParticipants(conversationToken: String?, userId: String, sourceType: String){ viewModelScope.launch { try { val participantsOverall = repository.addParticipants(conversationToken, userId, sourceType) diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt index 9b9d74f549..d7ef33ffca 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt @@ -79,6 +79,7 @@ import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager import com.nextcloud.talk.chat.ChatActivity +import com.nextcloud.talk.contacts.ContactsActivity import com.nextcloud.talk.contacts.ContactsActivityCompose import com.nextcloud.talk.conversationlist.viewmodels.ConversationsListViewModel import com.nextcloud.talk.data.network.NetworkMonitor @@ -1066,7 +1067,7 @@ class ConversationsListActivity : conversation.type === ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL private fun showNewConversationsScreen() { - val intent = Intent(context, ContactsActivityCompose::class.java) + val intent = Intent(context, ContactsActivity::class.java) intent.putExtra(KEY_NEW_CONVERSATION, true) startActivity(intent) } From 6d0144db1af31b5904bb9b74a6e07a0a7cf43b2c Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Fri, 16 Aug 2024 23:31:12 +0200 Subject: [PATCH 09/23] add participants list Signed-off-by: sowjanyakch --- .../talk/contacts/ContactsActivityCompose.kt | 48 ++--- .../talk/contacts/ContactsRepository.kt | 2 - .../talk/contacts/ContactsRepositoryImpl.kt | 27 --- .../talk/contacts/ContactsViewModel.kt | 18 +- .../ConversationCreationActivity.kt | 164 ++++++++++++++++-- .../ConversationCreationRepository.kt | 2 + .../ConversationCreationRepositoryImpl.kt | 28 +++ .../ConversationCreationViewModel.kt | 15 ++ .../ConversationsListActivity.kt | 3 +- app/src/main/res/values/strings.xml | 1 + 10 files changed, 211 insertions(+), 97 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt index d8625af87d..f8d7f6f25b 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt @@ -12,7 +12,7 @@ import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle -import android.util.Log +import android.os.Parcelable import androidx.activity.compose.setContent import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -127,12 +127,12 @@ fun ContactItemRow( ) { val isSelected = contact in selectedContacts val roomUiState by contactsViewModel.roomViewState.collectAsState() - val isAddParticipants = contactsViewModel.isAddParticipantsView.value + val isAddParticipants = contactsViewModel.isAddParticipantsView.collectAsState() Row( modifier = Modifier .fillMaxWidth() .clickable { - if (!isAddParticipants) { + if (!isAddParticipants.value) { contactsViewModel.createRoom( CompanionClass.ROOM_TYPE_ONE_ONE, contact.source!!, @@ -159,7 +159,7 @@ fun ContactItemRow( modifier = Modifier.size(width = 45.dp, height = 45.dp) ) Text(modifier = Modifier.padding(16.dp), text = contact.label!!) - if (isAddParticipants) { + if (isAddParticipants.value) { if (isSelected) { Spacer(modifier = Modifier.weight(1f)) Icon( @@ -176,7 +176,6 @@ fun ContactItemRow( val conversation = (roomUiState as RoomUiState.Success).conversation val bundle = Bundle() bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversation?.token) - // bundle.putString(BundleKeys.KEY_ROOM_ID, conversation?.roomId) val chatIntent = Intent(context, ChatActivity::class.java) chatIntent.putExtras(bundle) chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) @@ -198,8 +197,7 @@ fun ContactItemRow( fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel) { val searchQuery by contactsViewModel.searchQuery.collectAsState() val searchState = contactsViewModel.searchState.collectAsState() - val addParticipantsUiState = contactsViewModel.addParticipantsUiState.collectAsState() - val conversationToken:String? = null + val isAddParticipants = contactsViewModel.isAddParticipantsView.collectAsState() TopAppBar( title = { Text(text = title) }, @@ -216,42 +214,22 @@ fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel }) { Icon(Icons.Filled.Search, contentDescription = stringResource(R.string.search_icon)) } - if (contactsViewModel.isAddParticipantsView.value) { + if (isAddParticipants.value) { Text( text = stringResource(id = R.string.nc_contacts_done), modifier = Modifier.clickable { - for(contacts in contactsViewModel.selectedParticipantsList){ - contacts.let { contact -> - contactsViewModel.addParticipants( - conversationToken, - contact.id!!, - contact.source!! - ) - } - } + val selectedParticipants: List = contactsViewModel.selectedParticipantsList + val intent = Intent(context, ConversationCreationActivity::class.java) + intent.putParcelableArrayListExtra( + "selectedParticipants", + selectedParticipants as ArrayList + ) + context.startActivity(intent) } ) } } ) - val state = addParticipantsUiState.value - when(state){ - is AddParticipantsUiState.Error -> { - val errorMessage = state.message - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Text(text = "Error: $errorMessage", color = Color.Red) - } - - } - is AddParticipantsUiState.None -> { - - - } - is AddParticipantsUiState.Success -> { - val conversation = state.participants - Log.d("ContactsActivityCompose", "$conversation") - } - } if (searchState.value) { Row { DisplaySearch( diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepository.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepository.kt index e610909c2f..8655b79e13 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepository.kt @@ -9,11 +9,9 @@ package com.nextcloud.talk.contacts import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall import com.nextcloud.talk.models.json.conversations.RoomOverall -import com.nextcloud.talk.models.json.participants.AddParticipantOverall interface ContactsRepository { suspend fun getContacts(searchQuery: String?, shareTypes: List): AutocompleteOverall suspend fun createRoom(roomType: String, sourceType: String, userId: String, conversationName: String?): RoomOverall fun getImageUri(avatarId: String, requestBigSize: Boolean): String - suspend fun addParticipants(conversationToken: String?, userId: String, sourceType: String): AddParticipantOverall } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepositoryImpl.kt index 04634ff4cc..6fcd275124 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsRepositoryImpl.kt @@ -12,11 +12,8 @@ import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.RetrofitBucket import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall import com.nextcloud.talk.models.json.conversations.RoomOverall -import com.nextcloud.talk.models.json.participants.AddParticipantOverall import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils -import com.nextcloud.talk.utils.ApiUtils.getRetrofitBucketForAddParticipant -import com.nextcloud.talk.utils.ApiUtils.getRetrofitBucketForAddParticipantWithSource import com.nextcloud.talk.utils.ContactUtils class ContactsRepositoryImpl( @@ -75,28 +72,4 @@ class ContactsRepositoryImpl( requestBigSize ) } - - override suspend fun addParticipants( - conversationToken: String?, - userId: String, - sourceType: String - ): AddParticipantOverall { - val retrofitBucket: RetrofitBucket = if (sourceType == "users") { - getRetrofitBucketForAddParticipant( - apiVersion, - _currentUser.baseUrl, - conversationToken, - userId - ) - } else { - getRetrofitBucketForAddParticipantWithSource( - apiVersion, - _currentUser.baseUrl, - conversationToken, - sourceType, - userId - ) - } - return ncApiCoroutines.addParticipant(credentials, retrofitBucket.url, retrofitBucket.queryMap) - } } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt index ac0c7596af..7cbf47db48 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt @@ -24,18 +24,16 @@ class ContactsViewModel @Inject constructor( val contactsViewState: StateFlow = _contactsViewState private val _roomViewState = MutableStateFlow(RoomUiState.None) val roomViewState: StateFlow = _roomViewState - private val addParticipantsViewState = MutableStateFlow(AddParticipantsUiState.None) - val addParticipantsUiState: StateFlow = addParticipantsViewState private val _searchQuery = MutableStateFlow("") val searchQuery: StateFlow = _searchQuery private val shareTypes: MutableList = mutableListOf(ShareType.User.shareType) val shareTypeList: List = shareTypes private val _searchState = MutableStateFlow(false) val searchState: StateFlow = _searchState - private val _isAddParticipantsView = MutableStateFlow(false) - val isAddParticipantsView: StateFlow = _isAddParticipantsView private val selectedParticipants = mutableListOf() val selectedParticipantsList: List = selectedParticipants + private val _isAddParticipantsView = MutableStateFlow(false) + val isAddParticipantsView: StateFlow = _isAddParticipantsView init { getContactsFromSearchParams() @@ -75,7 +73,6 @@ class ContactsViewModel @Inject constructor( } } } - fun createRoom(roomType: String, sourceType: String, userId: String, conversationName: String?) { viewModelScope.launch { try { @@ -96,17 +93,6 @@ class ContactsViewModel @Inject constructor( fun getImageUri(avatarId: String, requestBigSize: Boolean): String { return repository.getImageUri(avatarId, requestBigSize) } - fun addParticipants(conversationToken: String?, userId: String, sourceType: String){ - viewModelScope.launch { - try { - val participantsOverall = repository.addParticipants(conversationToken, userId, sourceType) - val participants = participantsOverall.ocs?.data - addParticipantsViewState.value = AddParticipantsUiState.Success(participants) - } catch (exception: Exception) { - addParticipantsViewState.value = AddParticipantsUiState.Error(exception.message ?: "") - } - } - } } sealed class ContactsUiState { diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index 31d0c176bc..2092eb8ada 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -10,6 +10,7 @@ package com.nextcloud.talk.conversationcreation import android.app.Activity import android.content.Context import android.content.Intent +import android.os.Build import android.os.Bundle import androidx.activity.compose.setContent import androidx.compose.foundation.clickable @@ -22,9 +23,13 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -37,9 +42,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.ViewModelProvider @@ -49,7 +56,7 @@ import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.contacts.ContactsActivityCompose -import com.nextcloud.talk.contacts.ContactsViewModel +import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -64,7 +71,13 @@ class ConversationCreationActivity : BaseActivity() { NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) conversationCreationViewModel = ViewModelProvider(this, viewModelFactory)[ConversationCreationViewModel::class.java] - val contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java] + val selectedParticipants: List? = when ( + Build.VERSION.SDK_INT >= Build.VERSION_CODES + .TIRAMISU + ) { + true -> intent.getParcelableArrayListExtra("selectedParticipants") + else -> intent.extras?.getParcelableArrayList("selectedParticipants") + } ?: emptyList() setContent { val colorScheme = viewThemeUtils.getColorScheme(this) @@ -93,8 +106,9 @@ class ConversationCreationActivity : BaseActivity() { DefaultUserAvatar() UploadAvatar() ConversationNameAndDescription(conversationCreationViewModel) - AddParticipants(contactsViewModel, context) + AddParticipants(selectedParticipants, context) RoomCreationOptions(conversationCreationViewModel) + CreateConversation() } } ) @@ -192,12 +206,26 @@ fun ConversationNameAndDescription(conversationCreationViewModel: ConversationCr } @Composable -fun AddParticipants(contactsViewModel: ContactsViewModel, context: Context) { - Text( - text = stringResource(id = R.string.nc_participants).uppercase(), - fontSize = 14.sp, - modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp, bottom = 16.dp) - ) +fun AddParticipants(selectedParticipants: List?, context: Context) { + Row { + Text( + text = stringResource(id = R.string.nc_participants).uppercase(), + fontSize = 14.sp, + modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp, bottom = 16.dp) + ) + if (selectedParticipants?.isNotEmpty() == true) { + Text( + text = stringResource(id = R.string.nc_edit), + fontSize = 12.sp, + modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp, bottom = 16.dp) + .clickable { + val intent = Intent(context, ContactsActivityCompose::class.java) + context.startActivity(intent) + }, + textAlign = TextAlign.Right + ) + } + } Row( modifier = Modifier @@ -210,13 +238,27 @@ fun AddParticipants(contactsViewModel: ContactsViewModel, context: Context) { verticalAlignment = Alignment .CenterVertically ) { - Icon( - painter = painterResource(id = R.drawable.ic_account_plus), - contentDescription = null, - modifier = Modifier.size(24.dp) - ) + if (selectedParticipants?.isEmpty() == true) { + Icon( + painter = painterResource(id = R.drawable.ic_account_plus), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) - Text(text = stringResource(id = R.string.nc_add_participants), modifier = Modifier.padding(start = 16.dp)) + Text(text = stringResource(id = R.string.nc_add_participants), modifier = Modifier.padding(start = 16.dp)) + } else { + LazyColumn { + items(selectedParticipants!!) { participant -> + participant.label?.let { + Text( + text = it, + modifier = Modifier.padding(all = 16.dp) + ) + } + HorizontalDivider(thickness = 0.1.dp, color = Color.Black) + } + } + } } } @@ -307,3 +349,95 @@ fun ConversationOptions(icon: Int? = null, text: Int, switch: @Composable (() -> switch?.invoke() } } + +@Composable +fun CreateConversation() { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(all = 16.dp), + contentAlignment = Alignment.Center + ) { + Button( + onClick = { + } + ) { + Text(text = stringResource(id = R.string.create_conversation)) + } + } +} + +// +// @SuppressLint("UnrememberedMutableState") +// @OptIn(ExperimentalMaterial3Api::class) +// @Composable +// fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel) { +// val searchQuery by contactsViewModel.searchQuery.collectAsState() +// val searchState = contactsViewModel.searchState.collectAsState() +// val addParticipantsUiState = contactsViewModel.addParticipantsUiState.collectAsState() +// val conversationToken:String? = null +// +// TopAppBar( +// title = { Text(text = title) }, +// navigationIcon = { +// IconButton(onClick = { +// (context as? Activity)?.finish() +// }) { +// Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back_button)) +// } +// }, +// actions = { +// IconButton(onClick = { +// contactsViewModel.updateSearchState(true) +// }) { +// Icon(Icons.Filled.Search, contentDescription = stringResource(R.string.search_icon)) +// } +// if (contactsViewModel.isAddParticipantsView.value) { +// Text( +// text = stringResource(id = R.string.nc_contacts_done), +// modifier = Modifier.clickable { +// for(contacts in contactsViewModel.selectedParticipantsList){ +// contacts.let { contact -> +// contactsViewModel.addParticipants( +// conversationToken, +// contact.id!!, +// contact.source!! +// ) +// } +// } +// } +// ) +// } +// } +// ) +// val state = addParticipantsUiState.value +// when(state){ +// is AddParticipantsUiState.Error -> { +// val errorMessage = state.message +// Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { +// Text(text = "Error: $errorMessage", color = Color.Red) +// } +// +// } +// is AddParticipantsUiState.None -> { +// +// +// } +// is AddParticipantsUiState.Success -> { +// val conversation = state.participants +// Log.d("ContactsActivityCompose", "$conversation") +// } +// } +// if (searchState.value) { +// Row { +// DisplaySearch( +// text = searchQuery, +// onTextChange = { searchQuery -> +// contactsViewModel.updateSearchQuery(query = searchQuery) +// contactsViewModel.getContactsFromSearchParams() +// }, +// contactsViewModel = contactsViewModel +// ) +// } +// } +// } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt index d241e89937..889b89849a 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt @@ -8,8 +8,10 @@ package com.nextcloud.talk.conversationcreation import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.models.json.participants.AddParticipantOverall interface ConversationCreationRepository { suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall suspend fun setConversationDescription(roomToken: String, description: String?): GenericOverall + suspend fun addParticipants(conversationToken: String?, userId: String, sourceType: String): AddParticipantOverall } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt index ab41d84f9a..bfac8a244f 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt @@ -9,9 +9,13 @@ package com.nextcloud.talk.conversationcreation import com.nextcloud.talk.api.NcApiCoroutines import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.models.RetrofitBucket import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.models.json.participants.AddParticipantOverall import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.ApiUtils.getRetrofitBucketForAddParticipant +import com.nextcloud.talk.utils.ApiUtils.getRetrofitBucketForAddParticipantWithSource class ConversationCreationRepositoryImpl( private val ncApiCoroutines: NcApiCoroutines, @@ -45,4 +49,28 @@ class ConversationCreationRepositoryImpl( description ) } + + override suspend fun addParticipants( + conversationToken: String?, + userId: String, + sourceType: String + ): AddParticipantOverall { + val retrofitBucket: RetrofitBucket = if (sourceType == "users") { + getRetrofitBucketForAddParticipant( + apiVersion, + _currentUser.baseUrl, + conversationToken, + userId + ) + } else { + getRetrofitBucketForAddParticipantWithSource( + apiVersion, + _currentUser.baseUrl, + conversationToken, + sourceType, + userId + ) + } + return ncApiCoroutines.addParticipant(credentials, retrofitBucket.url, retrofitBucket.queryMap) + } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt index 9ba08cbecd..25b13daea3 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt @@ -11,6 +11,7 @@ import android.util.Log import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.nextcloud.talk.contacts.AddParticipantsUiState import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch @@ -28,6 +29,9 @@ class ConversationCreationViewModel @Inject constructor( var isConversationAvailableForRegisteredUsers = mutableStateOf(false) var openForGuestAppUsers = mutableStateOf(false) + private val addParticipantsViewState = MutableStateFlow(AddParticipantsUiState.None) + val addParticipantsUiState: StateFlow = addParticipantsViewState + fun updateRoomName(roomName: String) { _roomName.value = roomName } @@ -54,4 +58,15 @@ class ConversationCreationViewModel @Inject constructor( } } } + fun addParticipants(conversationToken: String?, userId: String, sourceType: String) { + viewModelScope.launch { + try { + val participantsOverall = repository.addParticipants(conversationToken, userId, sourceType) + val participants = participantsOverall.ocs?.data + addParticipantsViewState.value = AddParticipantsUiState.Success(participants) + } catch (exception: Exception) { + addParticipantsViewState.value = AddParticipantsUiState.Error(exception.message ?: "") + } + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt index d7ef33ffca..9b9d74f549 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt @@ -79,7 +79,6 @@ import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager import com.nextcloud.talk.chat.ChatActivity -import com.nextcloud.talk.contacts.ContactsActivity import com.nextcloud.talk.contacts.ContactsActivityCompose import com.nextcloud.talk.conversationlist.viewmodels.ConversationsListViewModel import com.nextcloud.talk.data.network.NetworkMonitor @@ -1067,7 +1066,7 @@ class ConversationsListActivity : conversation.type === ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL private fun showNewConversationsScreen() { - val intent = Intent(context, ContactsActivity::class.java) + val intent = Intent(context, ContactsActivityCompose::class.java) intent.putExtra(KEY_NEW_CONVERSATION, true) startActivity(intent) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8c3d0f3efe..eba1ad0022 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -567,6 +567,7 @@ How to translate with transifex: No phone number integration due to missing permissions Chat via %s Account not found + Edit Save From 6b82d7209f66dc7cacc485e794f29fcd169feab7 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Mon, 19 Aug 2024 23:48:44 +0200 Subject: [PATCH 10/23] Start ContactsActivity to edit participants Signed-off-by: sowjanyakch --- .../talk/contacts/ContactsActivityCompose.kt | 156 +++++++-- .../talk/contacts/ContactsViewModel.kt | 2 +- .../nextcloud/talk/contacts/ContentItem.kt | 118 ------- .../ConversationCreationActivity.kt | 304 ++++++++---------- .../ConversationCreationRepository.kt | 1 + .../ConversationCreationRepositoryImpl.kt | 8 + .../ConversationCreationViewModel.kt | 11 + 7 files changed, 296 insertions(+), 304 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt index f8d7f6f25b..aae3316a30 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt @@ -12,11 +12,15 @@ import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle -import android.os.Parcelable +import android.util.Log import androidx.activity.compose.setContent +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -26,15 +30,20 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.List import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable @@ -48,6 +57,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.ViewModelProvider @@ -75,21 +85,28 @@ class ContactsActivityCompose : BaseActivity() { super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java] - val isAddParticipants = intent.getBooleanExtra("isAddParticipants", false) - contactsViewModel.updateIsAddParticipants(isAddParticipants) - if (isAddParticipants) { - contactsViewModel.updateShareTypes( - listOf( - ShareType.Group.shareType, - ShareType.Email.shareType, - ShareType.Circle.shareType - ) - ) - contactsViewModel.getContactsFromSearchParams() - } setContent { + val isAddParticipants = intent.getBooleanExtra("isAddParticipants", false) + val isAddParticipantsEdit = intent.getBooleanExtra("isAddParticipantsEdit", false) + contactsViewModel.updateIsAddParticipants(isAddParticipants) + if (isAddParticipants) { + contactsViewModel.updateShareTypes( + listOf( + ShareType.Group.shareType, + ShareType.Email.shareType, + ShareType.Circle.shareType + ) + ) + contactsViewModel.getContactsFromSearchParams() + } val colorScheme = viewThemeUtils.getColorScheme(this) val uiState = contactsViewModel.contactsViewState.collectAsState() + val selectedParticipants: List = if (isAddParticipantsEdit) { + intent.getParcelableArrayListExtra("selectedParticipants") ?: emptyList() + } else { + emptyList() + } + contactsViewModel.updateSelectedParticipants(selectedParticipants) MaterialTheme( colorScheme = colorScheme ) { @@ -108,7 +125,8 @@ class ContactsActivityCompose : BaseActivity() { ContactsList( contactsUiState = uiState.value, contactsViewModel = contactsViewModel, - context = context + context = context, + selectedParticipants = selectedParticipants.toMutableList() ) } } @@ -218,13 +236,17 @@ fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel Text( text = stringResource(id = R.string.nc_contacts_done), modifier = Modifier.clickable { - val selectedParticipants: List = contactsViewModel.selectedParticipantsList - val intent = Intent(context, ConversationCreationActivity::class.java) - intent.putParcelableArrayListExtra( - "selectedParticipants", - selectedParticipants as ArrayList - ) - context.startActivity(intent) + val activity = context as? Activity + val resultIntent = Intent().apply { + putParcelableArrayListExtra( + "selectedParticipants", + contactsViewModel + .selectedParticipantsList as + ArrayList + ) + } + activity?.setResult(Activity.RESULT_OK, resultIntent) + activity?.finish() } ) } @@ -304,6 +326,98 @@ fun ConversationCreationOptions(context: Context, contactsViewModel: ContactsVie } } +@Composable +fun ContactsList( + contactsUiState: ContactsUiState, + contactsViewModel: ContactsViewModel, + context: Context, + selectedParticipants: MutableList +) { + when (contactsUiState) { + is ContactsUiState.None -> { + } + is ContactsUiState.Loading -> { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + CircularProgressIndicator() + } + } + is ContactsUiState.Success -> { + val contacts = contactsUiState.contacts + Log.d(CompanionClass.TAG, "Contacts:$contacts") + if (contacts != null) { + ContactsItem(contacts, contactsViewModel, context, selectedParticipants) + } + } + is ContactsUiState.Error -> { + val errorMessage = contactsUiState.message + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text(text = "Error: $errorMessage", color = Color.Red) + } + } + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun ContactsItem( + contacts: List, + contactsViewModel: ContactsViewModel, + context: Context, + selectedParticipants: MutableList +) { + val groupedContacts: Map> = contacts.groupBy { contact -> + ( + if (contact.source == "users") { + contact.label?.first()?.uppercase() + } else { + contact.source?.replaceFirstChar { actorType -> + actorType.uppercase() + } + } + ).toString() + } + LazyColumn( + modifier = Modifier + .padding(8.dp) + .fillMaxWidth(), + contentPadding = PaddingValues(all = 10.dp), + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + groupedContacts.forEach { (initial, contactsForInitial) -> + stickyHeader { + Column { + Surface(Modifier.fillParentMaxWidth()) { + Header(initial) + } + HorizontalDivider(thickness = 0.1.dp, color = Color.Black) + } + } + items(contactsForInitial) { contact -> + ContactItemRow( + contact = contact, + contactsViewModel = contactsViewModel, + context = context, + selectedContacts = selectedParticipants + ) + Log.d(CompanionClass.TAG, "Contacts:$contact") + } + } + } +} + +@Composable +fun Header(header: String) { + Text( + text = header, + modifier = Modifier + .fillMaxSize() + .background(Color.Transparent) + .padding(start = 60.dp), + color = Color.Blue, + fontWeight = FontWeight.Bold + ) +} + class CompanionClass { companion object { internal val TAG = ContactsActivityCompose::class.simpleName diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt index 7cbf47db48..6de632a7e7 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt @@ -31,7 +31,7 @@ class ContactsViewModel @Inject constructor( private val _searchState = MutableStateFlow(false) val searchState: StateFlow = _searchState private val selectedParticipants = mutableListOf() - val selectedParticipantsList: List = selectedParticipants + val selectedParticipantsList: MutableList = selectedParticipants private val _isAddParticipantsView = MutableStateFlow(false) val isAddParticipantsView: StateFlow = _isAddParticipantsView diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt deleted file mode 100644 index e1cd47940e..0000000000 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContentItem.kt +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Nextcloud Talk - Android Client - * - * SPDX-FileCopyrightText: 2024 Your Name - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -package com.nextcloud.talk.contacts - -import android.content.Context -import android.util.Log -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser - -@Composable -fun ContactsList(contactsUiState: ContactsUiState, contactsViewModel: ContactsViewModel, context: Context) { - when (contactsUiState) { - is ContactsUiState.None -> { - } - is ContactsUiState.Loading -> { - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - CircularProgressIndicator() - } - } - is ContactsUiState.Success -> { - val contacts = contactsUiState.contacts - Log.d(CompanionClass.TAG, "Contacts:$contacts") - if (contacts != null) { - ContactsItem(contacts, contactsViewModel, context) - } - } - is ContactsUiState.Error -> { - val errorMessage = contactsUiState.message - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Text(text = "Error: $errorMessage", color = Color.Red) - } - } - } -} - -@OptIn(ExperimentalFoundationApi::class) -@Composable -fun ContactsItem(contacts: List, contactsViewModel: ContactsViewModel, context: Context) { - val groupedContacts: Map> = contacts.groupBy { contact -> - ( - if (contact.source == "users") { - contact.label?.first()?.uppercase() - } else { - contact.source?.replaceFirstChar { actorType -> - actorType.uppercase() - } - } - ).toString() - } - val selectedContacts = remember { mutableStateListOf() } - LazyColumn( - modifier = Modifier - .padding(8.dp) - .fillMaxWidth(), - contentPadding = PaddingValues(all = 10.dp), - verticalArrangement = Arrangement.spacedBy(10.dp) - ) { - groupedContacts.forEach { (initial, contactsForInitial) -> - stickyHeader { - Column { - Surface(Modifier.fillParentMaxWidth()) { - Header(initial) - } - HorizontalDivider(thickness = 0.1.dp, color = Color.Black) - } - } - items(contactsForInitial) { contact -> - ContactItemRow( - contact = contact, - contactsViewModel = contactsViewModel, - context = context, - selectedContacts = selectedContacts - ) - Log.d(CompanionClass.TAG, "Contacts:$contact") - } - } - } -} - -@Composable -fun Header(header: String) { - Text( - text = header, - modifier = Modifier - .fillMaxSize() - .background(Color.Transparent) - .padding(start = 60.dp), - color = Color.Blue, - fontWeight = FontWeight.Bold - ) -} diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index 2092eb8ada..f7363fdd09 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -7,24 +7,29 @@ package com.nextcloud.talk.conversationcreation +import android.annotation.SuppressLint import android.app.Activity import android.content.Context import android.content.Intent -import android.os.Build import android.os.Bundle +import androidx.activity.compose.ManagedActivityResultLauncher +import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Button @@ -56,6 +61,7 @@ import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.contacts.ContactsActivityCompose +import com.nextcloud.talk.contacts.loadImage import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser import javax.inject.Inject @@ -63,58 +69,74 @@ import javax.inject.Inject class ConversationCreationActivity : BaseActivity() { @Inject lateinit var viewModelFactory: ViewModelProvider.Factory - private lateinit var conversationCreationViewModel: ConversationCreationViewModel - @OptIn(ExperimentalMaterial3Api::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) - conversationCreationViewModel = - ViewModelProvider(this, viewModelFactory)[ConversationCreationViewModel::class.java] - val selectedParticipants: List? = when ( - Build.VERSION.SDK_INT >= Build.VERSION_CODES - .TIRAMISU - ) { - true -> intent.getParcelableArrayListExtra("selectedParticipants") - else -> intent.extras?.getParcelableArrayList("selectedParticipants") - } ?: emptyList() + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + val conversationCreationViewModel = ViewModelProvider( + this, + viewModelFactory + )[ConversationCreationViewModel::class.java] setContent { val colorScheme = viewThemeUtils.getColorScheme(this) MaterialTheme( colorScheme = colorScheme ) { - val context = LocalContext.current - Scaffold( - topBar = { - TopAppBar( - title = { Text(text = stringResource(id = R.string.nc_new_conversation)) }, - navigationIcon = { - IconButton(onClick = { - (context as? Activity)?.finish() - }) { - Icon( - Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(R.string.back_button) - ) - } - } + ConversationCreationScreen(conversationCreationViewModel) + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ConversationCreationScreen(conversationCreationViewModel: ConversationCreationViewModel) { + val context = LocalContext.current + val launcher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult(), + + onResult = { result -> + if (result.resultCode == Activity.RESULT_OK) { + val data = result.data + val selectedParticipants = data?.getParcelableArrayListExtra("selectedParticipants") + ?: emptyList() + conversationCreationViewModel.updateSelectedParticipants(selectedParticipants) + } + } + ) + + Scaffold( + topBar = { + TopAppBar( + title = { Text(text = stringResource(id = R.string.nc_new_conversation)) }, + navigationIcon = { + IconButton(onClick = { + (context as? Activity)?.finish() + }) { + Icon( + Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringResource(id = R.string.back_button) ) - }, - content = { - Column(Modifier.padding(it)) { - DefaultUserAvatar() - UploadAvatar() - ConversationNameAndDescription(conversationCreationViewModel) - AddParticipants(selectedParticipants, context) - RoomCreationOptions(conversationCreationViewModel) - CreateConversation() - } } - ) + } + ) + }, + content = { paddingValues -> + Column( + modifier = Modifier + .padding(paddingValues) + .verticalScroll(rememberScrollState()) + ) { + DefaultUserAvatar() + UploadAvatar() + ConversationNameAndDescription(conversationCreationViewModel) + AddParticipants(launcher, context, conversationCreationViewModel) + RoomCreationOptions(conversationCreationViewModel) + CreateConversation() } } - } + ) } @Composable @@ -196,7 +218,7 @@ fun ConversationNameAndDescription(conversationCreationViewModel: ConversationCr OutlinedTextField( value = conversationDescription.value, onValueChange = { - conversationCreationViewModel.updateRoomName(it) + conversationCreationViewModel.updateConversationDescription(it) }, label = { Text(text = stringResource(id = R.string.nc_conversation_description)) }, modifier = Modifier @@ -205,59 +227,88 @@ fun ConversationNameAndDescription(conversationCreationViewModel: ConversationCr ) } +@SuppressLint("SuspiciousIndentation") @Composable -fun AddParticipants(selectedParticipants: List?, context: Context) { - Row { - Text( - text = stringResource(id = R.string.nc_participants).uppercase(), - fontSize = 14.sp, - modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp, bottom = 16.dp) - ) - if (selectedParticipants?.isNotEmpty() == true) { - Text( - text = stringResource(id = R.string.nc_edit), - fontSize = 12.sp, - modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp, bottom = 16.dp) - .clickable { - val intent = Intent(context, ContactsActivityCompose::class.java) - context.startActivity(intent) - }, - textAlign = TextAlign.Right - ) - } - } +fun AddParticipants( + launcher: ManagedActivityResultLauncher, + context: Context, + conversationCreationViewModel: ConversationCreationViewModel +) { + val participants = conversationCreationViewModel.selectedParticipants.collectAsState() - Row( - modifier = Modifier - .padding(start = 16.dp, end = 16.dp) - .clickable { - val intent = Intent(context, ContactsActivityCompose::class.java) - intent.putExtra("isAddParticipants", true) - context.startActivity(intent) - }, - verticalAlignment = Alignment - .CenterVertically + Column( + modifier = Modifier.fillMaxHeight() + .padding(start = 16.dp, end = 16.dp, top = 16.dp) ) { - if (selectedParticipants?.isEmpty() == true) { - Icon( - painter = painterResource(id = R.drawable.ic_account_plus), - contentDescription = null, - modifier = Modifier.size(24.dp) + Row { + Text( + text = stringResource(id = R.string.nc_participants).uppercase(), + fontSize = 14.sp, + modifier = Modifier.padding(start = 16.dp, bottom = 16.dp) ) + Spacer(modifier = Modifier.weight(1f)) + if (participants.value.isNotEmpty()) { + Text( + text = stringResource(id = R.string.nc_edit), + fontSize = 12.sp, + modifier = Modifier + .padding(start = 16.dp, bottom = 16.dp) + .clickable { + val intent = Intent(context, ContactsActivityCompose::class.java) + intent.putParcelableArrayListExtra( + "selected participants", + participants.value as ArrayList + ) + intent.putExtra("isAddParticipantsEdit", true) + launcher.launch(intent) + }, + textAlign = TextAlign.Right + ) + } + } - Text(text = stringResource(id = R.string.nc_add_participants), modifier = Modifier.padding(start = 16.dp)) - } else { - LazyColumn { - items(selectedParticipants!!) { participant -> - participant.label?.let { - Text( - text = it, - modifier = Modifier.padding(all = 16.dp) - ) - } - HorizontalDivider(thickness = 0.1.dp, color = Color.Black) + participants.value.forEach { participant -> + Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + val imageUri = participant.id?.let { conversationCreationViewModel.getImageUri(it, true) } + val errorPlaceholderImage: Int = R.drawable.account_circle_96dp + val loadedImage = loadImage(imageUri, context, errorPlaceholderImage) + AsyncImage( + model = loadedImage, + contentDescription = stringResource(id = R.string.user_avatar), + modifier = Modifier.size(width = 32.dp, height = 32.dp) + ) + participant.label?.let { + Text( + text = it, + modifier = Modifier.padding(all = 16.dp), + fontSize = 15.sp + ) } } + HorizontalDivider(thickness = 0.1.dp, color = Color.Black) + } + + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + val intent = Intent(context, ContactsActivityCompose::class.java) + intent.putExtra("isAddParticipants", true) + launcher.launch(intent) + }, + verticalAlignment = Alignment.CenterVertically + ) { + if (participants.value.isEmpty()) { + Icon( + painter = painterResource(id = R.drawable.ic_account_plus), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + Text( + text = stringResource(id = R.string.nc_add_participants), + modifier = Modifier.padding(start = 16.dp) + ) + } } } } @@ -366,78 +417,3 @@ fun CreateConversation() { } } } - -// -// @SuppressLint("UnrememberedMutableState") -// @OptIn(ExperimentalMaterial3Api::class) -// @Composable -// fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel) { -// val searchQuery by contactsViewModel.searchQuery.collectAsState() -// val searchState = contactsViewModel.searchState.collectAsState() -// val addParticipantsUiState = contactsViewModel.addParticipantsUiState.collectAsState() -// val conversationToken:String? = null -// -// TopAppBar( -// title = { Text(text = title) }, -// navigationIcon = { -// IconButton(onClick = { -// (context as? Activity)?.finish() -// }) { -// Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back_button)) -// } -// }, -// actions = { -// IconButton(onClick = { -// contactsViewModel.updateSearchState(true) -// }) { -// Icon(Icons.Filled.Search, contentDescription = stringResource(R.string.search_icon)) -// } -// if (contactsViewModel.isAddParticipantsView.value) { -// Text( -// text = stringResource(id = R.string.nc_contacts_done), -// modifier = Modifier.clickable { -// for(contacts in contactsViewModel.selectedParticipantsList){ -// contacts.let { contact -> -// contactsViewModel.addParticipants( -// conversationToken, -// contact.id!!, -// contact.source!! -// ) -// } -// } -// } -// ) -// } -// } -// ) -// val state = addParticipantsUiState.value -// when(state){ -// is AddParticipantsUiState.Error -> { -// val errorMessage = state.message -// Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { -// Text(text = "Error: $errorMessage", color = Color.Red) -// } -// -// } -// is AddParticipantsUiState.None -> { -// -// -// } -// is AddParticipantsUiState.Success -> { -// val conversation = state.participants -// Log.d("ContactsActivityCompose", "$conversation") -// } -// } -// if (searchState.value) { -// Row { -// DisplaySearch( -// text = searchQuery, -// onTextChange = { searchQuery -> -// contactsViewModel.updateSearchQuery(query = searchQuery) -// contactsViewModel.getContactsFromSearchParams() -// }, -// contactsViewModel = contactsViewModel -// ) -// } -// } -// } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt index 889b89849a..f419d326a9 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt @@ -14,4 +14,5 @@ interface ConversationCreationRepository { suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall suspend fun setConversationDescription(roomToken: String, description: String?): GenericOverall suspend fun addParticipants(conversationToken: String?, userId: String, sourceType: String): AddParticipantOverall + fun getImageUri(avatarId: String, requestBigSize: Boolean): String } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt index bfac8a244f..0f84331a45 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt @@ -73,4 +73,12 @@ class ConversationCreationRepositoryImpl( } return ncApiCoroutines.addParticipant(credentials, retrofitBucket.url, retrofitBucket.queryMap) } + + override fun getImageUri(avatarId: String, requestBigSize: Boolean): String { + return ApiUtils.getUrlForAvatar( + _currentUser.baseUrl, + avatarId, + requestBigSize + ) + } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt index 25b13daea3..e022f0dfcf 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.nextcloud.talk.contacts.AddParticipantsUiState +import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch @@ -20,6 +21,12 @@ import javax.inject.Inject class ConversationCreationViewModel @Inject constructor( private val repository: ConversationCreationRepository ) : ViewModel() { + private val _selectedParticipants = MutableStateFlow>(emptyList()) + val selectedParticipants: StateFlow> = _selectedParticipants + + fun updateSelectedParticipants(participants: List) { + _selectedParticipants.value = participants + } private val _roomName = MutableStateFlow("") val roomName: StateFlow = _roomName @@ -69,4 +76,8 @@ class ConversationCreationViewModel @Inject constructor( } } } + + fun getImageUri(avatarId: String, requestBigSize: Boolean): String { + return repository.getImageUri(avatarId, requestBigSize) + } } From 2959d8e13a1fb044f7afd3de89d0f192ce9844c6 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 20 Aug 2024 23:54:05 +0200 Subject: [PATCH 11/23] Create public conversation Signed-off-by: sowjanyakch --- .../com/nextcloud/talk/api/NcApiCoroutines.kt | 7 ++ .../talk/contacts/ContactsActivityCompose.kt | 34 ++++----- .../talk/contacts/ContactsViewModel.kt | 2 +- .../ConversationCreationActivity.kt | 69 +++++++++++++++---- .../ConversationCreationRepository.kt | 4 ++ .../ConversationCreationRepositoryImpl.kt | 56 +++++++++++++++ .../ConversationCreationViewModel.kt | 24 +++++++ 7 files changed, 167 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt index 21b3e4a998..b05714c0ad 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt +++ b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt @@ -11,6 +11,7 @@ import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.participants.AddParticipantOverall +import retrofit2.http.DELETE import retrofit2.http.Field import retrofit2.http.FormUrlEncoded import retrofit2.http.GET @@ -73,4 +74,10 @@ interface NcApiCoroutines { @Url url: String?, @QueryMap options: Map? ): AddParticipantOverall + + @POST + fun makeRoomPublic(@Header("Authorization") authorization: String?, @Url url: String?): GenericOverall + + @DELETE + fun makeRoomPrivate(@Header("Authorization") authorization: String?, @Url url: String?): GenericOverall } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt index aae3316a30..d000799740 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt @@ -49,6 +49,7 @@ import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -101,12 +102,15 @@ class ContactsActivityCompose : BaseActivity() { } val colorScheme = viewThemeUtils.getColorScheme(this) val uiState = contactsViewModel.contactsViewState.collectAsState() - val selectedParticipants: List = if (isAddParticipantsEdit) { - intent.getParcelableArrayListExtra("selectedParticipants") ?: emptyList() - } else { - emptyList() + val selectedParticipants: List = remember { + if (isAddParticipants) { + intent.getParcelableArrayListExtra("selected participants") ?: emptyList() + } else { + emptyList() + } } - contactsViewModel.updateSelectedParticipants(selectedParticipants) + val participants = selectedParticipants.toSet().toMutableList() + contactsViewModel.updateSelectedParticipants(participants) MaterialTheme( colorScheme = colorScheme ) { @@ -143,7 +147,7 @@ fun ContactItemRow( context: Context, selectedContacts: MutableList ) { - val isSelected = contact in selectedContacts + val isSelected = selectedContacts.contains(contact) val roomUiState by contactsViewModel.roomViewState.collectAsState() val isAddParticipants = contactsViewModel.isAddParticipantsView.collectAsState() Row( @@ -177,16 +181,14 @@ fun ContactItemRow( modifier = Modifier.size(width = 45.dp, height = 45.dp) ) Text(modifier = Modifier.padding(16.dp), text = contact.label!!) - if (isAddParticipants.value) { - if (isSelected) { - Spacer(modifier = Modifier.weight(1f)) - Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_check_circle), - contentDescription = "Selected", - tint = Color.Blue, - modifier = Modifier.padding(end = 8.dp) - ) - } + if (isAddParticipants.value && isSelected) { + Spacer(modifier = Modifier.weight(1f)) + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_check_circle), + contentDescription = "Selected", + tint = Color.Blue, + modifier = Modifier.padding(end = 8.dp) + ) } } when (roomUiState) { diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt index 6de632a7e7..7cbf47db48 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt @@ -31,7 +31,7 @@ class ContactsViewModel @Inject constructor( private val _searchState = MutableStateFlow(false) val searchState: StateFlow = _searchState private val selectedParticipants = mutableListOf() - val selectedParticipantsList: MutableList = selectedParticipants + val selectedParticipantsList: List = selectedParticipants private val _isAddParticipantsView = MutableStateFlow(false) val isAddParticipantsView: StateFlow = _isAddParticipantsView diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index f7363fdd09..31d98af123 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -45,6 +46,7 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -60,9 +62,12 @@ import coil.compose.AsyncImage import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.chat.ChatActivity import com.nextcloud.talk.contacts.ContactsActivityCompose +import com.nextcloud.talk.contacts.RoomUiState import com.nextcloud.talk.contacts.loadImage import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser +import com.nextcloud.talk.utils.bundle.BundleKeys import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -80,10 +85,11 @@ class ConversationCreationActivity : BaseActivity() { )[ConversationCreationViewModel::class.java] setContent { val colorScheme = viewThemeUtils.getColorScheme(this) + val context = LocalContext.current MaterialTheme( colorScheme = colorScheme ) { - ConversationCreationScreen(conversationCreationViewModel) + ConversationCreationScreen(conversationCreationViewModel, context) } } } @@ -91,7 +97,7 @@ class ConversationCreationActivity : BaseActivity() { @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ConversationCreationScreen(conversationCreationViewModel: ConversationCreationViewModel) { +fun ConversationCreationScreen(conversationCreationViewModel: ConversationCreationViewModel, context: Context) { val context = LocalContext.current val launcher = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartActivityForResult(), @@ -101,7 +107,8 @@ fun ConversationCreationScreen(conversationCreationViewModel: ConversationCreati val data = result.data val selectedParticipants = data?.getParcelableArrayListExtra("selectedParticipants") ?: emptyList() - conversationCreationViewModel.updateSelectedParticipants(selectedParticipants) + val participants = selectedParticipants.toMutableList() + conversationCreationViewModel.updateSelectedParticipants(participants) } } ) @@ -133,7 +140,7 @@ fun ConversationCreationScreen(conversationCreationViewModel: ConversationCreati ConversationNameAndDescription(conversationCreationViewModel) AddParticipants(launcher, context, conversationCreationViewModel) RoomCreationOptions(conversationCreationViewModel) - CreateConversation() + CreateConversation(conversationCreationViewModel, context) } } ) @@ -234,10 +241,11 @@ fun AddParticipants( context: Context, conversationCreationViewModel: ConversationCreationViewModel ) { - val participants = conversationCreationViewModel.selectedParticipants.collectAsState() + val participants = conversationCreationViewModel.selectedParticipants.collectAsState().value Column( - modifier = Modifier.fillMaxHeight() + modifier = Modifier + .fillMaxHeight() .padding(start = 16.dp, end = 16.dp, top = 16.dp) ) { Row { @@ -247,7 +255,7 @@ fun AddParticipants( modifier = Modifier.padding(start = 16.dp, bottom = 16.dp) ) Spacer(modifier = Modifier.weight(1f)) - if (participants.value.isNotEmpty()) { + if (participants.isNotEmpty()) { Text( text = stringResource(id = R.string.nc_edit), fontSize = 12.sp, @@ -257,8 +265,9 @@ fun AddParticipants( val intent = Intent(context, ContactsActivityCompose::class.java) intent.putParcelableArrayListExtra( "selected participants", - participants.value as ArrayList + participants as ArrayList ) + intent.putExtra("isAddParticipants", true) intent.putExtra("isAddParticipantsEdit", true) launcher.launch(intent) }, @@ -267,7 +276,9 @@ fun AddParticipants( } } - participants.value.forEach { participant -> + val participant = participants.toSet() + + participant.forEach { participant -> Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { val imageUri = participant.id?.let { conversationCreationViewModel.getImageUri(it, true) } val errorPlaceholderImage: Int = R.drawable.account_circle_96dp @@ -298,7 +309,7 @@ fun AddParticipants( }, verticalAlignment = Alignment.CenterVertically ) { - if (participants.value.isEmpty()) { + if (participant.isEmpty()) { Icon( painter = painterResource(id = R.drawable.ic_account_plus), contentDescription = null, @@ -402,18 +413,52 @@ fun ConversationOptions(icon: Int? = null, text: Int, switch: @Composable (() -> } @Composable -fun CreateConversation() { +fun CreateConversation(conversationCreationViewModel: ConversationCreationViewModel, context: Context) { + val roomUiState by conversationCreationViewModel.roomViewState.collectAsState() Box( modifier = Modifier .fillMaxWidth() - .padding(all = 16.dp), + .padding(all = 16.dp) + .clickable { + }, contentAlignment = Alignment.Center ) { Button( onClick = { + val roomType = if (conversationCreationViewModel.isGuestsAllowed.value) { + "ROOM_TYPE_PUBLIC" + } else { + "ROOM_TYPE_PRIVATE" + } + conversationCreationViewModel.createRoom( + roomType, + conversationCreationViewModel.roomName.value + ) } ) { Text(text = stringResource(id = R.string.create_conversation)) } } + when (roomUiState) { + is RoomUiState.Success -> { + val conversation = (roomUiState as RoomUiState.Success).conversation + val token = conversation?.token + if (token != null) { + conversationCreationViewModel.allowGuests(token, conversationCreationViewModel.isGuestsAllowed.value) + } + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_ROOM_TOKEN, token) + val chatIntent = Intent(context, ChatActivity::class.java) + chatIntent.putExtras(bundle) + chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + context.startActivity(chatIntent) + } + is RoomUiState.Error -> { + val errorMessage = (roomUiState as RoomUiState.Error).message + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text(text = "Error: $errorMessage", color = Color.Red) + } + } + is RoomUiState.None -> {} + } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt index f419d326a9..f561fd8629 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt @@ -7,12 +7,16 @@ package com.nextcloud.talk.conversationcreation +import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.participants.AddParticipantOverall interface ConversationCreationRepository { + + fun allowGuests(token: String, allow: Boolean): ConversationCreationRepositoryImpl.AllowGuestsResult suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall suspend fun setConversationDescription(roomToken: String, description: String?): GenericOverall suspend fun addParticipants(conversationToken: String?, userId: String, sourceType: String): AddParticipantOverall + suspend fun createRoom(roomType: String, conversationName: String?): RoomOverall fun getImageUri(avatarId: String, requestBigSize: Boolean): String } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt index 0f84331a45..2e3f6088ef 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt @@ -10,8 +10,10 @@ package com.nextcloud.talk.conversationcreation import com.nextcloud.talk.api.NcApiCoroutines import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.RetrofitBucket +import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.participants.AddParticipantOverall +import com.nextcloud.talk.repositories.conversations.ConversationsRepositoryImpl.Companion.STATUS_CODE_OK import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils.getRetrofitBucketForAddParticipant @@ -25,6 +27,9 @@ class ConversationCreationRepositoryImpl( val currentUser: User = _currentUser val credentials = ApiUtils.getCredentials(_currentUser.username, _currentUser.token) val apiVersion = ApiUtils.getConversationApiVersion(_currentUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1)) + data class AllowGuestsResult( + val allow: Boolean + ) override suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall { return ncApiCoroutines.renameRoom( @@ -81,4 +86,55 @@ class ConversationCreationRepositoryImpl( requestBigSize ) } + + override suspend fun createRoom(roomType: String, conversationName: String?): RoomOverall { + val retrofitBucket: RetrofitBucket = + if (roomType == "ROOM_TYPE_PUBLIC") { + ApiUtils.getRetrofitBucketForCreateRoom( + apiVersion, + currentUser.baseUrl!!, + "ROOM_TYPE_PUBLIC", + null, + null, + conversationName + ) + } else { + ApiUtils.getRetrofitBucketForCreateRoom( + apiVersion, + currentUser.baseUrl!!, + "ROOM_TYPE_GROUP", + null, + null, + conversationName + ) + } + val response = ncApiCoroutines.createRoom( + credentials, + retrofitBucket.url, + retrofitBucket.queryMap + ) + return response + } + + override fun allowGuests(token: String, allow: Boolean): AllowGuestsResult { + val url = ApiUtils.getUrlForRoomPublic( + apiVersion, + _currentUser.baseUrl!!, + token + ) + + val result = if (allow) { + ncApiCoroutines.makeRoomPublic( + credentials, + url + ) + } else { + ncApiCoroutines.makeRoomPrivate( + credentials, + url + ) + } + + return AllowGuestsResult(result.ocs!!.meta!!.statusCode == STATUS_CODE_OK && allow) + } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt index e022f0dfcf..df32c5bb21 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt @@ -12,7 +12,9 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.nextcloud.talk.contacts.AddParticipantsUiState +import com.nextcloud.talk.contacts.RoomUiState import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser +import com.nextcloud.talk.models.json.conversations.Conversation import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch @@ -23,6 +25,8 @@ class ConversationCreationViewModel @Inject constructor( ) : ViewModel() { private val _selectedParticipants = MutableStateFlow>(emptyList()) val selectedParticipants: StateFlow> = _selectedParticipants + private val _roomViewState = MutableStateFlow(RoomUiState.None) + val roomViewState: StateFlow = _roomViewState fun updateSelectedParticipants(participants: List) { _selectedParticipants.value = participants @@ -80,4 +84,24 @@ class ConversationCreationViewModel @Inject constructor( fun getImageUri(avatarId: String, requestBigSize: Boolean): String { return repository.getImageUri(avatarId, requestBigSize) } + + 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 ?: "") + } + } + } + + fun allowGuests(token: String, allow: Boolean): ConversationCreationRepositoryImpl.AllowGuestsResult { + return repository.allowGuests(token, allow) + } } From 3db1f729815655722d9dd5893cc078c34196a77f Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Wed, 21 Aug 2024 16:34:06 +0200 Subject: [PATCH 12/23] Created a public conversation Signed-off-by: sowjanyakch --- .../ConversationCreationActivity.kt | 9 +++++++-- .../ConversationCreationRepository.kt | 3 ++- .../ConversationCreationRepositoryImpl.kt | 17 +++++++++++++---- .../ConversationCreationViewModel.kt | 3 ++- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index 31d98af123..490fb850c7 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -67,6 +67,7 @@ import com.nextcloud.talk.contacts.ContactsActivityCompose import com.nextcloud.talk.contacts.RoomUiState import com.nextcloud.talk.contacts.loadImage import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser +import com.nextcloud.talk.models.json.conversations.ConversationEnums import com.nextcloud.talk.utils.bundle.BundleKeys import javax.inject.Inject @@ -415,6 +416,7 @@ fun ConversationOptions(icon: Int? = null, text: Int, switch: @Composable (() -> @Composable fun CreateConversation(conversationCreationViewModel: ConversationCreationViewModel, context: Context) { val roomUiState by conversationCreationViewModel.roomViewState.collectAsState() + val participants = conversationCreationViewModel.selectedParticipants.collectAsState().value.toSet() Box( modifier = Modifier .fillMaxWidth() @@ -426,9 +428,9 @@ fun CreateConversation(conversationCreationViewModel: ConversationCreationViewMo Button( onClick = { val roomType = if (conversationCreationViewModel.isGuestsAllowed.value) { - "ROOM_TYPE_PUBLIC" + ConversationEnums.ConversationType.ROOM_PUBLIC_CALL } else { - "ROOM_TYPE_PRIVATE" + ConversationEnums.ConversationType.ROOM_GROUP_CALL } conversationCreationViewModel.createRoom( roomType, @@ -446,6 +448,9 @@ fun CreateConversation(conversationCreationViewModel: ConversationCreationViewMo if (token != null) { conversationCreationViewModel.allowGuests(token, conversationCreationViewModel.isGuestsAllowed.value) } + for (participant in participants) { + participant.id?.let { conversationCreationViewModel.addParticipants(token, it, participant.source!!) } + } val bundle = Bundle() bundle.putString(BundleKeys.KEY_ROOM_TOKEN, token) val chatIntent = Intent(context, ChatActivity::class.java) diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt index f561fd8629..842f988217 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt @@ -7,6 +7,7 @@ package com.nextcloud.talk.conversationcreation +import com.nextcloud.talk.models.json.conversations.ConversationEnums import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.participants.AddParticipantOverall @@ -17,6 +18,6 @@ interface ConversationCreationRepository { suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall suspend fun setConversationDescription(roomToken: String, description: String?): GenericOverall suspend fun addParticipants(conversationToken: String?, userId: String, sourceType: String): AddParticipantOverall - suspend fun createRoom(roomType: String, conversationName: String?): RoomOverall + suspend fun createRoom(roomType: ConversationEnums.ConversationType?, conversationName: String?): RoomOverall fun getImageUri(avatarId: String, requestBigSize: Boolean): String } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt index 2e3f6088ef..54d9ce09d4 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt @@ -10,6 +10,7 @@ package com.nextcloud.talk.conversationcreation import com.nextcloud.talk.api.NcApiCoroutines 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.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.participants.AddParticipantOverall @@ -87,13 +88,16 @@ class ConversationCreationRepositoryImpl( ) } - override suspend fun createRoom(roomType: String, conversationName: String?): RoomOverall { + override suspend fun createRoom( + roomType: ConversationEnums.ConversationType?, + conversationName: String? + ): RoomOverall { val retrofitBucket: RetrofitBucket = - if (roomType == "ROOM_TYPE_PUBLIC") { + if (roomType == ConversationEnums.ConversationType.ROOM_PUBLIC_CALL) { ApiUtils.getRetrofitBucketForCreateRoom( apiVersion, currentUser.baseUrl!!, - "ROOM_TYPE_PUBLIC", + ROOM_TYPE_PUBLIC, null, null, conversationName @@ -102,7 +106,7 @@ class ConversationCreationRepositoryImpl( ApiUtils.getRetrofitBucketForCreateRoom( apiVersion, currentUser.baseUrl!!, - "ROOM_TYPE_GROUP", + ROOM_TYPE_GROUP, null, null, conversationName @@ -137,4 +141,9 @@ class ConversationCreationRepositoryImpl( return AllowGuestsResult(result.ocs!!.meta!!.statusCode == STATUS_CODE_OK && allow) } + + companion object { + private const val ROOM_TYPE_PUBLIC = "3" + private const val ROOM_TYPE_GROUP = "2" + } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt index df32c5bb21..08f754adc3 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt @@ -15,6 +15,7 @@ import com.nextcloud.talk.contacts.AddParticipantsUiState import com.nextcloud.talk.contacts.RoomUiState import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.json.conversations.ConversationEnums import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch @@ -85,7 +86,7 @@ class ConversationCreationViewModel @Inject constructor( return repository.getImageUri(avatarId, requestBigSize) } - fun createRoom(roomType: String, conversationName: String?) { + fun createRoom(roomType: ConversationEnums.ConversationType?, conversationName: String?) { viewModelScope.launch { try { val room = repository.createRoom( From dba56ddac00e73bd9dc6efbcda0fa44f536868d6 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Fri, 23 Aug 2024 23:19:50 +0200 Subject: [PATCH 13/23] Solved error unable to create call adapter for GenericOverall Signed-off-by: sowjanyakch --- .../com/nextcloud/talk/api/NcApiCoroutines.kt | 10 +- .../ConversationCreationActivity.kt | 54 +++------- .../ConversationCreationRepository.kt | 2 +- .../ConversationCreationRepositoryImpl.kt | 13 +-- .../ConversationCreationViewModel.kt | 100 ++++++++++++++++-- .../repository/FakeRepositorySuccess.kt | 4 - 6 files changed, 118 insertions(+), 65 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt index b05714c0ad..d01734d64e 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt +++ b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt @@ -54,7 +54,7 @@ interface NcApiCoroutines { */ @FormUrlEncoded @PUT - fun renameRoom( + suspend fun renameRoom( @Header("Authorization") authorization: String?, @Url url: String, @Field("roomName") roomName: String? @@ -62,22 +62,22 @@ interface NcApiCoroutines { @FormUrlEncoded @PUT - fun setConversationDescription( + suspend fun setConversationDescription( @Header("Authorization") authorization: String?, @Url url: String, @Field("description") description: String? ): GenericOverall @POST - fun addParticipant( + suspend fun addParticipant( @Header("Authorization") authorization: String?, @Url url: String?, @QueryMap options: Map? ): AddParticipantOverall @POST - fun makeRoomPublic(@Header("Authorization") authorization: String?, @Url url: String?): GenericOverall + suspend fun makeRoomPublic(@Header("Authorization") authorization: String?, @Url url: String): GenericOverall @DELETE - fun makeRoomPrivate(@Header("Authorization") authorization: String?, @Url url: String?): GenericOverall + suspend fun makeRoomPrivate(@Header("Authorization") authorization: String?, @Url url: String): GenericOverall } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index 490fb850c7..15607bbb00 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -64,7 +63,6 @@ import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.chat.ChatActivity import com.nextcloud.talk.contacts.ContactsActivityCompose -import com.nextcloud.talk.contacts.RoomUiState import com.nextcloud.talk.contacts.loadImage import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser import com.nextcloud.talk.models.json.conversations.ConversationEnums @@ -415,55 +413,37 @@ fun ConversationOptions(icon: Int? = null, text: Int, switch: @Composable (() -> @Composable fun CreateConversation(conversationCreationViewModel: ConversationCreationViewModel, context: Context) { - val roomUiState by conversationCreationViewModel.roomViewState.collectAsState() - val participants = conversationCreationViewModel.selectedParticipants.collectAsState().value.toSet() + val selectedParticipants by conversationCreationViewModel.selectedParticipants.collectAsState() + val isGuestsAllowed = conversationCreationViewModel.isGuestsAllowed.value + Box( modifier = Modifier .fillMaxWidth() - .padding(all = 16.dp) - .clickable { - }, + .padding(all = 16.dp), contentAlignment = Alignment.Center ) { Button( onClick = { - val roomType = if (conversationCreationViewModel.isGuestsAllowed.value) { + val roomType = if (isGuestsAllowed) { ConversationEnums.ConversationType.ROOM_PUBLIC_CALL } else { ConversationEnums.ConversationType.ROOM_GROUP_CALL } - conversationCreationViewModel.createRoom( - roomType, - conversationCreationViewModel.roomName.value - ) + conversationCreationViewModel.createRoomAndAddParticipants( + roomType = roomType, + conversationName = conversationCreationViewModel.roomName.value, + participants = selectedParticipants.toSet() + ) { + roomToken -> + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken) + val chatIntent = Intent(context, ChatActivity::class.java) + chatIntent.putExtras(bundle) + context.startActivity(chatIntent) + } } ) { Text(text = stringResource(id = R.string.create_conversation)) } } - when (roomUiState) { - is RoomUiState.Success -> { - val conversation = (roomUiState as RoomUiState.Success).conversation - val token = conversation?.token - if (token != null) { - conversationCreationViewModel.allowGuests(token, conversationCreationViewModel.isGuestsAllowed.value) - } - for (participant in participants) { - participant.id?.let { conversationCreationViewModel.addParticipants(token, it, participant.source!!) } - } - val bundle = Bundle() - bundle.putString(BundleKeys.KEY_ROOM_TOKEN, token) - val chatIntent = Intent(context, ChatActivity::class.java) - chatIntent.putExtras(bundle) - chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - context.startActivity(chatIntent) - } - is RoomUiState.Error -> { - val errorMessage = (roomUiState as RoomUiState.Error).message - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Text(text = "Error: $errorMessage", color = Color.Red) - } - } - is RoomUiState.None -> {} - } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt index 842f988217..776cabb137 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt @@ -14,7 +14,7 @@ import com.nextcloud.talk.models.json.participants.AddParticipantOverall interface ConversationCreationRepository { - fun allowGuests(token: String, allow: Boolean): ConversationCreationRepositoryImpl.AllowGuestsResult + suspend fun allowGuests(token: String, allow: Boolean): GenericOverall suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall suspend fun setConversationDescription(roomToken: String, description: String?): GenericOverall suspend fun addParticipants(conversationToken: String?, userId: String, sourceType: String): AddParticipantOverall diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt index 54d9ce09d4..4227b0e39a 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt @@ -14,7 +14,6 @@ import com.nextcloud.talk.models.json.conversations.ConversationEnums import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.participants.AddParticipantOverall -import com.nextcloud.talk.repositories.conversations.ConversationsRepositoryImpl.Companion.STATUS_CODE_OK import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils.getRetrofitBucketForAddParticipant @@ -28,9 +27,6 @@ class ConversationCreationRepositoryImpl( val currentUser: User = _currentUser val credentials = ApiUtils.getCredentials(_currentUser.username, _currentUser.token) val apiVersion = ApiUtils.getConversationApiVersion(_currentUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1)) - data class AllowGuestsResult( - val allow: Boolean - ) override suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall { return ncApiCoroutines.renameRoom( @@ -77,7 +73,8 @@ class ConversationCreationRepositoryImpl( userId ) } - return ncApiCoroutines.addParticipant(credentials, retrofitBucket.url, retrofitBucket.queryMap) + val participants = ncApiCoroutines.addParticipant(credentials, retrofitBucket.url, retrofitBucket.queryMap) + return participants } override fun getImageUri(avatarId: String, requestBigSize: Boolean): String { @@ -120,14 +117,14 @@ class ConversationCreationRepositoryImpl( return response } - override fun allowGuests(token: String, allow: Boolean): AllowGuestsResult { + override suspend fun allowGuests(token: String, allow: Boolean): GenericOverall { val url = ApiUtils.getUrlForRoomPublic( apiVersion, _currentUser.baseUrl!!, token ) - val result = if (allow) { + val result: GenericOverall = if (allow) { ncApiCoroutines.makeRoomPublic( credentials, url @@ -139,7 +136,7 @@ class ConversationCreationRepositoryImpl( ) } - return AllowGuestsResult(result.ocs!!.meta!!.statusCode == STATUS_CODE_OK && allow) + return result } companion object { diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt index 08f754adc3..a60005bcb4 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt @@ -11,11 +11,11 @@ import android.util.Log import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.nextcloud.talk.contacts.AddParticipantsUiState -import com.nextcloud.talk.contacts.RoomUiState import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.ConversationEnums +import com.nextcloud.talk.models.json.generic.GenericMeta +import com.nextcloud.talk.repositories.conversations.ConversationsRepositoryImpl.Companion.STATUS_CODE_OK import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch @@ -26,8 +26,8 @@ class ConversationCreationViewModel @Inject constructor( ) : ViewModel() { private val _selectedParticipants = MutableStateFlow>(emptyList()) val selectedParticipants: StateFlow> = _selectedParticipants - private val _roomViewState = MutableStateFlow(RoomUiState.None) - val roomViewState: StateFlow = _roomViewState + private val _roomViewState = MutableStateFlow(RoomUIState.None) + val roomViewState: StateFlow = _roomViewState fun updateSelectedParticipants(participants: List) { _selectedParticipants.value = participants @@ -44,6 +44,9 @@ class ConversationCreationViewModel @Inject constructor( private val addParticipantsViewState = MutableStateFlow(AddParticipantsUiState.None) val addParticipantsUiState: StateFlow = addParticipantsViewState + private val _allowGuestsResult = MutableStateFlow(AllowGuestsUiState.None) + val allowGuestsResult: StateFlow = _allowGuestsResult + fun updateRoomName(roomName: String) { _roomName.value = roomName } @@ -61,6 +64,7 @@ class ConversationCreationViewModel @Inject constructor( } } } + fun setConversationDescription(roomToken: String) { viewModelScope.launch { try { @@ -70,11 +74,12 @@ class ConversationCreationViewModel @Inject constructor( } } } + fun addParticipants(conversationToken: String?, userId: String, sourceType: String) { viewModelScope.launch { try { val participantsOverall = repository.addParticipants(conversationToken, userId, sourceType) - val participants = participantsOverall.ocs?.data + val participants: List? = participantsOverall.ocs?.data addParticipantsViewState.value = AddParticipantsUiState.Success(participants) } catch (exception: Exception) { addParticipantsViewState.value = AddParticipantsUiState.Error(exception.message ?: "") @@ -82,6 +87,68 @@ class ConversationCreationViewModel @Inject constructor( } } + fun allowGuests(token: String, allow: Boolean) { + viewModelScope.launch { + try { + val response = repository.allowGuests(token, allow) + val statusCode: Int = response.ocs?.meta?.statusCode!! + val result = (statusCode == STATUS_CODE_OK) + _allowGuestsResult.value = AllowGuestsUiState.Success(result) + } catch (exception: Exception) { + _allowGuestsResult.value = AllowGuestsUiState.Error(exception.message ?: "") + } + } + } + + fun createRoomAndAddParticipants( + roomType: ConversationEnums.ConversationType, + conversationName: String, + participants: Set, + onRoomCreated: (String) -> Unit + ) { + viewModelScope.launch { + _roomViewState.value = RoomUIState.None + try { + val roomResult = repository.createRoom(roomType, conversationName) + val conversation = roomResult.ocs?.data + + if (conversation != null) { + val token = conversation.token + if (token != null) { + try { + val allowGuestsResult = repository.allowGuests(token, isGuestsAllowed.value) + val statusCode: GenericMeta? = allowGuestsResult.ocs?.meta + val result = (statusCode?.statusCode == STATUS_CODE_OK) + if (result) { + _allowGuestsResult.value = AllowGuestsUiState.Success(result) + for (participant in participants) { + if (participant.id != null) { + val participantOverall = repository.addParticipants( + token, + participant.id!!, + participant.source!! + ).ocs?.data + addParticipantsViewState.value = + AddParticipantsUiState.Success(participantOverall) + } + onRoomCreated(token) + } + } + } catch (exception: Exception) { + _allowGuestsResult.value = AllowGuestsUiState.Error(exception.message ?: "") + } + } + _roomViewState.value = RoomUIState.Success(conversation) + } else { + _roomViewState.value = RoomUIState.Error("Conversation is null") + } + } catch (e: Exception) { + _roomViewState.value = RoomUIState.Error(e.message ?: "Unknown error") + Log.e("ConversationCreationViewModel", "Error - ${e.message}") + } + } + } + fun getImageUri(avatarId: String, requestBigSize: Boolean): String { return repository.getImageUri(avatarId, requestBigSize) } @@ -95,14 +162,27 @@ class ConversationCreationViewModel @Inject constructor( ) val conversation: Conversation? = room.ocs?.data - _roomViewState.value = RoomUiState.Success(conversation) + _roomViewState.value = RoomUIState.Success(conversation) } catch (exception: Exception) { - _roomViewState.value = RoomUiState.Error(exception.message ?: "") + _roomViewState.value = RoomUIState.Error(exception.message ?: "") } } } +} +sealed class AllowGuestsUiState { + data object None : AllowGuestsUiState() + data class Success(val result: Boolean) : AllowGuestsUiState() + data class Error(val message: String) : AllowGuestsUiState() +} - fun allowGuests(token: String, allow: Boolean): ConversationCreationRepositoryImpl.AllowGuestsResult { - return repository.allowGuests(token, allow) - } +sealed class RoomUIState { + data object None : RoomUIState() + data class Success(val conversation: Conversation?) : RoomUIState() + data class Error(val message: String) : RoomUIState() +} + +sealed class AddParticipantsUiState() { + data object None : AddParticipantsUiState() + data class Success(val participants: List?) : AddParticipantsUiState() + data class Error(val message: String) : AddParticipantsUiState() } diff --git a/app/src/test/java/com/nextcloud/talk/contacts/repository/FakeRepositorySuccess.kt b/app/src/test/java/com/nextcloud/talk/contacts/repository/FakeRepositorySuccess.kt index 9022ba0598..f5d87d9195 100644 --- a/app/src/test/java/com/nextcloud/talk/contacts/repository/FakeRepositorySuccess.kt +++ b/app/src/test/java/com/nextcloud/talk/contacts/repository/FakeRepositorySuccess.kt @@ -29,8 +29,4 @@ class FakeRepositorySuccess : ContactsRepository { override fun getImageUri(avatarId: String, requestBigSize: Boolean): String { return "https://mydomain.com/index.php/avatar/$avatarId/512" } - - override fun getImageUri(avatarId: String, requestBigSize: Boolean): String { - TODO("Not yet implemented") - } } From cb6fdf6fe174f6eb871d7be4d83a0a350cbca75a Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 27 Aug 2024 21:30:56 +0200 Subject: [PATCH 14/23] Refactor createRoom() Signed-off-by: sowjanyakch --- .../com/nextcloud/talk/api/NcApiCoroutines.kt | 8 +++ .../ConversationCreationActivity.kt | 24 ++++---- .../ConversationCreationRepository.kt | 4 +- .../ConversationCreationRepositoryImpl.kt | 60 ++++++++----------- .../ConversationCreationViewModel.kt | 7 +-- 5 files changed, 51 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt index d01734d64e..874df93b2d 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt +++ b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt @@ -80,4 +80,12 @@ interface NcApiCoroutines { @DELETE suspend fun makeRoomPrivate(@Header("Authorization") authorization: String?, @Url url: String): GenericOverall + + @FormUrlEncoded + @PUT + suspend fun setPassword( + @Header("Authorization") authorization: String?, + @Url url: String?, + @Field("password") password: String? + ): GenericOverall } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index 15607bbb00..4afb058b71 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -65,7 +65,6 @@ import com.nextcloud.talk.chat.ChatActivity import com.nextcloud.talk.contacts.ContactsActivityCompose import com.nextcloud.talk.contacts.loadImage import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser -import com.nextcloud.talk.models.json.conversations.ConversationEnums import com.nextcloud.talk.utils.bundle.BundleKeys import javax.inject.Inject @@ -407,15 +406,15 @@ fun ConversationOptions(icon: Int? = null, text: Int, switch: @Composable (() -> text = stringResource(id = text), modifier = Modifier.weight(1f) ) - switch?.invoke() + if (switch != null) { + switch() + } } } @Composable fun CreateConversation(conversationCreationViewModel: ConversationCreationViewModel, context: Context) { val selectedParticipants by conversationCreationViewModel.selectedParticipants.collectAsState() - val isGuestsAllowed = conversationCreationViewModel.isGuestsAllowed.value - Box( modifier = Modifier .fillMaxWidth() @@ -424,21 +423,16 @@ fun CreateConversation(conversationCreationViewModel: ConversationCreationViewMo ) { Button( onClick = { - val roomType = if (isGuestsAllowed) { - ConversationEnums.ConversationType.ROOM_PUBLIC_CALL - } else { - ConversationEnums.ConversationType.ROOM_GROUP_CALL - } conversationCreationViewModel.createRoomAndAddParticipants( - roomType = roomType, + roomType = CompanionClass.ROOM_TYPE_GROUP, conversationName = conversationCreationViewModel.roomName.value, participants = selectedParticipants.toSet() - ) { - roomToken -> + ) { roomToken -> val bundle = Bundle() bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken) val chatIntent = Intent(context, ChatActivity::class.java) chatIntent.putExtras(bundle) + chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) context.startActivity(chatIntent) } } @@ -447,3 +441,9 @@ fun CreateConversation(conversationCreationViewModel: ConversationCreationViewMo } } } +class CompanionClass { + companion object { + internal val TAG = ConversationCreationActivity::class.simpleName + internal const val ROOM_TYPE_GROUP = "2" + } +} diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt index 776cabb137..cd3b295c96 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt @@ -7,7 +7,6 @@ package com.nextcloud.talk.conversationcreation -import com.nextcloud.talk.models.json.conversations.ConversationEnums import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.participants.AddParticipantOverall @@ -18,6 +17,7 @@ interface ConversationCreationRepository { suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall suspend fun setConversationDescription(roomToken: String, description: String?): GenericOverall suspend fun addParticipants(conversationToken: String?, userId: String, sourceType: String): AddParticipantOverall - suspend fun createRoom(roomType: ConversationEnums.ConversationType?, conversationName: String?): RoomOverall + suspend fun createRoom(roomType: String, conversationName: String?): RoomOverall fun getImageUri(avatarId: String, requestBigSize: Boolean): String + suspend fun setPassword(roomToken: String, password: String): GenericOverall } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt index 4227b0e39a..df2fd538c0 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt @@ -10,7 +10,6 @@ package com.nextcloud.talk.conversationcreation import com.nextcloud.talk.api.NcApiCoroutines 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.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.participants.AddParticipantOverall @@ -77,6 +76,23 @@ class ConversationCreationRepositoryImpl( return participants } + override suspend fun createRoom(roomType: String, conversationName: String?): RoomOverall { + val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( + apiVersion, + _currentUser.baseUrl, + roomType, + null, + null, + conversationName + ) + val response = ncApiCoroutines.createRoom( + credentials, + retrofitBucket.url, + retrofitBucket.queryMap + ) + return response + } + override fun getImageUri(avatarId: String, requestBigSize: Boolean): String { return ApiUtils.getUrlForAvatar( _currentUser.baseUrl, @@ -85,36 +101,17 @@ class ConversationCreationRepositoryImpl( ) } - override suspend fun createRoom( - roomType: ConversationEnums.ConversationType?, - conversationName: String? - ): RoomOverall { - val retrofitBucket: RetrofitBucket = - if (roomType == ConversationEnums.ConversationType.ROOM_PUBLIC_CALL) { - ApiUtils.getRetrofitBucketForCreateRoom( - apiVersion, - currentUser.baseUrl!!, - ROOM_TYPE_PUBLIC, - null, - null, - conversationName - ) - } else { - ApiUtils.getRetrofitBucketForCreateRoom( - apiVersion, - currentUser.baseUrl!!, - ROOM_TYPE_GROUP, - null, - null, - conversationName - ) - } - val response = ncApiCoroutines.createRoom( + override suspend fun setPassword(roomToken: String, password: String): GenericOverall { + val result = ncApiCoroutines.setPassword( credentials, - retrofitBucket.url, - retrofitBucket.queryMap + ApiUtils.getUrlForRoomPassword( + apiVersion, + _currentUser.baseUrl!!, + roomToken + ), + password ) - return response + return result } override suspend fun allowGuests(token: String, allow: Boolean): GenericOverall { @@ -138,9 +135,4 @@ class ConversationCreationRepositoryImpl( return result } - - companion object { - private const val ROOM_TYPE_PUBLIC = "3" - private const val ROOM_TYPE_GROUP = "2" - } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt index a60005bcb4..7e16af01b1 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt @@ -13,7 +13,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser import com.nextcloud.talk.models.json.conversations.Conversation -import com.nextcloud.talk.models.json.conversations.ConversationEnums import com.nextcloud.talk.models.json.generic.GenericMeta import com.nextcloud.talk.repositories.conversations.ConversationsRepositoryImpl.Companion.STATUS_CODE_OK import kotlinx.coroutines.flow.MutableStateFlow @@ -101,7 +100,7 @@ class ConversationCreationViewModel @Inject constructor( } fun createRoomAndAddParticipants( - roomType: ConversationEnums.ConversationType, + roomType: String, conversationName: String, participants: Set, onRoomCreated: (String) -> Unit @@ -131,9 +130,9 @@ class ConversationCreationViewModel @Inject constructor( addParticipantsViewState.value = AddParticipantsUiState.Success(participantOverall) } - onRoomCreated(token) } } + onRoomCreated(token) } catch (exception: Exception) { _allowGuestsResult.value = AllowGuestsUiState.Error(exception.message ?: "") } @@ -153,7 +152,7 @@ class ConversationCreationViewModel @Inject constructor( return repository.getImageUri(avatarId, requestBigSize) } - fun createRoom(roomType: ConversationEnums.ConversationType?, conversationName: String?) { + fun createRoom(roomType: String, conversationName: String?) { viewModelScope.launch { try { val room = repository.createRoom( From 1187b41e51005178d0126a2f31df3bb1def17a81 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Wed, 28 Aug 2024 22:34:10 +0200 Subject: [PATCH 15/23] Set password Signed-off-by: sowjanyakch --- .../ConversationCreationActivity.kt | 55 +++++++++++++++++-- .../ConversationCreationViewModel.kt | 6 ++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index 4afb058b71..8b89b0dd25 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -32,6 +32,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider @@ -42,10 +43,14 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -345,13 +350,15 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM conversationCreationViewModel.isGuestsAllowed.value = it } ) - } + }, + showDialog = false ) if (isGuestsAllowed) { ConversationOptions( icon = R.drawable.ic_lock_grey600_24px, - text = R.string.nc_set_password + text = R.string.nc_set_password, + showDialog = true ) } @@ -365,7 +372,8 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM conversationCreationViewModel.isConversationAvailableForRegisteredUsers.value = it } ) - } + }, + showDialog = false ) if (isConversationAvailableForRegisteredUsers) { @@ -378,17 +386,26 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM conversationCreationViewModel.openForGuestAppUsers.value = it } ) - } + }, + showDialog = false ) } } @Composable -fun ConversationOptions(icon: Int? = null, text: Int, switch: @Composable (() -> Unit)? = null) { +fun ConversationOptions(icon: Int? = null, text: Int, switch: @Composable (() -> Unit)? = null, showDialog: Boolean) { Row( modifier = Modifier .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp, bottom = 8.dp), + .padding(start = 16.dp, end = 16.dp, bottom = 8.dp) + .then( + if (showDialog) { + Modifier.clickable { + } + } else { + Modifier + } + ), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { @@ -412,6 +429,32 @@ fun ConversationOptions(icon: Int? = null, text: Int, switch: @Composable (() -> } } +@Composable +fun ShowPasswordDialog() { + var password by remember { mutableStateOf("") } + + AlertDialog( + onDismissRequest = { /*TODO*/ }, + confirmButton = { + Button(onClick = { + }) { + Text(text = stringResource(id = R.string.nc_cancel)) + } + }, + title = { Text(text = "Set Password") }, + text = { + TextField(value = password, onValueChange = { + password = it + }) + }, + dismissButton = { + Button(onClick = { /* Handle cancel */ }) { + Text(text = stringResource(id = R.string.nc_cancel)) + } + } + ) +} + @Composable fun CreateConversation(conversationCreationViewModel: ConversationCreationViewModel, context: Context) { val selectedParticipants by conversationCreationViewModel.selectedParticipants.collectAsState() diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt index 7e16af01b1..b69fc9b71d 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt @@ -34,6 +34,8 @@ class ConversationCreationViewModel @Inject constructor( private val _roomName = MutableStateFlow("") val roomName: StateFlow = _roomName + private val _password = MutableStateFlow("") + val password: StateFlow = _password private val _conversationDescription = MutableStateFlow("") val conversationDescription: StateFlow = _conversationDescription var isGuestsAllowed = mutableStateOf(false) @@ -50,6 +52,10 @@ class ConversationCreationViewModel @Inject constructor( _roomName.value = roomName } + fun updatePassword(password: String) { + _password.value = password + } + fun updateConversationDescription(conversationDescription: String) { _conversationDescription.value = conversationDescription } From 5f3d73efcee16489adfe9317dc2633ec27b47208 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Thu, 29 Aug 2024 18:02:09 +0200 Subject: [PATCH 16/23] make conversation available for registered users and guest app users Signed-off-by: sowjanyakch --- .../com/nextcloud/talk/api/NcApiCoroutines.kt | 8 ++++ .../ConversationCreationActivity.kt | 48 ++++++++++++++----- .../ConversationCreationRepository.kt | 1 + .../ConversationCreationRepositoryImpl.kt | 12 +++++ .../ConversationCreationViewModel.kt | 13 +++++ .../java/com/nextcloud/talk/utils/ApiUtils.kt | 4 ++ 6 files changed, 74 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt index 874df93b2d..d08d2b317a 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt +++ b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt @@ -60,6 +60,14 @@ interface NcApiCoroutines { @Field("roomName") roomName: String? ): GenericOverall + @FormUrlEncoded + @PUT + suspend fun openConversation( + @Header("Authorization") authorization: String?, + @Url url: String, + @Field("scope") scope: Int + ): GenericOverall + @FormUrlEncoded @PUT suspend fun setConversationDescription( diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index 8b89b0dd25..c5dfdfc4b3 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -351,14 +351,16 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM } ) }, - showDialog = false + showDialog = false, + conversationCreationViewModel = conversationCreationViewModel ) if (isGuestsAllowed) { ConversationOptions( icon = R.drawable.ic_lock_grey600_24px, text = R.string.nc_set_password, - showDialog = true + showDialog = true, + conversationCreationViewModel = conversationCreationViewModel ) } @@ -373,7 +375,8 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM } ) }, - showDialog = false + showDialog = false, + conversationCreationViewModel = conversationCreationViewModel ) if (isConversationAvailableForRegisteredUsers) { @@ -387,13 +390,21 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM } ) }, - showDialog = false + showDialog = false, + conversationCreationViewModel = conversationCreationViewModel ) } } @Composable -fun ConversationOptions(icon: Int? = null, text: Int, switch: @Composable (() -> Unit)? = null, showDialog: Boolean) { +fun ConversationOptions( + icon: Int? = null, + text: Int, + switch: @Composable (() -> Unit)? = null, + showDialog: Boolean, + conversationCreationViewModel: ConversationCreationViewModel +) { + var showPasswordDialog by remember { mutableStateOf(false) } Row( modifier = Modifier .fillMaxWidth() @@ -401,6 +412,7 @@ fun ConversationOptions(icon: Int? = null, text: Int, switch: @Composable (() -> .then( if (showDialog) { Modifier.clickable { + showPasswordDialog = true } } else { Modifier @@ -426,29 +438,41 @@ fun ConversationOptions(icon: Int? = null, text: Int, switch: @Composable (() -> if (switch != null) { switch() } + if (showPasswordDialog) { + ShowPasswordDialog( + onDismiss = { showPasswordDialog = false }, + conversationCreationViewModel = conversationCreationViewModel + ) + } } } @Composable -fun ShowPasswordDialog() { +fun ShowPasswordDialog(onDismiss: () -> Unit, conversationCreationViewModel: ConversationCreationViewModel) { var password by remember { mutableStateOf("") } AlertDialog( - onDismissRequest = { /*TODO*/ }, + onDismissRequest = onDismiss, confirmButton = { Button(onClick = { + conversationCreationViewModel.updatePassword(password) + onDismiss() }) { - Text(text = stringResource(id = R.string.nc_cancel)) + Text(text = "Save") } }, title = { Text(text = "Set Password") }, text = { - TextField(value = password, onValueChange = { - password = it - }) + TextField( + value = password, + onValueChange = { + password = it + }, + label = { Text(text = "Enter a password") } + ) }, dismissButton = { - Button(onClick = { /* Handle cancel */ }) { + Button(onClick = { onDismiss() }) { Text(text = stringResource(id = R.string.nc_cancel)) } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt index cd3b295c96..6ebb6c3508 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt @@ -16,6 +16,7 @@ interface ConversationCreationRepository { suspend fun allowGuests(token: String, allow: Boolean): GenericOverall suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall suspend fun setConversationDescription(roomToken: String, description: String?): GenericOverall + suspend fun openConversation(roomToken: String, scope: Int): GenericOverall suspend fun addParticipants(conversationToken: String?, userId: String, sourceType: String): AddParticipantOverall suspend fun createRoom(roomType: String, conversationName: String?): RoomOverall fun getImageUri(avatarId: String, requestBigSize: Boolean): String diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt index df2fd538c0..4e00f174c7 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt @@ -51,6 +51,18 @@ class ConversationCreationRepositoryImpl( ) } + override suspend fun openConversation(roomToken: String, scope: Int): GenericOverall { + return ncApiCoroutines.openConversation( + credentials, + ApiUtils.getUrlForOpeningConversations( + apiVersion, + _currentUser.baseUrl, + roomToken + ), + scope + ) + } + override suspend fun addParticipants( conversationToken: String?, userId: String, diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt index b69fc9b71d..9a097f9ab6 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt @@ -8,6 +8,7 @@ package com.nextcloud.talk.conversationcreation import android.util.Log +import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -41,6 +42,7 @@ class ConversationCreationViewModel @Inject constructor( var isGuestsAllowed = mutableStateOf(false) var isConversationAvailableForRegisteredUsers = mutableStateOf(false) var openForGuestAppUsers = mutableStateOf(false) + private val scope: MutableState = mutableStateOf(null) private val addParticipantsViewState = MutableStateFlow(AddParticipantsUiState.None) val addParticipantsUiState: StateFlow = addParticipantsViewState @@ -111,6 +113,11 @@ class ConversationCreationViewModel @Inject constructor( participants: Set, onRoomCreated: (String) -> Unit ) { + val scope = when { + isConversationAvailableForRegisteredUsers.value && !openForGuestAppUsers.value -> 1 + isConversationAvailableForRegisteredUsers.value && openForGuestAppUsers.value -> 2 + else -> 0 + } viewModelScope.launch { _roomViewState.value = RoomUIState.None try { @@ -121,6 +128,10 @@ class ConversationCreationViewModel @Inject constructor( val token = conversation.token if (token != null) { try { + val conversationDescription = repository.setConversationDescription( + token, + _conversationDescription.value + ) val allowGuestsResult = repository.allowGuests(token, isGuestsAllowed.value) val statusCode: GenericMeta? = allowGuestsResult.ocs?.meta val result = (statusCode?.statusCode == STATUS_CODE_OK) @@ -138,6 +149,8 @@ class ConversationCreationViewModel @Inject constructor( } } } + val passwordResult = repository.setPassword(token, _password.value) + repository.openConversation(token, scope) onRoomCreated(token) } catch (exception: Exception) { _allowGuestsResult.value = AllowGuestsUiState.Error(exception.message ?: "") diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt index 28d4e4ff7b..c0168c4135 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt @@ -539,6 +539,10 @@ object ApiUtils { return getUrlForRoom(version, baseUrl, token) + "/description" } + fun getUrlForOpeningConversations(version: Int, baseUrl: String?, token: String): String { + return getUrlForRoom(version, baseUrl, token) + "/listable" + } + fun getUrlForTranslation(baseUrl: String): String { return "$baseUrl$OCS_API_VERSION/translation/translate" } From ff63bcd855ad4d0d979fda47ae401ce8377e2b03 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Mon, 2 Sep 2024 21:00:22 +0200 Subject: [PATCH 17/23] Properly select and deselect contact items Signed-off-by: sowjanyakch --- .../talk/contacts/ContactsActivityCompose.kt | 78 +++++++++++-------- .../talk/contacts/ContactsViewModel.kt | 6 +- .../ConversationCreationActivity.kt | 24 +++--- .../ConversationCreationViewModel.kt | 7 -- 4 files changed, 58 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt index d000799740..5fd1edb284 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt @@ -11,6 +11,7 @@ import android.annotation.SuppressLint import android.app.Activity import android.content.Context import android.content.Intent +import android.os.Build import android.os.Bundle import android.util.Log import androidx.activity.compose.setContent @@ -49,7 +50,9 @@ import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -88,7 +91,6 @@ class ContactsActivityCompose : BaseActivity() { contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java] setContent { val isAddParticipants = intent.getBooleanExtra("isAddParticipants", false) - val isAddParticipantsEdit = intent.getBooleanExtra("isAddParticipantsEdit", false) contactsViewModel.updateIsAddParticipants(isAddParticipants) if (isAddParticipants) { contactsViewModel.updateShareTypes( @@ -102,11 +104,12 @@ class ContactsActivityCompose : BaseActivity() { } val colorScheme = viewThemeUtils.getColorScheme(this) val uiState = contactsViewModel.contactsViewState.collectAsState() - val selectedParticipants: List = remember { - if (isAddParticipants) { - intent.getParcelableArrayListExtra("selected participants") ?: emptyList() + val selectedParticipants = remember { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + intent.getParcelableArrayListExtra("selectedParticipants", AutocompleteUser::class.java) ?: emptyList() } else { - emptyList() + @Suppress("DEPRECATION") + intent.getParcelableArrayListExtra("selectedParticipants") ?: emptyList() } } val participants = selectedParticipants.toSet().toMutableList() @@ -147,29 +150,34 @@ fun ContactItemRow( context: Context, selectedContacts: MutableList ) { - val isSelected = selectedContacts.contains(contact) + var isSelected by remember { mutableStateOf(selectedContacts.contains(contact)) } val roomUiState by contactsViewModel.roomViewState.collectAsState() val isAddParticipants = contactsViewModel.isAddParticipantsView.collectAsState() Row( modifier = Modifier .fillMaxWidth() - .clickable { - if (!isAddParticipants.value) { - contactsViewModel.createRoom( - CompanionClass.ROOM_TYPE_ONE_ONE, - contact.source!!, - contact.id!!, - null - ) - } else { - if (isSelected) { - selectedContacts.remove(contact) + .clickable( + onClick = { + if (!isAddParticipants.value) { + contactsViewModel.createRoom( + CompanionClass.ROOM_TYPE_ONE_ONE, + contact.source!!, + contact.id!!, + null + ) } else { - selectedContacts.add(contact) + isSelected = !isSelected + selectedContacts.apply { + if (isSelected) { + add(contact) + } else { + remove(contact) + } + } + contactsViewModel.updateSelectedParticipants(selectedContacts) } - contactsViewModel.updateSelectedParticipants(selectedContacts) } - }, + ), verticalAlignment = Alignment.CenterVertically ) { val imageUri = contact.id?.let { contactsViewModel.getImageUri(it, true) } @@ -181,14 +189,16 @@ fun ContactItemRow( modifier = Modifier.size(width = 45.dp, height = 45.dp) ) Text(modifier = Modifier.padding(16.dp), text = contact.label!!) - if (isAddParticipants.value && isSelected) { - Spacer(modifier = Modifier.weight(1f)) - Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_check_circle), - contentDescription = "Selected", - tint = Color.Blue, - modifier = Modifier.padding(end = 8.dp) - ) + if (isAddParticipants.value) { + if (isSelected) { + Spacer(modifier = Modifier.weight(1f)) + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_check_circle), + contentDescription = "Selected", + tint = Color.Blue, + modifier = Modifier.padding(end = 8.dp) + ) + } } } when (roomUiState) { @@ -238,17 +248,17 @@ fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel Text( text = stringResource(id = R.string.nc_contacts_done), modifier = Modifier.clickable { - val activity = context as? Activity val resultIntent = Intent().apply { putParcelableArrayListExtra( "selectedParticipants", - contactsViewModel - .selectedParticipantsList as - ArrayList + ArrayList( + contactsViewModel + .selectedParticipantsList.value + ) ) } - activity?.setResult(Activity.RESULT_OK, resultIntent) - activity?.finish() + (context as? Activity)?.setResult(Activity.RESULT_OK, resultIntent) + (context as? Activity)?.finish() } ) } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt index 7cbf47db48..c1ba90db2c 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt @@ -30,8 +30,8 @@ class ContactsViewModel @Inject constructor( val shareTypeList: List = shareTypes private val _searchState = MutableStateFlow(false) val searchState: StateFlow = _searchState - private val selectedParticipants = mutableListOf() - val selectedParticipantsList: List = selectedParticipants + private val _selectedParticipants = MutableStateFlow>(emptyList()) + val selectedParticipantsList: StateFlow> = _selectedParticipants private val _isAddParticipantsView = MutableStateFlow(false) val isAddParticipantsView: StateFlow = _isAddParticipantsView @@ -44,7 +44,7 @@ class ContactsViewModel @Inject constructor( } fun updateSelectedParticipants(participants: List) { - selectedParticipants.addAll(participants) + _selectedParticipants.value = participants } fun updateSearchState(searchState: Boolean) { _searchState.value = searchState diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index c5dfdfc4b3..6f016b94c2 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -5,6 +5,8 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +@file:Suppress("DEPRECATION") + package com.nextcloud.talk.conversationcreation import android.annotation.SuppressLint @@ -267,7 +269,7 @@ fun AddParticipants( .clickable { val intent = Intent(context, ContactsActivityCompose::class.java) intent.putParcelableArrayListExtra( - "selected participants", + "selectedParticipants", participants as ArrayList ) intent.putExtra("isAddParticipants", true) @@ -278,10 +280,7 @@ fun AddParticipants( ) } } - - val participant = participants.toSet() - - participant.forEach { participant -> + participants.toSet().forEach { participant -> Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { val imageUri = participant.id?.let { conversationCreationViewModel.getImageUri(it, true) } val errorPlaceholderImage: Int = R.drawable.account_circle_96dp @@ -312,7 +311,7 @@ fun AddParticipants( }, verticalAlignment = Alignment.CenterVertically ) { - if (participant.isEmpty()) { + if (participants.isEmpty()) { Icon( painter = painterResource(id = R.drawable.ic_account_plus), contentDescription = null, @@ -329,17 +328,16 @@ fun AddParticipants( @Composable fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewModel) { - var isGuestsAllowed = conversationCreationViewModel.isGuestsAllowed.value - var isConversationAvailableForRegisteredUsers = conversationCreationViewModel + val isGuestsAllowed = conversationCreationViewModel.isGuestsAllowed.value + val isConversationAvailableForRegisteredUsers = conversationCreationViewModel .isConversationAvailableForRegisteredUsers.value - var isOpenForGuestAppUsers = conversationCreationViewModel.openForGuestAppUsers.value + val isOpenForGuestAppUsers = conversationCreationViewModel.openForGuestAppUsers.value Text( text = stringResource(id = R.string.nc_visible).uppercase(), fontSize = 14.sp, modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp) ) - ConversationOptions( icon = R.drawable.ic_avatar_link, text = R.string.nc_guest_access_allow_title, @@ -458,17 +456,17 @@ fun ShowPasswordDialog(onDismiss: () -> Unit, conversationCreationViewModel: Con conversationCreationViewModel.updatePassword(password) onDismiss() }) { - Text(text = "Save") + Text(text = stringResource(id = R.string.save)) } }, - title = { Text(text = "Set Password") }, + title = { Text(text = stringResource(id = R.string.nc_set_password)) }, text = { TextField( value = password, onValueChange = { password = it }, - label = { Text(text = "Enter a password") } + label = { Text(text = stringResource(id = R.string.nc_guest_access_password_dialog_hint)) } ) }, dismissButton = { diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt index 9a097f9ab6..97d6163491 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt @@ -8,7 +8,6 @@ package com.nextcloud.talk.conversationcreation import android.util.Log -import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -42,14 +41,8 @@ class ConversationCreationViewModel @Inject constructor( var isGuestsAllowed = mutableStateOf(false) var isConversationAvailableForRegisteredUsers = mutableStateOf(false) var openForGuestAppUsers = mutableStateOf(false) - private val scope: MutableState = mutableStateOf(null) - private val addParticipantsViewState = MutableStateFlow(AddParticipantsUiState.None) - val addParticipantsUiState: StateFlow = addParticipantsViewState - private val _allowGuestsResult = MutableStateFlow(AllowGuestsUiState.None) - val allowGuestsResult: StateFlow = _allowGuestsResult - fun updateRoomName(roomName: String) { _roomName.value = roomName } From 52378327f9e78ae8a641033bd3ae0ba0a3fbe795 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 3 Sep 2024 10:58:06 +0200 Subject: [PATCH 18/23] Work on warnings Signed-off-by: sowjanyakch --- .../talk/contacts/ContactsActivityCompose.kt | 12 ++- .../talk/contacts/ContactsViewModel.kt | 12 +-- .../ConversationCreationActivity.kt | 25 ++++-- .../ConversationCreationViewModel.kt | 76 ++++--------------- 4 files changed, 45 insertions(+), 80 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt index 5fd1edb284..2c6611084a 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt @@ -106,7 +106,8 @@ class ContactsActivityCompose : BaseActivity() { val uiState = contactsViewModel.contactsViewState.collectAsState() val selectedParticipants = remember { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableArrayListExtra("selectedParticipants", AutocompleteUser::class.java) ?: emptyList() + intent.getParcelableArrayListExtra("selectedParticipants", AutocompleteUser::class.java) + ?: emptyList() } else { @Suppress("DEPRECATION") intent.getParcelableArrayListExtra("selectedParticipants") ?: emptyList() @@ -224,7 +225,10 @@ fun ContactItemRow( @SuppressLint("UnrememberedMutableState") @OptIn(ExperimentalMaterial3Api::class) @Composable -fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel) { +fun AppBar( + title: String, + context: Context, + contactsViewModel: ContactsViewModel) { val searchQuery by contactsViewModel.searchQuery.collectAsState() val searchState = contactsViewModel.searchState.collectAsState() val isAddParticipants = contactsViewModel.isAddParticipantsView.collectAsState() @@ -279,7 +283,9 @@ fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel } @Composable -fun ConversationCreationOptions(context: Context, contactsViewModel: ContactsViewModel) { +fun ConversationCreationOptions( + context: Context, + contactsViewModel: ContactsViewModel) { val isAddParticipants by contactsViewModel.isAddParticipantsView.collectAsState() if (!isAddParticipants) { Column { diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt index c1ba90db2c..7dda675913 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt @@ -30,8 +30,8 @@ class ContactsViewModel @Inject constructor( val shareTypeList: List = shareTypes private val _searchState = MutableStateFlow(false) val searchState: StateFlow = _searchState - private val _selectedParticipants = MutableStateFlow>(emptyList()) - val selectedParticipantsList: StateFlow> = _selectedParticipants + private val selectedParticipants = MutableStateFlow>(emptyList()) + val selectedParticipantsList: StateFlow> = selectedParticipants private val _isAddParticipantsView = MutableStateFlow(false) val isAddParticipantsView: StateFlow = _isAddParticipantsView @@ -44,7 +44,7 @@ class ContactsViewModel @Inject constructor( } fun updateSelectedParticipants(participants: List) { - _selectedParticipants.value = participants + selectedParticipants.value = participants } fun updateSearchState(searchState: Boolean) { _searchState.value = searchState @@ -107,9 +107,3 @@ sealed class RoomUiState { data class Success(val conversation: Conversation?) : RoomUiState() data class Error(val message: String) : RoomUiState() } - -sealed class AddParticipantsUiState() { - data object None : AddParticipantsUiState() - data class Success(val participants: List?) : AddParticipantsUiState() - data class Error(val message: String) : AddParticipantsUiState() -} diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index 6f016b94c2..60661589cd 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -102,8 +102,9 @@ class ConversationCreationActivity : BaseActivity() { @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ConversationCreationScreen(conversationCreationViewModel: ConversationCreationViewModel, context: Context) { - val context = LocalContext.current +fun ConversationCreationScreen( + conversationCreationViewModel: ConversationCreationViewModel, + context: Context) { val launcher = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartActivityForResult(), @@ -159,7 +160,7 @@ fun DefaultUserAvatar() { ) { AsyncImage( model = R.drawable.ic_circular_group, - contentDescription = "User Avatar", + contentDescription = stringResource(id = R.string.user_avatar), modifier = Modifier .size(width = 84.dp, height = 84.dp) .padding(top = 8.dp) @@ -214,7 +215,9 @@ fun UploadAvatar() { } @Composable -fun ConversationNameAndDescription(conversationCreationViewModel: ConversationCreationViewModel) { +fun ConversationNameAndDescription( + conversationCreationViewModel: ConversationCreationViewModel +) { val conversationRoomName = conversationCreationViewModel.roomName.collectAsState() val conversationDescription = conversationCreationViewModel.conversationDescription.collectAsState() OutlinedTextField( @@ -327,7 +330,9 @@ fun AddParticipants( } @Composable -fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewModel) { +fun RoomCreationOptions( + conversationCreationViewModel: ConversationCreationViewModel +) { val isGuestsAllowed = conversationCreationViewModel.isGuestsAllowed.value val isConversationAvailableForRegisteredUsers = conversationCreationViewModel .isConversationAvailableForRegisteredUsers.value @@ -446,7 +451,10 @@ fun ConversationOptions( } @Composable -fun ShowPasswordDialog(onDismiss: () -> Unit, conversationCreationViewModel: ConversationCreationViewModel) { +fun ShowPasswordDialog( + onDismiss: () -> Unit, + conversationCreationViewModel: ConversationCreationViewModel +) { var password by remember { mutableStateOf("") } AlertDialog( @@ -478,7 +486,10 @@ fun ShowPasswordDialog(onDismiss: () -> Unit, conversationCreationViewModel: Con } @Composable -fun CreateConversation(conversationCreationViewModel: ConversationCreationViewModel, context: Context) { +fun CreateConversation( + conversationCreationViewModel: ConversationCreationViewModel, + context: Context +) { val selectedParticipants by conversationCreationViewModel.selectedParticipants.collectAsState() Box( modifier = Modifier diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt index 97d6163491..2ee9276c2d 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt @@ -25,8 +25,7 @@ class ConversationCreationViewModel @Inject constructor( ) : ViewModel() { private val _selectedParticipants = MutableStateFlow>(emptyList()) val selectedParticipants: StateFlow> = _selectedParticipants - private val _roomViewState = MutableStateFlow(RoomUIState.None) - val roomViewState: StateFlow = _roomViewState + private val roomViewState = MutableStateFlow(RoomUIState.None) fun updateSelectedParticipants(participants: List) { _selectedParticipants.value = participants @@ -42,7 +41,7 @@ class ConversationCreationViewModel @Inject constructor( var isConversationAvailableForRegisteredUsers = mutableStateOf(false) var openForGuestAppUsers = mutableStateOf(false) private val addParticipantsViewState = MutableStateFlow(AddParticipantsUiState.None) - private val _allowGuestsResult = MutableStateFlow(AllowGuestsUiState.None) + private val allowGuestsResult = MutableStateFlow(AllowGuestsUiState.None) fun updateRoomName(roomName: String) { _roomName.value = roomName } @@ -55,51 +54,6 @@ class ConversationCreationViewModel @Inject constructor( _conversationDescription.value = conversationDescription } - fun renameConversation(roomToken: String) { - viewModelScope.launch { - try { - repository.renameConversation(roomToken, roomName.value) - } catch (e: Exception) { - Log.d("ConversationCreationViewModel", "${e.message}") - } - } - } - - fun setConversationDescription(roomToken: String) { - viewModelScope.launch { - try { - repository.setConversationDescription(roomToken, conversationDescription.value) - } catch (e: Exception) { - Log.d("ConversationCreationViewModel", "${e.message}") - } - } - } - - fun addParticipants(conversationToken: String?, userId: String, sourceType: String) { - viewModelScope.launch { - try { - val participantsOverall = repository.addParticipants(conversationToken, userId, sourceType) - val participants: List? = participantsOverall.ocs?.data - addParticipantsViewState.value = AddParticipantsUiState.Success(participants) - } catch (exception: Exception) { - addParticipantsViewState.value = AddParticipantsUiState.Error(exception.message ?: "") - } - } - } - - fun allowGuests(token: String, allow: Boolean) { - viewModelScope.launch { - try { - val response = repository.allowGuests(token, allow) - val statusCode: Int = response.ocs?.meta?.statusCode!! - val result = (statusCode == STATUS_CODE_OK) - _allowGuestsResult.value = AllowGuestsUiState.Success(result) - } catch (exception: Exception) { - _allowGuestsResult.value = AllowGuestsUiState.Error(exception.message ?: "") - } - } - } - fun createRoomAndAddParticipants( roomType: String, conversationName: String, @@ -112,7 +66,7 @@ class ConversationCreationViewModel @Inject constructor( else -> 0 } viewModelScope.launch { - _roomViewState.value = RoomUIState.None + roomViewState.value = RoomUIState.None try { val roomResult = repository.createRoom(roomType, conversationName) val conversation = roomResult.ocs?.data @@ -121,15 +75,15 @@ class ConversationCreationViewModel @Inject constructor( val token = conversation.token if (token != null) { try { - val conversationDescription = repository.setConversationDescription( + repository.setConversationDescription( token, _conversationDescription.value ) - val allowGuestsResult = repository.allowGuests(token, isGuestsAllowed.value) - val statusCode: GenericMeta? = allowGuestsResult.ocs?.meta + val allowGuestResultOverall = repository.allowGuests(token, isGuestsAllowed.value) + val statusCode: GenericMeta? = allowGuestResultOverall.ocs?.meta val result = (statusCode?.statusCode == STATUS_CODE_OK) if (result) { - _allowGuestsResult.value = AllowGuestsUiState.Success(result) + allowGuestsResult.value = AllowGuestsUiState.Success(result) for (participant in participants) { if (participant.id != null) { val participantOverall = repository.addParticipants( @@ -142,19 +96,19 @@ class ConversationCreationViewModel @Inject constructor( } } } - val passwordResult = repository.setPassword(token, _password.value) + repository.setPassword(token, _password.value) repository.openConversation(token, scope) onRoomCreated(token) } catch (exception: Exception) { - _allowGuestsResult.value = AllowGuestsUiState.Error(exception.message ?: "") + allowGuestsResult.value = AllowGuestsUiState.Error(exception.message ?: "") } } - _roomViewState.value = RoomUIState.Success(conversation) + roomViewState.value = RoomUIState.Success(conversation) } else { - _roomViewState.value = RoomUIState.Error("Conversation is null") + roomViewState.value = RoomUIState.Error("Conversation is null") } } catch (e: Exception) { - _roomViewState.value = RoomUIState.Error(e.message ?: "Unknown error") + roomViewState.value = RoomUIState.Error(e.message ?: "Unknown error") Log.e("ConversationCreationViewModel", "Error - ${e.message}") } } @@ -173,9 +127,9 @@ class ConversationCreationViewModel @Inject constructor( ) val conversation: Conversation? = room.ocs?.data - _roomViewState.value = RoomUIState.Success(conversation) + roomViewState.value = RoomUIState.Success(conversation) } catch (exception: Exception) { - _roomViewState.value = RoomUIState.Error(exception.message ?: "") + roomViewState.value = RoomUIState.Error(exception.message ?: "") } } } @@ -192,7 +146,7 @@ sealed class RoomUIState { data class Error(val message: String) : RoomUIState() } -sealed class AddParticipantsUiState() { +sealed class AddParticipantsUiState { data object None : AddParticipantsUiState() data class Success(val participants: List?) : AddParticipantsUiState() data class Error(val message: String) : AddParticipantsUiState() From ecb3d5380c15b4f42ee52ab71e96a1da8ec477bf Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 3 Sep 2024 11:40:59 +0200 Subject: [PATCH 19/23] Ktlint Format Signed-off-by: sowjanyakch --- .../talk/contacts/ContactsActivityCompose.kt | 9 ++------ .../ConversationCreationActivity.kt | 22 +++++-------------- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt index 2c6611084a..cc03741349 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt @@ -225,10 +225,7 @@ fun ContactItemRow( @SuppressLint("UnrememberedMutableState") @OptIn(ExperimentalMaterial3Api::class) @Composable -fun AppBar( - title: String, - context: Context, - contactsViewModel: ContactsViewModel) { +fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel) { val searchQuery by contactsViewModel.searchQuery.collectAsState() val searchState = contactsViewModel.searchState.collectAsState() val isAddParticipants = contactsViewModel.isAddParticipantsView.collectAsState() @@ -283,9 +280,7 @@ fun AppBar( } @Composable -fun ConversationCreationOptions( - context: Context, - contactsViewModel: ContactsViewModel) { +fun ConversationCreationOptions(context: Context, contactsViewModel: ContactsViewModel) { val isAddParticipants by contactsViewModel.isAddParticipantsView.collectAsState() if (!isAddParticipants) { Column { diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index 60661589cd..440173fb41 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -102,9 +102,7 @@ class ConversationCreationActivity : BaseActivity() { @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ConversationCreationScreen( - conversationCreationViewModel: ConversationCreationViewModel, - context: Context) { +fun ConversationCreationScreen(conversationCreationViewModel: ConversationCreationViewModel, context: Context) { val launcher = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartActivityForResult(), @@ -215,9 +213,7 @@ fun UploadAvatar() { } @Composable -fun ConversationNameAndDescription( - conversationCreationViewModel: ConversationCreationViewModel -) { +fun ConversationNameAndDescription(conversationCreationViewModel: ConversationCreationViewModel) { val conversationRoomName = conversationCreationViewModel.roomName.collectAsState() val conversationDescription = conversationCreationViewModel.conversationDescription.collectAsState() OutlinedTextField( @@ -330,9 +326,7 @@ fun AddParticipants( } @Composable -fun RoomCreationOptions( - conversationCreationViewModel: ConversationCreationViewModel -) { +fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewModel) { val isGuestsAllowed = conversationCreationViewModel.isGuestsAllowed.value val isConversationAvailableForRegisteredUsers = conversationCreationViewModel .isConversationAvailableForRegisteredUsers.value @@ -451,10 +445,7 @@ fun ConversationOptions( } @Composable -fun ShowPasswordDialog( - onDismiss: () -> Unit, - conversationCreationViewModel: ConversationCreationViewModel -) { +fun ShowPasswordDialog(onDismiss: () -> Unit, conversationCreationViewModel: ConversationCreationViewModel) { var password by remember { mutableStateOf("") } AlertDialog( @@ -486,10 +477,7 @@ fun ShowPasswordDialog( } @Composable -fun CreateConversation( - conversationCreationViewModel: ConversationCreationViewModel, - context: Context -) { +fun CreateConversation(conversationCreationViewModel: ConversationCreationViewModel, context: Context) { val selectedParticipants by conversationCreationViewModel.selectedParticipants.collectAsState() Box( modifier = Modifier From 8d1d44692e082c3b8f0f505302b4e5d61c72cc51 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 4 Sep 2024 11:09:44 +0200 Subject: [PATCH 20/23] only set password if not empty otherwise response will be 400 Signed-off-by: Marcel Hibbe --- .../conversationcreation/ConversationCreationViewModel.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt index 2ee9276c2d..759637cde1 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt @@ -96,7 +96,9 @@ class ConversationCreationViewModel @Inject constructor( } } } - repository.setPassword(token, _password.value) + if (_password.value.isNotEmpty()) { + repository.setPassword(token, _password.value) + } repository.openConversation(token, scope) onRoomCreated(token) } catch (exception: Exception) { @@ -134,6 +136,7 @@ class ConversationCreationViewModel @Inject constructor( } } } + sealed class AllowGuestsUiState { data object None : AllowGuestsUiState() data class Success(val result: Boolean) : AllowGuestsUiState() From 8a59dd900d8928cc0dc20e571f598285f3511797 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 4 Sep 2024 11:23:04 +0200 Subject: [PATCH 21/23] fix to align Participants header Signed-off-by: Marcel Hibbe --- .../talk/conversationcreation/ConversationCreationActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index 440173fb41..edd94e42cd 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -256,7 +256,7 @@ fun AddParticipants( Text( text = stringResource(id = R.string.nc_participants).uppercase(), fontSize = 14.sp, - modifier = Modifier.padding(start = 16.dp, bottom = 16.dp) + modifier = Modifier.padding(start = 0.dp, bottom = 16.dp) ) Spacer(modifier = Modifier.weight(1f)) if (participants.isNotEmpty()) { From 5001fd0b95db6ee15996c59810c6c82c06c391f4 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 4 Sep 2024 12:34:38 +0200 Subject: [PATCH 22/23] colorize status bar ...for ConversationCreationActivity and ContactsActivityCompose just a quick fix, this may not be the best solution! Signed-off-by: Marcel Hibbe --- .../talk/contacts/ContactsActivityCompose.kt | 23 +++++++++++++++++++ .../ConversationCreationActivity.kt | 22 ++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt index cc03741349..5fdbc3c733 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt @@ -18,6 +18,7 @@ import androidx.activity.compose.setContent import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -48,6 +49,7 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -58,12 +60,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.core.view.WindowCompat import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector import coil.compose.AsyncImage @@ -140,6 +144,25 @@ class ContactsActivityCompose : BaseActivity() { } ) } + + SetStatusBarColor() + } + } + + @Composable + private fun SetStatusBarColor() { + val view = LocalView.current + val isDarkMod = isSystemInDarkTheme() + + DisposableEffect(isDarkMod) { + val activity = view.context as Activity + activity.window.statusBarColor = resources.getColor(R.color.bg_default) + + WindowCompat.getInsetsController(activity.window, activity.window.decorView).apply { + isAppearanceLightStatusBars = !isDarkMod + } + + onDispose { } } } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index edd94e42cd..7746dae467 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -20,6 +20,7 @@ import androidx.activity.compose.setContent import androidx.activity.result.ActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.clickable +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -48,6 +49,7 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -57,11 +59,13 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.core.view.WindowCompat import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector import coil.compose.AsyncImage @@ -96,10 +100,28 @@ class ConversationCreationActivity : BaseActivity() { ) { ConversationCreationScreen(conversationCreationViewModel, context) } + SetStatusBarColor() } } } +@Composable +private fun SetStatusBarColor() { + val view = LocalView.current + val isDarkMod = isSystemInDarkTheme() + + DisposableEffect(isDarkMod) { + val activity = view.context as Activity + activity.window.statusBarColor = activity.getColor(R.color.bg_default) + + WindowCompat.getInsetsController(activity.window, activity.window.decorView).apply { + isAppearanceLightStatusBars = !isDarkMod + } + + onDispose { } + } +} + @OptIn(ExperimentalMaterial3Api::class) @Composable fun ConversationCreationScreen(conversationCreationViewModel: ConversationCreationViewModel, context: Context) { From 2fbe3dbb143715db65401d9a858ba4460fbee95b Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 4 Sep 2024 12:41:43 +0200 Subject: [PATCH 23/23] change wording for visibility of new conversation Signed-off-by: Marcel Hibbe --- .../talk/conversationcreation/ConversationCreationActivity.kt | 2 +- app/src/main/res/values/strings.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index 7746dae467..51297510a8 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -355,7 +355,7 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM val isOpenForGuestAppUsers = conversationCreationViewModel.openForGuestAppUsers.value Text( - text = stringResource(id = R.string.nc_visible).uppercase(), + text = stringResource(id = R.string.nc_new_conversation_visibility).uppercase(), fontSize = 14.sp, modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp) ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eba1ad0022..13ed2b442f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -244,6 +244,7 @@ How to translate with transifex: Join open conversations Open conversation to registered users Also open to guest app users + Visibility Added conversation %1$s to favorites Removed conversation %1$s from favorites