Skip to content

Commit

Permalink
Merge pull request #45 from Nexters/feature/develop-remained-feature
Browse files Browse the repository at this point in the history
Beta 출시 피쳐 구현
  • Loading branch information
DwEnn authored Oct 3, 2023
2 parents 03bd763 + a179fb8 commit 50ce6ca
Show file tree
Hide file tree
Showing 68 changed files with 1,540 additions and 471 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ plugins {
id("com.google.dagger.hilt.android")
id("org.jlleitschuh.gradle.ktlint") version Versions.KTLINT
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
}

android {
Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:screenOrientation="portrait"
android:theme="@style/Theme.Keymeandroid">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
41 changes: 37 additions & 4 deletions app/src/main/java/com/keyme/app/ui/KeymeApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
Expand All @@ -23,12 +24,20 @@ import com.keyme.app.navigation.TopLevelDestination
import com.keyme.app.navigation.keymeTopLevelDestinations
import com.keyme.presentation.dailykeymetest.dailyKeymeTestGraph
import com.keyme.presentation.designsystem.theme.KeymeTheme
import com.keyme.presentation.designsystem.theme.keyme_bottom
import com.keyme.presentation.designsystem.theme.keyme_white
import com.keyme.presentation.editprofile.ui.EditProfileDestination
import com.keyme.presentation.editprofile.ui.editProfileGraph
import com.keyme.presentation.myprofile.ui.KeymeQuestionResultDestination
import com.keyme.presentation.myprofile.ui.keymeQuestionResultGraph
import com.keyme.presentation.myprofile.ui.myProfileGraph
import com.keyme.presentation.onboarding.onboardingGraph
import com.keyme.presentation.setting.ui.SettingDestination
import com.keyme.presentation.setting.ui.settingGraph
import com.keyme.presentation.takekeymetest.TakeKeymeTestDestination
import com.keyme.presentation.takekeymetest.takeKeymeTestGraph
import com.keyme.presentation.tutorial.ui.TutorialDestination
import com.keyme.presentation.tutorial.ui.tutorialGraph
import com.keyme.presentation.utils.topBorder

@Composable
Expand Down Expand Up @@ -71,9 +80,16 @@ fun KeymeApp() {
)

dailyKeymeTestGraph(
navigateToTutorial = { appState.navigate(TutorialDestination) },
navigateToTakeKeymeTest = { appState.navigate(TakeKeymeTestDestination, it) },
navigateToQuestionResult = { appState.navigate(KeymeQuestionResultDestination, it.questionId) },
navigateToQuestionResult = {
appState.navigate(
KeymeQuestionResultDestination,
it.questionId,
)
},
nestedGraphs = {
tutorialGraph(onBackClick = appState::onBackClick)
keymeQuestionResultGraph(onBackClick = appState::onBackClick)
takeKeymeTestGraph(
onBackClick = appState::onBackClick,
Expand All @@ -89,8 +105,19 @@ fun KeymeApp() {
question.questionId,
)
},
navigateToSetting = {
appState.navigate(SettingDestination)
},
nestedGraphs = {
keymeQuestionResultGraph(onBackClick = appState::onBackClick)
settingGraph(
onBackClick = appState::onBackClick,
navigateToEditProfile = {
appState.navigate(EditProfileDestination)
},
) {
editProfileGraph(onBackClick = appState::onBackClick)
}
},
)
}
Expand All @@ -108,8 +135,10 @@ fun KeymeBottomBar(
onNavigateToDestination: (TopLevelDestination) -> Unit,
) {
NavigationBar(
modifier = Modifier.topBorder(width = 1.dp, color = Color(0xFF363636)),
containerColor = Color(0x80232323),
modifier = Modifier
.topBorder(width = 1.dp, color = Color(0xFF363636))
.height(65.dp),
containerColor = keyme_bottom,
tonalElevation = 4.dp,
) {
keymeTopLevelDestinations.forEach { destination ->
Expand All @@ -129,7 +158,11 @@ fun KeymeBottomBar(
),
contentAlignment = Alignment.Center,
) {
Icon(painter = painterResource(id = iconResId), contentDescription = "")
Icon(
painter = painterResource(id = iconResId),
contentDescription = "",
tint = keyme_white,
)
}
}
}
Expand Down
19 changes: 15 additions & 4 deletions app/src/main/java/com/keyme/app/ui/KeymeAppState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,28 @@ import androidx.navigation.compose.rememberNavController
import com.keyme.app.navigation.TopLevelDestination
import com.keyme.presentation.navigation.KeymeNavigationDestination
import com.keyme.presentation.onboarding.OnboardingDestination
import com.keyme.presentation.onboarding.OnboardingViewModel
import com.keyme.presentation.takekeymetest.TakeKeymeTestDestination
import com.keyme.presentation.tutorial.ui.TutorialDestination
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

@Composable
fun rememberKeymeAppState(
onboardingViewModel: OnboardingViewModel = hiltViewModel(),
keymeAppStateViewModel: KeymeAppStateViewModel = hiltViewModel(),
navController: NavHostController = rememberNavController(),
): KeymeAppState {
val coroutineScope = rememberCoroutineScope()
return remember(navController) {
KeymeAppState(coroutineScope, navController, onboardingViewModel)
KeymeAppState(coroutineScope, navController, keymeAppStateViewModel)
}
}

@Stable
class KeymeAppState(
private val coroutineScope: CoroutineScope,
val navController: NavHostController,
private val onboardingViewModel: OnboardingViewModel,
private val viewModel: KeymeAppStateViewModel,
) {
val currentDestination: NavDestination?
@Composable get() = navController.currentBackStackEntryAsState().value?.destination
Expand All @@ -45,6 +47,14 @@ class KeymeAppState(
val showBottomBar: Boolean
@Composable get() = currentDestination?.route.showBottomBar()

init {
coroutineScope.launch {
viewModel.myMemberInfoState.collectLatest {
if (it == null) startDestination = OnboardingDestination
}
}
}

fun navigate(destination: KeymeNavigationDestination) {
if (destination is TopLevelDestination) {
navController.navigate(destination.route) {
Expand Down Expand Up @@ -77,6 +87,7 @@ class KeymeAppState(
if (this == null) return false
if (this.contains(OnboardingDestination.destination)) return false
if (this.contains(TakeKeymeTestDestination.route)) return false
if (this.contains(TutorialDestination.route)) return false

return true
}
Expand Down
22 changes: 22 additions & 0 deletions app/src/main/java/com/keyme/app/ui/KeymeAppStateViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.keyme.app.ui

import com.keyme.domain.repository.MyMemberInfoRepository
import com.keyme.presentation.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject

@HiltViewModel
class KeymeAppStateViewModel @Inject constructor(
private val myMemberInfoRepository: MyMemberInfoRepository,
) : BaseViewModel() {

val myMemberInfoState = myMemberInfoRepository
.getInfo()
.stateIn(
started = SharingStarted.Eagerly,
scope = baseViewModelScope,
initialValue = null,
)
}
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ plugins {
id("com.google.dagger.hilt.android") version Versions.HILT apply false
id("org.jetbrains.kotlin.android") version Versions.KOTLIN apply false
id("com.google.gms.google-services") version Versions.GOOGLE apply false
id("com.google.firebase.crashlytics") version Versions.CRASHLYTICS apply false
}

buildscript {
Expand Down
8 changes: 8 additions & 0 deletions buildSrc/src/main/java/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,18 @@ object Dependencies {
// fcm
private val firebase_bom = "com.google.firebase:firebase-bom:32.2.2"
private val firebase_messaging = "com.google.firebase:firebase-messaging-ktx"
private val firebase_crashlytics = "com.google.firebase:firebase-crashlytics-ktx"
private val firebase_analytics = "com.google.firebase:firebase-analytics-ktx"

// webview
private val webView = "com.google.accompanist:accompanist-webview:${Versions.accompanist}"

// pager
private const val pager_indicators = "com.google.accompanist:accompanist-pager-indicators:${Versions.accompanist}"

// system_ui_controller
private const val system_ui_controller = "com.google.accompanist:accompanist-systemuicontroller:${Versions.accompanist}"

// Test
private val junit = "junit:junit:${Versions.JUNIT}"

Expand Down Expand Up @@ -123,6 +128,7 @@ object Dependencies {
implementation(composeViewModel)
implementation(composeNavigation)
implementation(runtime_compose)
implementation(system_ui_controller)
}

fun DependencyHandler.setViewModelDependencies() {
Expand Down Expand Up @@ -161,6 +167,8 @@ object Dependencies {
fun DependencyHandler.setFirebaseDependencies() {
implementation(platform(firebase_bom))
implementation(firebase_messaging)
implementation(firebase_crashlytics)
implementation(firebase_analytics)
}

fun DependencyHandler.setDatabaseDependencies() {
Expand Down
3 changes: 3 additions & 0 deletions buildSrc/src/main/java/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ object Versions {
// Google-Serivces
const val GOOGLE = "4.3.15"

// Crashlytics Gradle plugin
const val CRASHLYTICS = "2.9.9"

// Coroutines
const val COROUTINES = "1.3.9"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.keyme.data.local.datasource

import android.content.Context
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import com.keyme.data.local.di.ApplicationScope
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject

class TutorialDataSource @Inject constructor(
@ApplicationContext context: Context,
@ApplicationScope private val applicationScope: CoroutineScope,
) {
private val dataStore = context.dataStore

val isLearned = dataStore.data.map { it[KEY_TUTORIAL_LEARNED] ?: false }

fun setLearned(value: Boolean) {
applicationScope.launch {
dataStore.edit {
it[KEY_TUTORIAL_LEARNED] = value
}
}
}

companion object {
private val KEY_TUTORIAL_LEARNED = booleanPreferencesKey("tutorial_learned")
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.keyme.data.local.di

import com.keyme.data.local.repositoryimpl.SharedPrefRepositoryImpl
import com.keyme.data.local.repositoryimpl.TutorialRepositoryImpl
import com.keyme.data.remote.repositoryimpl.MyMemberInfoRepositoryImpl
import com.keyme.domain.repository.MyMemberInfoRepository
import com.keyme.domain.repository.SharedPrefRepository
import com.keyme.domain.repository.TutorialRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand All @@ -16,6 +18,9 @@ abstract class RepositoryModule {
@Binds
abstract fun bindMyMemberInfoRepository(impl: MyMemberInfoRepositoryImpl): MyMemberInfoRepository

@Binds
abstract fun bindTutorialRepository(impl: TutorialRepositoryImpl): TutorialRepository

@Binds
abstract fun bindSharedPrefRepository(impl: SharedPrefRepositoryImpl): SharedPrefRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.keyme.data.local.repositoryimpl

import com.keyme.data.local.datasource.TutorialDataSource
import com.keyme.domain.repository.TutorialRepository
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

class TutorialRepositoryImpl @Inject constructor(
private val tutorialDataSource: TutorialDataSource,
) : TutorialRepository {
override fun isLearned(): Flow<Boolean> {
return tutorialDataSource.isLearned
}

override fun setLearned(value: Boolean) {
tutorialDataSource.setLearned(value)
}
}
4 changes: 4 additions & 0 deletions data/src/main/java/com/keyme/data/remote/api/KeymeApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.keyme.domain.entity.response.UploadProfileImageResponse
import com.keyme.domain.entity.response.VerifyNicknameResponse
import okhttp3.MultipartBody
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.Multipart
import retrofit2.http.PATCH
Expand Down Expand Up @@ -53,6 +54,9 @@ interface KeymeApi {
@Path("memberId") memberId: String,
): MemberResponse

@DELETE("/members")
suspend fun withdraw(): EmptyResponse

@Multipart
@POST("/images")
suspend fun uploadProfileImage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class QuestionSolvedScoreListPagingSource(
LoadResult.Page(
data = response.data.results,
prevKey = null,
nextKey = response.data.results.last().id,
nextKey = kotlin.runCatching { response.data.results.last().id }.getOrNull(),
)
} else {
LoadResult.Error(NetworkErrorException(response.message))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.keyme.data.remote.repositoryimpl

import com.keyme.data.remote.api.KeymeApi
import com.keyme.domain.entity.response.EmptyResponse
import com.keyme.domain.entity.response.MemberResponse
import com.keyme.domain.entity.response.MemberStatistics
import com.keyme.domain.entity.response.MemberStatisticsResponse
Expand All @@ -20,4 +21,8 @@ class MemberRepositoryImpl @Inject constructor(
): MemberStatisticsResponse {
return keymeApi.getMemberStatistics(memberId = memberId, type = type)
}

override suspend fun withdraw(): EmptyResponse {
return keymeApi.withdraw()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class MyMemberInfoRepositoryImpl @Inject constructor(
myMemberInfoDataSource.setMyMemberInfo(member)
}

override suspend fun clearInfo(member: Member) {
override suspend fun clearInfo() {
myMemberInfoDataSource.clear()
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.keyme.data.remote.repositoryimpl

import android.util.Base64
import com.keyme.data.remote.datasource.SignInDataSource
import com.keyme.domain.entity.response.SignInResponse
import com.keyme.domain.entity.response.UpdateMemberResponse
Expand Down Expand Up @@ -34,8 +35,9 @@ class SignInRepositoryImpl @Inject constructor(
override suspend fun uploadProfileImage(
imageString: String,
): UploadProfileImageResponse {
val imageRequestBody = imageString.toRequestBody("image/*".toMediaTypeOrNull())
val multipartImage = MultipartBody.Part.createFormData("image", "image.jpg", imageRequestBody)
val image = Base64.decode(imageString, Base64.DEFAULT)
val imageRequestBody = image.toRequestBody("image/jpeg".toMediaTypeOrNull())
val multipartImage = MultipartBody.Part.createFormData("image", "image.jpeg", imageRequestBody)

return signInDataSource.uploadProfileImage(
multipartImage = multipartImage,
Expand Down
Loading

0 comments on commit 50ce6ca

Please sign in to comment.