2022-02-24 17:40:28 +00:00
|
|
|
package org.thoughtcrime.securesms.stories.viewer.page
|
|
|
|
|
2022-02-24 21:39:56 +00:00
|
|
|
import android.net.Uri
|
2022-02-24 17:40:28 +00:00
|
|
|
import androidx.lifecycle.LiveData
|
|
|
|
import androidx.lifecycle.ViewModel
|
|
|
|
import androidx.lifecycle.ViewModelProvider
|
|
|
|
import io.reactivex.rxjava3.core.Completable
|
|
|
|
import io.reactivex.rxjava3.core.Observable
|
|
|
|
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
|
|
|
import io.reactivex.rxjava3.kotlin.plusAssign
|
|
|
|
import io.reactivex.rxjava3.subjects.PublishSubject
|
|
|
|
import io.reactivex.rxjava3.subjects.Subject
|
|
|
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
|
|
|
import org.thoughtcrime.securesms.util.livedata.Store
|
|
|
|
import java.util.Optional
|
|
|
|
import kotlin.math.min
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Encapsulates presentation logic for displaying a collection of posts from a given user's story
|
|
|
|
*/
|
|
|
|
class StoryViewerPageViewModel(
|
|
|
|
private val recipientId: RecipientId,
|
|
|
|
private val repository: StoryViewerPageRepository
|
|
|
|
) : ViewModel() {
|
|
|
|
|
|
|
|
private val store = Store(StoryViewerPageState())
|
|
|
|
private val disposables = CompositeDisposable()
|
2022-02-28 16:37:37 +00:00
|
|
|
private val storyViewerDialogSubject: Subject<Optional<StoryViewerDialog>> = PublishSubject.create()
|
2022-02-24 17:40:28 +00:00
|
|
|
|
2022-02-24 21:39:56 +00:00
|
|
|
private val storyViewerPlaybackStore = Store(StoryViewerPlaybackState())
|
|
|
|
|
|
|
|
val storyViewerPlaybackState: LiveData<StoryViewerPlaybackState> = storyViewerPlaybackStore.stateLiveData
|
|
|
|
|
2022-02-28 16:37:37 +00:00
|
|
|
val groupDirectReplyObservable: Observable<Optional<StoryViewerDialog>> = storyViewerDialogSubject
|
2022-02-24 17:40:28 +00:00
|
|
|
|
|
|
|
val state: LiveData<StoryViewerPageState> = store.stateLiveData
|
|
|
|
|
|
|
|
fun getStateSnapshot(): StoryViewerPageState = store.state
|
|
|
|
|
|
|
|
init {
|
|
|
|
refresh()
|
|
|
|
}
|
|
|
|
|
2022-02-24 21:39:56 +00:00
|
|
|
fun setDuration(uri: Uri, duration: Long) {
|
|
|
|
store.update {
|
|
|
|
it.copy(durations = it.durations + (uri to duration))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-24 17:40:28 +00:00
|
|
|
fun refresh() {
|
|
|
|
disposables.clear()
|
|
|
|
disposables += repository.getStoryPostsFor(recipientId).subscribe { posts ->
|
|
|
|
store.update {
|
|
|
|
it.copy(
|
|
|
|
posts = posts,
|
|
|
|
replyState = resolveSwipeToReplyState(it)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-25 20:33:33 +00:00
|
|
|
fun kickPlaybackState() {
|
|
|
|
storyViewerPlaybackStore.update { it }
|
|
|
|
}
|
|
|
|
|
2022-02-24 17:40:28 +00:00
|
|
|
override fun onCleared() {
|
|
|
|
disposables.clear()
|
|
|
|
}
|
|
|
|
|
|
|
|
fun hideStory(): Completable {
|
|
|
|
return repository.hideStory(recipientId)
|
|
|
|
}
|
|
|
|
|
|
|
|
fun setSelectedPostIndex(index: Int) {
|
2022-02-25 14:29:48 +00:00
|
|
|
repository.markViewed(getPostAt(index))
|
2022-02-24 17:40:28 +00:00
|
|
|
store.update {
|
|
|
|
it.copy(
|
|
|
|
selectedPostIndex = index,
|
|
|
|
replyState = resolveSwipeToReplyState(it, index)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun getRestartIndex(): Int {
|
|
|
|
return min(store.state.selectedPostIndex, store.state.posts.lastIndex)
|
|
|
|
}
|
|
|
|
|
|
|
|
fun getSwipeToReplyState(): StoryViewerPageState.ReplyState {
|
|
|
|
return store.state.replyState
|
|
|
|
}
|
|
|
|
|
|
|
|
fun getPost(): StoryPost {
|
|
|
|
return store.state.posts[store.state.selectedPostIndex]
|
|
|
|
}
|
|
|
|
|
|
|
|
fun startDirectReply(storyId: Long, recipientId: RecipientId) {
|
|
|
|
storyViewerDialogSubject.onNext(Optional.of(StoryViewerDialog.GroupDirectReply(recipientId, storyId)))
|
|
|
|
}
|
|
|
|
|
2022-02-25 20:33:33 +00:00
|
|
|
fun setIsUserScrollingParent(isUserScrollingParent: Boolean) {
|
|
|
|
storyViewerPlaybackStore.update { it.copy(isUserScrollingParent = isUserScrollingParent) }
|
|
|
|
}
|
|
|
|
|
|
|
|
fun setIsSelectedPage(isSelectedPage: Boolean) {
|
|
|
|
storyViewerPlaybackStore.update { it.copy(isSelectedPage = isSelectedPage) }
|
|
|
|
}
|
|
|
|
|
2022-02-24 21:39:56 +00:00
|
|
|
fun setIsDisplayingContextMenu(isDisplayingContextMenu: Boolean) {
|
|
|
|
storyViewerPlaybackStore.update { it.copy(isDisplayingContextMenu = isDisplayingContextMenu) }
|
|
|
|
}
|
|
|
|
|
|
|
|
fun setIsDisplayingForwardDialog(isDisplayingForwardDialog: Boolean) {
|
|
|
|
storyViewerPlaybackStore.update { it.copy(isDisplayingForwardDialog = isDisplayingForwardDialog) }
|
2022-02-24 17:40:28 +00:00
|
|
|
}
|
|
|
|
|
2022-02-24 21:39:56 +00:00
|
|
|
fun setIsDisplayingDeleteDialog(isDisplayingDeleteDialog: Boolean) {
|
|
|
|
storyViewerPlaybackStore.update { it.copy(isDisplayingDeleteDialog = isDisplayingDeleteDialog) }
|
2022-02-24 17:40:28 +00:00
|
|
|
}
|
|
|
|
|
2022-02-24 21:39:56 +00:00
|
|
|
fun setIsDisplayingViewsAndRepliesDialog(isDisplayingViewsAndRepliesDialog: Boolean) {
|
|
|
|
storyViewerPlaybackStore.update { it.copy(isDisplayingViewsAndRepliesDialog = isDisplayingViewsAndRepliesDialog) }
|
2022-02-24 17:40:28 +00:00
|
|
|
}
|
|
|
|
|
2022-02-24 21:39:56 +00:00
|
|
|
fun setIsDisplayingDirectReplyDialog(isDisplayingDirectReplyDialog: Boolean) {
|
|
|
|
storyViewerPlaybackStore.update { it.copy(isDisplayingDirectReplyDialog = isDisplayingDirectReplyDialog) }
|
2022-02-24 17:40:28 +00:00
|
|
|
}
|
|
|
|
|
2022-02-24 21:39:56 +00:00
|
|
|
fun setIsDisplayingCaptionOverlay(isDisplayingCaptionOverlay: Boolean) {
|
|
|
|
storyViewerPlaybackStore.update { it.copy(isDisplayingCaptionOverlay = isDisplayingCaptionOverlay) }
|
2022-02-24 17:40:28 +00:00
|
|
|
}
|
|
|
|
|
2022-02-24 21:39:56 +00:00
|
|
|
fun setIsUserTouching(isUserTouching: Boolean) {
|
|
|
|
storyViewerPlaybackStore.update { it.copy(isUserTouching = isUserTouching) }
|
2022-02-24 17:40:28 +00:00
|
|
|
}
|
|
|
|
|
2022-02-24 21:39:56 +00:00
|
|
|
fun setAreSegmentsInitialized(areSegmentsInitialized: Boolean) {
|
|
|
|
storyViewerPlaybackStore.update { it.copy(areSegmentsInitialized = areSegmentsInitialized) }
|
2022-02-24 17:40:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun resolveSwipeToReplyState(state: StoryViewerPageState, index: Int = state.selectedPostIndex): StoryViewerPageState.ReplyState {
|
|
|
|
if (index !in state.posts.indices) {
|
|
|
|
return StoryViewerPageState.ReplyState.NONE
|
|
|
|
}
|
|
|
|
|
|
|
|
val post = state.posts[index]
|
|
|
|
val isFromSelf = post.sender.isSelf
|
|
|
|
val isToGroup = post.group != null
|
|
|
|
|
|
|
|
return StoryViewerPageState.ReplyState.resolve(isFromSelf, isToGroup)
|
|
|
|
}
|
|
|
|
|
|
|
|
fun getPostAt(index: Int): StoryPost {
|
|
|
|
return store.state.posts[index]
|
|
|
|
}
|
|
|
|
|
|
|
|
class Factory(private val recipientId: RecipientId, private val repository: StoryViewerPageRepository) : ViewModelProvider.Factory {
|
|
|
|
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
|
|
|
return modelClass.cast(StoryViewerPageViewModel(recipientId, repository)) as T
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|