More work on stories

This commit is contained in:
Matthieu 2023-05-09 22:48:06 +02:00
parent 888c6328d9
commit f60889ea14
5 changed files with 149 additions and 8 deletions

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

@ -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>

View File

@ -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>