Implement identity proof api (#1597)
* implement identity proof api * fix warnings in AccountActivity * fix createClickableText method * improve error handling * use combineOptionalLiveData to simplify code
This commit is contained in:
parent
c253f6b23b
commit
c44dd455b4
7 changed files with 95 additions and 21 deletions
|
@ -19,8 +19,7 @@ import android.animation.ArgbEvaluator
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.graphics.Color
|
import android.graphics.*
|
||||||
import android.graphics.PorterDuff
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
@ -52,6 +51,8 @@ import com.keylesspalace.tusky.components.compose.ComposeActivity
|
||||||
import com.keylesspalace.tusky.components.report.ReportActivity
|
import com.keylesspalace.tusky.components.report.ReportActivity
|
||||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||||
import com.keylesspalace.tusky.entity.Account
|
import com.keylesspalace.tusky.entity.Account
|
||||||
|
import com.keylesspalace.tusky.entity.Field
|
||||||
|
import com.keylesspalace.tusky.entity.IdentityProof
|
||||||
import com.keylesspalace.tusky.entity.Relationship
|
import com.keylesspalace.tusky.entity.Relationship
|
||||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
|
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
|
||||||
import com.keylesspalace.tusky.interfaces.LinkListener
|
import com.keylesspalace.tusky.interfaces.LinkListener
|
||||||
|
@ -118,7 +119,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
||||||
viewModel = ViewModelProviders.of(this, viewModelFactory)[AccountViewModel::class.java]
|
viewModel = ViewModelProviders.of(this, viewModelFactory)[AccountViewModel::class.java]
|
||||||
|
|
||||||
// Obtain information to fill out the profile.
|
// Obtain information to fill out the profile.
|
||||||
viewModel.setAccountInfo(intent.getStringExtra(KEY_ACCOUNT_ID))
|
viewModel.setAccountInfo(intent.getStringExtra(KEY_ACCOUNT_ID)!!)
|
||||||
|
|
||||||
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
|
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
animateAvatar = sharedPrefs.getBoolean("animateGifAvatars", false)
|
animateAvatar = sharedPrefs.getBoolean("animateGifAvatars", false)
|
||||||
|
@ -350,6 +351,11 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
viewModel.accountFieldData.observe(this, Observer<List<Either<IdentityProof, Field>>> {
|
||||||
|
accountFieldAdapter.fields = it
|
||||||
|
accountFieldAdapter.notifyDataSetChanged()
|
||||||
|
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -378,7 +384,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
||||||
val emojifiedNote = CustomEmojiHelper.emojifyText(account.note, account.emojis, accountNoteTextView)
|
val emojifiedNote = CustomEmojiHelper.emojifyText(account.note, account.emojis, accountNoteTextView)
|
||||||
LinkHelper.setClickableText(accountNoteTextView, emojifiedNote, null, this)
|
LinkHelper.setClickableText(accountNoteTextView, emojifiedNote, null, this)
|
||||||
|
|
||||||
accountFieldAdapter.fields = account.fields ?: emptyList()
|
// accountFieldAdapter.fields = account.fields ?: emptyList()
|
||||||
accountFieldAdapter.emojis = account.emojis ?: emptyList()
|
accountFieldAdapter.emojis = account.emojis ?: emptyList()
|
||||||
accountFieldAdapter.notifyDataSetChanged()
|
accountFieldAdapter.notifyDataSetChanged()
|
||||||
|
|
||||||
|
@ -472,7 +478,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
||||||
// this is necessary because API 19 can't handle vector compound drawables
|
// this is necessary because API 19 can't handle vector compound drawables
|
||||||
val movedIcon = ContextCompat.getDrawable(this, R.drawable.ic_briefcase)?.mutate()
|
val movedIcon = ContextCompat.getDrawable(this, R.drawable.ic_briefcase)?.mutate()
|
||||||
val textColor = ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
|
val textColor = ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
|
||||||
movedIcon?.setColorFilter(textColor, PorterDuff.Mode.SRC_IN)
|
movedIcon?.colorFilter = PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN)
|
||||||
|
|
||||||
accountMovedText.setCompoundDrawablesRelativeWithIntrinsicBounds(movedIcon, null, null, null)
|
accountMovedText.setCompoundDrawablesRelativeWithIntrinsicBounds(movedIcon, null, null, null)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
package com.keylesspalace.tusky.adapter
|
package com.keylesspalace.tusky.adapter
|
||||||
|
|
||||||
|
import android.text.method.LinkMovementMethod
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -23,15 +24,17 @@ import android.widget.TextView
|
||||||
import com.keylesspalace.tusky.R
|
import com.keylesspalace.tusky.R
|
||||||
import com.keylesspalace.tusky.entity.Emoji
|
import com.keylesspalace.tusky.entity.Emoji
|
||||||
import com.keylesspalace.tusky.entity.Field
|
import com.keylesspalace.tusky.entity.Field
|
||||||
|
import com.keylesspalace.tusky.entity.IdentityProof
|
||||||
import com.keylesspalace.tusky.interfaces.LinkListener
|
import com.keylesspalace.tusky.interfaces.LinkListener
|
||||||
import com.keylesspalace.tusky.util.CustomEmojiHelper
|
import com.keylesspalace.tusky.util.CustomEmojiHelper
|
||||||
|
import com.keylesspalace.tusky.util.Either
|
||||||
import com.keylesspalace.tusky.util.LinkHelper
|
import com.keylesspalace.tusky.util.LinkHelper
|
||||||
import kotlinx.android.synthetic.main.item_account_field.view.*
|
import kotlinx.android.synthetic.main.item_account_field.view.*
|
||||||
|
|
||||||
class AccountFieldAdapter(private val linkListener: LinkListener) : RecyclerView.Adapter<AccountFieldAdapter.ViewHolder>() {
|
class AccountFieldAdapter(private val linkListener: LinkListener) : RecyclerView.Adapter<AccountFieldAdapter.ViewHolder>() {
|
||||||
|
|
||||||
var emojis: List<Emoji> = emptyList()
|
var emojis: List<Emoji> = emptyList()
|
||||||
var fields: List<Field> = emptyList()
|
var fields: List<Either<IdentityProof, Field>> = emptyList()
|
||||||
|
|
||||||
override fun getItemCount() = fields.size
|
override fun getItemCount() = fields.size
|
||||||
|
|
||||||
|
@ -41,18 +44,30 @@ class AccountFieldAdapter(private val linkListener: LinkListener) : RecyclerView
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
|
||||||
val field = fields[position]
|
val proofOrField = fields[position]
|
||||||
|
|
||||||
val emojifiedName = CustomEmojiHelper.emojifyString(field.name, emojis, viewHolder.nameTextView)
|
if(proofOrField.isLeft()) {
|
||||||
viewHolder.nameTextView.text = emojifiedName
|
val identityProof = proofOrField.asLeft()
|
||||||
|
|
||||||
val emojifiedValue = CustomEmojiHelper.emojifyText(field.value, emojis, viewHolder.valueTextView)
|
viewHolder.nameTextView.text = identityProof.provider
|
||||||
LinkHelper.setClickableText(viewHolder.valueTextView, emojifiedValue, null, linkListener)
|
viewHolder.valueTextView.text = LinkHelper.createClickableText(identityProof.username, identityProof.profileUrl)
|
||||||
|
|
||||||
|
viewHolder.valueTextView.movementMethod = LinkMovementMethod.getInstance()
|
||||||
|
|
||||||
if(field.verifiedAt != null) {
|
|
||||||
viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_check_circle, 0)
|
viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_check_circle, 0)
|
||||||
} else {
|
} else {
|
||||||
viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0 )
|
val field = proofOrField.asRight()
|
||||||
|
val emojifiedName = CustomEmojiHelper.emojifyString(field.name, emojis, viewHolder.nameTextView)
|
||||||
|
viewHolder.nameTextView.text = emojifiedName
|
||||||
|
|
||||||
|
val emojifiedValue = CustomEmojiHelper.emojifyText(field.value, emojis, viewHolder.valueTextView)
|
||||||
|
LinkHelper.setClickableText(viewHolder.valueTextView, emojifiedValue, null, linkListener)
|
||||||
|
|
||||||
|
if(field.verifiedAt != null) {
|
||||||
|
viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_check_circle, 0)
|
||||||
|
} else {
|
||||||
|
viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0 )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,10 +115,7 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
||||||
timestampInfo.append(" • ");
|
timestampInfo.append(" • ");
|
||||||
|
|
||||||
if (app.getWebsite() != null) {
|
if (app.getWebsite() != null) {
|
||||||
URLSpan span = new CustomURLSpan(app.getWebsite());
|
CharSequence text = LinkHelper.createClickableText(app.getName(), app.getWebsite());
|
||||||
|
|
||||||
SpannableStringBuilder text = new SpannableStringBuilder(app.getName());
|
|
||||||
text.setSpan(span, 0, app.getName().length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
|
||||||
timestampInfo.append(text);
|
timestampInfo.append(text);
|
||||||
timestampInfo.setMovementMethod(LinkMovementMethod.getInstance());
|
timestampInfo.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.keylesspalace.tusky.entity
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
data class IdentityProof(
|
||||||
|
val provider: String,
|
||||||
|
@SerializedName("provider_username") val username: String,
|
||||||
|
@SerializedName("profile_url") val profileUrl: String
|
||||||
|
)
|
|
@ -318,6 +318,11 @@ interface MastodonApi {
|
||||||
@Query("id[]") accountIds: List<String>
|
@Query("id[]") accountIds: List<String>
|
||||||
): Call<List<Relationship>>
|
): Call<List<Relationship>>
|
||||||
|
|
||||||
|
@GET("api/v1/accounts/{id}/identity_proofs")
|
||||||
|
fun identityProofs(
|
||||||
|
@Path("id") accountId: String
|
||||||
|
): Call<List<IdentityProof>>
|
||||||
|
|
||||||
@GET("api/v1/blocks")
|
@GET("api/v1/blocks")
|
||||||
fun blocks(
|
fun blocks(
|
||||||
@Query("max_id") maxId: String?
|
@Query("max_id") maxId: String?
|
||||||
|
|
|
@ -179,6 +179,14 @@ public class LinkHelper {
|
||||||
view.setMovementMethod(LinkMovementMethod.getInstance());
|
view.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CharSequence createClickableText(String text, String link) {
|
||||||
|
URLSpan span = new CustomURLSpan(link);
|
||||||
|
|
||||||
|
SpannableStringBuilder clickableText = new SpannableStringBuilder(text);
|
||||||
|
clickableText.setSpan(span, 0, text.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||||
|
return clickableText;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a link, depending on the settings, either in the browser or in a custom tab
|
* Opens a link, depending on the settings, either in the browser or in a custom tab
|
||||||
*
|
*
|
||||||
|
|
|
@ -6,12 +6,11 @@ import androidx.lifecycle.ViewModel
|
||||||
import com.keylesspalace.tusky.appstore.*
|
import com.keylesspalace.tusky.appstore.*
|
||||||
import com.keylesspalace.tusky.db.AccountManager
|
import com.keylesspalace.tusky.db.AccountManager
|
||||||
import com.keylesspalace.tusky.entity.Account
|
import com.keylesspalace.tusky.entity.Account
|
||||||
|
import com.keylesspalace.tusky.entity.Field
|
||||||
|
import com.keylesspalace.tusky.entity.IdentityProof
|
||||||
import com.keylesspalace.tusky.entity.Relationship
|
import com.keylesspalace.tusky.entity.Relationship
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
import com.keylesspalace.tusky.util.Error
|
import com.keylesspalace.tusky.util.*
|
||||||
import com.keylesspalace.tusky.util.Loading
|
|
||||||
import com.keylesspalace.tusky.util.Resource
|
|
||||||
import com.keylesspalace.tusky.util.Success
|
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
|
@ -27,6 +26,14 @@ class AccountViewModel @Inject constructor(
|
||||||
val accountData = MutableLiveData<Resource<Account>>()
|
val accountData = MutableLiveData<Resource<Account>>()
|
||||||
val relationshipData = MutableLiveData<Resource<Relationship>>()
|
val relationshipData = MutableLiveData<Resource<Relationship>>()
|
||||||
|
|
||||||
|
private val identityProofData = MutableLiveData<List<IdentityProof>>()
|
||||||
|
|
||||||
|
val accountFieldData = combineOptionalLiveData(accountData, identityProofData) { accountRes, identityProofs ->
|
||||||
|
identityProofs.orEmpty().map { Either.Left<IdentityProof, Field>(it) }
|
||||||
|
.plus(accountRes?.data?.fields.orEmpty().map { Either.Right<IdentityProof, Field>(it) })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private val callList: MutableList<Call<*>> = mutableListOf()
|
private val callList: MutableList<Call<*>> = mutableListOf()
|
||||||
private val disposable: Disposable = eventHub.events
|
private val disposable: Disposable = eventHub.events
|
||||||
.subscribe { event ->
|
.subscribe { event ->
|
||||||
|
@ -60,6 +67,7 @@ class AccountViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(call: Call<Account>, t: Throwable) {
|
override fun onFailure(call: Call<Account>, t: Throwable) {
|
||||||
|
Log.w(TAG, "failed obtaining account", t)
|
||||||
accountData.postValue(Error())
|
accountData.postValue(Error())
|
||||||
isDataLoading = false
|
isDataLoading = false
|
||||||
isRefreshing.postValue(false)
|
isRefreshing.postValue(false)
|
||||||
|
@ -90,6 +98,7 @@ class AccountViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(call: Call<List<Relationship>>, t: Throwable) {
|
override fun onFailure(call: Call<List<Relationship>>, t: Throwable) {
|
||||||
|
Log.w(TAG, "failed obtaining relationships", t)
|
||||||
relationshipData.postValue(Error())
|
relationshipData.postValue(Error())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -98,6 +107,30 @@ class AccountViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun obtainIdentityProof(reload: Boolean = false) {
|
||||||
|
if (identityProofData.value == null || reload) {
|
||||||
|
|
||||||
|
val call = mastodonApi.identityProofs(accountId)
|
||||||
|
call.enqueue(object : Callback<List<IdentityProof>> {
|
||||||
|
override fun onResponse(call: Call<List<IdentityProof>>,
|
||||||
|
response: Response<List<IdentityProof>>) {
|
||||||
|
val proofs = response.body()
|
||||||
|
if (response.isSuccessful && proofs != null ) {
|
||||||
|
identityProofData.postValue(proofs)
|
||||||
|
} else {
|
||||||
|
identityProofData.postValue(emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<List<IdentityProof>>, t: Throwable) {
|
||||||
|
Log.w(TAG, "failed obtaining identity proofs", t)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
callList.add(call)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun changeFollowState() {
|
fun changeFollowState() {
|
||||||
val relationship = relationshipData.value?.data
|
val relationship = relationshipData.value?.data
|
||||||
if (relationship?.following == true || relationship?.requested == true) {
|
if (relationship?.following == true || relationship?.requested == true) {
|
||||||
|
@ -227,6 +260,7 @@ class AccountViewModel @Inject constructor(
|
||||||
return
|
return
|
||||||
accountId.let {
|
accountId.let {
|
||||||
obtainAccount(isReload)
|
obtainAccount(isReload)
|
||||||
|
obtainIdentityProof()
|
||||||
if (!isSelf)
|
if (!isSelf)
|
||||||
obtainRelationship(isReload)
|
obtainRelationship(isReload)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue