Как в currentStoryId = достать StoryId из StoriesPreviewFilter. package ru.maxi.mobile.feature.stories.viewmodel

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import ru.maxi.mobile.common.utils.android.screenArgs
import ru.maxi.mobile.core.data.LoadableData
import ru.maxi.mobile.core.data.dataValue
import ru.maxi.mobile.core.data.emitError
import ru.maxi.mobile.core.data.emitLoading
import ru.maxi.mobile.core.data.emitSuccess
import ru.maxi.mobile.feature.stories.di.StoriesComponent
import ru.maxi.mobile.feature.stories.model.StoryId
import ru.maxi.mobile.feature.stories.usecase.StoriesPreviewUseCase
import ru.maxi.mobile.feature.stories.view.preview.entity.StoriesPreviewArgs
import ru.maxi.mobile.feature.stories.view.preview.entity.StoriesPreviewData
import ru.maxi.mobile.feature.stories.view.preview.entity.StoriesPreviewNavigationEvent

/**
 * [ViewModel] экрана просмотра Stories
 */
class StoriesPreviewViewModel private constructor(
    savedStateHandle: SavedStateHandle,
    private val storiesPreviewUseCase: StoriesPreviewUseCase,
) : ViewModel() {

    companion object {

        @Suppress("ktlint:standard:function-naming")
        fun Factory(storiesComponent: StoriesComponent) = viewModelFactory {
            initializer {
                val savedStateHandle = createSavedStateHandle()
                val storiesPreviewUseCase = storiesComponent.storiesPreviewUseCase()
                StoriesPreviewViewModel(savedStateHandle, storiesPreviewUseCase)
            }
        }
    }

    private val screenArgs by screenArgs(savedStateHandle, StoriesPreviewArgs)

    private val _previewDataStateFlow = MutableStateFlow<LoadableData<StoriesPreviewData>>(LoadableData.Loading())
    private val activeStoryIdFlow = MutableSharedFlow<StoryId?>(
        replay = 1,
        onBufferOverflow = BufferOverflow.DROP_OLDEST,
    )
    private val _navigationEvent = Channel<StoriesPreviewNavigationEvent>(Channel.BUFFERED)

    /** Подписка на состояние данных экрана */
    val previewDataStateFlow: Flow<LoadableData<StoriesPreviewData>> =
        _previewDataStateFlow.asStateFlow()
            .onStart { loadData() }
            .stateIn(viewModelScope, SharingStarted.Lazily, _previewDataStateFlow.value)

    /** Подписка на идентификатор текущей активной Story */
    val activeStoryId: Flow<StoryId?> = activeStoryIdFlow.asSharedFlow()

    /** События навигации */
    val navigationEvent = _navigationEvent.receiveAsFlow()

    /**
     * Загрузить данные Stories
     */
    fun loadData() {
        viewModelScope.launch {
            _previewDataStateFlow.emitLoading(saveValue = false)
            try {
                val storyIds = storiesPreviewUseCase.getStoryIds(screenArgs.filter)
                val previewData = StoriesPreviewData(storyIds, currentStoryId = screenArgs.filter)
                _previewDataStateFlow.emitSuccess(previewData)
            } catch (exception: Exception) {
                _previewDataStateFlow.emitError(exception)
            }
        }
    }

    /**
     * Сохранить идентификатор [storyId] текущей активной Story
     */
    fun setActiveStoryId(storyId: StoryId?) {
        activeStoryIdFlow.tryEmit(storyId)
    }

    /**
     * Обработать клик по кнопке закрытия просмотра Stories
     */
    fun onCloseButtonClick() {
        _navigationEvent.trySend(StoriesPreviewNavigationEvent.Exit)
    }

    /**
     * Обработать переход на Story в позиции [position]
     */
    fun onPageSelected(position: Int) {
        val data = _previewDataStateFlow.dataValue ?: return
        val selectedStoryId = data.storyIds.getOrNull(position) ?: return

        _previewDataStateFlow.emitSuccess(
            data.copy(currentStoryId = selectedStoryId),
        )
    }

    /**
     * Есть ли возможность перейти на предыдущую Story
     */
    fun canGoToPrevStory(): Boolean {
        val data = _previewDataStateFlow.dataValue ?: return false
        return data.storyIds.indexOf(data.currentStoryId) > 0
    }

    /**
     * Перейти к предыдущей Story или выйти с экрана
     */
    fun goToPrevStoryOrExit() {
        val data = _previewDataStateFlow.dataValue ?: return

        val currentStoryIndex = data.storyIds.indexOf(data.currentStoryId)
        if (currentStoryIndex != 0) {
            _previewDataStateFlow.emitSuccess(
                data.copy(
                    currentStoryId = data.storyIds[currentStoryIndex - 1],
                ),
            )
        } else {
            _navigationEvent.trySend(StoriesPreviewNavigationEvent.Exit)
        }
    }

    /**
     * Перейти к следующей Story или выйти с экрана
     */
    fun goToNextStoryOrExit() {
        val data = _previewDataStateFlow.dataValue ?: return

        val currentStoryIndex = data.storyIds.indexOf(data.currentStoryId)
        if (currentStoryIndex < (data.storyIds.size - 1)) {
            _previewDataStateFlow.emitSuccess(
                data.copy(
                    currentStoryId = data.storyIds[currentStoryIndex + 1],
                ),
            )
        } else {
            _navigationEvent.trySend(StoriesPreviewNavigationEvent.Exit)
        }
    }
}
package ru.maxi.mobile.feature.stories.view.preview.entity

import androidx.core.os.bundleOf
import androidx.lifecycle.SavedStateHandle
import kotlinx.parcelize.Parcelize
import ru.maxi.mobile.core.navigation.FragmentScreenArgs
import ru.maxi.mobile.feature.stories.model.StoriesPreviewFilter

/**
 * Аргументы экрана просмотра Stories
 */
@Parcelize
class StoriesPreviewArgs(
    val filter: StoriesPreviewFilter,
) : FragmentScreenArgs {

    override fun toBundle() = bundleOf(
        STORIES_FILTER_KEY to filter,
    )

    companion object : FragmentScreenArgs.Factory<StoriesPreviewArgs> {

        private const val STORIES_FILTER_KEY = "stories_filter"

        override fun fromSavedStateHandle(savedStateHandle: SavedStateHandle) = StoriesPreviewArgs(
            filter = requireNotNull(savedStateHandle[STORIES_FILTER_KEY]),
        )
    }
}
package ru.maxi.mobile.feature.stories.model

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

/**
 * Состояния для фильтра просмотра Stories
 */

sealed interface StoriesPreviewFilter : Parcelable {
    @Parcelize
    data class All(val storyId: StoryId) : StoriesPreviewFilter

    @Parcelize
    data class NotViewed(val storyId: StoryId) : StoriesPreviewFilter

    @Parcelize
    data class StoriesList(val storyIds: List<StoryId>) : StoriesPreviewFilter {

        // TODO удалить после удаления MainNavigationPresenterImpl
        companion object {
            @JvmStatic
            fun createFromLong(storyIds: List<Long>): StoriesList {
                return StoriesList(storyIds.map { StoryId(it) })
            }
        }
    }
}