More work on stories
This commit is contained in:
parent
888c6328d9
commit
f60889ea14
|
@ -4,6 +4,8 @@ import android.graphics.drawable.Drawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.widget.doAfterTextChanged
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
@ -13,7 +15,9 @@ import com.bumptech.glide.load.engine.GlideException
|
||||||
import com.bumptech.glide.request.RequestListener
|
import com.bumptech.glide.request.RequestListener
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.databinding.ActivityStoriesBinding
|
import org.pixeldroid.app.databinding.ActivityStoriesBinding
|
||||||
import org.pixeldroid.app.posts.setTextViewFromISO8601
|
import org.pixeldroid.app.posts.setTextViewFromISO8601
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithoutBarActivity
|
import org.pixeldroid.app.utils.BaseThemedWithoutBarActivity
|
||||||
|
@ -43,6 +47,23 @@ class StoriesActivity: BaseThemedWithoutBarActivity() {
|
||||||
|
|
||||||
uiState.age?.let { setTextViewFromISO8601(it, binding.storyAge, false) }
|
uiState.age?.let { setTextViewFromISO8601(it, binding.storyAge, false) }
|
||||||
|
|
||||||
|
if (uiState.errorMessage != null) {
|
||||||
|
binding.storyErrorText.text = uiState.errorMessage
|
||||||
|
binding.storyErrorCard.isVisible = true
|
||||||
|
} else binding.storyErrorCard.isVisible = false
|
||||||
|
|
||||||
|
if (uiState.snackBar != null) {
|
||||||
|
Snackbar.make(
|
||||||
|
binding.root, uiState.snackBar,
|
||||||
|
Snackbar.LENGTH_SHORT
|
||||||
|
).setAnchorView(binding.storyReplyField).show()
|
||||||
|
model.shownSnackbar()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uiState.username != null) {
|
||||||
|
binding.storyReplyField.hint = getString(R.string.replyToStory).format(uiState.username)
|
||||||
|
} else binding.storyReplyField.hint = null
|
||||||
|
|
||||||
uiState.profilePicture?.let {
|
uiState.profilePicture?.let {
|
||||||
Glide.with(binding.storyAuthorProfilePicture)
|
Glide.with(binding.storyAuthorProfilePicture)
|
||||||
.load(it)
|
.load(it)
|
||||||
|
@ -79,6 +100,22 @@ class StoriesActivity: BaseThemedWithoutBarActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
binding.storyReplyField.editText?.doAfterTextChanged {
|
||||||
|
it?.let { text ->
|
||||||
|
val string = text.toString()
|
||||||
|
if(string != model.uiState.value.reply) model.replyChanged(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.storyReplyField.setEndIconOnClickListener {
|
||||||
|
binding.storyReplyField.editText?.text?.let { text ->
|
||||||
|
model.sendReply(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.storyErrorCard.setOnClickListener{
|
||||||
|
model.dismissError()
|
||||||
|
}
|
||||||
|
|
||||||
model.count.observe(this) { state ->
|
model.count.observe(this) { state ->
|
||||||
// Render state in UI
|
// Render state in UI
|
||||||
|
@ -96,11 +133,7 @@ class StoriesActivity: BaseThemedWithoutBarActivity() {
|
||||||
binding.pause.setOnClickListener {
|
binding.pause.setOnClickListener {
|
||||||
//Set the button's appearance
|
//Set the button's appearance
|
||||||
it.isSelected = !it.isSelected
|
it.isSelected = !it.isSelected
|
||||||
if (it.isSelected) {
|
model.pause()
|
||||||
//Handle selected state change
|
|
||||||
} else {
|
|
||||||
//Handle de-select state change
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.storyImage.setOnClickListener {
|
binding.storyImage.setOnClickListener {
|
||||||
|
|
|
@ -2,16 +2,19 @@ package org.pixeldroid.app.stories
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.os.CountDownTimer
|
import android.os.CountDownTimer
|
||||||
|
import android.text.Editable
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.utils.PixelDroidApplication
|
import org.pixeldroid.app.utils.PixelDroidApplication
|
||||||
import org.pixeldroid.app.utils.api.objects.StoryCarousel
|
import org.pixeldroid.app.utils.api.objects.StoryCarousel
|
||||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
|
@ -27,6 +30,8 @@ data class StoriesUiState(
|
||||||
val durationList: List<Int> = emptyList(),
|
val durationList: List<Int> = emptyList(),
|
||||||
val paused: Boolean = false,
|
val paused: Boolean = false,
|
||||||
val errorMessage: String? = null,
|
val errorMessage: String? = null,
|
||||||
|
val snackBar: String? = null,
|
||||||
|
val reply: String = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
class StoriesViewModel(
|
class StoriesViewModel(
|
||||||
|
@ -134,6 +139,44 @@ class StoriesViewModel(
|
||||||
currentUiState.copy(paused = !currentUiState.paused)
|
currentUiState.copy(paused = !currentUiState.paused)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sendReply(text: Editable) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
|
//TODO don't just take the first here, choose from activity input somehow?
|
||||||
|
val id = carousel?.nodes?.firstOrNull()?.nodes?.getOrNull(uiState.value.currentImage)?.id
|
||||||
|
id?.let { api.storyComment(it, text.toString()) }
|
||||||
|
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(snackBar = "Sent reply")
|
||||||
|
}
|
||||||
|
} catch (exception: Exception){
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(errorMessage = "Something went wrong sending reply")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun replyChanged(text: String) {
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(reply = text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismissError() {
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(errorMessage = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shownSnackbar() {
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(snackBar = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StoriesViewModelFactory(val application: Application) : ViewModelProvider.Factory {
|
class StoriesViewModelFactory(val application: Application) : ViewModelProvider.Factory {
|
||||||
|
|
|
@ -236,10 +236,16 @@ interface PixelfedAPI {
|
||||||
): StoryCarousel
|
): StoryCarousel
|
||||||
|
|
||||||
@POST("/api/v1.1/stories/seen")
|
@POST("/api/v1.1/stories/seen")
|
||||||
suspend fun carouselSeen(
|
suspend fun storySeen(
|
||||||
@Query("id") id: String //TODO figure out if this is the id of post or of user?
|
@Query("id") id: String //TODO figure out if this is the id of post or of user?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@POST("/api/v1.1/stories/comment")
|
||||||
|
suspend fun storyComment(
|
||||||
|
@Query("sid") sid: String,
|
||||||
|
@Query("caption") caption: String
|
||||||
|
)
|
||||||
|
|
||||||
@POST("/api/v1.1/stories/self-expire/{id}")
|
@POST("/api/v1.1/stories/self-expire/{id}")
|
||||||
suspend fun deleteCarousel(
|
suspend fun deleteCarousel(
|
||||||
@Path("id") storyId: String
|
@Path("id") storyId: String
|
||||||
|
|
|
@ -6,16 +6,59 @@
|
||||||
android:background="@color/black"
|
android:background="@color/black"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/storyErrorCard"
|
||||||
|
style="?attr/materialCardViewElevatedStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="20dp"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:cardBackgroundColor="?attr/colorSecondaryContainer"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/storyAuthorProfilePicture"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:layout_marginEnd="8dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/storyErrorIcon"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/error"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:tint="?attr/colorOnSecondaryContainer" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/storyErrorText"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/storyErrorIcon"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/storyErrorIcon"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/storyErrorIcon"
|
||||||
|
tools:text="Something is wrong with stories" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/storyImage"
|
android:id="@+id/storyImage"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:contentDescription="@string/story_image"
|
android:contentDescription="@string/story_image"
|
||||||
|
tools:scaleType="centerCrop"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/progressBarStory"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/progressBarStory"
|
||||||
tools:srcCompat="@tools:sample/backgrounds/scenic[10]" />
|
tools:srcCompat="@tools:sample/backgrounds/scenic[10]" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
|
@ -72,8 +115,23 @@
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/storyAuthorProfilePicture"
|
app:layout_constraintBottom_toBottomOf="@+id/storyAuthorProfilePicture"
|
||||||
app:layout_constraintStart_toEndOf="@+id/storyAuthor"
|
app:layout_constraintStart_toEndOf="@+id/storyAuthor"
|
||||||
app:layout_constraintTop_toTopOf="@+id/storyAuthorProfilePicture"
|
app:layout_constraintTop_toTopOf="@+id/storyAuthorProfilePicture"
|
||||||
app:layout_constraintVertical_bias="0.517"
|
|
||||||
tools:text="48m" />
|
tools:text="48m" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/storyReplyField"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:hint="Reply to PixelDroid"
|
||||||
|
app:endIconMode="custom"
|
||||||
|
app:endIconDrawable="@drawable/ic_send_blue"
|
||||||
|
app:endIconContentDescription="TODO">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -331,4 +331,5 @@ For more info about Pixelfed, you can check here: https://pixelfed.org"</string>
|
||||||
<string name="switch_accounts">Switch accounts</string>
|
<string name="switch_accounts">Switch accounts</string>
|
||||||
<string name="summary_always_show_nsfw">NSFW/CW posts will not be blurred, and will be shown by default.</string>
|
<string name="summary_always_show_nsfw">NSFW/CW posts will not be blurred, and will be shown by default.</string>
|
||||||
<string name="story_image">Story image</string>
|
<string name="story_image">Story image</string>
|
||||||
|
<string name="replyToStory">Reply to %1$s</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue