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

[AN/USER] feat: 학생 인증 화면 구현 (#370) #429

Merged
merged 35 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
c6dedda
feat: 학생 인증 레포지토리 정의
SeongHoonC Aug 24, 2023
f64f815
feat: 학교 레포지토리 임시 정의
SeongHoonC Aug 24, 2023
90ca80f
feat: 학교 인증 화면 뷰모델 구현
SeongHoonC Aug 24, 2023
306b1dc
test: 학생 인증 화면 뷰모델 테스트 작성
SeongHoonC Aug 25, 2023
fa3831d
refactor: LiveData stateFlow 로 리팩터링
SeongHoonC Sep 5, 2023
a36f8e1
feat: StudentVerificationActivity 생성
SeongHoonC Sep 5, 2023
b3a81da
feat: 학생 인증 화면 xml 그리기
SeongHoonC Sep 5, 2023
9b84313
feat: 재사용 가능한 문장 검증 로직 생성 및 테스트
SeongHoonC Sep 7, 2023
5b42940
feat: 학생 인증 검증 로직 생성 및 테스트
SeongHoonC Sep 7, 2023
024c3d2
feat: SchoolId Long 으로 변경
SeongHoonC Sep 7, 2023
1600176
feat: 인증 코드 확인 요청 기능 작성
SeongHoonC Sep 7, 2023
ee4175b
feat: 코드 형식 확인 인증
SeongHoonC Sep 7, 2023
cb765d4
feat: 임시 학교 인증 화면 이동
SeongHoonC Sep 7, 2023
5b853fc
refator: ktlint check
SeongHoonC Sep 7, 2023
4c50f49
refator: 클래스 및 변수 명 변경
SeongHoonC Sep 8, 2023
12faf1a
fix: 화면 회전 시 학교 이메일을 재요청하지 않음
SeongHoonC Sep 8, 2023
e8bd130
refactor: 사용하지않는 코드 주석 제거
SeongHoonC Sep 11, 2023
3b896e4
refactor: repeatOnStartedState util 추가
SeongHoonC Sep 11, 2023
9165d3b
refactor: StudentVerificationActivity 전체 리팩터링
SeongHoonC Sep 11, 2023
981fdfe
refactor: Students 를 Student 로 통일
SeongHoonC Sep 11, 2023
56056f4
refactor: State 성공 시 처리 함수 분리 및 네이밍 변경
SeongHoonC Sep 11, 2023
b4eface
refactor: 글자 30개까지 입력으로 변경
SeongHoonC Sep 11, 2023
56d7ca2
feat: sealed interface 학생 인증 이벤트 추가
SeongHoonC Sep 11, 2023
3743d5b
refactor: 학생 인증 ViewModel 리팩터링
SeongHoonC Sep 11, 2023
4dcf7c3
test: 학생 인증 이벤트 테스트 추가
SeongHoonC Sep 11, 2023
da5a951
refactor: 사용하지 않는 뷰 제거
SeongHoonC Sep 11, 2023
2c09605
test: 학생 인증 코드 예외 테스트 추가
SeongHoonC Sep 11, 2023
738f96e
feat: Event observing 추가
SeongHoonC Sep 11, 2023
e522752
refactor: 테스트 리팩터링
SeongHoonC Sep 12, 2023
917d18a
refactor: 테스트 네이밍 리팩터링
SeongHoonC Sep 12, 2023
c25372f
refactor: turbine 추가 및 sharedFlow 테스트 리팩터링
SeongHoonC Sep 13, 2023
f135fcd
refactor: Text 검증 테스트 네이밍 및 구조 리팩터링
SeongHoonC Sep 14, 2023
c2615df
refactor: TextValidator 생성 로직 통일
SeongHoonC Sep 14, 2023
84e963e
refactor: given when 함수 분리 및 테스트 리팩터링
SeongHoonC Sep 14, 2023
6e6ce87
refactor: seal interface 는 isExactlyInstanceOf 로 테스트
SeongHoonC Sep 14, 2023
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
10 changes: 5 additions & 5 deletions android/festago/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<uses-permission android:name="android.permission.INTERNET" />

<application
Expand All @@ -18,6 +17,9 @@
android:theme="@style/Theme.Festago"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<activity
android:name=".presentation.ui.studentsverification.StudentVerificationActivity"
android:exported="false" />
<activity
android:name=".presentation.ui.signin.SignInActivity"
android:exported="false" />
Expand Down Expand Up @@ -54,11 +56,9 @@
android:scheme="@string/kakao_redirection_scheme" />
</intent-filter>
</activity>

<activity
android:name=".presentation.ui.tickethistory.TicketHistoryActivity"
android:exported="false">
</activity>
android:exported="false"></activity>
</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.festago.festago.data.dto

import kotlinx.serialization.Serializable

@Serializable
data class SendVerificationRequest(
val userName: String,
val schoolId: Int,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.festago.festago.data.repository

import com.festago.festago.repository.SchoolRepository

class SchoolDefaultRepository() : SchoolRepository {

override suspend fun loadSchoolEmail(schoolId: Long): Result<String> {
// TODO: API 연동 작업 필요
return Result.success("festago.com")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.festago.festago.data.repository

import com.festago.festago.data.service.StudentsVerificationRetrofitService
import com.festago.festago.model.StudentVerificationCode
import com.festago.festago.repository.StudentsVerificationRepository

class StudentsVerificationDefaultRepository(
private val studentsVerificationRetrofitService: StudentsVerificationRetrofitService,
) : StudentsVerificationRepository {

override suspend fun sendVerificationCode(userName: String, schoolId: Long): Result<Unit> {
// TODO: API 연동 작업 필요
return Result.success(Unit)

// studentsVerificationRetrofitService.sendVerificationCode(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 주석은 나중에 추가할 내용 같은데 삭제하는것이 어떤가요??

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR 과 관련없는 내용이네요. 삭제하겠습니다!

// SendVerificationRequest(userName = userName, schoolId = schoolId),
// ).runCatchingWithErrorHandler()
// .getOrElse { error -> return Result.failure(error) }
// .let { return Result.success(Unit) }
}

override suspend fun requestVerificationCodeConfirm(code: StudentVerificationCode): Result<Unit> {
// TODO: API 연동 작업 필요
return Result.success(Unit)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.festago.festago.data.service

import com.festago.festago.data.dto.SendVerificationRequest
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.POST

interface StudentsVerificationRetrofitService {
@POST("/students/send-verification")
suspend fun sendVerificationCode(
@Body sendVerificationRequest: SendVerificationRequest,
): Response<Unit>
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.festago.festago.di

import com.festago.festago.data.retrofit.AuthInterceptor
import com.festago.festago.data.service.StudentsVerificationRetrofitService
import com.festago.festago.data.service.TicketRetrofitService
import com.festago.festago.data.service.UserRetrofitService
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
Expand Down Expand Up @@ -29,4 +30,8 @@ class AuthServiceContainer(baseUrl: String, tokenContainer: TokenContainer) {
val userRetrofitService: UserRetrofitService by lazy {
authRetrofit.create(UserRetrofitService::class.java)
}

val studentsVerificationRetrofitService: StudentsVerificationRetrofitService by lazy {
authRetrofit.create(StudentsVerificationRetrofitService::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ package com.festago.festago.di
import com.festago.festago.data.repository.AuthDefaultRepository
import com.festago.festago.data.repository.FestivalDefaultRepository
import com.festago.festago.data.repository.ReservationTicketDefaultRepository
import com.festago.festago.data.repository.SchoolDefaultRepository
import com.festago.festago.data.repository.StudentsVerificationDefaultRepository
import com.festago.festago.data.repository.TicketDefaultRepository
import com.festago.festago.data.repository.UserDefaultRepository
import com.festago.festago.repository.AuthRepository
import com.festago.festago.repository.FestivalRepository
import com.festago.festago.repository.ReservationTicketRepository
import com.festago.festago.repository.SchoolRepository
import com.festago.festago.repository.StudentsVerificationRepository
import com.festago.festago.repository.TicketRepository
import com.festago.festago.repository.UserRepository

Expand Down Expand Up @@ -40,4 +44,12 @@ class RepositoryContainer(
get() = ReservationTicketDefaultRepository(
reservationTicketRetrofitService = normalServiceContainer.reservationTicketRetrofitService,
)

val studentsVerificationRepository: StudentsVerificationRepository
get() = StudentsVerificationDefaultRepository(
studentsVerificationRetrofitService = authServiceContainer.studentsVerificationRetrofitService,
)

val schoolRepository: SchoolRepository
get() = SchoolDefaultRepository()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.festago.festago.presentation.ui.home.festivallist.FestivalListViewMod
import com.festago.festago.presentation.ui.home.mypage.MyPageViewModel
import com.festago.festago.presentation.ui.home.ticketlist.TicketListViewModel
import com.festago.festago.presentation.ui.signin.SignInViewModel
import com.festago.festago.presentation.ui.studentsverification.StudentsVerificationViewModel
import com.festago.festago.presentation.ui.ticketentry.TicketEntryViewModel
import com.festago.festago.presentation.ui.tickethistory.TicketHistoryViewModel
import com.festago.festago.presentation.ui.ticketreserve.TicketReserveViewModel
Expand Down Expand Up @@ -63,6 +64,12 @@ val FestagoViewModelFactory: ViewModelProvider.Factory = object : ViewModelProvi
analyticsHelper = analysisContainer.analyticsHelper,
)

modelClass.isAssignableFrom(StudentsVerificationViewModel::class.java) -> StudentsVerificationViewModel(
schoolRepository = repositoryContainer.schoolRepository,
studentsVerificationRepository = repositoryContainer.studentsVerificationRepository,
analyticsHelper = analysisContainer.analyticsHelper,
)

else -> throw IllegalArgumentException("ViewModelFactory에 정의되지않은 뷰모델을 생성하였습니다 : ${modelClass.name}")
} as T
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.festago.festago.databinding.FragmentMyPageBinding
import com.festago.festago.presentation.ui.FestagoViewModelFactory
import com.festago.festago.presentation.ui.home.HomeActivity
import com.festago.festago.presentation.ui.signin.SignInActivity
import com.festago.festago.presentation.ui.studentsverification.StudentVerificationActivity
import com.festago.festago.presentation.ui.tickethistory.TicketHistoryActivity

class MyPageFragment : Fragment(R.layout.fragment_my_page) {
Expand Down Expand Up @@ -102,6 +103,10 @@ class MyPageFragment : Fragment(R.layout.fragment_my_page) {
binding.srlMyPage.setOnRefreshListener {
vm.loadUserInfo()
}
// TODO: 학교 선택 화면 변경 필요
binding.tvSchoolAuthorization.setOnClickListener {
startActivity(StudentVerificationActivity.getIntent(requireContext(), 1L))
}
}

private fun handleSuccess(uiState: MyPageUiState.Success) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.festago.festago.presentation.ui.studentsverification

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.festago.festago.R
import com.festago.festago.databinding.ActivityStudentVerificationBinding
import com.festago.festago.presentation.ui.FestagoViewModelFactory
import kotlinx.coroutines.launch
import java.time.LocalTime
import java.time.format.DateTimeFormatter

class StudentVerificationActivity : AppCompatActivity() {

private lateinit var binding: ActivityStudentVerificationBinding
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private lateinit var binding: ActivityStudentVerificationBinding
private val binding: ActivityStudentVerificationBinding by lazy {
ActivityStudentVerificationBinding.inflate(layoutInflater)
}

바인딩 같은 경우는 변경될 일이 없기 때문에 다음과 같이 선언하는 건 어떤가요?


private val vm: StudentsVerificationViewModel by viewModels { FestagoViewModelFactory }

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initBinding()
initObserving()
initView()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private fun 호출 순서에 맞추면 가독성이 더 좋을 것같습니다!

}

private fun initBinding() {
binding = ActivityStudentVerificationBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.lifecycleOwner = this
binding.vm = vm
}

private fun initView() {
val schoolId = intent.getLongExtra(KEY_SCHOOL_ID, -1L)
vm.loadSchoolEmail(schoolId)
initRequestVerificationCodeBtn(schoolId)
}

private fun initRequestVerificationCodeBtn(schoolId: Long) {
binding.btnRequestVerificationCode.setOnClickListener {
vm.sendVerificationCode(binding.tieVerificationCode.text.toString(), schoolId)
}
}

private fun initObserving() {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

관용적으로 사용되는 repeatOnStarted를 하나 만들어서 depth를 줄이는건 어떤가요?

vm.uiState.collect { uiState ->
binding.uiState = uiState
when (uiState) {
is StudentVerificationUiState.Success -> handleSuccess(uiState)
is StudentVerificationUiState.Loading, StudentVerificationUiState.Error -> {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
is StudentVerificationUiState.Loading, StudentVerificationUiState.Error -> {}
is StudentVerificationUiState.Loading, StudentVerificationUiState.Error -> Unit

}
}
}
}
}

private fun handleSuccess(uiState: StudentVerificationUiState.Success) {
binding.tvSchoolEmail.text =
getString(R.string.student_verification_tv_email_format, uiState.schoolEmail)

val format =
DateTimeFormatter.ofPattern(getString(R.string.student_verification_tv_timer_format))
binding.tvTimerVerificationCode.text = LocalTime.ofSecondOfDay(uiState.remainTime.toLong())
.format(format)

binding.btnVerificationConfirm.isEnabled = uiState.isValidateCode
}

companion object {

private const val KEY_SCHOOL_ID = "KEY_SCHOOL_ID"
fun getIntent(context: Context, schoolId: Long): Intent {
return Intent(context, StudentVerificationActivity::class.java).apply {
putExtra(KEY_SCHOOL_ID, schoolId)
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
companion object {
private const val KEY_SCHOOL_ID = "KEY_SCHOOL_ID"
fun getIntent(context: Context, schoolId: Long): Intent {
return Intent(context, StudentVerificationActivity::class.java).apply {
putExtra(KEY_SCHOOL_ID, schoolId)
}
}
}
companion object {
private const val KEY_SCHOOL_ID = "KEY_SCHOOL_ID"
fun getIntent(context: Context, schoolId: Long): Intent {
return Intent(context, StudentVerificationActivity::class.java).apply {
putExtra(KEY_SCHOOL_ID, schoolId)
}
}
}

함수하고 변수는 떨어져 있는게 더 가독성있는거 같습니다!

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.festago.festago.presentation.ui.studentsverification

sealed interface StudentVerificationUiState {
object Loading : StudentVerificationUiState

data class Success(
val schoolEmail: String,
val remainTime: Int,
val isValidateCode: Boolean = false,
) : StudentVerificationUiState

object Error : StudentVerificationUiState

val shouldShowSuccess get() = this is Success
val shouldShowLoading get() = this is Loading
val shouldShowError get() = this is Error
}
Loading