diff --git a/Habitica/res/layout/fragment_compose.xml b/Habitica/res/layout/fragment_compose.xml
index 3896c2c2f..877b7b085 100644
--- a/Habitica/res/layout/fragment_compose.xml
+++ b/Habitica/res/layout/fragment_compose.xml
@@ -1,4 +1,6 @@
-
\ No newline at end of file
+ android:layout_height="match_parent" />
diff --git a/Habitica/res/layout/fragment_party_detail.xml b/Habitica/res/layout/fragment_party_detail.xml
index c77318dda..1655ff4e6 100644
--- a/Habitica/res/layout/fragment_party_detail.xml
+++ b/Habitica/res/layout/fragment_party_detail.xml
@@ -191,6 +191,12 @@
android:layout_height="wrap_content"
android:text="@string/members"
style="@style/SegmentTitle"/>
+
-
\ No newline at end of file
+
diff --git a/Habitica/res/navigation/navigation.xml b/Habitica/res/navigation/navigation.xml
index a7fe59f7c..d25a6780f 100644
--- a/Habitica/res/navigation/navigation.xml
+++ b/Habitica/res/navigation/navigation.xml
@@ -67,6 +67,11 @@
android:name="com.habitrpg.android.habitica.ui.fragments.social.party.NoPartyFragmentFragment"
android:label="@string/sidebar_party">
+
+
@@ -516,4 +521,4 @@
app:argType="string" />
-
\ No newline at end of file
+
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt
index 405803ccc..5be8de488 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt
@@ -462,4 +462,7 @@ interface ApiService {
@POST("tasks/{taskID}/needs-work/{userID}")
suspend fun markTaskNeedsWork(@Path("taskID") taskID: String, @Path("userID") userID: String): HabitResponse
+
+ @GET("party-seekers")
+ suspend fun retrievePartySeekingUsers(): HabitResponse>
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/components/UserComponent.java b/Habitica/src/main/java/com/habitrpg/android/habitica/components/UserComponent.java
index ad9cad2f0..749d8dbaf 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/components/UserComponent.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/components/UserComponent.java
@@ -96,6 +96,9 @@ import com.habitrpg.android.habitica.ui.fragments.social.party.NoPartyFragmentFr
import com.habitrpg.android.habitica.ui.fragments.social.party.PartyDetailFragment;
import com.habitrpg.android.habitica.ui.fragments.social.party.PartyFragment;
import com.habitrpg.android.habitica.ui.fragments.social.party.PartyInviteFragment;
+import com.habitrpg.android.habitica.ui.fragments.social.party.PartyInvitePagerFragment;
+import com.habitrpg.android.habitica.ui.fragments.social.party.PartySeekingFragment;
+import com.habitrpg.android.habitica.ui.fragments.social.party.PartySeekingViewModel;
import com.habitrpg.android.habitica.ui.fragments.support.BugFixFragment;
import com.habitrpg.android.habitica.ui.fragments.support.FAQDetailFragment;
import com.habitrpg.android.habitica.ui.fragments.support.FAQOverviewFragment;
@@ -376,4 +379,10 @@ public interface UserComponent {
void inject(@NotNull AvatarEquipmentFragment avatarEquipmentFragment);
void inject(@NotNull BirthdayActivity birthdayActivity);
+
+ void inject(@NotNull PartySeekingFragment partySeekingFragment);
+
+ void inject(@NotNull PartySeekingViewModel partySeekingViewModel);
+
+ void inject(@NotNull PartyInvitePagerFragment partyInvitePagerFragment);
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
index d6de7a6a0..7680c74eb 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
@@ -276,4 +276,5 @@ interface ApiClient {
suspend fun updateMember(memberID: String, updateData: Map): Member?
suspend fun getHallMember(userId: String): Member?
suspend fun markTaskNeedsWork(taskID: String, userID: String): Task?
+ suspend fun retrievePartySeekingUsers() : List?
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt
index 0cd7df3ae..c1c42f39c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt
@@ -121,4 +121,5 @@ interface SocialRepository : BaseRepository {
suspend fun blockMember(userID: String): List?
fun getMember(userID: String?): Flow
suspend fun updateMember(memberID: String, key: String, value: Any?): Member?
+ suspend fun retrievePartySeekingUsers(): List?
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
index 218727df1..f1fbe95a6 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
@@ -622,6 +622,10 @@ class ApiClientImpl(
return process { apiService.markTaskNeedsWork(taskID, userID) }
}
+ override suspend fun retrievePartySeekingUsers() : List? {
+ return process { apiService.retrievePartySeekingUsers() }
+ }
+
override suspend fun getMember(memberId: String) = processResponse(apiService.getMember(memberId))
override suspend fun getMemberWithUsername(username: String) = processResponse(apiService.getMemberWithUsername(username))
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt
index 02c38806f..9bf69a378 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt
@@ -48,6 +48,10 @@ class SocialRepositoryImpl(
return apiClient.updateMember(memberID, mapOf(key to value))
}
+ override suspend fun retrievePartySeekingUsers() : List? {
+ return apiClient.retrievePartySeekingUsers()
+ }
+
override fun getGroupMembership(id: String) = localRepository.getGroupMembership(userID, id)
override fun getGroupMemberships(): Flow> {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt
index 679b7c026..852a2b255 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt
@@ -9,6 +9,7 @@ import android.widget.TextView
import androidx.appcompat.widget.AppCompatEditText
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
+import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
@@ -93,6 +94,9 @@ class PartyDetailFragment : BaseFragment() {
binding?.questDetailButton?.setOnClickListener { questDetailButtonClicked() }
binding?.leaveButton?.setOnClickListener { leaveParty() }
+ binding?.findNewMember?.setOnClickListener {
+ MainNavigationController.navigate(R.id.partyInvitationFragment)
+ }
binding?.invitationsView?.setLeader = null
binding?.invitationsView?.acceptCall = {
@@ -153,6 +157,8 @@ class PartyDetailFragment : BaseFragment() {
binding?.questImageWrapper?.visibility = View.GONE
binding?.questProgressView?.visibility = View.GONE
}
+
+ binding?.findNewMember?.isVisible = viewModel?.isLeader == true
}
private fun updateUser(user: User?) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt
index e40d5c466..a9e03f6ed 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt
@@ -56,11 +56,10 @@ class PartyFragment : BaseMainFragment() {
viewModel.groupViewType = GroupViewType.PARTY
viewModel.getGroupData().observe(
- viewLifecycleOwner,
- {
- updateGroupUI(it)
- }
- )
+ viewLifecycleOwner
+ ) {
+ updateGroupUI(it)
+ }
binding?.viewPager?.currentItem = 0
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInviteFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInviteFragment.kt
index d26464b6c..84e91b437 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInviteFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInviteFragment.kt
@@ -1,70 +1,38 @@
package com.habitrpg.android.habitica.ui.fragments.social.party
import android.os.Bundle
-import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.EditText
-import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
-import com.habitrpg.android.habitica.databinding.FragmentPartyInviteBinding
+import com.habitrpg.android.habitica.databinding.FragmentComposeBinding
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
+import com.habitrpg.android.habitica.ui.viewmodels.BaseViewModel
import javax.inject.Inject
-class PartyInviteFragment : BaseFragment() {
+class PartyInviteViewModel: BaseViewModel() {
+ override fun inject(component : UserComponent) {
+
+ }
+}
+
+class PartyInviteFragment : BaseFragment() {
@Inject
lateinit var configManager: AppConfigManager
- override var binding: FragmentPartyInviteBinding? = null
+ override var binding: FragmentComposeBinding? = null
- override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentPartyInviteBinding {
- return FragmentPartyInviteBinding.inflate(inflater, container, false)
+ override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentComposeBinding {
+ return FragmentComposeBinding.inflate(inflater, container, false)
}
- var isEmailInvite: Boolean = false
-
- val values: Array
- get() {
- val values = ArrayList()
- for (i in 0 until (binding?.invitationWrapper?.childCount ?: 0)) {
- val valueEditText = binding?.invitationWrapper?.getChildAt(i) as? EditText
- if (valueEditText?.text?.toString()?.isNotEmpty() == true) {
- values.add(valueEditText.text.toString())
- }
- }
- return values.toTypedArray()
- }
-
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
-
- if (isEmailInvite) {
- binding?.inviteDescription?.text = getString(R.string.invite_email_description)
- } else {
- binding?.inviteDescription?.text = getString(R.string.invite_username_description)
- }
-
- addInviteField()
-
- binding?.addInviteButton?.setOnClickListener { addInviteField() }
}
override fun injectFragment(component: UserComponent) {
component.inject(this)
}
-
- private fun addInviteField() {
- val editText = EditText(context)
-
- if (isEmailInvite) {
- editText.setHint(R.string.email)
- editText.inputType = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
- } else {
- editText.setHint(R.string.username)
- }
- binding?.invitationWrapper?.addView(editText)
- }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInvitePagerFragmennt.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInvitePagerFragmennt.kt
new file mode 100644
index 000000000..2a3c93950
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInvitePagerFragmennt.kt
@@ -0,0 +1,80 @@
+package com.habitrpg.android.habitica.ui.fragments.social.party
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.viewpager2.adapter.FragmentStateAdapter
+import com.google.android.material.tabs.TabLayoutMediator
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.components.UserComponent
+import com.habitrpg.android.habitica.databinding.FragmentViewpagerBinding
+import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
+
+class PartyInvitePagerFragment : BaseMainFragment() {
+
+ override var binding : FragmentViewpagerBinding? = null
+
+ override fun createBinding(
+ inflater : LayoutInflater,
+ container : ViewGroup?
+ ) : FragmentViewpagerBinding {
+ return FragmentViewpagerBinding.inflate(inflater, container, false)
+ }
+
+ override fun onCreateView(
+ inflater : LayoutInflater,
+ container : ViewGroup?,
+ savedInstanceState : Bundle?
+ ) : View? {
+ this.usesTabLayout = true
+ this.hidesToolbar = true
+ return super.onCreateView(inflater, container, savedInstanceState)
+ }
+
+ override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ setViewPagerAdapter()
+ binding?.viewPager?.currentItem = 0
+ }
+
+ override fun injectFragment(component : UserComponent) {
+ component.inject(this)
+ }
+
+ private fun setViewPagerAdapter() {
+ val fragmentManager = childFragmentManager
+ binding?.viewPager?.adapter = object : FragmentStateAdapter(fragmentManager, lifecycle) {
+ override fun createFragment(position : Int) : Fragment {
+ return when (position) {
+ 0 -> {
+ PartySeekingFragment()
+ }
+
+ 1 -> {
+ PartyInviteFragment()
+ }
+
+ else -> Fragment()
+ }
+ }
+
+ override fun getItemCount() : Int {
+ return 2
+ }
+ }
+ tabLayout?.let {
+ binding?.viewPager?.let { it1 ->
+ TabLayoutMediator(it, it1) { tab, position ->
+ tab.text = when (position) {
+ 0 -> context?.getString(R.string.list)
+ 1 -> context?.getString(R.string.by_invite)
+ else -> ""
+ }
+ }.attach()
+ }
+ }
+ }
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartySeekingFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartySeekingFragment.kt
new file mode 100644
index 000000000..7bdebd352
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartySeekingFragment.kt
@@ -0,0 +1,127 @@
+package com.habitrpg.android.habitica.ui.fragments.social.party
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.Column
+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.material.ExperimentalMaterialApi
+import androidx.compose.material.Text
+import androidx.compose.material.pullrefresh.pullRefresh
+import androidx.compose.material.pullrefresh.rememberPullRefreshState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+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.fragment.app.viewModels
+import androidx.lifecycle.viewModelScope
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.components.UserComponent
+import com.habitrpg.android.habitica.data.SocialRepository
+import com.habitrpg.android.habitica.databinding.FragmentComposeBinding
+import com.habitrpg.android.habitica.models.members.Member
+import com.habitrpg.android.habitica.ui.fragments.BaseFragment
+import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
+import com.habitrpg.android.habitica.ui.viewmodels.BaseViewModel
+import com.habitrpg.common.habitica.helpers.launchCatching
+import javax.inject.Inject
+
+class PartySeekingViewModel: BaseViewModel() {
+ val isRefreshing = mutableStateOf(false)
+
+ @Inject
+ lateinit var socialRepository: SocialRepository
+
+ val seekingUsers = mutableStateOf>(emptyList())
+
+ override fun inject(component : UserComponent) {
+ component.inject(this)
+ }
+
+ init {
+ retrieveUsers()
+ }
+
+ fun retrieveUsers() {
+ isRefreshing.value = true
+ viewModelScope.launchCatching {
+ seekingUsers.value = socialRepository.retrievePartySeekingUsers() ?: emptyList()
+ isRefreshing.value = false
+ }
+ }
+}
+
+class PartySeekingFragment: BaseFragment() {
+ val viewModel: PartySeekingViewModel by viewModels()
+
+ override var binding: FragmentComposeBinding? = null
+ override fun createBinding(
+ inflater : LayoutInflater,
+ container : ViewGroup?
+ ) : FragmentComposeBinding {
+ return FragmentComposeBinding.inflate(inflater)
+ }
+
+ override fun injectFragment(component : UserComponent) {
+ component.inject(this)
+ }
+
+ override fun onCreateView(
+ inflater : LayoutInflater,
+ container : ViewGroup?,
+ savedInstanceState : Bundle?
+ ) : View? {
+ val view = super.onCreateView(inflater, container, savedInstanceState)
+ binding?.composeView?.setContent {
+ HabiticaTheme {
+ PartySeekingView(viewModel)
+ }
+ }
+ return view
+ }
+
+ override fun onResume() {
+ super.onResume()
+ viewModel.retrieveUsers()
+ }
+}
+
+@Composable
+fun PartySeekingListItem(user: Member,
+ modifier : Modifier = Modifier) {
+ Column(modifier.fillMaxWidth()) {
+ Text(user.username ?: "")
+ }
+}
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+fun PartySeekingView(
+ viewModel: PartySeekingViewModel,
+ modifier : Modifier = Modifier
+) {
+ val users: List by viewModel.seekingUsers
+ val refreshing by viewModel.isRefreshing
+ val pullRefreshState = rememberPullRefreshState(refreshing, { viewModel.retrieveUsers() })
+
+ LazyColumn(modifier.pullRefresh(pullRefreshState)) {
+ item {
+ Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth().padding(top = 22.dp, bottom = 14.dp)) {
+ Text(stringResource(R.string.find_more_members), color = HabiticaTheme.colors.textPrimary, fontSize = 16.sp, fontWeight = FontWeight.Medium)
+ Text(stringResource(R.string.habiticans_looking_party), color = HabiticaTheme.colors.textSecondary)
+ }
+ }
+ items(users) {
+ PartySeekingListItem(user = it)
+ }
+ }
+}
diff --git a/common/src/main/java/com/habitrpg/common/habitica/api/HostConfig.kt b/common/src/main/java/com/habitrpg/common/habitica/api/HostConfig.kt
index ab9d7cdff..6ad142aa9 100644
--- a/common/src/main/java/com/habitrpg/common/habitica/api/HostConfig.kt
+++ b/common/src/main/java/com/habitrpg/common/habitica/api/HostConfig.kt
@@ -31,7 +31,7 @@ class HostConfig {
}
} else {
val address = sharedPreferences.getString("server_url", null)
- if (address != null && address.isNotEmpty()) {
+ if (!address.isNullOrEmpty()) {
this.address = address
} else {
this.address = context.getString(com.habitrpg.common.habitica.R.string.base_url)