Improve consistency of ViewModel and UI
This commit is contained in:
parent
06478cf8a7
commit
1dcf605976
@ -58,23 +58,24 @@ class EditProfileActivity : BaseActivity() {
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
model.uiState.collect { uiState ->
|
||||
if(uiState.profileLoaded){
|
||||
binding.bioEditText.setText(uiState.bio)
|
||||
binding.nameEditText.setText(uiState.name)
|
||||
model.changesApplied()
|
||||
}
|
||||
binding.progressCard.visibility = if(uiState.loadingProfile || uiState.sendingProfile || uiState.profileSent || uiState.error) View.VISIBLE else View.INVISIBLE
|
||||
if(binding.bioEditText.text.toString() != uiState.bio) binding.bioEditText.setText(uiState.bio)
|
||||
if(binding.nameEditText.text.toString() != uiState.name) binding.nameEditText.setText(uiState.name)
|
||||
|
||||
binding.progressCard.visibility = if(uiState.loadingProfile || uiState.sendingProfile || uiState.uploadingPicture || uiState.profileSent || uiState.error) View.VISIBLE else View.INVISIBLE
|
||||
|
||||
if(uiState.loadingProfile) binding.progressText.setText(R.string.fetching_profile)
|
||||
else if(uiState.sendingProfile) binding.progressText.setText(R.string.saving_profile)
|
||||
|
||||
binding.privateSwitch.isChecked = uiState.privateAccount == true
|
||||
Glide.with(binding.profilePic).load(uiState.profilePictureUri)
|
||||
.apply(RequestOptions.circleCropTransform())
|
||||
.into(binding.profilePic)
|
||||
|
||||
binding.savingProgressBar.visibility = if(uiState.error || uiState.profileSent) View.GONE
|
||||
else View.VISIBLE
|
||||
binding.savingProgressBar.visibility =
|
||||
if(uiState.error || (uiState.profileSent && !uiState.uploadingPicture)) View.GONE
|
||||
else View.VISIBLE
|
||||
|
||||
if(uiState.profileSent){
|
||||
if(uiState.profileSent && !uiState.uploadingPicture && !uiState.error){
|
||||
binding.progressText.setText(R.string.profile_saved)
|
||||
binding.done.visibility = View.VISIBLE
|
||||
} else {
|
||||
|
@ -23,6 +23,7 @@ import org.pixeldroid.app.postCreation.ProgressRequestBody
|
||||
import org.pixeldroid.app.posts.fromHtml
|
||||
import org.pixeldroid.app.utils.PixelDroidApplication
|
||||
import org.pixeldroid.app.utils.api.objects.Account
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -31,10 +32,13 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||
@Inject
|
||||
lateinit var apiHolder: PixelfedAPIHolder
|
||||
|
||||
@Inject
|
||||
lateinit var db: AppDatabase
|
||||
|
||||
private val _uiState = MutableStateFlow(EditProfileActivityUiState())
|
||||
val uiState: StateFlow<EditProfileActivityUiState> = _uiState
|
||||
|
||||
var oldProfile: Account? = null
|
||||
private var oldProfile: Account? = null
|
||||
|
||||
init {
|
||||
(application as PixelDroidApplication).getAppComponent().inject(this)
|
||||
@ -76,15 +80,10 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||
fun sendProfile() {
|
||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
|
||||
val requestBody =
|
||||
null //MultipartBody.Part.createFormData("avatar", System.currentTimeMillis().toString(), avatarBody)
|
||||
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
sendingProfile = true,
|
||||
profileSent = false,
|
||||
loadingProfile = false,
|
||||
profileLoaded = false,
|
||||
error = false
|
||||
)
|
||||
}
|
||||
@ -97,19 +96,16 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||
note = bio,
|
||||
locked = privateAccount,
|
||||
)
|
||||
val newAvatarUri = if (profilePictureChanged) {
|
||||
uploadImage()
|
||||
profilePictureUri
|
||||
} else {
|
||||
account.anyAvatar()?.toUri()
|
||||
}
|
||||
oldProfile = account
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
bio = account.source?.note
|
||||
?: account.note?.let { fromHtml(it).toString() },
|
||||
name = account.display_name,
|
||||
profilePictureUri = newAvatarUri,
|
||||
profilePictureUri = if (profilePictureChanged) profilePictureUri
|
||||
else account.anyAvatar()?.toUri(),
|
||||
uploadProgress = 0,
|
||||
uploadingPicture = profilePictureChanged,
|
||||
privateAccount = account.locked,
|
||||
sendingProfile = false,
|
||||
profileSent = true,
|
||||
@ -118,14 +114,13 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||
error = false
|
||||
)
|
||||
}
|
||||
if(profilePictureChanged) uploadImage()
|
||||
} catch (exception: Exception) {
|
||||
Log.e("TAG", exception.toString())
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
sendingProfile = false,
|
||||
profileSent = false,
|
||||
loadingProfile = false,
|
||||
profileLoaded = false,
|
||||
error = true
|
||||
)
|
||||
}
|
||||
@ -152,12 +147,6 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||
}
|
||||
}
|
||||
|
||||
fun changesApplied() {
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(profileLoaded = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun madeChanges(): Boolean =
|
||||
with(uiState.value) {
|
||||
val privateChanged = oldProfile?.locked != privateAccount
|
||||
@ -166,6 +155,7 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||
// If source note is null, check note
|
||||
?: oldProfile?.note?.let { fromHtml(it).toString() != bio }
|
||||
?: true
|
||||
|
||||
profilePictureChanged || privateChanged || displayNameChanged || bioChanged
|
||||
}
|
||||
|
||||
@ -243,20 +233,31 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||
var postSub: Disposable? = null
|
||||
|
||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
val inter = api.updateProfilePicture(requestBody.parts[0])
|
||||
|
||||
val pixelfed = db.instanceDao().getActiveInstance().pixelfed
|
||||
|
||||
val inter =
|
||||
if(pixelfed) api.updateProfilePicture(requestBody.parts[0])
|
||||
else api.updateProfilePictureMastodon((requestBody.parts[0]))
|
||||
|
||||
postSub = inter
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ it: Account ->
|
||||
Log.i("ACCOUNT", it.toString())
|
||||
/* onNext = */ { account: Account ->
|
||||
account.anyAvatar()?.let {
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
profilePictureUri = it.toUri()
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{ e: Throwable ->
|
||||
/* onError = */ { e: Throwable ->
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
uploadProgress = 0,
|
||||
uploadingPicture = true,
|
||||
uploadingPicture = false,
|
||||
error = true
|
||||
)
|
||||
}
|
||||
@ -264,7 +265,7 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||
postSub?.dispose()
|
||||
sub.dispose()
|
||||
},
|
||||
{
|
||||
/* onComplete = */ {
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
profilePictureChanged = false,
|
||||
|
@ -338,21 +338,34 @@ interface PixelfedAPI {
|
||||
@Header("Authorization") authorization: String? = null
|
||||
): Account
|
||||
|
||||
//@Multipart
|
||||
@PATCH("/api/v1/accounts/update_credentials")
|
||||
suspend fun updateCredentials(
|
||||
@Query(value = "display_name") displayName: String?,
|
||||
@Query(value = "note") note: String?,
|
||||
@Query(value = "locked") locked: Boolean?,
|
||||
// @Part avatar: MultipartBody.Part?,
|
||||
): Account
|
||||
|
||||
/**
|
||||
* Pixelfed uses PHP, multipart uploads don't work through PATCH so we use POST as suggested
|
||||
* here: https://github.com/pixelfed/pixelfed/issues/4250
|
||||
* However, changing to POST breaks the upload on Mastodon.
|
||||
*
|
||||
* To have this work on Pixelfed and Mastodon without special logic to distinguish the two,
|
||||
* we'll have to wait for PHP 8.4 and https://wiki.php.net/rfc/rfc1867-non-post
|
||||
* which should come out end of 2024
|
||||
*/
|
||||
@Multipart
|
||||
@POST("/api/v1/accounts/update_credentials")
|
||||
fun updateProfilePicture(
|
||||
@Part avatar: MultipartBody.Part?
|
||||
): Observable<Account>
|
||||
|
||||
@Multipart
|
||||
@PATCH("/api/v1/accounts/update_credentials")
|
||||
fun updateProfilePictureMastodon(
|
||||
@Part avatar: MultipartBody.Part?
|
||||
): Observable<Account>
|
||||
|
||||
@GET("/api/v1/accounts/{id}/statuses")
|
||||
suspend fun accountPosts(
|
||||
@Path("id") account_id: String,
|
||||
|
@ -22,7 +22,7 @@ import org.pixeldroid.app.utils.api.objects.Notification
|
||||
PublicFeedStatusDatabaseEntity::class,
|
||||
Notification::class
|
||||
],
|
||||
version = 5
|
||||
version = 6
|
||||
)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
@ -44,4 +44,9 @@ val MIGRATION_4_5 = object : Migration(4, 5) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE instances ADD COLUMN videoEnabled INTEGER NOT NULL DEFAULT 1")
|
||||
}
|
||||
}
|
||||
val MIGRATION_5_6 = object : Migration(5, 6) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE instances ADD COLUMN pixelfed INTEGER NOT NULL DEFAULT 1")
|
||||
}
|
||||
}
|
@ -37,17 +37,21 @@ fun storeInstance(db: AppDatabase, nodeInfo: NodeInfo?, instance: Instance? = nu
|
||||
uri = normalizeDomain(metadata?.config?.site?.url!!),
|
||||
title = metadata.config.site.name!!,
|
||||
maxStatusChars = metadata.config.uploader?.max_caption_length!!.toInt(),
|
||||
maxPhotoSize = metadata.config.uploader.max_photo_size?.toIntOrNull() ?: DEFAULT_MAX_PHOTO_SIZE,
|
||||
maxPhotoSize = metadata.config.uploader.max_photo_size?.toIntOrNull()
|
||||
?: DEFAULT_MAX_PHOTO_SIZE,
|
||||
// Pixelfed doesn't distinguish between max photo and video size
|
||||
maxVideoSize = metadata.config.uploader.max_photo_size?.toIntOrNull() ?: DEFAULT_MAX_VIDEO_SIZE,
|
||||
maxVideoSize = metadata.config.uploader.max_photo_size?.toIntOrNull()
|
||||
?: DEFAULT_MAX_VIDEO_SIZE,
|
||||
albumLimit = metadata.config.uploader.album_limit?.toIntOrNull() ?: DEFAULT_ALBUM_LIMIT,
|
||||
videoEnabled = metadata.config.features?.video ?: DEFAULT_VIDEO_ENABLED
|
||||
videoEnabled = metadata.config.features?.video ?: DEFAULT_VIDEO_ENABLED,
|
||||
pixelfed = metadata.software?.repo?.contains("pixelfed", ignoreCase = true) == true
|
||||
)
|
||||
} ?: instance?.run {
|
||||
InstanceDatabaseEntity(
|
||||
uri = normalizeDomain(uri.orEmpty()),
|
||||
title = title.orEmpty(),
|
||||
maxStatusChars = max_toot_chars?.toInt() ?: DEFAULT_MAX_TOOT_CHARS,
|
||||
uri = normalizeDomain(uri.orEmpty()),
|
||||
title = title.orEmpty(),
|
||||
maxStatusChars = max_toot_chars?.toInt() ?: DEFAULT_MAX_TOOT_CHARS,
|
||||
pixelfed = false
|
||||
)
|
||||
} ?: throw IllegalArgumentException("Cannot store instance where both are null")
|
||||
|
||||
|
@ -11,6 +11,10 @@ interface InstanceDao {
|
||||
@Query("SELECT * FROM instances WHERE uri=:instanceUri")
|
||||
fun getInstance(instanceUri: String): InstanceDatabaseEntity
|
||||
|
||||
|
||||
@Query("SELECT * FROM instances WHERE uri=(SELECT users.instance_uri FROM users WHERE isActive=1)")
|
||||
fun getActiveInstance(): InstanceDatabaseEntity
|
||||
|
||||
/**
|
||||
* Insert an instance, if it already exists return -1
|
||||
*/
|
||||
|
@ -4,20 +4,22 @@ import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "instances")
|
||||
data class InstanceDatabaseEntity (
|
||||
@PrimaryKey var uri: String,
|
||||
var title: String,
|
||||
var maxStatusChars: Int = DEFAULT_MAX_TOOT_CHARS,
|
||||
// Per-file file-size limit in KB. Defaults to 15000 (15MB). Default limit for Mastodon is 8MB
|
||||
var maxPhotoSize: Int = DEFAULT_MAX_PHOTO_SIZE,
|
||||
// Mastodon has different file limits for videos, default of 40MB
|
||||
var maxVideoSize: Int = DEFAULT_MAX_VIDEO_SIZE,
|
||||
// How many photos can go into an album. Default limit for Pixelfed and Mastodon is 4
|
||||
var albumLimit: Int = DEFAULT_ALBUM_LIMIT,
|
||||
// Is video functionality enabled on this instance?
|
||||
var videoEnabled: Boolean = DEFAULT_VIDEO_ENABLED,
|
||||
data class InstanceDatabaseEntity(
|
||||
@PrimaryKey var uri: String,
|
||||
var title: String,
|
||||
var maxStatusChars: Int = DEFAULT_MAX_TOOT_CHARS,
|
||||
// Per-file file-size limit in KB. Defaults to 15000 (15MB). Default limit for Mastodon is 8MB
|
||||
var maxPhotoSize: Int = DEFAULT_MAX_PHOTO_SIZE,
|
||||
// Mastodon has different file limits for videos, default of 40MB
|
||||
var maxVideoSize: Int = DEFAULT_MAX_VIDEO_SIZE,
|
||||
// How many photos can go into an album. Default limit for Pixelfed and Mastodon is 4
|
||||
var albumLimit: Int = DEFAULT_ALBUM_LIMIT,
|
||||
// Is video functionality enabled on this instance?
|
||||
var videoEnabled: Boolean = DEFAULT_VIDEO_ENABLED,
|
||||
// Is this Pixelfed instance?
|
||||
var pixelfed: Boolean = true,
|
||||
) {
|
||||
companion object{
|
||||
companion object {
|
||||
// Default max number of chars for Mastodon: used when their is no other value supplied by
|
||||
// either NodeInfo or the instance endpoint
|
||||
const val DEFAULT_MAX_TOOT_CHARS = 500
|
||||
|
@ -7,6 +7,7 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import org.pixeldroid.app.utils.db.MIGRATION_3_4
|
||||
import org.pixeldroid.app.utils.db.MIGRATION_4_5
|
||||
import org.pixeldroid.app.utils.db.MIGRATION_5_6
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@ -18,7 +19,7 @@ class DatabaseModule(private val context: Context) {
|
||||
return Room.databaseBuilder(
|
||||
context,
|
||||
AppDatabase::class.java, "pixeldroid"
|
||||
).addMigrations(MIGRATION_3_4).addMigrations(MIGRATION_4_5)
|
||||
).addMigrations(MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6)
|
||||
.allowMainThreadQueries().build()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user