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

Implement upload mission #16

Merged
merged 21 commits into from
Apr 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7154340
feat: add ContentUriRequestBody
giovannijunseokim Apr 5, 2024
dfc73fe
chore: remove fixed size
giovannijunseokim Apr 5, 2024
09a8085
chore: add http functions in mission service
giovannijunseokim Apr 5, 2024
b625c97
chore: implement MissionRepository
giovannijunseokim Apr 5, 2024
927cc4d
chore: add missionNo in Mission class
giovannijunseokim Apr 5, 2024
c12d679
chore: make MissionListDialog scrollable
giovannijunseokim Apr 5, 2024
aa0bf00
chore: use mission class
giovannijunseokim Apr 5, 2024
7f5d231
feat: implement MissionCertificationResultDialog
giovannijunseokim Apr 5, 2024
2a481e0
chore: use TextButton instead of clickable
giovannijunseokim Apr 5, 2024
9ee2520
chore: functionalize and add logic
giovannijunseokim Apr 5, 2024
6be65c9
chore: add selectedMission in UiState
giovannijunseokim Apr 5, 2024
2df0799
chore: update Json
giovannijunseokim Apr 5, 2024
a270b3c
chore: change Dto for OpenAi Service
giovannijunseokim Apr 5, 2024
1272df6
chore: change parameters for OpenAi Service
giovannijunseokim Apr 5, 2024
aacb591
chore: reflect missionClass changes
giovannijunseokim Apr 5, 2024
39205e6
fix: use emit
giovannijunseokim Apr 5, 2024
d603bba
chore: add setSelectedMission
giovannijunseokim Apr 5, 2024
7a319b2
chore: do logic with only when uri is non-null
giovannijunseokim Apr 5, 2024
b7c0b9b
chore: add setSelectedMission
giovannijunseokim Apr 5, 2024
279d34c
chore: update HomeScreen
giovannijunseokim Apr 5, 2024
227ab22
feat: implement UploadMissionUseCase
giovannijunseokim Apr 5, 2024
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
3 changes: 3 additions & 0 deletions app/src/main/java/univ/earthbreaker/namu/data/ApiFactory.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package univ.earthbreaker.namu.data

import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
Expand All @@ -13,9 +14,11 @@ import univ.earthbreaker.namu.data.service.MissionService
import univ.earthbreaker.namu.data.service.OpenAIApiService

