diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RemoteDataSourceModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RemoteDataSourceModule.kt
index a4fb587d19a..14efaaac634 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RemoteDataSourceModule.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RemoteDataSourceModule.kt
@@ -4,7 +4,7 @@
* @author David González Verdugo
* @author Jorge Aguado Recio
*
- * Copyright (C) 2025 ownCloud GmbH.
+ * Copyright (C) 2026 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
@@ -33,6 +33,8 @@ import com.owncloud.android.data.capabilities.datasources.implementation.OCRemot
import com.owncloud.android.data.capabilities.datasources.mapper.RemoteCapabilityMapper
import com.owncloud.android.data.files.datasources.RemoteFileDataSource
import com.owncloud.android.data.files.datasources.implementation.OCRemoteFileDataSource
+import com.owncloud.android.data.members.datasources.RemoteMembersDataSource
+import com.owncloud.android.data.members.datasources.implementation.OCRemoteMembersDataSource
import com.owncloud.android.data.oauth.datasources.RemoteOAuthDataSource
import com.owncloud.android.data.oauth.datasources.implementation.OCRemoteOAuthDataSource
import com.owncloud.android.data.roles.datasources.RemoteRolesDataSource
@@ -76,6 +78,7 @@ val remoteDataSourceModule = module {
singleOf(::OCRemoteAuthenticationDataSource) bind RemoteAuthenticationDataSource::class
singleOf(::OCRemoteCapabilitiesDataSource) bind RemoteCapabilitiesDataSource::class
singleOf(::OCRemoteFileDataSource) bind RemoteFileDataSource::class
+ singleOf(::OCRemoteMembersDataSource) bind RemoteMembersDataSource::class
singleOf(::OCRemoteOAuthDataSource) bind RemoteOAuthDataSource::class
singleOf(::OCRemoteRolesDataSource) bind RemoteRolesDataSource::class
singleOf(::OCRemoteServerInfoDataSource) bind RemoteServerInfoDataSource::class
diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt
index ae4ea9fcf79..44e1e762781 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt
@@ -6,7 +6,7 @@
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
- * Copyright (C) 2025 ownCloud GmbH.
+ * Copyright (C) 2026 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
@@ -28,6 +28,7 @@ import com.owncloud.android.data.authentication.repository.OCAuthenticationRepos
import com.owncloud.android.data.capabilities.repository.OCCapabilityRepository
import com.owncloud.android.data.files.repository.OCFileRepository
import com.owncloud.android.data.folderbackup.repository.OCFolderBackupRepository
+import com.owncloud.android.data.members.repository.OCMembersRepository
import com.owncloud.android.data.oauth.repository.OCOAuthRepository
import com.owncloud.android.data.roles.repository.OCRolesRepository
import com.owncloud.android.data.server.repository.OCServerInfoRepository
@@ -43,6 +44,7 @@ import com.owncloud.android.domain.authentication.oauth.OAuthRepository
import com.owncloud.android.domain.automaticuploads.FolderBackupRepository
import com.owncloud.android.domain.capabilities.CapabilityRepository
import com.owncloud.android.domain.files.FileRepository
+import com.owncloud.android.domain.members.MembersRepository
import com.owncloud.android.domain.roles.RolesRepository
import com.owncloud.android.domain.server.ServerInfoRepository
import com.owncloud.android.domain.sharing.sharees.ShareeRepository
@@ -61,6 +63,7 @@ val repositoryModule = module {
factoryOf(::OCCapabilityRepository) bind CapabilityRepository::class
factoryOf(::OCFileRepository) bind FileRepository::class
factoryOf(::OCFolderBackupRepository) bind FolderBackupRepository::class
+ factoryOf(::OCMembersRepository) bind MembersRepository::class
factoryOf(::OCOAuthRepository) bind OAuthRepository::class
factoryOf(::OCRolesRepository) bind RolesRepository::class
factoryOf(::OCServerInfoRepository) bind ServerInfoRepository::class
diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt
index 90299a510ee..5758eeb2456 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt
@@ -7,7 +7,7 @@
* @author Aitor Ballesteros Pavón
* @author Jorge Aguado Recio
*
- * Copyright (C) 2025 ownCloud GmbH.
+ * Copyright (C) 2026 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
@@ -107,6 +107,7 @@ import com.owncloud.android.domain.spaces.usecases.GetSpaceWithSpecialsByIdForAc
import com.owncloud.android.domain.spaces.usecases.GetSpacesFromEveryAccountUseCaseAsStream
import com.owncloud.android.domain.spaces.usecases.RefreshSpacesFromServerAsyncUseCase
import com.owncloud.android.domain.spaces.usecases.GetSpaceMembersUseCase
+import com.owncloud.android.domain.members.usecases.SearchUsersUseCase
import com.owncloud.android.domain.transfers.usecases.ClearSuccessfulTransferByIdUseCase
import com.owncloud.android.domain.transfers.usecases.ClearSuccessfulTransfersUseCase
import com.owncloud.android.domain.transfers.usecases.GetAllTransfersAsStreamUseCase
@@ -306,4 +307,7 @@ val useCaseModule = module {
// Roles
factoryOf(::GetRolesAsyncUseCase)
+
+ // Members
+ factoryOf(::SearchUsersUseCase)
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/AddMemberFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/AddMemberFragment.kt
new file mode 100644
index 00000000000..efbd789933a
--- /dev/null
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/AddMemberFragment.kt
@@ -0,0 +1,138 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Jorge Aguado Recio
+ *
+ * Copyright (C) 2026 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.owncloud.android.presentation.spaces.members
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.owncloud.android.R
+import com.owncloud.android.databinding.AddMemberFragmentBinding
+import com.owncloud.android.domain.members.model.OCMember
+import com.owncloud.android.domain.spaces.model.OCSpace
+import com.owncloud.android.extensions.collectLatestLifecycleFlow
+import com.owncloud.android.extensions.showErrorInSnackbar
+import com.owncloud.android.presentation.common.UIResult
+import org.koin.androidx.viewmodel.ext.android.viewModel
+import org.koin.core.parameter.parametersOf
+
+class AddMemberFragment: Fragment() {
+ private var _binding: AddMemberFragmentBinding? = null
+ private val binding get() = _binding!!
+
+ private val spaceMembersViewModel: SpaceMembersViewModel by viewModel {
+ parametersOf(
+ requireArguments().getString(ARG_ACCOUNT_NAME),
+ requireArguments().getParcelable(ARG_CURRENT_SPACE)
+ )
+ }
+
+ private lateinit var searchMembersAdapter: SearchMembersAdapter
+ private lateinit var recyclerView: RecyclerView
+
+ private var listOfUsers = emptyList()
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ _binding = AddMemberFragmentBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ searchMembersAdapter = SearchMembersAdapter()
+ recyclerView = binding.membersRecyclerView
+ recyclerView.apply {
+ layoutManager = LinearLayoutManager(requireContext())
+ adapter = searchMembersAdapter
+ }
+
+ collectLatestLifecycleFlow(spaceMembersViewModel.users) { event ->
+ event?.let {
+ when (val uiResult = event.peekContent()) {
+ is UIResult.Success -> {
+ uiResult.data?.let {
+ listOfUsers = it
+ spaceMembersViewModel.getSpaceMembers()
+ }
+ }
+ is UIResult.Loading -> { }
+ is UIResult.Error -> {
+ showErrorInSnackbar(R.string.search_members_failed, uiResult.error)
+ }
+ }
+ }
+ }
+
+ collectLatestLifecycleFlow(spaceMembersViewModel.spaceMembers) { event ->
+ event?.let {
+ when (val uiResult = event.peekContent()) {
+ is UIResult.Success -> {
+ uiResult.data?.let {
+ listOfUsers = listOfUsers.filter { user -> !it.members.any { member -> member.id == "u:${user.id}" } }
+ searchMembersAdapter.addUserMembers(listOfUsers)
+ }
+ }
+ is UIResult.Loading -> { }
+ is UIResult.Error -> { }
+ }
+ }
+ }
+
+
+ binding.searchBar.apply {
+ requestFocus()
+ setOnQueryTextListener(object : androidx.appcompat.widget.SearchView.OnQueryTextListener {
+ override fun onQueryTextSubmit(query: String): Boolean = true
+
+ override fun onQueryTextChange(newText: String): Boolean {
+ if (newText.length > 2) { spaceMembersViewModel.searchUsers(newText) } else { spaceMembersViewModel.clearSearch() }
+ return true
+ }
+ })
+ }
+ }
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+ requireActivity().setTitle(R.string.add_member)
+ }
+
+ companion object {
+ private const val ARG_ACCOUNT_NAME = "ACCOUNT_NAME"
+ private const val ARG_CURRENT_SPACE = "CURRENT_SPACE"
+
+ fun newInstance(
+ accountName: String,
+ currentSpace: OCSpace
+ ): AddMemberFragment {
+ val args = Bundle().apply {
+ putString(ARG_ACCOUNT_NAME, accountName)
+ putParcelable(ARG_CURRENT_SPACE, currentSpace)
+ }
+ return AddMemberFragment().apply {
+ arguments = args
+ }
+ }
+ }
+}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SearchMembersAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SearchMembersAdapter.kt
new file mode 100644
index 00000000000..07b375f81fe
--- /dev/null
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SearchMembersAdapter.kt
@@ -0,0 +1,71 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Jorge Aguado Recio
+ *
+ * Copyright (C) 2026 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.owncloud.android.presentation.spaces.members
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.owncloud.android.R
+import com.owncloud.android.databinding.MemberItemBinding
+import com.owncloud.android.domain.members.model.OCMember
+import com.owncloud.android.utils.PreferenceUtils
+
+class SearchMembersAdapter: RecyclerView.Adapter() {
+
+ private var members: List = emptyList()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchMembersViewHolder {
+ val inflater = LayoutInflater.from(parent.context)
+
+ val view = inflater.inflate(R.layout.member_item, parent, false)
+ view.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(parent.context)
+
+ return SearchMembersViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: SearchMembersViewHolder, position: Int) {
+ val member = members[position]
+
+ holder.binding.apply {
+ memberIcon.setImageResource(R.drawable.ic_user)
+ memberName.text = member.displayName
+ memberName.contentDescription = holder.itemView.context.getString(R.string.content_description_member_user, member.displayName)
+ memberRole.text = if (member.surname == USER_SURNAME) holder.itemView.context.getString(R.string.member_user) else member.surname
+ }
+ }
+
+ override fun getItemCount(): Int = members.size
+
+ fun addUserMembers(members: List) {
+ this.members = members
+ notifyDataSetChanged()
+ }
+
+ class SearchMembersViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ val binding = MemberItemBinding.bind(itemView)
+ }
+
+ companion object {
+ private const val USER_SURNAME = "User"
+ }
+
+}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersActivity.kt
index 1d96476ef63..a7ed783a306 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersActivity.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersActivity.kt
@@ -3,7 +3,7 @@
*
* @author Jorge Aguado Recio
*
- * Copyright (C) 2025 ownCloud GmbH.
+ * Copyright (C) 2026 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
@@ -25,21 +25,42 @@ import android.view.Menu
import android.view.MenuItem
import androidx.fragment.app.transaction
import com.owncloud.android.R
+import com.owncloud.android.databinding.MembersActivityBinding
import com.owncloud.android.domain.spaces.model.OCSpace
import com.owncloud.android.ui.activity.FileActivity
+import com.owncloud.android.utils.DisplayUtils
-class SpaceMembersActivity: FileActivity() {
+class SpaceMembersActivity: FileActivity(), SpaceMembersFragment.SpaceMemberFragmentListener {
+
+ private lateinit var binding: MembersActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.members_activity)
+ binding = MembersActivityBinding.inflate(layoutInflater)
+ setContentView(binding.root)
setupStandardToolbar(title = null, displayHomeAsUpEnabled = true, homeButtonEnabled = true, displayShowTitleEnabled = true)
supportActionBar?.setHomeActionContentDescription(R.string.common_back)
- val currentSpace = intent.getParcelableExtra(EXTRA_SPACE)
+ val currentSpace = intent.getParcelableExtra(EXTRA_SPACE) ?: return
+ binding.apply {
+ itemName.text = currentSpace.name
+ currentSpace.quota?.let { quota ->
+ val usedQuota = quota.used
+ val totalQuota = quota.total
+ itemSize.text = when {
+ usedQuota == null -> getString(R.string.drawer_unavailable_used_storage)
+ totalQuota == 0L -> DisplayUtils.bytesToHumanReadable(usedQuota, baseContext, true)
+ else -> getString(
+ R.string.drawer_quota,
+ DisplayUtils.bytesToHumanReadable(usedQuota, baseContext, true),
+ DisplayUtils.bytesToHumanReadable(totalQuota, baseContext, true),
+ quota.getRelative().toString())
+ }
+ }
+ }
supportFragmentManager.transaction {
if (savedInstanceState == null && currentSpace != null) {
@@ -59,8 +80,17 @@ class SpaceMembersActivity: FileActivity() {
super.onOptionsItemSelected(item)
}
+ override fun addMember(space: OCSpace) {
+ supportFragmentManager.transaction {
+ val fragment = AddMemberFragment.newInstance(account.name, space)
+ replace(R.id.members_fragment_container, fragment, TAG_ADD_MEMBER_FRAGMENT)
+ addToBackStack(null)
+ }
+ }
+
companion object {
private const val TAG_SPACE_MEMBERS_FRAGMENT = "SPACE_MEMBERS_FRAGMENT"
+ private const val TAG_ADD_MEMBER_FRAGMENT ="ADD_MEMBER_FRAGMENT"
const val EXTRA_SPACE = "EXTRA_SPACE"
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersFragment.kt
index 00ed053774d..a57ed533693 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersFragment.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersFragment.kt
@@ -3,7 +3,7 @@
*
* @author Jorge Aguado Recio
*
- * Copyright (C) 2025 ownCloud GmbH.
+ * Copyright (C) 2026 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
@@ -20,6 +20,7 @@
package com.owncloud.android.presentation.spaces.members
+import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -33,9 +34,9 @@ import com.owncloud.android.domain.roles.model.OCRole
import com.owncloud.android.domain.spaces.model.OCSpace
import com.owncloud.android.extensions.collectLatestLifecycleFlow
import com.owncloud.android.presentation.common.UIResult
-import com.owncloud.android.utils.DisplayUtils
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
+import timber.log.Timber
class SpaceMembersFragment : Fragment() {
private var _binding: MembersFragmentBinding? = null
@@ -52,6 +53,7 @@ class SpaceMembersFragment : Fragment() {
private lateinit var recyclerView: RecyclerView
private var roles: List = emptyList()
+ private var listener: SpaceMemberFragmentListener? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = MembersFragmentBinding.inflate(inflater, container, false)
@@ -96,28 +98,49 @@ class SpaceMembersFragment : Fragment() {
}
}
- val currentSpace = requireArguments().getParcelable(ARG_CURRENT_SPACE) ?: return
- binding.apply {
- itemName.text = currentSpace.name
- currentSpace.quota?.let { quota ->
- val usedQuota = quota.used
- val totalQuota = quota.total
- itemSize.text = when {
- usedQuota == null -> getString(R.string.drawer_unavailable_used_storage)
- totalQuota == 0L -> DisplayUtils.bytesToHumanReadable(usedQuota, requireContext(), true)
- else -> getString(
- R.string.drawer_quota,
- DisplayUtils.bytesToHumanReadable(usedQuota, requireContext(), true),
- DisplayUtils.bytesToHumanReadable(totalQuota, requireContext(), true),
- quota.getRelative().toString())
+ collectLatestLifecycleFlow(spaceMembersViewModel.spacePermissions) { event ->
+ event?.let {
+ when (val uiResult = event.peekContent()) {
+ is UIResult.Success -> {
+ uiResult.data?.let { spacePermissions ->
+ if (DRIVES_CREATE_PERMISSION in spacePermissions) { binding.addMemberButton.visibility = View.VISIBLE }
+ }
+ }
+ is UIResult.Loading -> { }
+ is UIResult.Error -> { }
}
}
}
+
+ val currentSpace = requireArguments().getParcelable(ARG_CURRENT_SPACE) ?: return
+ binding.addMemberButton.setOnClickListener {
+ listener?.addMember(currentSpace)
+ }
+ }
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+ requireActivity().setTitle(R.string.space_members_label)
+ }
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ try {
+ listener = context as SpaceMemberFragmentListener?
+ } catch (e: ClassCastException) {
+ Timber.e(e, "The activity attached does not implement SpaceMemberFragmentListener")
+ throw ClassCastException(activity.toString() + " must implement SpaceMemberFragmentListener")
+ }
+ }
+
+ interface SpaceMemberFragmentListener {
+ fun addMember(space: OCSpace)
}
companion object {
private const val ARG_CURRENT_SPACE = "CURRENT_SPACE"
private const val ARG_ACCOUNT_NAME = "ACCOUNT_NAME"
+ private const val DRIVES_CREATE_PERMISSION = "libre.graph/driveItem/permissions/create"
fun newInstance(
accountName: String,
diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersViewModel.kt
index 72b4fa673fb..b6f6662a1fd 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersViewModel.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersViewModel.kt
@@ -3,7 +3,7 @@
*
* @author Jorge Aguado Recio
*
- * Copyright (C) 2025 ownCloud GmbH.
+ * Copyright (C) 2026 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
@@ -21,11 +21,14 @@
package com.owncloud.android.presentation.spaces.members
import androidx.lifecycle.ViewModel
+import com.owncloud.android.domain.members.model.OCMember
import com.owncloud.android.domain.roles.model.OCRole
import com.owncloud.android.domain.spaces.model.OCSpace
import com.owncloud.android.domain.spaces.model.SpaceMembers
import com.owncloud.android.domain.spaces.usecases.GetSpaceMembersUseCase
import com.owncloud.android.domain.roles.usecases.GetRolesAsyncUseCase
+import com.owncloud.android.domain.spaces.usecases.GetSpacePermissionsAsyncUseCase
+import com.owncloud.android.domain.members.usecases.SearchUsersUseCase
import com.owncloud.android.domain.utils.Event
import com.owncloud.android.extensions.ViewModelExt.runUseCaseWithResult
import com.owncloud.android.presentation.common.UIResult
@@ -36,6 +39,8 @@ import kotlinx.coroutines.flow.StateFlow
class SpaceMembersViewModel(
private val getRolesAsyncUseCase: GetRolesAsyncUseCase,
private val getSpaceMembersUseCase: GetSpaceMembersUseCase,
+ private val getSpacePermissionsAsyncUseCase: GetSpacePermissionsAsyncUseCase,
+ private val searchUsersUseCase: SearchUsersUseCase,
private val accountName: String,
private val space: OCSpace,
private val coroutineDispatcherProvider: CoroutinesDispatcherProvider
@@ -47,6 +52,12 @@ class SpaceMembersViewModel(
private val _spaceMembers = MutableStateFlow>?>(null)
val spaceMembers: StateFlow>?> = _spaceMembers
+ private val _spacePermissions = MutableStateFlow>>?>(null)
+ val spacePermissions: StateFlow>>?> = _spacePermissions
+
+ private val _users = MutableStateFlow>>?>(null)
+ val users: StateFlow>>?> = _users
+
init {
runUseCaseWithResult(
coroutineDispatcher = coroutineDispatcherProvider.io,
@@ -56,6 +67,16 @@ class SpaceMembersViewModel(
showLoading = false,
requiresConnection = true
)
+
+ runUseCaseWithResult(
+ coroutineDispatcher = coroutineDispatcherProvider.io,
+ flow = _spacePermissions,
+ useCase = getSpacePermissionsAsyncUseCase,
+ useCaseParams = GetSpacePermissionsAsyncUseCase.Params(accountName = accountName, spaceId = space.id),
+ showLoading = false,
+ requiresConnection = true
+ )
+
}
fun getSpaceMembers() = runUseCaseWithResult(
@@ -67,4 +88,17 @@ class SpaceMembersViewModel(
requiresConnection = true
)
+ fun searchUsers(query: String) = runUseCaseWithResult(
+ coroutineDispatcher = coroutineDispatcherProvider.io,
+ flow = _users,
+ useCase = searchUsersUseCase,
+ useCaseParams = SearchUsersUseCase.Params(accountName = accountName, query = query),
+ showLoading = false,
+ requiresConnection = true
+ )
+
+ fun clearSearch() {
+ _users.value = Event(UIResult.Success(emptyList()))
+ }
+
}
diff --git a/owncloudApp/src/main/res/layout/add_member_fragment.xml b/owncloudApp/src/main/res/layout/add_member_fragment.xml
new file mode 100644
index 00000000000..fe412782e9c
--- /dev/null
+++ b/owncloudApp/src/main/res/layout/add_member_fragment.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/owncloudApp/src/main/res/layout/member_item.xml b/owncloudApp/src/main/res/layout/member_item.xml
index ab5f52997ee..c23fa978ec3 100644
--- a/owncloudApp/src/main/res/layout/member_item.xml
+++ b/owncloudApp/src/main/res/layout/member_item.xml
@@ -1,7 +1,7 @@
-
+
+
+
+
+
+
+
+
+
+