Merge branch 'edit_profile_picture' into 'master'
Fix profile pic edit Closes #377 See merge request pixeldroid/PixelDroid!569
This commit is contained in:
commit
370aeda4a6
|
@ -27,7 +27,7 @@ android {
|
||||||
}
|
}
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
versionCode 26
|
versionCode 27
|
||||||
targetSdkVersion 34
|
targetSdkVersion 34
|
||||||
versionName "1.0.beta" + versionCode
|
versionName "1.0.beta" + versionCode
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.activity.addCallback
|
import androidx.activity.addCallback
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
@ -29,6 +30,7 @@ import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
import com.google.android.material.color.DynamicColors
|
import com.google.android.material.color.DynamicColors
|
||||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||||
|
@ -36,7 +38,12 @@ import com.mikepenz.materialdrawer.iconics.iconicsIcon
|
||||||
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
|
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
|
||||||
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
|
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
|
||||||
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem
|
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem
|
||||||
import com.mikepenz.materialdrawer.model.interfaces.*
|
import com.mikepenz.materialdrawer.model.interfaces.IProfile
|
||||||
|
import com.mikepenz.materialdrawer.model.interfaces.descriptionRes
|
||||||
|
import com.mikepenz.materialdrawer.model.interfaces.descriptionText
|
||||||
|
import com.mikepenz.materialdrawer.model.interfaces.iconUrl
|
||||||
|
import com.mikepenz.materialdrawer.model.interfaces.nameRes
|
||||||
|
import com.mikepenz.materialdrawer.model.interfaces.nameText
|
||||||
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
|
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
|
||||||
import com.mikepenz.materialdrawer.util.DrawerImageLoader
|
import com.mikepenz.materialdrawer.util.DrawerImageLoader
|
||||||
import com.mikepenz.materialdrawer.widget.AccountHeaderView
|
import com.mikepenz.materialdrawer.widget.AccountHeaderView
|
||||||
|
@ -53,10 +60,10 @@ import org.pixeldroid.app.searchDiscover.SearchDiscoverFragment
|
||||||
import org.pixeldroid.app.settings.SettingsActivity
|
import org.pixeldroid.app.settings.SettingsActivity
|
||||||
import org.pixeldroid.app.utils.BaseActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.api.objects.Notification
|
import org.pixeldroid.app.utils.api.objects.Notification
|
||||||
import org.pixeldroid.app.utils.db.addUser
|
|
||||||
import org.pixeldroid.app.utils.db.entities.HomeStatusDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.HomeStatusDatabaseEntity
|
||||||
import org.pixeldroid.app.utils.db.entities.PublicFeedStatusDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.PublicFeedStatusDatabaseEntity
|
||||||
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
|
import org.pixeldroid.app.utils.db.updateUserInfoDb
|
||||||
import org.pixeldroid.app.utils.hasInternet
|
import org.pixeldroid.app.utils.hasInternet
|
||||||
import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker.Companion.INSTANCE_NOTIFICATION_TAG
|
import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker.Companion.INSTANCE_NOTIFICATION_TAG
|
||||||
import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker.Companion.SHOW_NOTIFICATION_TAG
|
import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker.Companion.SHOW_NOTIFICATION_TAG
|
||||||
|
@ -71,6 +78,8 @@ class MainActivity : BaseActivity() {
|
||||||
private lateinit var header: AccountHeaderView
|
private lateinit var header: AccountHeaderView
|
||||||
private var user: UserDatabaseEntity? = null
|
private var user: UserDatabaseEntity? = null
|
||||||
|
|
||||||
|
private lateinit var model: MainActivityViewModel
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val ADD_ACCOUNT_IDENTIFIER: Long = -13
|
const val ADD_ACCOUNT_IDENTIFIER: Long = -13
|
||||||
}
|
}
|
||||||
|
@ -102,6 +111,12 @@ class MainActivity : BaseActivity() {
|
||||||
} else {
|
} else {
|
||||||
sendTraceDroidStackTracesIfExist("contact@pixeldroid.org", this)
|
sendTraceDroidStackTracesIfExist("contact@pixeldroid.org", this)
|
||||||
|
|
||||||
|
val _model: MainActivityViewModel by viewModels {
|
||||||
|
MainActivityViewModelFactory(application)
|
||||||
|
}
|
||||||
|
model = _model
|
||||||
|
|
||||||
|
|
||||||
setupDrawer()
|
setupDrawer()
|
||||||
val tabs: List<() -> Fragment> = listOf(
|
val tabs: List<() -> Fragment> = listOf(
|
||||||
{
|
{
|
||||||
|
@ -196,6 +211,7 @@ class MainActivity : BaseActivity() {
|
||||||
Glide.with(this@MainActivity)
|
Glide.with(this@MainActivity)
|
||||||
.load(uri)
|
.load(uri)
|
||||||
.placeholder(placeholder)
|
.placeholder(placeholder)
|
||||||
|
.circleCrop()
|
||||||
.into(imageView)
|
.into(imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,16 +297,12 @@ class MainActivity : BaseActivity() {
|
||||||
|
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
try {
|
try {
|
||||||
val domain = user?.instance_uri.orEmpty()
|
|
||||||
val accessToken = user?.accessToken.orEmpty()
|
|
||||||
val refreshToken = user?.refreshToken
|
|
||||||
val clientId = user?.clientId.orEmpty()
|
|
||||||
val clientSecret = user?.clientSecret.orEmpty()
|
|
||||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
|
|
||||||
val account = api.verifyCredentials()
|
val account = api.verifyCredentials()
|
||||||
addUser(db, account, domain, accessToken = accessToken, refreshToken = refreshToken, clientId = clientId, clientSecret = clientSecret)
|
updateUserInfoDb(db, account)
|
||||||
fillDrawerAccountInfo(account.id!!)
|
|
||||||
|
//No need to update drawer account info here, the ViewModel listens to db updates
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e("ACCOUNT UPDATE:", exception.toString())
|
Log.e("ACCOUNT UPDATE:", exception.toString())
|
||||||
}
|
}
|
||||||
|
@ -337,35 +349,41 @@ class MainActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fillDrawerAccountInfo(account: String) {
|
private fun fillDrawerAccountInfo(account: String) {
|
||||||
val users = db.userDao().getAll().toMutableList()
|
lifecycleScope.launch {
|
||||||
users.sortWith { l, r ->
|
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
when {
|
model.users.collect { list ->
|
||||||
l.isActive && !r.isActive -> -1
|
val users = list.toMutableList()
|
||||||
r.isActive && !l.isActive -> 1
|
users.sortWith { l, r ->
|
||||||
else -> 0
|
when {
|
||||||
|
l.isActive && !r.isActive -> -1
|
||||||
|
r.isActive && !l.isActive -> 1
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val profiles: MutableList<IProfile> = users.map { user ->
|
||||||
|
ProfileDrawerItem().apply {
|
||||||
|
isSelected = user.isActive
|
||||||
|
nameText = user.display_name
|
||||||
|
iconUrl = user.avatar_static
|
||||||
|
isNameShown = true
|
||||||
|
identifier = user.user_id.toLong()
|
||||||
|
descriptionText = user.fullHandle
|
||||||
|
tag = user.instance_uri
|
||||||
|
}
|
||||||
|
}.toMutableList()
|
||||||
|
|
||||||
|
// reuse the already existing "add account" item
|
||||||
|
header.profiles.orEmpty()
|
||||||
|
.filter { it.identifier == ADD_ACCOUNT_IDENTIFIER }
|
||||||
|
.take(1)
|
||||||
|
.forEach { profiles.add(it) }
|
||||||
|
|
||||||
|
header.clear()
|
||||||
|
header.profiles = profiles
|
||||||
|
header.setActiveProfile(account.toLong())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val profiles: MutableList<IProfile> = users.map { user ->
|
|
||||||
ProfileDrawerItem().apply {
|
|
||||||
isSelected = user.isActive
|
|
||||||
nameText = user.display_name
|
|
||||||
iconUrl = user.avatar_static
|
|
||||||
isNameShown = true
|
|
||||||
identifier = user.user_id.toLong()
|
|
||||||
descriptionText = user.fullHandle
|
|
||||||
tag = user.instance_uri
|
|
||||||
}
|
|
||||||
}.toMutableList()
|
|
||||||
|
|
||||||
// reuse the already existing "add account" item
|
|
||||||
header.profiles.orEmpty()
|
|
||||||
.filter { it.identifier == ADD_ACCOUNT_IDENTIFIER }
|
|
||||||
.take(1)
|
|
||||||
.forEach { profiles.add(it) }
|
|
||||||
|
|
||||||
header.clear()
|
|
||||||
header.profiles = profiles
|
|
||||||
header.setActiveProfile(account.toLong())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package org.pixeldroid.app
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.pixeldroid.app.utils.PixelDroidApplication
|
||||||
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class MainActivityViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var db: AppDatabase
|
||||||
|
|
||||||
|
// Mutable state flow that will be used internally in the ViewModel, empty list is given as initial value.
|
||||||
|
private val _users = MutableStateFlow(emptyList<UserDatabaseEntity>())
|
||||||
|
|
||||||
|
// Immutable state flow exposed to UI
|
||||||
|
val users = _users.asStateFlow()
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
(application as PixelDroidApplication).getAppComponent().inject(this)
|
||||||
|
getUsers()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getUsers() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
db.userDao().getAllFlow().flowOn(Dispatchers.IO)
|
||||||
|
.collect { users: List<UserDatabaseEntity> ->
|
||||||
|
_users.update { users }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainActivityViewModelFactory(
|
||||||
|
val application: Application,
|
||||||
|
) : ViewModelProvider.Factory {
|
||||||
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
|
return modelClass.getConstructor(Application::class.java).newInstance(application)
|
||||||
|
}
|
||||||
|
}
|
|
@ -73,6 +73,7 @@ internal fun <T: Any> initAdapter(
|
||||||
|
|
||||||
swipeRefreshLayout.setOnRefreshListener {
|
swipeRefreshLayout.setOnRefreshListener {
|
||||||
adapter.refresh()
|
adapter.refresh()
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
header?.refreshStories()
|
header?.refreshStories()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@ class EditProfileActivity : BaseActivity() {
|
||||||
}.show()
|
}.show()
|
||||||
} else {
|
} else {
|
||||||
this.isEnabled = false
|
this.isEnabled = false
|
||||||
|
if (model.submittedChanges) setResult(RESULT_OK)
|
||||||
super.onBackPressedDispatcher.onBackPressed()
|
super.onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,23 +59,24 @@ class EditProfileActivity : BaseActivity() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
model.uiState.collect { uiState ->
|
model.uiState.collect { uiState ->
|
||||||
if(uiState.profileLoaded){
|
if(binding.bioEditText.text.toString() != uiState.bio) binding.bioEditText.setText(uiState.bio)
|
||||||
binding.bioEditText.setText(uiState.bio)
|
if(binding.nameEditText.text.toString() != uiState.name) binding.nameEditText.setText(uiState.name)
|
||||||
binding.nameEditText.setText(uiState.name)
|
|
||||||
model.changesApplied()
|
binding.progressCard.visibility = if(uiState.loadingProfile || uiState.sendingProfile || uiState.uploadingPicture || uiState.profileSent || uiState.error) View.VISIBLE else View.INVISIBLE
|
||||||
}
|
|
||||||
binding.progressCard.visibility = if(uiState.loadingProfile || uiState.sendingProfile || uiState.profileSent || uiState.error) View.VISIBLE else View.INVISIBLE
|
|
||||||
if(uiState.loadingProfile) binding.progressText.setText(R.string.fetching_profile)
|
if(uiState.loadingProfile) binding.progressText.setText(R.string.fetching_profile)
|
||||||
else if(uiState.sendingProfile) binding.progressText.setText(R.string.saving_profile)
|
else if(uiState.sendingProfile) binding.progressText.setText(R.string.saving_profile)
|
||||||
|
|
||||||
binding.privateSwitch.isChecked = uiState.privateAccount == true
|
binding.privateSwitch.isChecked = uiState.privateAccount == true
|
||||||
Glide.with(binding.profilePic).load(uiState.profilePictureUri)
|
Glide.with(binding.profilePic).load(uiState.profilePictureUri)
|
||||||
.apply(RequestOptions.circleCropTransform())
|
.apply(RequestOptions.circleCropTransform())
|
||||||
.into(binding.profilePic)
|
.into(binding.profilePic)
|
||||||
|
|
||||||
binding.savingProgressBar.visibility = if(uiState.error || uiState.profileSent) View.GONE
|
binding.savingProgressBar.visibility =
|
||||||
else View.VISIBLE
|
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.progressText.setText(R.string.profile_saved)
|
||||||
binding.done.visibility = View.VISIBLE
|
binding.done.visibility = View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
|
@ -112,18 +114,18 @@ class EditProfileActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// binding.changeImageButton.setOnClickListener {
|
binding.profilePic.setOnClickListener {
|
||||||
// Intent(Intent.ACTION_GET_CONTENT).apply {
|
Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||||
// type = "*/*"
|
type = "*/*"
|
||||||
// putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*"))
|
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*"))
|
||||||
// action = Intent.ACTION_GET_CONTENT
|
action = Intent.ACTION_GET_CONTENT
|
||||||
// addCategory(Intent.CATEGORY_OPENABLE)
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
// putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)
|
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)
|
||||||
// uploadImageResultContract.launch(
|
uploadImageResultContract.launch(
|
||||||
// Intent.createChooser(this, null)
|
Intent.createChooser(this, null)
|
||||||
// )
|
)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val uploadImageResultContract = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
private val uploadImageResultContract = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
|
@ -137,10 +139,10 @@ class EditProfileActivity : BaseActivity() {
|
||||||
val imageUri: String = clipData.getItemAt(i).uri.toString()
|
val imageUri: String = clipData.getItemAt(i).uri.toString()
|
||||||
images.add(imageUri)
|
images.add(imageUri)
|
||||||
}
|
}
|
||||||
model.uploadImage(images.first())
|
model.updateImage(images.first())
|
||||||
} else if (data.data != null) {
|
} else if (data.data != null) {
|
||||||
images.add(data.data!!.toString())
|
images.add(data.data!!.toString())
|
||||||
model.uploadImage(images.first())
|
model.updateImage(images.first())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,10 @@ import org.pixeldroid.app.postCreation.ProgressRequestBody
|
||||||
import org.pixeldroid.app.posts.fromHtml
|
import org.pixeldroid.app.posts.fromHtml
|
||||||
import org.pixeldroid.app.utils.PixelDroidApplication
|
import org.pixeldroid.app.utils.PixelDroidApplication
|
||||||
import org.pixeldroid.app.utils.api.objects.Account
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
|
import org.pixeldroid.app.utils.db.updateUserInfoDb
|
||||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
|
import retrofit2.HttpException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class EditProfileViewModel(application: Application) : AndroidViewModel(application) {
|
class EditProfileViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
@ -31,10 +34,16 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var apiHolder: PixelfedAPIHolder
|
lateinit var apiHolder: PixelfedAPIHolder
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var db: AppDatabase
|
||||||
|
|
||||||
private val _uiState = MutableStateFlow(EditProfileActivityUiState())
|
private val _uiState = MutableStateFlow(EditProfileActivityUiState())
|
||||||
val uiState: StateFlow<EditProfileActivityUiState> = _uiState
|
val uiState: StateFlow<EditProfileActivityUiState> = _uiState
|
||||||
|
|
||||||
var oldProfile: Account? = null
|
private var oldProfile: Account? = null
|
||||||
|
|
||||||
|
var submittedChanges = false
|
||||||
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
(application as PixelDroidApplication).getAppComponent().inject(this)
|
(application as PixelDroidApplication).getAppComponent().inject(this)
|
||||||
|
@ -46,6 +55,7 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
try {
|
try {
|
||||||
val profile = api.verifyCredentials()
|
val profile = api.verifyCredentials()
|
||||||
|
updateUserInfoDb(db, profile)
|
||||||
if (oldProfile == null) oldProfile = profile
|
if (oldProfile == null) oldProfile = profile
|
||||||
_uiState.update { currentUiState ->
|
_uiState.update { currentUiState ->
|
||||||
currentUiState.copy(
|
currentUiState.copy(
|
||||||
|
@ -76,15 +86,10 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||||
fun sendProfile() {
|
fun sendProfile() {
|
||||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
|
|
||||||
val requestBody =
|
|
||||||
null //MultipartBody.Part.createFormData("avatar", System.currentTimeMillis().toString(), avatarBody)
|
|
||||||
|
|
||||||
_uiState.update { currentUiState ->
|
_uiState.update { currentUiState ->
|
||||||
currentUiState.copy(
|
currentUiState.copy(
|
||||||
sendingProfile = true,
|
sendingProfile = true,
|
||||||
profileSent = false,
|
profileSent = false,
|
||||||
loadingProfile = false,
|
|
||||||
profileLoaded = false,
|
|
||||||
error = false
|
error = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -97,12 +102,17 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||||
note = bio,
|
note = bio,
|
||||||
locked = privateAccount,
|
locked = privateAccount,
|
||||||
)
|
)
|
||||||
|
if (madeChanges()) submittedChanges = true
|
||||||
oldProfile = account
|
oldProfile = account
|
||||||
_uiState.update { currentUiState ->
|
_uiState.update { currentUiState ->
|
||||||
currentUiState.copy(
|
currentUiState.copy(
|
||||||
bio = account.source?.note ?: account.note?.let {fromHtml(it).toString()},
|
bio = account.source?.note
|
||||||
|
?: account.note?.let { fromHtml(it).toString() },
|
||||||
name = account.display_name,
|
name = account.display_name,
|
||||||
profilePictureUri = account.anyAvatar()?.toUri(),
|
profilePictureUri = if (profilePictureChanged) profilePictureUri
|
||||||
|
else account.anyAvatar()?.toUri(),
|
||||||
|
uploadProgress = 0,
|
||||||
|
uploadingPicture = profilePictureChanged,
|
||||||
privateAccount = account.locked,
|
privateAccount = account.locked,
|
||||||
sendingProfile = false,
|
sendingProfile = false,
|
||||||
profileSent = true,
|
profileSent = true,
|
||||||
|
@ -111,14 +121,13 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||||
error = false
|
error = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if(profilePictureChanged) uploadImage()
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e("TAG", exception.toString())
|
Log.e("TAG", exception.toString())
|
||||||
_uiState.update { currentUiState ->
|
_uiState.update { currentUiState ->
|
||||||
currentUiState.copy(
|
currentUiState.copy(
|
||||||
sendingProfile = false,
|
sendingProfile = false,
|
||||||
profileSent = false,
|
profileSent = false,
|
||||||
loadingProfile = false,
|
|
||||||
profileLoaded = false,
|
|
||||||
error = true
|
error = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -145,20 +154,16 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changesApplied() {
|
|
||||||
_uiState.update { currentUiState ->
|
|
||||||
currentUiState.copy(profileLoaded = false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun madeChanges(): Boolean =
|
fun madeChanges(): Boolean =
|
||||||
with(uiState.value) {
|
with(uiState.value) {
|
||||||
val bioUnchanged: Boolean = oldProfile?.source?.note?.let { it != bio }
|
val privateChanged = oldProfile?.locked != privateAccount
|
||||||
// If source note is null, check note
|
val displayNameChanged = oldProfile?.display_name != name
|
||||||
|
val bioChanged: Boolean = oldProfile?.source?.note?.let { it != bio }
|
||||||
|
// If source note is null, check note
|
||||||
?: oldProfile?.note?.let { fromHtml(it).toString() != bio }
|
?: oldProfile?.note?.let { fromHtml(it).toString() != bio }
|
||||||
?: true
|
?: true
|
||||||
oldProfile?.locked != privateAccount || oldProfile?.display_name != name
|
|
||||||
|| bioUnchanged
|
profilePictureChanged || privateChanged || displayNameChanged || bioChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clickedCard() {
|
fun clickedCard() {
|
||||||
|
@ -178,16 +183,27 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun uploadImage(image: String) {
|
fun updateImage(image: String) {
|
||||||
//TODO fix
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(
|
||||||
|
profilePictureUri = image.toUri(),
|
||||||
|
profilePictureChanged = true,
|
||||||
|
profileSent = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun uploadImage() {
|
||||||
|
val image = uiState.value.profilePictureUri!!
|
||||||
|
|
||||||
val inputStream =
|
val inputStream =
|
||||||
getApplication<PixelDroidApplication>().contentResolver.openInputStream(image.toUri())
|
getApplication<PixelDroidApplication>().contentResolver.openInputStream(image)
|
||||||
?: return
|
?: return
|
||||||
|
|
||||||
val size: Long =
|
val size: Long =
|
||||||
if (image.toUri().scheme == "content") {
|
if (image.scheme == "content") {
|
||||||
getApplication<PixelDroidApplication>().contentResolver.query(
|
getApplication<PixelDroidApplication>().contentResolver.query(
|
||||||
image.toUri(),
|
image,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
@ -203,7 +219,7 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||||
cursor.getLong(sizeIndex)
|
cursor.getLong(sizeIndex)
|
||||||
} ?: 0
|
} ?: 0
|
||||||
} else {
|
} else {
|
||||||
image.toUri().toFile().length()
|
image.toFile().length()
|
||||||
}
|
}
|
||||||
|
|
||||||
val imagePart = ProgressRequestBody(inputStream, size, "image/*")
|
val imagePart = ProgressRequestBody(inputStream, size, "image/*")
|
||||||
|
@ -225,21 +241,32 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||||
var postSub: Disposable? = null
|
var postSub: Disposable? = null
|
||||||
|
|
||||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
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
|
postSub = inter
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
{ it: Account ->
|
/* onNext = */ { account: Account ->
|
||||||
Log.e("qsdfqsdfs", it.toString())
|
account.anyAvatar()?.let {
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(
|
||||||
|
profilePictureUri = it.toUri()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ e: Throwable ->
|
/* onError = */ { e: Throwable ->
|
||||||
|
Log.e("error", (e as? HttpException)?.message().orEmpty())
|
||||||
_uiState.update { currentUiState ->
|
_uiState.update { currentUiState ->
|
||||||
currentUiState.copy(
|
currentUiState.copy(
|
||||||
uploadProgress = 0,
|
uploadProgress = 0,
|
||||||
uploadingPicture = true,
|
uploadingPicture = false,
|
||||||
error = true
|
error = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -247,9 +274,10 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||||
postSub?.dispose()
|
postSub?.dispose()
|
||||||
sub.dispose()
|
sub.dispose()
|
||||||
},
|
},
|
||||||
{
|
/* onComplete = */ {
|
||||||
_uiState.update { currentUiState ->
|
_uiState.update { currentUiState ->
|
||||||
currentUiState.copy(
|
currentUiState.copy(
|
||||||
|
profilePictureChanged = false,
|
||||||
uploadProgress = 100,
|
uploadProgress = 100,
|
||||||
uploadingPicture = false
|
uploadingPicture = false
|
||||||
)
|
)
|
||||||
|
@ -265,7 +293,8 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat
|
||||||
data class EditProfileActivityUiState(
|
data class EditProfileActivityUiState(
|
||||||
val name: String? = null,
|
val name: String? = null,
|
||||||
val bio: String? = null,
|
val bio: String? = null,
|
||||||
val profilePictureUri: Uri?= null,
|
val profilePictureUri: Uri? = null,
|
||||||
|
val profilePictureChanged: Boolean = false,
|
||||||
val privateAccount: Boolean? = null,
|
val privateAccount: Boolean? = null,
|
||||||
val loadingProfile: Boolean = true,
|
val loadingProfile: Boolean = true,
|
||||||
val profileLoaded: Boolean = false,
|
val profileLoaded: Boolean = false,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.text.method.LinkMovementMethod
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
@ -16,11 +17,14 @@ import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.databinding.ActivityProfileBinding
|
import org.pixeldroid.app.databinding.ActivityProfileBinding
|
||||||
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedFeedFragment
|
||||||
import org.pixeldroid.app.posts.parseHTMLText
|
import org.pixeldroid.app.posts.parseHTMLText
|
||||||
import org.pixeldroid.app.utils.BaseActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import org.pixeldroid.app.utils.api.objects.Account
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
|
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||||
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
|
import org.pixeldroid.app.utils.db.updateUserInfoDb
|
||||||
import org.pixeldroid.app.utils.setProfileImageFromURL
|
import org.pixeldroid.app.utils.setProfileImageFromURL
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -56,7 +60,7 @@ class ProfileActivity : BaseActivity() {
|
||||||
setContent(account)
|
setContent(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createProfileTabs(account: Account?): Array<Fragment>{
|
private fun createProfileTabs(account: Account?): Array<UncachedFeedFragment<FeedContent>> {
|
||||||
|
|
||||||
val profileFeedFragment = ProfileFeedFragment()
|
val profileFeedFragment = ProfileFeedFragment()
|
||||||
profileFeedFragment.arguments = Bundle().apply {
|
profileFeedFragment.arguments = Bundle().apply {
|
||||||
|
@ -80,7 +84,7 @@ class ProfileActivity : BaseActivity() {
|
||||||
putSerializable(ProfileFeedFragment.COLLECTIONS, true)
|
putSerializable(ProfileFeedFragment.COLLECTIONS, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
val returnArray: Array<Fragment> = arrayOf(
|
val returnArray: Array<UncachedFeedFragment<FeedContent>> = arrayOf(
|
||||||
profileGridFragment,
|
profileGridFragment,
|
||||||
profileFeedFragment,
|
profileFeedFragment,
|
||||||
profileCollectionsFragment
|
profileCollectionsFragment
|
||||||
|
@ -100,7 +104,7 @@ class ProfileActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupTabs(
|
private fun setupTabs(
|
||||||
tabs: Array<Fragment>
|
tabs: Array<UncachedFeedFragment<FeedContent>>
|
||||||
){
|
){
|
||||||
binding.viewPager.adapter = object : FragmentStateAdapter(this) {
|
binding.viewPager.adapter = object : FragmentStateAdapter(this) {
|
||||||
override fun createFragment(position: Int): Fragment {
|
override fun createFragment(position: Int): Fragment {
|
||||||
|
@ -134,7 +138,6 @@ class ProfileActivity : BaseActivity() {
|
||||||
}.attach()
|
}.attach()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun setContent(account: Account?) {
|
private fun setContent(account: Account?) {
|
||||||
if(account != null) {
|
if(account != null) {
|
||||||
setViews(account)
|
setViews(account)
|
||||||
|
@ -152,6 +155,9 @@ class ProfileActivity : BaseActivity() {
|
||||||
).show()
|
).show()
|
||||||
return@launchWhenResumed
|
return@launchWhenResumed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateUserInfoDb(db, myAccount)
|
||||||
|
|
||||||
setViews(myAccount)
|
setViews(myAccount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,9 +223,15 @@ class ProfileActivity : BaseActivity() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val editResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
|
if (it.resultCode == RESULT_OK) {
|
||||||
|
// Profile was edited, reload
|
||||||
|
setContent(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun onClickEditButton() {
|
private fun onClickEditButton() {
|
||||||
val intent = Intent(this, EditProfileActivity::class.java)
|
editResult.launch(Intent(this, EditProfileActivity::class.java))
|
||||||
ContextCompat.startActivity(this, intent, null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onClickFollowers(account: Account?) {
|
private fun onClickFollowers(account: Account?) {
|
||||||
|
|
|
@ -338,18 +338,31 @@ interface PixelfedAPI {
|
||||||
@Header("Authorization") authorization: String? = null
|
@Header("Authorization") authorization: String? = null
|
||||||
): Account
|
): Account
|
||||||
|
|
||||||
//@Multipart
|
|
||||||
@PATCH("/api/v1/accounts/update_credentials")
|
@PATCH("/api/v1/accounts/update_credentials")
|
||||||
suspend fun updateCredentials(
|
suspend fun updateCredentials(
|
||||||
@Query(value = "display_name") displayName: String?,
|
@Query(value = "display_name") displayName: String?,
|
||||||
@Query(value = "note") note: String?,
|
@Query(value = "note") note: String?,
|
||||||
@Query(value = "locked") locked: Boolean?,
|
@Query(value = "locked") locked: Boolean?,
|
||||||
// @Part avatar: MultipartBody.Part?,
|
|
||||||
): Account
|
): 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
|
@Multipart
|
||||||
@PATCH("/api/v1/accounts/update_credentials")
|
@PATCH("/api/v1/accounts/update_credentials")
|
||||||
fun updateProfilePicture(
|
fun updateProfilePictureMastodon(
|
||||||
@Part avatar: MultipartBody.Part?
|
@Part avatar: MultipartBody.Part?
|
||||||
): Observable<Account>
|
): Observable<Account>
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import org.pixeldroid.app.utils.api.objects.Notification
|
||||||
PublicFeedStatusDatabaseEntity::class,
|
PublicFeedStatusDatabaseEntity::class,
|
||||||
Notification::class
|
Notification::class
|
||||||
],
|
],
|
||||||
version = 5
|
version = 6
|
||||||
)
|
)
|
||||||
@TypeConverters(Converters::class)
|
@TypeConverters(Converters::class)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
@ -44,4 +44,9 @@ val MIGRATION_4_5 = object : Migration(4, 5) {
|
||||||
override fun migrate(database: SupportSQLiteDatabase) {
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
database.execSQL("ALTER TABLE instances ADD COLUMN videoEnabled INTEGER NOT NULL DEFAULT 1")
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -13,21 +13,34 @@ import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity.Companion.DEF
|
||||||
import org.pixeldroid.app.utils.normalizeDomain
|
import org.pixeldroid.app.utils.normalizeDomain
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
|
|
||||||
fun addUser(db: AppDatabase, account: Account, instance_uri: String, activeUser: Boolean = true,
|
suspend fun addUser(
|
||||||
accessToken: String, refreshToken: String?, clientId: String, clientSecret: String) {
|
db: AppDatabase, account: Account, instance_uri: String, activeUser: Boolean = true,
|
||||||
|
accessToken: String, refreshToken: String?, clientId: String, clientSecret: String,
|
||||||
|
) {
|
||||||
db.userDao().insertOrUpdate(
|
db.userDao().insertOrUpdate(
|
||||||
UserDatabaseEntity(
|
UserDatabaseEntity(
|
||||||
user_id = account.id!!,
|
user_id = account.id!!,
|
||||||
instance_uri = normalizeDomain(instance_uri),
|
instance_uri = normalizeDomain(instance_uri),
|
||||||
username = account.username!!,
|
username = account.username!!,
|
||||||
display_name = account.getDisplayName(),
|
display_name = account.getDisplayName(),
|
||||||
avatar_static = account.anyAvatar().orEmpty(),
|
avatar_static = account.anyAvatar().orEmpty(),
|
||||||
isActive = activeUser,
|
isActive = activeUser,
|
||||||
accessToken = accessToken,
|
accessToken = accessToken,
|
||||||
refreshToken = refreshToken,
|
refreshToken = refreshToken,
|
||||||
clientId = clientId,
|
clientId = clientId,
|
||||||
clientSecret = clientSecret
|
clientSecret = clientSecret
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun updateUserInfoDb(db: AppDatabase, account: Account) {
|
||||||
|
val user = db.userDao().getActiveUser()!!
|
||||||
|
db.userDao().updateUserAccountDetails(
|
||||||
|
account.username.orEmpty(),
|
||||||
|
account.display_name.orEmpty(),
|
||||||
|
account.anyAvatar().orEmpty(),
|
||||||
|
user.user_id,
|
||||||
|
user.instance_uri
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,17 +50,21 @@ fun storeInstance(db: AppDatabase, nodeInfo: NodeInfo?, instance: Instance? = nu
|
||||||
uri = normalizeDomain(metadata?.config?.site?.url!!),
|
uri = normalizeDomain(metadata?.config?.site?.url!!),
|
||||||
title = metadata.config.site.name!!,
|
title = metadata.config.site.name!!,
|
||||||
maxStatusChars = metadata.config.uploader?.max_caption_length!!.toInt(),
|
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
|
// 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,
|
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 {
|
} ?: instance?.run {
|
||||||
InstanceDatabaseEntity(
|
InstanceDatabaseEntity(
|
||||||
uri = normalizeDomain(uri.orEmpty()),
|
uri = normalizeDomain(uri.orEmpty()),
|
||||||
title = title.orEmpty(),
|
title = title.orEmpty(),
|
||||||
maxStatusChars = max_toot_chars?.toInt() ?: DEFAULT_MAX_TOOT_CHARS,
|
maxStatusChars = max_toot_chars?.toInt() ?: DEFAULT_MAX_TOOT_CHARS,
|
||||||
|
pixelfed = false
|
||||||
)
|
)
|
||||||
} ?: throw IllegalArgumentException("Cannot store instance where both are null")
|
} ?: throw IllegalArgumentException("Cannot store instance where both are null")
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,10 @@ interface InstanceDao {
|
||||||
@Query("SELECT * FROM instances WHERE uri=:instanceUri")
|
@Query("SELECT * FROM instances WHERE uri=:instanceUri")
|
||||||
fun getInstance(instanceUri: String): InstanceDatabaseEntity
|
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
|
* Insert an instance, if it already exists return -1
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.pixeldroid.app.utils.db.dao
|
package org.pixeldroid.app.utils.db.dao
|
||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
|
@ -9,17 +10,21 @@ interface UserDao {
|
||||||
* Insert a user, if it already exists return -1
|
* Insert a user, if it already exists return -1
|
||||||
*/
|
*/
|
||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
fun insertUser(user: UserDatabaseEntity): Long
|
suspend fun insertUser(user: UserDatabaseEntity): Long
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
fun insertOrUpdate(user: UserDatabaseEntity) {
|
suspend fun insertOrUpdate(user: UserDatabaseEntity) {
|
||||||
if (insertUser(user) == -1L) {
|
if (insertUser(user) == -1L) {
|
||||||
updateUser(user)
|
updateUser(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
fun updateUser(user: UserDatabaseEntity)
|
suspend fun updateUser(user: UserDatabaseEntity)
|
||||||
|
|
||||||
|
@Query("UPDATE users SET username = :username, display_name = :displayName, avatar_static = :avatarStatic WHERE user_id = :id and instance_uri = :instanceUri")
|
||||||
|
suspend fun updateUserAccountDetails(username: String, displayName: String, avatarStatic: String, id: String, instanceUri: String)
|
||||||
|
|
||||||
|
|
||||||
@Query("UPDATE users SET accessToken = :accessToken, refreshToken = :refreshToken WHERE user_id = :id and instance_uri = :instanceUri")
|
@Query("UPDATE users SET accessToken = :accessToken, refreshToken = :refreshToken WHERE user_id = :id and instance_uri = :instanceUri")
|
||||||
fun updateAccessToken(accessToken: String, refreshToken: String, id: String, instanceUri: String)
|
fun updateAccessToken(accessToken: String, refreshToken: String, id: String, instanceUri: String)
|
||||||
|
@ -27,6 +32,9 @@ interface UserDao {
|
||||||
@Query("SELECT * FROM users")
|
@Query("SELECT * FROM users")
|
||||||
fun getAll(): List<UserDatabaseEntity>
|
fun getAll(): List<UserDatabaseEntity>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM users")
|
||||||
|
fun getAllFlow(): Flow<List<UserDatabaseEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM users WHERE isActive=1")
|
@Query("SELECT * FROM users WHERE isActive=1")
|
||||||
fun getActiveUser(): UserDatabaseEntity?
|
fun getActiveUser(): UserDatabaseEntity?
|
||||||
|
|
||||||
|
|
|
@ -4,20 +4,22 @@ import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
@Entity(tableName = "instances")
|
@Entity(tableName = "instances")
|
||||||
data class InstanceDatabaseEntity (
|
data class InstanceDatabaseEntity(
|
||||||
@PrimaryKey var uri: String,
|
@PrimaryKey var uri: String,
|
||||||
var title: String,
|
var title: String,
|
||||||
var maxStatusChars: Int = DEFAULT_MAX_TOOT_CHARS,
|
var maxStatusChars: Int = DEFAULT_MAX_TOOT_CHARS,
|
||||||
// Per-file file-size limit in KB. Defaults to 15000 (15MB). Default limit for Mastodon is 8MB
|
// Per-file file-size limit in KB. Defaults to 15000 (15MB). Default limit for Mastodon is 8MB
|
||||||
var maxPhotoSize: Int = DEFAULT_MAX_PHOTO_SIZE,
|
var maxPhotoSize: Int = DEFAULT_MAX_PHOTO_SIZE,
|
||||||
// Mastodon has different file limits for videos, default of 40MB
|
// Mastodon has different file limits for videos, default of 40MB
|
||||||
var maxVideoSize: Int = DEFAULT_MAX_VIDEO_SIZE,
|
var maxVideoSize: Int = DEFAULT_MAX_VIDEO_SIZE,
|
||||||
// How many photos can go into an album. Default limit for Pixelfed and Mastodon is 4
|
// How many photos can go into an album. Default limit for Pixelfed and Mastodon is 4
|
||||||
var albumLimit: Int = DEFAULT_ALBUM_LIMIT,
|
var albumLimit: Int = DEFAULT_ALBUM_LIMIT,
|
||||||
// Is video functionality enabled on this instance?
|
// Is video functionality enabled on this instance?
|
||||||
var videoEnabled: Boolean = DEFAULT_VIDEO_ENABLED,
|
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
|
// Default max number of chars for Mastodon: used when their is no other value supplied by
|
||||||
// either NodeInfo or the instance endpoint
|
// either NodeInfo or the instance endpoint
|
||||||
const val DEFAULT_MAX_TOOT_CHARS = 500
|
const val DEFAULT_MAX_TOOT_CHARS = 500
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.pixeldroid.app.utils.PixelDroidApplication
|
||||||
import org.pixeldroid.app.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import org.pixeldroid.app.utils.BaseFragment
|
import org.pixeldroid.app.utils.BaseFragment
|
||||||
import dagger.Component
|
import dagger.Component
|
||||||
|
import org.pixeldroid.app.MainActivityViewModel
|
||||||
import org.pixeldroid.app.postCreation.PostCreationViewModel
|
import org.pixeldroid.app.postCreation.PostCreationViewModel
|
||||||
import org.pixeldroid.app.profile.EditProfileViewModel
|
import org.pixeldroid.app.profile.EditProfileViewModel
|
||||||
import org.pixeldroid.app.stories.StoriesViewModel
|
import org.pixeldroid.app.stories.StoriesViewModel
|
||||||
|
@ -25,6 +26,7 @@ interface ApplicationComponent {
|
||||||
fun inject(postCreationViewModel: PostCreationViewModel)
|
fun inject(postCreationViewModel: PostCreationViewModel)
|
||||||
fun inject(editProfileViewModel: EditProfileViewModel)
|
fun inject(editProfileViewModel: EditProfileViewModel)
|
||||||
fun inject(storiesViewModel: StoriesViewModel)
|
fun inject(storiesViewModel: StoriesViewModel)
|
||||||
|
fun inject(mainActivityViewModel: MainActivityViewModel)
|
||||||
|
|
||||||
val context: Context?
|
val context: Context?
|
||||||
val application: Application?
|
val application: Application?
|
||||||
|
|
|
@ -7,6 +7,7 @@ import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import org.pixeldroid.app.utils.db.MIGRATION_3_4
|
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_4_5
|
||||||
|
import org.pixeldroid.app.utils.db.MIGRATION_5_6
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
|
@ -18,7 +19,7 @@ class DatabaseModule(private val context: Context) {
|
||||||
return Room.databaseBuilder(
|
return Room.databaseBuilder(
|
||||||
context,
|
context,
|
||||||
AppDatabase::class.java, "pixeldroid"
|
AppDatabase::class.java, "pixeldroid"
|
||||||
).addMigrations(MIGRATION_3_4).addMigrations(MIGRATION_4_5)
|
).addMigrations(MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6)
|
||||||
.allowMainThreadQueries().build()
|
.allowMainThreadQueries().build()
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue