finalize first version of party seeking

This commit is contained in:
Phillip Thelen 2023-03-17 15:16:08 +01:00
parent 8252969a26
commit 2a78e89624
21 changed files with 99 additions and 75 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="8dp" />
<stroke android:color="@color/background_green" android:width="3dp" />
</shape>

View file

@ -12,9 +12,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="?android:actionBarSize">
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -57,7 +55,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="@dimen/spacing_large">
android:layout_margin="@dimen/spacing_sides">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -86,15 +84,25 @@
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical"
android:layout_margin="@dimen/spacing_large">
android:paddingHorizontal="@dimen/spacing_sides"
android:paddingTop="@dimen/spacing_sides"
android:paddingBottom="60dp"
android:background="@color/window_background">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/party_seeking"
android:layout_marginBottom="@dimen/spacing_large"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Headline"
android:textSize="16sp"
android:text="@string/join_party_title"
android:text="@string/seeking_party_title"
android:gravity="center"
android:layout_marginBottom="@dimen/spacing_medium"/>
<TextView
@ -102,23 +110,51 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Body2"
android:text="@string/join_party_description"
android:text="@string/seeking_party_description"
android:textColor="@color/text_quad"
android:textColorLink="@color/text_brand_neon"
android:gravity="center"
android:layout_marginStart="@dimen/spacing_large"
android:layout_marginEnd="@dimen/spacing_large"/>
android:layout_marginEnd="@dimen/spacing_large"
android:layout_marginBottom="@dimen/spacing_large"/>
<Button
android:id="@+id/username_textview"
android:id="@+id/seek_party_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/HabiticaButton.Gray"
android:layout_marginTop="@dimen/spacing_large"
android:layout_marginBottom="@dimen/spacing_large"/>
style="@style/HabiticaButton.Primary"
android:text="@string/look_for_party"/>
<LinearLayout
android:id="@+id/seeking_party_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="@string/you_re_looking_for_party"
android:textColor="@color/text_green"
style="@style/Body1_Button"
android:background="@drawable/success_border"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/seeking_hint"
android:textColor="@color/text_green"
style="@style/Body1"
android:gravity="center"
android:layout_marginTop="6dp"/>
<Button
android:id="@+id/leave_seeking_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/HabiticaButton.Borderless"
android:textColor="@color/text_maroon"
android:text="@string/leave_party_finder"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View file

@ -1370,6 +1370,12 @@
<string name="invite_with_username_email">Invite with @username or email</string>
<string name="habiticans_send_invite">Send an invite directly to Habiticans you know</string>
<string name="username_or_email">Username or email address</string>
<string name="seeking_party_description">Want to join a Party with others but dont know any Habiticans? Join our Party Finder to let Party leaders know youre looking for an invite!</string>
<string name="seeking_party_title">Looking for a Party?</string>
<string name="look_for_party">Look for a Party</string>
<string name="you_re_looking_for_party">Youre looking for a Party!</string>
<string name="seeking_hint">Keep an eye out for an invite or start your own Party at any time</string>
<string name="leave_party_finder">Leave Party Finder</string>
<plurals name="you_x_others">
<item quantity="zero">You</item>

View file

@ -1015,7 +1015,7 @@
<style name="FlatCardView">
<item name="android:background">@drawable/layout_rounded_bg_window</item>
<item name="android:layout_margin">@dimen/spacing_medium</item>
<item name="android:layout_margin">@dimen/spacing_sides</item>
</style>
<style name="CountLabel">

View file