object ApiFactory {
@OptIn(ExperimentalSerializationApi::class)
private val json: Json by lazy {
Json {
encodeDefaults = true
explicitNulls = false
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ data class CertifyMissionRequest(
) {
@Serializable
data class Message(
val role: String = "user",
@SerialName("content") val contents: List<ImageContent>,
val role: String,
@SerialName("content") val contents: List<Content>,
) {
@Serializable
data class ImageContent(
@SerialName("image_url") val imageUrl: ImageUrl,
val prompt: String,
@SerialName("type") val contentType: String = "image_url",
data class Content(
val type: String,
val text: String? = null,
@SerialName("image_url") val imageURL: ImageURL? = null,
) {
@Serializable
data class ImageUrl(
val url: String,
data class ImageURL(
@SerialName("url") val url: String,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ data class CertifyMissionResponse(
@SerialName("object") val objectType: String = "chat.completion",
val created: Int,
val model: String,
@SerialName("system_fingerprint") val systemFingerprint: String,
@SerialName("system_fingerprint") val systemFingerprint: String?,
val choices: List<Choice>,
val usage: Usage,
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ data class GetMissionListResponse(
fun toEntity(): MissionListInfo = MissionListInfo(
todayMissions = todayMissions.map {
MissionListInfo.Mission(
missionNo = it.missionNo,
missionName = MissionActivity.valueOf(it.title).missionName,
isBeforeChallenge = when (MissionStatus.valueOf(it.status)) {
MissionStatus.READY -> {
Expand All @@ -32,6 +33,7 @@ data class GetMissionListResponse(
},
additionalMissions = defaultMissions.map {
MissionListInfo.Mission(
missionNo = it.missionNo,
missionName = MissionActivity.valueOf(it.title).missionName,
isBeforeChallenge = when (MissionStatus.valueOf(it.status)) {
MissionStatus.READY -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,62 @@
package univ.earthbreaker.namu.data.repository.mission

import okhttp3.MultipartBody
import okhttp3.RequestBody
import univ.earthbreaker.namu.data.ServicePool
import univ.earthbreaker.namu.data.model.mission.CertifyMissionRequest
import univ.earthbreaker.namu.data.model.mission.CertifyMissionRequest.Message
import univ.earthbreaker.namu.data.model.mission.CertifyMissionRequest.Message.ImageContent
import univ.earthbreaker.namu.data.model.mission.CertifyMissionRequest.Message.ImageContent.ImageUrl
import univ.earthbreaker.namu.data.model.mission.CertifyMissionRequest.Message.Content
import univ.earthbreaker.namu.data.model.mission.CertifyMissionRequest.Message.Content.ImageURL
import univ.earthbreaker.namu.domain.entity.mission.MissionCertificationInfo
import univ.earthbreaker.namu.domain.entity.mission.MissionListInfo
import univ.earthbreaker.namu.domain.repository.MissionRepository

class MissionRepositoryImpl :
MissionRepository {
class MissionRepositoryImpl : MissionRepository {
override suspend fun certifyMission(
text: String,
prompt: String,
imageBase64: String,
): MissionCertificationInfo =
ServicePool.openAIApiService.certifyMission(
certifyMissionRequest = CertifyMissionRequest(
messages = listOf(
Message(
contents = listOf(
ImageContent(
imageUrl = ImageUrl(
url = imageBase64,
),
prompt = prompt,
): MissionCertificationInfo = ServicePool.openAIApiService.certifyMission(
certifyMissionRequest = CertifyMissionRequest(
messages = listOf(
Message(
role = "system",
contents = listOf(
Content(
type = "text",
text = prompt,
),
),
),
Message(
role = "user",
contents = listOf(
Content(
type = "text",
text = text,
),
Content(
type = "image_url",
imageURL = ImageURL(
url = "data:image/jpeg;base64,$imageBase64",
),
),
),
),
),
).toEntity()
),
).toEntity()

override suspend fun getMissionList(): MissionListInfo =
ServicePool.missionService.getMissionList().toEntity()

override suspend fun uploadMission(
missionNo: Int,
content: RequestBody,
imageFile: MultipartBody.Part,
) = ServicePool.missionService.uploadMission(
missionNo = missionNo,
content = content,
imageFile = imageFile,
)
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
package univ.earthbreaker.namu.data.service

import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part
import retrofit2.http.Path
import univ.earthbreaker.namu.data.model.mission.GetMissionListResponse

interface MissionService {
@GET("missions")
suspend fun getMissionList(): GetMissionListResponse

@Multipart
@POST("missions/certification/success/{missionNo}")
suspend fun uploadMission(
@Path("missionNo") missionNo: Int,
@Part("content") content: RequestBody,
@Part imageFile: MultipartBody.Part,
): Response<Void>
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ data class MissionListInfo(
val additionalMissions: List<Mission> = emptyList(),
) {
data class Mission(
val missionNo: Int,
val missionName: String,
val isBeforeChallenge: Boolean,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
package univ.earthbreaker.namu.domain.repository

import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.Response
import univ.earthbreaker.namu.domain.entity.mission.MissionCertificationInfo
import univ.earthbreaker.namu.domain.entity.mission.MissionListInfo

interface MissionRepository {
suspend fun certifyMission(
text: String,
prompt: String,
imageBase64: String,
): MissionCertificationInfo

suspend fun getMissionList(): MissionListInfo

suspend fun uploadMission(
missionNo: Int,
content: RequestBody,
imageFile: MultipartBody.Part,
): Response<Void>
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ import univ.earthbreaker.namu.domain.entity.mission.MissionCertificationInfo
import univ.earthbreaker.namu.domain.repository.MissionRepository

class CertifyMissionUseCase(private val missionRepository: MissionRepository = MissionRepositoryImpl()) {
suspend operator fun invoke(imageBase64: String): Flow<MissionCertificationInfo> =
suspend operator fun invoke(text: String, imageBase64: String): Flow<MissionCertificationInfo> =
flow {
emit(
missionRepository.certifyMission(
imageBase64 = imageBase64,
text = text,
prompt = """you're a helpful assistant who classify image by true and false.
if an image is related to practicing environmental conservation,
answer true. else false.
answer true. else false. you should answer with lower case! Because
We want to change that string into Boolean.
""".trimIndent(),
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ import univ.earthbreaker.namu.domain.repository.MissionRepository

class GetMissionListUseCase(private val missionRepository: MissionRepository = MissionRepositoryImpl()) {
operator fun invoke(): Flow<MissionListInfo> =
flow<MissionListInfo> { missionRepository.getMissionList() }.flowOn(Dispatchers.IO)
flow { emit(missionRepository.getMissionList()) }.flowOn(Dispatchers.IO)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package univ.earthbreaker.namu.domain.usecase.mission

import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.toRequestBody
import univ.earthbreaker.namu.data.ServicePool
import univ.earthbreaker.namu.util.ContentUriRequestBody

class UploadMissionUseCase {
suspend operator fun invoke(
missionNo: Int,
content: String,
imageRequestBody: ContentUriRequestBody,
) {
val imagePart = MultipartBody.Part.createFormData(
"imageFile",
imageRequestBody.fileName,
imageRequestBody,
)
ServicePool.missionService.uploadMission(
missionNo = missionNo,
content = content.toRequestBody(),
imageFile = imagePart,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,19 @@ import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia
import androidx.activity.viewModels
import univ.earthbreaker.namu.ui.theme.GTTheme
import univ.earthbreaker.namu.util.ContentUriRequestBody
import univ.earthbreaker.namu.util.getBase64FromUri

class HomeActivity : ComponentActivity() {
private val viewModel: HomeViewModel by viewModels()
private val pickMedia = registerForActivityResult(PickVisualMedia()) { uri ->
viewModel.setMissionImage(imageUri = uri, base64 = getBase64FromUri(uri = uri))
uri?.let {
viewModel.setMissionImage(
imageUri = uri,
base64 = getBase64FromUri(uri = uri),
contentUriRequestBody = ContentUriRequestBody(this, uri),
)
}
}

override fun onCreate(savedInstanceState: Bundle?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ fun rememberHomeActions(viewModel: HomeViewModel, onClickCameraIcon: () -> Unit)
certifyMission = viewModel::certifyMission,
getHomeInfo = viewModel::getHomeInfo,
getMissionListInfo = viewModel::getMissionListInfo,
setSelectedMission = viewModel::setSelectedMission,
)
}
}
34 changes: 24 additions & 10 deletions app/src/main/java/univ/earthbreaker/namu/feature/home/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ fun HomeScreen(
state: HomeUiState.Success,
actions: HomeActions,
) {
actions.getHomeInfo()
Column(
modifier = Modifier
.fillMaxSize()
Expand Down Expand Up @@ -99,19 +98,19 @@ fun HomeScreen(
if (state.homeDialogState.doesMissionListDialogExist) {
MissionListDialog(
onDismissRequest = actions.dismissDialog,
onClickChallenge = {
onClickChallenge = { selectedMission ->
actions.setSelectedMission(selectedMission)
actions.openDialog(
HomeDialogState(
doesMissionCertificationDialogExist = true,
),
)
},
getMissionListInfo = actions.getMissionListInfo,
missionListInfo = state.missionListInfo,
)
}

if (state.homeDialogState.doesMissionCertificationDialogExist) {
if (state.homeDialogState.doesMissionCertificationDialogExist && state.selectedMission != null) {
MissionCertificationDialog(
onDismissRequest = {
actions.dismissDialog()
Expand All @@ -121,19 +120,34 @@ fun HomeScreen(
),
)
},
missionName = "비건 카페 방문하기",
mission = state.selectedMission,
missionDescription = state.missionDescription,
missionImageUri = state.missionImageUri,
onClickCameraImage = actions.onClickCameraIcon,
onMissionDescriptionChanged = actions.onMissionDescriptionChanged,
certifyMission = actions.certifyMission,
modifier = Modifier
.padding(all = 20.dp)
.fillMaxWidth(),
)
}

if (state.homeDialogState.doesMissionCertificationResultDialogExist) {
val isMissionCertificationSuccessful = state.isMissionCertificationSuccessful ?: false
MissionCertificationResultDialog(
isSuccessful = state.isMissionCertificationSuccessful ?: false,
onDismissRequest = {},
isSuccessful = isMissionCertificationSuccessful,
onDismissRequest = {
if (isMissionCertificationSuccessful) {
actions.getHomeInfo()
actions.getMissionListInfo()
}
actions.openDialog(HomeDialogState(doesMissionListDialogExist = true))
},
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = 20.dp,
),
)
}
}
Expand Down Expand Up @@ -180,16 +194,14 @@ fun HomeTopCard(
OpenDialogCard(
text = stringResource(R.string.adding_friends),
onClick = {},
width = 85.dp,
)
Spacer(Modifier.height(7.dp))
OpenDialogCard(
text = stringResource(R.string.guestbook),
onClick = {},
width = 72.dp,
)
}
Column {
Column(horizontalAlignment = Alignment.End) {
UserEnergyCard(energyCount = energy)
Spacer(Modifier.height(7.dp))
OpenDialogCard(
Expand Down Expand Up @@ -251,6 +263,7 @@ private fun HomeScreenPreview() {
missionImageUri = null,
missionDescription = "",
isMissionCertificationSuccessful = false,
selectedMission = MissionListInfo.Mission(0, "", false),
missionListInfo = MissionListInfo(emptyList(), emptyList()),
),
actions = HomeActions(
Expand All @@ -262,6 +275,7 @@ private fun HomeScreenPreview() {
certifyMission = {},
getHomeInfo = {},
getMissionListInfo = {},
setSelectedMission = {},
),
)
}
Expand Down
Loading