Skip to content

Commit

Permalink
Merge pull request #95 from snuhcs-course/feat/android-emoji-sort
Browse files Browse the repository at this point in the history
feat: add sorting mode in emoji tab
  • Loading branch information
yangchanhk98 authored Dec 4, 2023
2 parents 8ef4b62 + 49a1fc9 commit 361f1ba
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 50 deletions.
17 changes: 0 additions & 17 deletions android/.idea/deploymentTargetDropDown.xml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum class EmojiFetchType {

class EmojiPagingSource @Inject constructor(
private val api: EmojiApi,
private val sortByDate : Int,
private val type: EmojiFetchType
): PagingSource<Int, EmojiDto>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, EmojiDto> {
Expand All @@ -20,13 +21,13 @@ class EmojiPagingSource @Inject constructor(
return try {
val response: List<EmojiDto>? = when (type) {
EmojiFetchType.GENERAL -> {
api.fetchEmojiList(1, cursor, count).body()
api.fetchEmojiList(sortByDate, cursor, count).body()
}
EmojiFetchType.MY_CREATED -> {
api.fetchMyCreatedEmojiList(1, cursor, count).body()
api.fetchMyCreatedEmojiList(sortByDate, cursor, count).body()
}
EmojiFetchType.MY_SAVED -> {
api.fetchMySavedEmojiList(1, cursor, count).body()
api.fetchMySavedEmojiList(sortByDate, cursor, count).body()
}
}
val data = response ?: listOf()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import javax.inject.Inject
import javax.inject.Singleton

interface EmojiRepository {
suspend fun fetchEmojiList(): Flow<PagingData<EmojiDto>>
suspend fun fetchEmojiList(sortByDate: Int): Flow<PagingData<EmojiDto>>
suspend fun fetchMyCreatedEmojiList(): Flow<PagingData<EmojiDto>>
suspend fun fetchMySavedEmojiList(): Flow<PagingData<EmojiDto>>
suspend fun getEmojiWithId(id: String): EmojiDto?
Expand All @@ -43,24 +43,24 @@ class EmojiRepositoryImpl @Inject constructor(
private val emojiApi: EmojiApi,
@ApplicationContext private val context: Context
): EmojiRepository {
override suspend fun fetchEmojiList(): Flow<PagingData<EmojiDto>> {
override suspend fun fetchEmojiList(sortByDate: Int): Flow<PagingData<EmojiDto>> {
return Pager(
config = PagingConfig(pageSize = 10, initialLoadSize = 10, enablePlaceholders = false),
pagingSourceFactory = { EmojiPagingSource(emojiApi, EmojiFetchType.GENERAL) }
pagingSourceFactory = { EmojiPagingSource(emojiApi, sortByDate, EmojiFetchType.GENERAL) }
).flow
}

override suspend fun fetchMyCreatedEmojiList(): Flow<PagingData<EmojiDto>> {
return Pager(
config = PagingConfig(pageSize = 10, initialLoadSize = 10, enablePlaceholders = false),
pagingSourceFactory = { EmojiPagingSource(emojiApi, EmojiFetchType.MY_CREATED) }
pagingSourceFactory = { EmojiPagingSource(emojiApi, 1, EmojiFetchType.MY_CREATED) }
).flow
}

override suspend fun fetchMySavedEmojiList(): Flow<PagingData<EmojiDto>> {
return Pager(
config = PagingConfig(pageSize = 10, initialLoadSize = 10, enablePlaceholders = false),
pagingSourceFactory = { EmojiPagingSource(emojiApi, EmojiFetchType.MY_SAVED) }
pagingSourceFactory = { EmojiPagingSource(emojiApi, 1, EmojiFetchType.MY_SAVED) }
).flow
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface EmojiUseCase {
suspend fun updateEmojiList(data: PagingData<Emoji>)
suspend fun updateMyCreatedEmojiList(data: PagingData<Emoji>)
suspend fun updateMySavedEmojiList(data: PagingData<Emoji>)
suspend fun fetchEmojiList(): Flow<PagingData<Emoji>>
suspend fun fetchEmojiList(sortByDate: Int): Flow<PagingData<Emoji>>
suspend fun fetchMyCreatedEmojiList(): Flow<PagingData<Emoji>>
suspend fun fetchMySavedEmojiList(): Flow<PagingData<Emoji>>
suspend fun createEmoji(videoUri: Uri, topK: Int): List<CreatedEmoji>
Expand Down Expand Up @@ -65,8 +65,8 @@ class EmojiUseCaseImpl @Inject constructor(
_mySavedEmojiList.emit(data)
}

override suspend fun fetchEmojiList(): Flow<PagingData<Emoji>> {
return emojiRepository.fetchEmojiList().map { it.map { dto -> Emoji(dto) } }
override suspend fun fetchEmojiList(sortByDate: Int): Flow<PagingData<Emoji>> {
return emojiRepository.fetchEmojiList(sortByDate).map { it.map { dto -> Emoji(dto) } }
}

override suspend fun fetchMyCreatedEmojiList(): Flow<PagingData<Emoji>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.goliath.emojihub.viewmodels
import android.net.Uri
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
Expand Down Expand Up @@ -36,6 +37,8 @@ class EmojiViewModel @Inject constructor(
private val _unSaveEmojiState = MutableStateFlow<Result<Unit>?>(null)
val unSaveEmojiState = _unSaveEmojiState.asStateFlow()

var sortByDate by mutableIntStateOf(0)

val emojiList = emojiUseCase.emojiList
val myCreatedEmojiList = emojiUseCase.myCreatedEmojiList
val mySavedEmojiList = emojiUseCase.mySavedEmojiList
Expand All @@ -46,14 +49,19 @@ class EmojiViewModel @Inject constructor(

fun fetchEmojiList() {
viewModelScope.launch {
emojiUseCase.fetchEmojiList()
emojiUseCase.fetchEmojiList(sortByDate)
.cachedIn(viewModelScope)
.collect {
emojiUseCase.updateEmojiList(it)
}
}
}

fun toggleSortingMode() {
sortByDate = sortByDate xor 1
fetchEmojiList()
}

fun fetchMyCreatedEmojiList() {
viewModelScope.launch {
emojiUseCase.fetchMyCreatedEmojiList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.goliath.emojihub.views.components.EmojiCell
Expand All @@ -33,6 +38,8 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.paging.compose.collectAsLazyPagingItems
import com.goliath.emojihub.LocalNavController
import com.goliath.emojihub.NavigationDestination
import com.goliath.emojihub.ui.theme.Color.Black
import com.goliath.emojihub.ui.theme.Color.LightGray
import com.goliath.emojihub.ui.theme.Color.White
import com.goliath.emojihub.viewmodels.EmojiViewModel
import com.goliath.emojihub.views.components.EmojiCellDisplay
Expand Down Expand Up @@ -92,7 +99,23 @@ fun EmojiPage(
Column(Modifier.padding(horizontal = 16.dp)) {
Spacer(Modifier.height(28.dp))

Text("Trending 🔥", fontSize = 20.sp, fontWeight = FontWeight.Bold)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text("Trending 🔥", fontSize = 20.sp, fontWeight = FontWeight.Bold)

Button(
onClick = { viewModel.toggleSortingMode() },
colors = ButtonDefaults.buttonColors(
backgroundColor = if (viewModel.sortByDate == 0) Black else LightGray,
contentColor = White
)
) {
Text(text = if (viewModel.sortByDate == 1) "Sort by Date" else "Sort by Save Count", fontSize = 12.sp)
}
}

LazyVerticalGrid(
columns = GridCells.Fixed(2),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class EmojiRepositoryImplTest {
emojiApi.fetchEmojiList(any(), any(), any())
} returns Response.success(sampleEmojiDtoList)
// when
val fetchedEmojiPagingDataFlow = runBlocking { emojiRepositoryImpl.fetchEmojiList() }
val fetchedEmojiPagingDataFlow = runBlocking { emojiRepositoryImpl.fetchEmojiList(1) }
val fetchedEmojiDtoList = runBlocking { fetchedEmojiPagingDataFlow.asSnapshot() }
// then
coVerify(exactly = 2) { emojiApi.fetchEmojiList(any(), any(), any()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.goliath.emojihub.usecases
import androidx.paging.PagingData
import androidx.paging.map
import androidx.paging.testing.asSnapshot
import com.goliath.emojihub.createDeterministicDummyEmojiDtoList
import com.goliath.emojihub.createDeterministicTrendingEmojiDtoList
import com.goliath.emojihub.data_sources.ApiErrorController
import com.goliath.emojihub.mockLogClass
import com.goliath.emojihub.models.CreatedEmoji
Expand Down Expand Up @@ -72,15 +72,15 @@ class EmojiUseCaseImplTest {
@Test
fun fetchEmojiList_returnsFlowOfEmojiPagingData() {
// given
val sampleEmojiPagingDataFlow = createDeterministicDummyEmojiDtoList(5)
val sampleEmojiPagingDataFlow = createDeterministicTrendingEmojiDtoList(5)
val sampleAnswer = sampleEmojiPagingDataFlow.map { it.map { dto -> Emoji(dto) } }
coEvery {
emojiRepository.fetchEmojiList()
emojiRepository.fetchEmojiList(1)
} returns sampleEmojiPagingDataFlow
// when
val fetchedEmojiPagingDataFlow = runBlocking { emojiUseCaseImpl.fetchEmojiList() }
val fetchedEmojiPagingDataFlow = runBlocking { emojiUseCaseImpl.fetchEmojiList(1) }
// then
coVerify(exactly = 1) { emojiRepository.fetchEmojiList() }
coVerify(exactly = 1) { emojiRepository.fetchEmojiList(1) }
runBlocking {
val sampleAnswerAsSnapshot = sampleAnswer.asSnapshot()
val fetchedEmojiPagingDataFlowAsSnapshot = fetchedEmojiPagingDataFlow.asSnapshot()
Expand All @@ -96,7 +96,7 @@ class EmojiUseCaseImplTest {
@Test
fun fetchMyCreatedEmojiList_returnsFlowOfEmojiPagingData() {
// given
val sampleEmojiPagingDataFlow = createDeterministicDummyEmojiDtoList(5)
val sampleEmojiPagingDataFlow = createDeterministicTrendingEmojiDtoList(5)
val sampleAnswer = sampleEmojiPagingDataFlow.map { it.map { dto -> Emoji(dto) } }
coEvery {
emojiRepository.fetchMyCreatedEmojiList()
Expand All @@ -120,7 +120,7 @@ class EmojiUseCaseImplTest {
@Test
fun fetchMySavedEmojiList_returnsFlowOfEmojiPagingData() {
// given
val sampleEmojiPagingDataFlow = createDeterministicDummyEmojiDtoList(5)
val sampleEmojiPagingDataFlow = createDeterministicTrendingEmojiDtoList(5)
val sampleAnswer = sampleEmojiPagingDataFlow.map { it.map { dto -> Emoji(dto) } }
coEvery {
emojiRepository.fetchMySavedEmojiList()
Expand Down
10 changes: 5 additions & 5 deletions android/app/src/test/java/com/goliath/emojihub/utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ fun mockLogClass() {
val dummyUsernames = listOf("channn", "doggydog", "meow_0w0", "mpunchmm", "kick_back")
val dummyUnicodes = listOf("U+1F44D", "U+1F600", "U+1F970", "U+1F60E", "U+1F621", "U+1F63A", "U+1F496", "U+1F415")
const val dummyMaxSavedCounts = 2000
fun createDeterministicDummyEmojiDtoList(listSize : Int): Flow<PagingData<EmojiDto>> {
fun createDeterministicTrendingEmojiDtoList(listSize : Int): Flow<PagingData<EmojiDto>> {
val dummyEmojiList = mutableListOf<EmojiDto>()
for (i in 0 until listSize) {
dummyEmojiList.add(
EmojiDto(
createdBy = dummyUsernames[i % dummyUsernames.size],
createdAt = "2023.09.16",
savedCount = dummyMaxSavedCounts % (i + 1),
createdAt = "2023."+i%12+".16",
savedCount = dummyMaxSavedCounts - i*10,
videoLink = "",
thumbnailLink = "",
unicode = dummyUnicodes[i % dummyUnicodes.size],
Expand All @@ -44,8 +44,8 @@ fun createDeterministicDummyEmojiDtoList(listSize : Int): Flow<PagingData<EmojiD
}
return flowOf(PagingData.from(dummyEmojiList))
}
fun createDeterministicDummyEmojiList(listSize: Int): Flow<PagingData<Emoji>> {
return createDeterministicDummyEmojiDtoList(listSize).map { it.map { dto -> Emoji(dto) } }
fun createDeterministicTrendingEmojiList(listSize: Int): Flow<PagingData<Emoji>> {
return createDeterministicTrendingEmojiDtoList(listSize).map { it.map { dto -> Emoji(dto) } }
}

// POST TESTING UTILS
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.goliath.emojihub.viewmodels

import android.net.Uri
import com.goliath.emojihub.createDeterministicDummyEmojiList
import com.goliath.emojihub.createDeterministicTrendingEmojiList
import com.goliath.emojihub.mockLogClass
import com.goliath.emojihub.models.CreatedEmoji
import com.goliath.emojihub.usecases.EmojiUseCase
Expand Down Expand Up @@ -44,24 +44,41 @@ class EmojiViewModelTest {
}

@Test
fun fetchEmojiList_success_updateEmojiList() = runTest {
fun fetchEmojiList_success_updateTrendingEmojiList() = runTest {
// given
val sampleFetchedEmojiList = createDeterministicDummyEmojiList(10)
val sampleFetchedEmojiList = createDeterministicTrendingEmojiList(10)
coEvery {
emojiUseCase.fetchEmojiList()
emojiUseCase.fetchEmojiList(0)
} returns sampleFetchedEmojiList
// when
emojiViewModel.fetchEmojiList()
advanceUntilIdle()
// then
coVerify(exactly = 1) { emojiUseCase.fetchEmojiList() }
coVerify(exactly = 1) { emojiUseCase.fetchEmojiList(0) }
coVerify(exactly = 1) { emojiUseCase.updateEmojiList(any()) }
}

@Test
fun toggleSortingMode_success_updateTrendingEmojiList() = runTest {
// given
// for simplicity of testing, we return the same list for both cases
val sampleFetchedEmojiList = createDeterministicTrendingEmojiList(10)
coEvery {
emojiUseCase.fetchEmojiList(1)
} returns sampleFetchedEmojiList
// when
emojiViewModel.toggleSortingMode()
advanceUntilIdle()
// then
coVerify(exactly = 1) { emojiUseCase.fetchEmojiList(1) }
coVerify(exactly = 1) { emojiUseCase.updateEmojiList(any()) }
assertEquals(1, emojiViewModel.sortByDate)
}

@Test
fun fetchMyCreatedEmojiList_success_updateMyCreatedEmojiList() = runTest {
// given
val sampleFetchedMyCreatedEmojiList = createDeterministicDummyEmojiList(10)
val sampleFetchedMyCreatedEmojiList = createDeterministicTrendingEmojiList(10)
coEvery {
emojiUseCase.fetchMyCreatedEmojiList()
} returns sampleFetchedMyCreatedEmojiList
Expand All @@ -76,7 +93,7 @@ class EmojiViewModelTest {
@Test
fun fetchMySavedEmojiList_success_updateMySavedEmojiList() = runTest {
// given
val sampleFetchedMySavedEmojiList = createDeterministicDummyEmojiList(10)
val sampleFetchedMySavedEmojiList = createDeterministicTrendingEmojiList(10)
coEvery {
emojiUseCase.fetchMySavedEmojiList()
} returns sampleFetchedMySavedEmojiList
Expand Down

0 comments on commit 361f1ba

Please sign in to comment.