@ -72,7 +72,7 @@ interface ApiService {
suspend fun getContent(@Query("language") language: String?): HabitResponse<ContentResult>
@PUT("user/")
suspend fun updateUser(@Body updateDictionary: Map<String, Any>): HabitResponse<User>
suspend fun updateUser(@Body updateDictionary: Map<String, Any?>): HabitResponse<User>
@PUT("user/")
suspend fun registrationLanguage(@Header("Accept-Language") registrationLanguage: String): HabitResponse<User>

View file

@ -141,6 +141,7 @@ public class GSonFactoryCreator {
.registerTypeAdapter(Notification.class, new NotificationDeserializer())
.registerTypeAdapter(SocialAuthentication.class, new SocialAuthenticationDeserializer())
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.serializeNulls()
.create();
}
public static GsonConverterFactory create() {

View file

@ -56,7 +56,7 @@ interface ApiClient {
var languageCode: String?
suspend fun getContent(language: String? = null): ContentResult?
suspend fun updateUser(updateDictionary: Map<String, Any>): User?
suspend fun updateUser(updateDictionary: Map<String, Any?>): User?
suspend fun registrationLanguage(registrationLanguage: String): User?

View file

@ -20,8 +20,8 @@ interface UserRepository : BaseRepository {
fun getUser(): Flow<User?>
fun getUser(userID: String): Flow<User?>
suspend fun updateUser(updateData: Map<String, Any>): User?
suspend fun updateUser(key: String, value: Any): User?
suspend fun updateUser(updateData: Map<String, Any?>): User?
suspend fun updateUser(key : String, value : Any?): User?
suspend fun retrieveUser(withTasks: Boolean = false, forced: Boolean = false, overrideExisting: Boolean = false): User?

View file

@ -328,7 +328,7 @@ class ApiClientImpl(
return process { apiService.getContent(language ?: this.languageCode) }
}
override suspend fun updateUser(updateDictionary: Map<String, Any>): User? {
override suspend fun updateUser(updateDictionary: Map<String, Any?>): User? {
return process { apiService.updateUser(updateDictionary) }
}

View file

@ -46,21 +46,21 @@ class UserRepositoryImpl(
override fun getUser(): Flow<User?> = getUser(userID)
override fun getUser(userID: String): Flow<User?> = localRepository.getUser(userID)
private suspend fun updateUser(userID: String, updateData: Map<String, Any>): User? {
private suspend fun updateUser(userID: String, updateData: Map<String, Any?>): User? {
val networkUser = apiClient.updateUser(updateData) ?: return null
val oldUser = localRepository.getUser(userID).firstOrNull()
return mergeUser(oldUser, networkUser)
}
private suspend fun updateUser(userID: String, key: String, value: Any): User? {
private suspend fun updateUser(userID : String, key : String, value : Any?): User? {
return updateUser(userID, mapOf(key to value))
}
override suspend fun updateUser(updateData: Map<String, Any>): User? {
override suspend fun updateUser(updateData: Map<String, Any?>): User? {
return updateUser(userID, updateData)
}
override suspend fun updateUser(key: String, value: Any): User? {
override suspend fun updateUser(key : String, value : Any?): User? {
return updateUser(userID, key, value)
}

View file

@ -5,12 +5,14 @@ import com.habitrpg.android.habitica.models.BaseObject
import com.habitrpg.android.habitica.models.inventory.Quest
import io.realm.RealmObject
import io.realm.annotations.RealmClass
import java.util.Date
@RealmClass(embedded = true)
open class UserParty : RealmObject(), BaseObject {
@SerializedName("_id")
var id: String = ""
var quest: Quest? = null
var seeking: Date? = null
@SerializedName("order")
var partyOrder: String? = null // Order to display ppl
var orderAscending: String? = null // Order type

View file

@ -24,7 +24,6 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.map
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.databinding.FragmentComposeScrollingBinding
import com.habitrpg.android.habitica.helpers.MainNavigationController

View file

@ -1,14 +1,10 @@
package com.habitrpg.android.habitica.ui.fragments.social.party
import android.app.Activity
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Shader
import android.graphics.drawable.BitmapDrawable
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -16,8 +12,8 @@ import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.graphics.drawable.toBitmap
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.google.firebase.analytics.FirebaseAnalytics
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.databinding.FragmentNoPartyBinding
@ -26,15 +22,14 @@ import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.ui.activities.GroupFormActivity
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
import com.habitrpg.common.habitica.extensions.DataBindingUtils
import com.habitrpg.common.habitica.helpers.ExceptionHandler
import com.habitrpg.common.habitica.helpers.launchCatching
import com.habitrpg.common.habitica.helpers.setMarkdown
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.math.roundToInt
import dagger.hilt.android.AndroidEntryPoint
import java.util.Date
@AndroidEntryPoint
class NoPartyFragmentFragment : BaseMainFragment<FragmentNoPartyBinding>() {
@ -97,15 +92,21 @@ class NoPartyFragmentFragment : BaseMainFragment<FragmentNoPartyBinding>() {
} else {
binding?.invitationWrapper?.visibility = View.GONE
}
val isSeeking = user?.party?.seeking != null
binding?.seekPartyButton?.isVisible = !isSeeking
binding?.seekingPartyWrapper?.isVisible = isSeeking
}
binding?.usernameTextview?.setOnClickListener {
val clipboard = context?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
val clip = ClipData.newPlainText(context?.getString(R.string.username), userViewModel.username)
clipboard?.setPrimaryClip(clip)
val activity = mainActivity
if (activity != null && Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
HabiticaSnackbar.showSnackbar(activity.snackbarContainer, getString(R.string.username_copied), HabiticaSnackbar.SnackbarDisplayType.NORMAL)
binding?.seekPartyButton?.setOnClickListener {
lifecycleScope.launchCatching {
userRepository.updateUser("party.seeking", Date())
}
}
binding?.leaveSeekingButton?.setOnClickListener {
lifecycleScope.launchCatching {
userRepository.updateUser("party.seeking", null)
}
}
@ -132,16 +133,6 @@ class NoPartyFragmentFragment : BaseMainFragment<FragmentNoPartyBinding>() {
}
}
}
if (configManager.noPartyLinkPartyGuild()) {
binding?.joinPartyDescriptionTextview?.setMarkdown(getString(R.string.join_party_description_guild, "[Party Wanted Guild](https://habitica.com/groups/guild/f2db2a7f-13c5-454d-b3ee-ea1f5089e601)"))
binding?.joinPartyDescriptionTextview?.setOnClickListener {
context?.let { FirebaseAnalytics.getInstance(it).logEvent("clicked_party_wanted", null) }
MainNavigationController.navigate(R.id.guildFragment, bundleOf("groupID" to "f2db2a7f-13c5-454d-b3ee-ea1f5089e601"))
}
}
binding?.usernameTextview?.text = userViewModel.formattedUsername
}
private val groupFormResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
@ -184,20 +175,7 @@ class NoPartyFragmentFragment : BaseMainFragment<FragmentNoPartyBinding>() {
}
override fun onDestroy() {
userRepository.close()
socialRepository.close()
super.onDestroy()
}
companion object {
fun newInstance(): NoPartyFragmentFragment {
val args = Bundle()
val fragment = NoPartyFragmentFragment()
fragment.arguments = args
return fragment
}
}
}

View file

@ -160,7 +160,7 @@ class PartyFragment : BaseMainFragment<FragmentViewpagerBinding>() {
val inviteData = HashMap<String, Any>()
inviteData["inviter"] = viewModel.user.value?.profile?.name ?: ""
val emails = it.data?.getStringArrayExtra(GroupInviteActivity.EMAILS_KEY)
if (emails != null && emails.isNotEmpty()) {
if (!emails.isNullOrEmpty()) {
val invites = ArrayList<HashMap<String, String>>()
emails.forEach { email ->
val invite = HashMap<String, String>()
@ -171,7 +171,7 @@ class PartyFragment : BaseMainFragment<FragmentViewpagerBinding>() {
inviteData["emails"] = invites
}
val userIDs = it.data?.getStringArrayExtra(GroupInviteActivity.USER_IDS_KEY)
if (userIDs != null && userIDs.isNotEmpty()) {
if (!userIDs.isNullOrEmpty()) {
val invites = ArrayList<String>()
userIDs.forEach { invites.add(it) }
inviteData["usernames"] = invites

View file

@ -45,7 +45,7 @@ class InboxViewModel @Inject constructor(
.setEnablePlaceholders(false)
.build()
val dataSourceFactory = MessagesDataSourceFactory(socialRepository, recipientID, ChatMessage())
private val dataSourceFactory = MessagesDataSourceFactory(socialRepository, recipientID, ChatMessage())
val messages: LiveData<PagedList<ChatMessage>> = dataSourceFactory.toLiveData(config)
private val member = memberIDFlow
.filterNotNull()

View file

@ -18,9 +18,6 @@ import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.pullRefreshIndicatorTransform
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
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
@ -47,9 +44,6 @@ fun HabiticaPullRefreshIndicator(
}
}
if (!isInitial) {
val showElevation by remember(isRefreshing, state) {
derivedStateOf { isRefreshing || state.progress > 0.5f }
}
Surface(
modifier = modifier
.pullRefreshIndicatorTransform(state, scale),

View file

@ -19,7 +19,7 @@ buildscript {
kotest_version = '5.5.5'
kotlin_version = '1.8.10'
ktlint_version = '0.48.2'
lifecycle_version = '2.5.1'
lifecycle_version = '2.6.0'
markwon_version = '4.6.2'
mockk_version = '1.13.4'
moshi_version = '1.14.0'
@ -30,7 +30,7 @@ buildscript {
preferences_version = '1.2.0'
realm_version = '1.0.2'
retrofit_version = '2.9.0'
recyclerview_version = '1.2.1'
recyclerview_version = '1.3.0'
}
repositories {
@ -39,7 +39,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.1'
classpath 'com.android.tools.build:gradle:7.4.2'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'com.google.gms:google-services:4.3.15'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.4'

View file

@ -11,6 +11,7 @@
<color name="text_inverted">@color/gray_10</color>
<color name="text_brand">@color/brand_600</color>
<color name="text_brand_neon">@color/brand_500</color>
<color name="text_maroon">@color/maroon_100</color>
<color name="text_red">@color/red_100</color>
<color name="text_orange">@color/orange_100</color>
<color name="text_yellow">@color/yellow_100</color>

View file

@ -151,6 +151,7 @@
<color name="text_inverted">@color/gray_700</color>
<color name="text_brand">@color/brand_300</color>
<color name="text_brand_neon">@color/brand_400</color>
<color name="text_maroon">@color/maroon_10</color>
<color name="text_red">@color/maroon_100</color>
<color name="text_orange">@color/orange_10</color>
<color name="text_yellow">@color/yellow_10</color>

View file

@ -10,9 +10,10 @@
<dimen name="spacing_large">16dp</dimen>
<dimen name="spacing_medium">8dp</dimen>
<dimen name="spacing_xlarge">32dp</dimen>
<dimen name="spacing_sides">25dp</dimen>
<dimen name="icon_size">18dp</dimen>
<dimen name="card_medium_text">18.0sp</dimen>
<dimen name="card_small_text">14.0sp</dimen>
</resources>
</resources>