Skip to content

Commit 105c938

Browse files
committed
happy new year
1 parent 9e42039 commit 105c938

13 files changed

Lines changed: 134 additions & 249 deletions

File tree

.kotlin/sessions/kotlin-compiler-204136539978830062.salive

Whitespace-only changes.

app/src/main/java/com/songlib/data/sources/local/AppDatabase.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import com.songlib.data.sources.local.daos.*
1010
version = 2, exportSchema = false
1111
)
1212
abstract class AppDatabase : RoomDatabase() {
13-
abstract fun bookDao(): BookDao
14-
abstract fun historyDao(): HistoryDao
15-
abstract fun listingDao(): ListingDao
16-
abstract fun searchDao(): SearchDao
17-
abstract fun songDao(): SongDao
13+
abstract fun booksDao(): BookDao
14+
abstract fun historiesDao(): HistoryDao
15+
abstract fun listingsDao(): ListingDao
16+
abstract fun searchesDao(): SearchDao
17+
abstract fun songsDao(): SongDao
1818

1919
companion object {
2020
@Volatile

app/src/main/java/com/songlib/data/sources/local/daos/BookDao.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ interface BookDao {
1212
@Insert(onConflict = OnConflictStrategy.REPLACE)
1313
suspend fun insert(book: Book)
1414

15+
@Insert(onConflict = OnConflictStrategy.Companion.REPLACE)
16+
suspend fun insertAll(books: List<Book>)
17+
1518
@Update()
1619
fun update(book: Book)
1720

app/src/main/java/com/songlib/data/sources/local/daos/SongDao.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ interface SongDao {
1515
@Insert(onConflict = OnConflictStrategy.REPLACE)
1616
suspend fun insert(song: Song)
1717

18+
@Insert(onConflict = OnConflictStrategy.Companion.REPLACE)
19+
suspend fun insertAll(songs: List<Song>)
20+
1821
@Update()
1922
fun update(song: Song)
2023

app/src/main/java/com/songlib/domain/repos/SongBookRepo.kt

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,144 @@
11
package com.songlib.domain.repos
22

33
import android.content.*
4+
import android.util.Log
45
import com.songlib.data.models.*
56
import com.songlib.data.sources.local.*
67
import com.songlib.data.sources.local.daos.*
78
import com.songlib.data.sources.remote.ApiService
89
import kotlinx.coroutines.*
910
import kotlinx.coroutines.flow.*
1011
import javax.inject.*
12+
import kotlin.collections.isNotEmpty
13+
import kotlin.collections.map
1114

1215
@Singleton
1316
class SongBookRepo @Inject constructor(
1417
context: Context,
1518
private val apiService: ApiService,
1619
) {
17-
private var bookDao: BookDao?
18-
private var songDao: SongDao?
20+
private var booksDao: BookDao?
21+
private var songsDao: SongDao?
1922

2023
init {
2124
val db = AppDatabase.getDatabase(context)
22-
bookDao = db?.bookDao()
23-
songDao = db?.songDao()
25+
booksDao = db?.booksDao()
26+
songsDao = db?.songsDao()
2427
}
2528

2629
fun fetchRemoteBooks(): Flow<List<Book>> = flow {
27-
val books = apiService.getBooks()
28-
emit(books)
30+
try {
31+
val books = apiService.getBooks()
32+
if (books.isNotEmpty()) {
33+
emit(books)
34+
} else {
35+
Log.d("TAG", "⚠️ No books fetched from remote")
36+
emit(emptyList())
37+
}
38+
} catch (e: Exception) {
39+
Log.e("TAG", "❌ Error fetching books: ${e.message}", e)
40+
throw e
41+
}
2942
}
3043

31-
fun fetchRemoteSongs(books: String): Flow<List<Song>> = flow {
32-
val songs = apiService.getSongs(books)
33-
emit(songs)
44+
suspend fun fetchAndSaveSongs(bookIds: List<Int>) {
45+
try {
46+
val booksParam = bookIds.joinToString(",")
47+
val songs = apiService.getSongs(booksParam)
48+
Log.d("TAG", "${songs.size} songs fetched for books: $booksParam")
49+
50+
if (songs.isNotEmpty()) {
51+
saveSongs(songs)
52+
} else {
53+
Log.d("TAG", "⚠️ No songs fetched from remote")
54+
}
55+
} catch (e: Exception) {
56+
Log.e("TAG", "❌ Error fetching songs: ${e.message}", e)
57+
}
3458
}
3559

3660
suspend fun saveBook(book: Book) {
3761
withContext(Dispatchers.IO) {
38-
bookDao?.insert(book)
62+
booksDao?.insert(book)
3963
}
4064
}
4165

42-
suspend fun saveSong(song: Song) {
43-
withContext(Dispatchers.IO) {
44-
songDao?.insert(song)
66+
suspend fun saveBooks(books: List<Book>) {
67+
if (books.isEmpty()) {
68+
Log.d("TAG", "⚠️ No books to save")
69+
return
70+
}
71+
72+
try {
73+
booksDao?.insertAll(books)
74+
Log.d("TAG", "${books.size} books saved successfully")
75+
} catch (e: Exception) {
76+
Log.e("TAG", "❌ Error saving books: ${e.message}", e)
77+
throw e
78+
}
79+
}
80+
81+
suspend fun saveSongs(songs: List<Song>) {
82+
if (songs.isEmpty()) {
83+
Log.d("TAG", "⚠️ No songs to save")
84+
return
85+
}
86+
87+
try {
88+
songsDao?.insertAll(songs)
89+
Log.d("TAG", "${songs.size} songs saved successfully")
90+
} catch (e: Exception) {
91+
Log.e("TAG", "❌ Error saving songs: ${e.message}", e)
92+
throw e
4593
}
4694
}
4795

4896
suspend fun fetchLocalBooks(): List<Book> {
4997
var allBooks: List<Book>
5098
withContext(Dispatchers.IO) {
51-
allBooks = bookDao?.getAll() ?: emptyList()
99+
allBooks = booksDao?.getAll() ?: emptyList()
52100
}
53101
return allBooks
54102
}
55103

56104
suspend fun fetchLocalSongs(): List<Song> {
57105
var allSongs: List<Song>
58106
withContext(Dispatchers.IO) {
59-
allSongs = songDao?.getAll() ?: emptyList()
107+
allSongs = songsDao?.getAll() ?: emptyList()
60108
}
61109
return allSongs
62110
}
63111

64112
suspend fun fetchSong(songId: Int): Song {
65113
var song: Song
66114
withContext(Dispatchers.IO) {
67-
song = songDao?.getSong(songId)!!
115+
song = songsDao?.getSong(songId)!!
68116
}
69117
return song
70118
}
71119

72120
suspend fun updateSong(song: Song) {
73121
withContext(Dispatchers.IO) {
74-
songDao?.update(song)
122+
songsDao?.update(song)
75123
}
76124
}
77125

78126
suspend fun deleteById(bookId: Int) {
79127
withContext(Dispatchers.IO) {
80-
bookDao?.deleteById(bookId)
128+
booksDao?.deleteById(bookId)
81129
}
82130
}
83131

84132
suspend fun deleteAllData() {
85133
withContext(Dispatchers.IO) {
86-
bookDao?.deleteAll()
87-
songDao?.deleteAll()
134+
booksDao?.deleteAll()
135+
songsDao?.deleteAll()
88136
}
89137
}
90138

91139
suspend fun deleteByBookId(bookId: Int) {
92140
withContext(Dispatchers.IO) {
93-
songDao?.deleteById(bookId)
141+
songsDao?.deleteById(bookId)
94142
}
95143
}
96144

app/src/main/java/com/songlib/presentation/navigation/AppNavHost.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import com.songlib.presentation.listing.ListingViewModel
1414
import com.songlib.presentation.listing.view.ListingScreen
1515
import com.songlib.presentation.presenter.PresenterViewModel
1616
import com.songlib.presentation.presenter.view.PresenterScreen
17-
import com.songlib.presentation.selection.step1.Step1ViewModel
18-
import com.songlib.presentation.selection.step1.view.Step1Screen
17+
import com.songlib.presentation.selection.SelectionViewModel
18+
import com.songlib.presentation.selection.view.Step1Screen
1919
import com.songlib.presentation.selection.step2.Step2ViewModel
2020
import com.songlib.presentation.selection.step2.view.Step2Screen
2121
import com.songlib.presentation.settings.SettingsViewModel
@@ -40,7 +40,7 @@ fun AppNavHost(
4040
}
4141

4242
composable(Routes.STEP_1) {
43-
val viewModel: Step1ViewModel = hiltViewModel()
43+
val viewModel: SelectionViewModel = hiltViewModel()
4444
Step1Screen(
4545
navController = navController,
4646
viewModel = viewModel,

app/src/main/java/com/songlib/presentation/selection/step1/Step1ViewModel.kt renamed to app/src/main/java/com/songlib/presentation/selection/SelectionViewModel.kt

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
1-
package com.songlib.presentation.selection.step1
1+
package com.songlib.presentation.selection
22

33
import android.util.Log
4-
import androidx.lifecycle.ViewModel
5-
import androidx.lifecycle.viewModelScope
4+
import androidx.lifecycle.*
65
import com.songlib.data.models.Book
7-
import com.songlib.domain.entity.Selectable
8-
import com.songlib.domain.entity.UiState
9-
import com.songlib.domain.repos.PrefsRepo
10-
import com.songlib.domain.repos.SongBookRepo
11-
import com.songlib.domain.repos.SubsRepo
6+
import com.songlib.domain.entity.*
7+
import com.songlib.domain.repos.*
128
import dagger.hilt.android.lifecycle.HiltViewModel
139
import kotlinx.coroutines.Dispatchers
14-
import kotlinx.coroutines.flow.MutableStateFlow
15-
import kotlinx.coroutines.flow.StateFlow
16-
import kotlinx.coroutines.flow.asStateFlow
17-
import kotlinx.coroutines.flow.catch
18-
import kotlinx.coroutines.launch
10+
import kotlinx.coroutines.flow.*
11+
import kotlinx.coroutines.*
1912
import retrofit2.HttpException
2013
import javax.inject.Inject
2114

2215
@HiltViewModel
23-
class Step1ViewModel @Inject constructor(
16+
class SelectionViewModel @Inject constructor(
2417
private val prefsRepo: PrefsRepo,
2518
private val subsRepo: SubsRepo,
2619
private val songbkRepo: SongBookRepo,
@@ -51,10 +44,10 @@ class Step1ViewModel @Inject constructor(
5144

5245
viewModelScope.launch {
5346
songbkRepo.fetchRemoteBooks().catch { exception ->
54-
Log.d("TAG", "fetching books")
47+
Log.d("TAG", "fetching books error")
5548
val errorMessage = when (exception) {
56-
is HttpException -> "We're sorry. We can't access the songbooks at the moment due to a HTTP Error: ${exception.code()} error on our server. Kindly try again a little later."
57-
else -> "We're sorry. We can't access the songbooks at the moment due to a ${exception.message} error on our server. Kindly try again a little later."
49+
is HttpException -> "Oops! We can't access the songbooks at the moment due to a HTTP Error: ${exception.code()} error on our server. Kindly try again a little later."
50+
else -> "Oops! We can't access the songbooks at the moment due to a ${exception.message} error on our server. Kindly try again a little later."
5851
}
5952
Log.d("TAG", errorMessage)
6053
_uiState.tryEmit(UiState.Error(errorMessage))
@@ -89,9 +82,9 @@ class Step1ViewModel @Inject constructor(
8982

9083
private fun saveBooks(books: List<Book>) {
9184
_uiState.tryEmit(UiState.Saving)
92-
Log.d("TAG", "saving books")
85+
Log.d("TAG", "saving ${books.size} books")
9386

94-
viewModelScope.launch(Dispatchers.IO) {
87+
viewModelScope.launch {
9588
try {
9689
if (prefsRepo.selectAfresh) {
9790
val existingIds = getSelectedIds()
@@ -100,24 +93,40 @@ class Step1ViewModel @Inject constructor(
10093
val booksToInsert = books.filter { it.bookId !in existingIds }
10194
val idsToDelete = existingIds - newIds
10295

103-
booksToInsert.forEach { songbkRepo.saveBook(it) }
10496
idsToDelete.forEach { songbkRepo.deleteById(it) }
97+
booksToInsert.forEach { songbkRepo.saveBook(it) }
10598

10699
prefsRepo.selectedBooks = newIds.joinToString(",")
107100
} else {
108-
books.forEach { songbkRepo.saveBook(it) }
101+
songbkRepo.saveBooks(books)
109102
prefsRepo.selectedBooks = books.joinToString(",") { it.bookId.toString() }
110103
}
111104

112105
prefsRepo.isDataSelected = true
113-
_uiState.emit(UiState.Saved)
106+
fetchRemoteSongs(books.map { it.bookId })
114107
} catch (e: Exception) {
115108
Log.e("SaveBooks", "Failed to save books", e)
116109
_uiState.emit(UiState.Error("Failed to save books: ${e.message}"))
117110
}
118111
}
119112
}
120113

114+
private suspend fun fetchRemoteSongs(bookIds: List<Int>) {
115+
try {
116+
Log.d("TAG", "Starting song fetch for ${bookIds.size} books")
117+
withContext(Dispatchers.IO) {
118+
songbkRepo.fetchAndSaveSongs(bookIds)
119+
}
120+
121+
Log.d("TAG", "Song fetch and save completed")
122+
_uiState.tryEmit(UiState.Saved)
123+
124+
} catch (e: Exception) {
125+
Log.e("TAG", "Song fetch failed: ${e.message}", e)
126+
_uiState.tryEmit(UiState.Saved)
127+
}
128+
}
129+
121130
fun toggleBookSelection(book: Selectable<Book>) {
122131
val currentSelectedCount = _books.value.count { it.isSelected }
123132
val isCurrentlySelected = book.isSelected
@@ -129,7 +138,7 @@ class Step1ViewModel @Inject constructor(
129138
return
130139
}
131140

132-
if (currentSelectedCount >= 3) {
141+
if (currentSelectedCount >= 4) {
133142
_pendingBookSelection.value = book
134143
_showUpgradeDialog.value = true
135144
} else {
@@ -142,12 +151,10 @@ class Step1ViewModel @Inject constructor(
142151
fun onUpgradeProceed() {
143152
_showUpgradeDialog.value = false
144153
_pendingBookSelection.value = null
145-
// refreshSubscription(on)
146154
}
147155

148156
fun onUpgradeDismis() {
149157
_showUpgradeDialog.value = false
150158
_pendingBookSelection.value = null
151159
}
152-
153160
}

app/src/main/java/com/songlib/presentation/selection/step1/components/Step1Fab.kt renamed to app/src/main/java/com/songlib/presentation/selection/components/SelectionFab.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
package com.songlib.presentation.selection.step1.components
1+
package com.songlib.presentation.selection.components
22

33
import androidx.compose.material.icons.Icons
44
import androidx.compose.material.icons.filled.*
55
import androidx.compose.material3.*
66
import androidx.compose.runtime.*
77
import com.songlib.data.models.Book
88
import com.songlib.presentation.components.general.*
9-
import com.songlib.presentation.selection.step1.Step1ViewModel
9+
import com.songlib.presentation.selection.SelectionViewModel
1010

1111
@Composable
1212
fun Step1Fab(
13-
viewModel: Step1ViewModel,
13+
viewModel: SelectionViewModel,
1414
onSaveConfirmed: (List<Book>) -> Unit
1515
) {
1616
var showConfirmDialog by remember { mutableStateOf(false) }

0 commit comments

Comments
 (0)