v0.3.5: update MainViewModel.kt
This commit is contained in:
1 parent
af33f0d02b
commit
9785f55b81
1 file changed
+35
-13
@@ -10,6 +10,7 @@ import com.hotdeal.alarm.data.local.db.entity.SiteConfigEntity
|
||||
import com.hotdeal.alarm.data.local.preferences.AppSettings
|
||||
import com.hotdeal.alarm.domain.model.Keyword
|
||||
import com.hotdeal.alarm.domain.model.SiteType
|
||||
import com.hotdeal.alarm.domain.usecase.HotDealScraperManager
|
||||
import com.hotdeal.alarm.worker.WorkerScheduler
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.*
|
||||
@@ -21,6 +22,7 @@ class MainViewModel @Inject constructor(
|
||||
private val hotDealDao: HotDealDao,
|
||||
private val siteConfigDao: SiteConfigDao,
|
||||
private val keywordDao: KeywordDao,
|
||||
private val scraperManager: HotDealScraperManager,
|
||||
private val workerScheduler: WorkerScheduler,
|
||||
private val appSettings: AppSettings
|
||||
) : ViewModel() {
|
||||
@@ -28,6 +30,10 @@ class MainViewModel @Inject constructor(
|
||||
private val _uiState = MutableStateFlow<MainUiState>(MainUiState.Loading)
|
||||
val uiState: StateFlow<MainUiState> = _uiState.asStateFlow()
|
||||
|
||||
// 새로고침 진행 상태
|
||||
private val _isRefreshing = MutableStateFlow(false)
|
||||
val isRefreshing: StateFlow<Boolean> = _isRefreshing.asStateFlow()
|
||||
|
||||
// 폴링 주기
|
||||
val pollingInterval: StateFlow<Int> = appSettings.pollingInterval
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, 2)
|
||||
@@ -44,18 +50,19 @@ class MainViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun initializeApp() {
|
||||
// 1. 사이트 설정 초기화 후 DB Flow 구독 (별도 코루틴 — collect는 영원히 suspend)
|
||||
// 1. 사이트 설정 초기화 후 DB Flow 구독 (별도 코루틴 — collect는 지속 실행)
|
||||
viewModelScope.launch {
|
||||
initializeDefaultSiteConfigs()
|
||||
loadState() // combine().collect — 이 코루틴은 영원히 살아있음
|
||||
loadState()
|
||||
}
|
||||
|
||||
// 2. 백그라운드 폴링 등록 + 즉시 1회 스크래핑 (별도 코루틴)
|
||||
// 2. 백그라운드 폴링 등록 + 앱 진입 즉시 직접 1회 스크래핑 실행
|
||||
viewModelScope.launch {
|
||||
val savedInterval = appSettings.pollingInterval.first()
|
||||
workerScheduler.schedulePeriodicPolling(savedInterval.toLong())
|
||||
// 앱 시작 시 즉시 1회 실행하여 최신 데이터 갱신
|
||||
workerScheduler.executeOnce()
|
||||
|
||||
// 지연 없는 직접 스크래핑 실행 -> Room Flow를 통해 메인화면에 즉시 반영됨!
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +77,7 @@ class MainViewModel @Inject constructor(
|
||||
siteName = site.name,
|
||||
boardName = board.id,
|
||||
displayName = "${site.displayName} - ${board.displayName}",
|
||||
isEnabled = true // 기본값: 활성화 (설치 즉시 수집 작동)
|
||||
isEnabled = true // 기본 활성화 (설치 즉시 수집 가동)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -88,7 +95,6 @@ class MainViewModel @Inject constructor(
|
||||
keywordDao.observeAllKeywords(),
|
||||
appSettings.keywordOrder
|
||||
) { deals, configs, keywords, keywordOrder ->
|
||||
// 사용자가 설정한 키워드 순서에 따라 정렬
|
||||
val domainKeywords = keywords.map { it.toDomain() }
|
||||
val sortedKeywords = if (keywordOrder.isEmpty()) {
|
||||
domainKeywords
|
||||
@@ -112,12 +118,14 @@ class MainViewModel @Inject constructor(
|
||||
fun toggleSiteConfig(siteBoardKey: String, enabled: Boolean) {
|
||||
viewModelScope.launch {
|
||||
siteConfigDao.updateEnabled(siteBoardKey, enabled)
|
||||
// 사이트 활성화 변경 시 즉시 새 핫딜 갱신
|
||||
if (enabled) {
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun reorderSites(fromIndex: Int, toIndex: Int) {
|
||||
// siteOrder는 appSettings.siteOrder에서 stateIn으로 가져오므로
|
||||
// appSettings를 즉시 저장하고 Flow가 업데이트되도록 함
|
||||
val current = siteOrder.value.toMutableList()
|
||||
if (fromIndex in current.indices && toIndex in current.indices) {
|
||||
val item = current.removeAt(fromIndex)
|
||||
@@ -140,6 +148,8 @@ class MainViewModel @Inject constructor(
|
||||
val currentOrder = appSettings.keywordOrder.first().toMutableList()
|
||||
currentOrder.add(0, newId)
|
||||
appSettings.setKeywordOrder(currentOrder)
|
||||
// 키워드 등록 후 매칭 검사를 위해 즉시 갱신
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,19 +165,19 @@ class MainViewModel @Inject constructor(
|
||||
fun toggleKeyword(id: Long, enabled: Boolean) {
|
||||
viewModelScope.launch {
|
||||
keywordDao.updateEnabled(id, enabled)
|
||||
if (enabled) {
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun reorderKeywords(fromIndex: Int, toIndex: Int) {
|
||||
// uiState를 동기적으로 즉시 업데이트하여 빠른 드래그 시 stale 데이터 방지
|
||||
val state = _uiState.value as? MainUiState.Success ?: return
|
||||
val current = state.keywords.toMutableList()
|
||||
if (fromIndex in current.indices && toIndex in current.indices) {
|
||||
val item = current.removeAt(fromIndex)
|
||||
current.add(toIndex, item)
|
||||
// UI 즉시 갱신
|
||||
_uiState.value = state.copy(keywords = current)
|
||||
// DataStore 비동기 영속화
|
||||
viewModelScope.launch {
|
||||
appSettings.setKeywordOrder(current.map { it.id })
|
||||
}
|
||||
@@ -186,8 +196,20 @@ class MainViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 직접 즉시 스크래핑 실행 (WorkManager 큐 대기 지연 없이 UI 및 DB 즉시 갱신)
|
||||
*/
|
||||
fun refresh() {
|
||||
workerScheduler.executeOnce()
|
||||
viewModelScope.launch {
|
||||
_isRefreshing.value = true
|
||||
try {
|
||||
scraperManager.fetchAndProcessDeals()
|
||||
} catch (e: Exception) {
|
||||
// 에러 발생 시 무시 (UI는 DB Flow로 유지)
|
||||
} finally {
|
||||
_isRefreshing.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun startPolling(intervalMinutes: Long = WorkerScheduler.DEFAULT_INTERVAL_MINUTES) {
|
||||
|
||||
Reference in new issue
Block a user