Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: emoji pagination #67

Merged
merged 3 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/springboot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ jobs:
distribution: 'temurin'
cache: gradle

- name: Add test account key from secrets
run: |
echo "${{ secrets.FIREBASE_TEST_ACCOUNT_KEY }}" | base64 -d > \
src/test/kotlin/com/goliath/emojihub/springboot/TestServiceAccountKey.json
ls -al src/test/kotlin/com/goliath/emojihub/springboot/

- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.goliath.emojihub.data_sources

import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.goliath.emojihub.data_sources.api.EmojiApi
import com.goliath.emojihub.models.EmojiDto
import javax.inject.Inject

class EmojiPagingSource @Inject constructor(
private val api: EmojiApi
): PagingSource<Int, EmojiDto>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, EmojiDto> {
val cursor = params.key ?: 1
val count = params.loadSize
return try {
val response = api.fetchEmojiList(1, cursor, count).body()
val data = response ?: listOf()
LoadResult.Page(
data = data,
prevKey = if (cursor == 1) null else cursor - 1,
nextKey = if (data.isEmpty()) null else cursor + 1
)
} catch (exception: Exception) {
LoadResult.Error(exception)
}
}

override fun getRefreshKey(state: PagingState<Int, EmojiDto>): Int? {
return state.anchorPosition?.let { anchorPosition ->
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package com.goliath.emojihub.repositories.remote

import android.util.Log
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.goliath.emojihub.data_sources.EmojiPagingSource
import com.goliath.emojihub.data_sources.PostPagingSource
import com.goliath.emojihub.data_sources.api.EmojiApi
import com.goliath.emojihub.models.EmojiDto
import com.goliath.emojihub.models.FetchEmojiListDto
import com.goliath.emojihub.models.PostDto
import com.goliath.emojihub.models.UploadEmojiDto
import com.google.gson.Gson
import kotlinx.coroutines.flow.Flow
import okhttp3.MediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody
Expand All @@ -17,7 +24,7 @@ import javax.inject.Inject
import javax.inject.Singleton

interface EmojiRepository {
suspend fun fetchEmojiList(numLimit: Int): List<EmojiDto>
suspend fun fetchEmojiList(): Flow<PagingData<EmojiDto>>
suspend fun getEmojiWithId(id: String): EmojiDto?
suspend fun uploadEmoji(videoFile: File, emojiDto: UploadEmojiDto): Boolean
suspend fun saveEmoji(id: String): Response<Unit>
Expand All @@ -29,22 +36,11 @@ interface EmojiRepository {
class EmojiRepositoryImpl @Inject constructor(
private val emojiApi: EmojiApi
): EmojiRepository {
override suspend fun fetchEmojiList(numLimit: Int): List<EmojiDto> {
// val fetchEmojiListDto = FetchEmojiListDto(1, 0, 10)
try {
val response = emojiApi.fetchEmojiList(1, 1, 10)

if(response.isSuccessful && response.body() != null) {
Log.d("Fetch_E_L", "Successfully fetched ${response.body()!!.size} emojis")
return response.body()!!
} else {
val errorBody = response.errorBody()?.string() ?: "Unknown error"
Log.d("Fetch_E_L", "Failed to fetch emojis: $errorBody")
}
} catch(e: Exception) {
Log.e("Fetch_E_L", "Error fetching emojis", e)
}
return listOf()
override suspend fun fetchEmojiList(): Flow<PagingData<EmojiDto>> {
return Pager(
config = PagingConfig(pageSize = 10, initialLoadSize = 10, enablePlaceholders = false),
pagingSourceFactory = { EmojiPagingSource(emojiApi) }
).flow
}

override suspend fun getEmojiWithId(id: String): EmojiDto? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@ package com.goliath.emojihub.usecases

import android.net.Uri
import android.util.Log
import androidx.paging.PagingData
import androidx.paging.map
import com.goliath.emojihub.data_sources.ApiErrorController
import com.goliath.emojihub.models.CreatedEmoji
import com.goliath.emojihub.models.Emoji
import com.goliath.emojihub.models.EmojiDto
import com.goliath.emojihub.models.Post

import com.goliath.emojihub.models.UploadEmojiDto
import com.goliath.emojihub.repositories.local.X3dRepository
import com.goliath.emojihub.repositories.remote.EmojiRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton

interface EmojiUseCase {
val emojiListState: StateFlow<List<EmojiDto>>
suspend fun fetchEmojiList(numInt: Int)
val emojiList: StateFlow<PagingData<Emoji>>
suspend fun updateEmojiList(data: PagingData<Emoji>)
suspend fun fetchEmojiList(): Flow<PagingData<Emoji>>
suspend fun createEmoji(videoUri: Uri, topK: Int): List<CreatedEmoji>
suspend fun uploadEmoji(emojiUnicode: String, emojiLabel: String, videoFile: File): Boolean
suspend fun saveEmoji(id: String): Boolean
Expand All @@ -32,18 +39,15 @@ class EmojiUseCaseImpl @Inject constructor(
private val errorController: ApiErrorController
): EmojiUseCase {

private val _emojiListState = MutableStateFlow<List<EmojiDto>>(emptyList())
override val emojiListState: StateFlow<List<EmojiDto>>
get() = _emojiListState.asStateFlow()
private val _emojiList = MutableStateFlow<PagingData<Emoji>>(PagingData.empty())
override val emojiList: StateFlow<PagingData<Emoji>>
get() = _emojiList

override suspend fun fetchEmojiList(numInt: Int) {
try{
val emojiList = emojiRepository.fetchEmojiList(numInt)
_emojiListState.emit(emojiList)
Log.d("Fetch_E_L", "USECASE DONE: $emojiList")
} catch (e: Exception) {
errorController.setErrorState(-1)
}
override suspend fun updateEmojiList(data: PagingData<Emoji>) {
_emojiList.emit(data)
}
override suspend fun fetchEmojiList(): Flow<PagingData<Emoji>> {
return emojiRepository.fetchEmojiList().map { it.map { dto -> Emoji(dto) } }
}

override suspend fun createEmoji(videoUri: Uri, topK: Int): List<CreatedEmoji> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.cachedIn
import com.goliath.emojihub.models.CreatedEmoji
import com.goliath.emojihub.models.Emoji
import com.goliath.emojihub.usecases.EmojiUseCase
Expand All @@ -28,19 +29,18 @@ class EmojiViewModel @Inject constructor(
var currentEmoji: Emoji? = null
var isBottomSheetShown by mutableStateOf(false)

private val _emojiList = MutableStateFlow<List<Emoji>>(emptyList())
val emojiList: StateFlow<List<Emoji>> = _emojiList.asStateFlow()
val emojiList = emojiUseCase.emojiList

private val _topK = 3

fun fetchEmojiList(numInt: Int)
fun fetchEmojiList()
{
viewModelScope.launch {
emojiUseCase.fetchEmojiList(numInt)

val emojis = emojiUseCase.emojiListState.value.map { dto -> Emoji(dto) }
_emojiList.emit(emojis)
Log.d("Fetch_E_L", "VIEWMODEL DONE: $emojis")
emojiUseCase.fetchEmojiList()
.cachedIn(viewModelScope)
.collect {
emojiUseCase.updateEmojiList(it)
}
}
}

Expand Down
17 changes: 10 additions & 7 deletions android/app/src/main/java/com/goliath/emojihub/views/EmojiPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat
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.models.createDummyEmoji
Expand Down Expand Up @@ -64,13 +65,13 @@ fun EmojiPage(
}
}

val emojiList = viewModel.emojiList.collectAsLazyPagingItems()

LaunchedEffect(Unit)
{
viewModel.fetchEmojiList(10)
viewModel.fetchEmojiList()
}

val emojiList = viewModel.emojiList.collectAsState().value

Column(Modifier.background(White)) {
TopNavigationBar("Emoji", shouldNavigate = false) {
IconButton(onClick = {
Expand Down Expand Up @@ -107,10 +108,12 @@ fun EmojiPage(
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
items(emojiList, key = { it.id }) { emoji ->
EmojiCell(emoji = emoji) {
viewModel.currentEmoji = emoji
navController.navigate(NavigationDestination.PlayEmojiVideo)
items(emojiList.itemCount) { index ->
emojiList[index]?.let{
EmojiCell(emoji = it) { selectedEmoji ->
viewModel.currentEmoji = selectedEmoji
navController.navigate(NavigationDestination.PlayEmojiVideo)
}
}
}
}
Expand Down