Skip to content

Commit

Permalink
Merge: #48 프로필 화면 구현
Browse files Browse the repository at this point in the history
close #48
  • Loading branch information
ki960213 authored Aug 1, 2023
1 parent c2b4c00 commit 69f1f4a
Show file tree
Hide file tree
Showing 55 changed files with 1,473 additions and 54 deletions.
21 changes: 16 additions & 5 deletions android/2023-emmsale/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
import com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension

plugins {
id("org.jetbrains.kotlin.android")
id("com.android.application") version "8.0.2"
id("com.google.gms.google-services")
kotlin("plugin.serialization") version "1.8.21"
id("kotlin-kapt")
id("com.google.firebase.crashlytics")
}

android {
Expand Down Expand Up @@ -63,6 +61,12 @@ android {
dataBinding {
enable = true
}
tasks.withType(Test::class) {
useJUnitPlatform()
testLogging {
events.addAll(arrayOf(org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED, org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED, org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED))
}
}
flavorDimensions += "environment"
productFlavors {
create("staging") {
Expand Down Expand Up @@ -95,19 +99,26 @@ dependencies {
implementation("androidx.browser:browser:1.5.0")
implementation("androidx.work:work-runtime-ktx:2.8.1")
implementation("com.google.firebase:firebase-messaging-ktx:23.2.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
implementation("androidx.fragment:fragment-ktx:1.6.0")
implementation("androidx.fragment:fragment-ktx:1.6.1")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.okhttp3:okhttp:4.11.0")
implementation("com.squareup.okhttp3:mockwebserver:4.11.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
implementation("com.github.bumptech.glide:glide:4.15.1")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")

implementation(platform("com.google.firebase:firebase-bom:32.2.0"))
implementation("com.google.firebase:firebase-analytics-ktx")
implementation("com.google.firebase:firebase-messaging-ktx")
implementation("com.google.firebase:firebase-crashlytics-ktx")
implementation("com.google.firebase:firebase-messaging:23.2.0")

implementation("com.github.bumptech.glide:glide:4.15.1")
testImplementation("org.junit.jupiter", "junit-jupiter", "5.8.2")
testImplementation("org.assertj", "assertj-core", "3.22.0")
testImplementation("io.mockk:mockk-android:1.13.5")
testImplementation("io.mockk:mockk-agent:1.13.5")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.2")
}
4 changes: 2 additions & 2 deletions android/2023-emmsale/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
tools:targetApi="33">
<activity
android:name=".presentation.ui.main.MainActivity"
android:exported="true" />
android:exported="false" />
<activity
android:name=".presentation.ui.onboarding.OnboardingActivity"
android:exported="true" />
Expand Down Expand Up @@ -53,4 +53,4 @@
</service>
</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.emmsale.data.activity

data class Activity1(
val id: Long,
val activityType: ActivityType,
val name: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.emmsale.data.activity

enum class ActivityType {
EDUCATION, CLUB, EVENT, JOB
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.emmsale.data.member

import com.emmsale.data.activity.Activity1
import com.emmsale.data.activity.ActivityType

data class Member1(
val id: Long,
val name: String,
val description: String,
val imageUrl: String,
val activities: List<Activity1>,
) {
fun getActivities(activityType: ActivityType): List<Activity1> =
activities.filter { it.activityType == activityType }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ package com.emmsale.data.member
import com.emmsale.data.common.ApiResult

interface MemberRepository {

suspend fun getMember(memberId: Long): ApiResult<Member1>

suspend fun updateMember(member: Member): ApiResult<Unit>
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
package com.emmsale.data.member

import com.emmsale.data.common.ApiError
import com.emmsale.data.common.ApiResult
import com.emmsale.data.common.ApiSuccess
import com.emmsale.data.common.handleApi
import com.emmsale.data.member.dto.MemberApiModel
import com.emmsale.data.member.mapper.toData
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.withContext

class MemberRepositoryImpl(
private val dispatcher: CoroutineDispatcher = Dispatchers.IO,
private val memberService: MemberService,
) : MemberRepository {

override suspend fun getMember(memberId: Long): ApiResult<Member1> = withContext(dispatcher) {
val memberResponseDeferred = async { memberService.getMember(memberId) }
val activitiesResponseDeferred = async { memberService.getActivities(memberId) }
val memberResponse = memberResponseDeferred.await()
val activitiesResponse = activitiesResponseDeferred.await()

val memberApiModel = memberResponse.body() ?: return@withContext ApiError(
memberResponse.code(),
memberResponse.errorBody().toString()
)
val activitiesApiModels = activitiesResponse.body() ?: return@withContext ApiError(
activitiesResponse.code(),
activitiesResponse.errorBody().toString()
)

ApiSuccess(memberApiModel.toData(activitiesApiModels))
}

override suspend fun updateMember(member: Member): ApiResult<Unit> = withContext(dispatcher) {
handleApi(memberService.updateMember(MemberApiModel.from(member))) { }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
package com.emmsale.data.member

import com.emmsale.data.member.dto.ActivitiesAssociatedByActivityTypeApiModel
import com.emmsale.data.member.dto.MemberApiModel
import com.emmsale.data.member.dto.MemberWithoutActivitiesApiModel
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path

interface MemberService {

@GET("members/{memberId}")
suspend fun getMember(@Path("memberId") memberId: Long): Response<MemberWithoutActivitiesApiModel>

@GET("members/{memberId}/activities")
suspend fun getActivities(@Path("memberId") memberId: Long): Response<List<ActivitiesAssociatedByActivityTypeApiModel>>

@POST("/members")
suspend fun updateMember(@Body member: MemberApiModel): Response<Unit>
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.emmsale.data.member.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ActivitiesAssociatedByActivityTypeApiModel(
@SerialName("activityType")
val activityType: String,
@SerialName("memberActivityResponses")
val memberActivityResponses: List<ActivityApiModel>,
)

@Serializable
data class ActivityApiModel(
val id: Long,
val name: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.emmsale.data.member.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class MemberWithoutActivitiesApiModel(
@SerialName("id")
val id: Long,
@SerialName("name")
val name: String = "",
@SerialName("description")
val description: String = "",
@SerialName("imageUrl")
val imageUrl: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.emmsale.data.member.mapper

import com.emmsale.data.activity.Activity1
import com.emmsale.data.activity.ActivityType
import com.emmsale.data.member.Member1
import com.emmsale.data.member.dto.ActivitiesAssociatedByActivityTypeApiModel
import com.emmsale.data.member.dto.MemberWithoutActivitiesApiModel

fun MemberWithoutActivitiesApiModel.toData(activities: List<ActivitiesAssociatedByActivityTypeApiModel>): Member1 =
Member1(
id = this.id,
name = this.name,
description = this.description,
imageUrl = this.imageUrl,
activities = activities.toData()
)

private fun List<ActivitiesAssociatedByActivityTypeApiModel>.toData(): List<Activity1> =
this.flatMap { it.toData() }

private fun ActivitiesAssociatedByActivityTypeApiModel.toData(): List<Activity1> =
memberActivityResponses.map {
Activity1(
id = it.id,
activityType = activityType.toData(),
name = it.name
)
}

private fun String.toData(): ActivityType =
when (this) {
"동아리" -> ActivityType.CLUB
"컨퍼런스" -> ActivityType.EVENT
"교육" -> ActivityType.EDUCATION
"직무" -> ActivityType.JOB
else -> throw IllegalStateException("회원의 활동 Json 데이터를 도메인 모델로 매핑하는 데 실패했습니다. 서버와 Api 스펙을 다시 상의해보세요.")
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import androidx.core.content.ContextCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import com.emmsale.R
import com.emmsale.presentation.utils.extension.px
import com.emmsale.presentation.utils.extension.dp

class ActivityTag : AppCompatCheckBox {
constructor(context: Context) : super(context)
Expand All @@ -25,7 +25,7 @@ class ActivityTag : AppCompatCheckBox {
gravity = Gravity.CENTER
background = ContextCompat.getDrawable(context, R.drawable.bg_activity_tag)
setTextColor(ContextCompat.getColor(context, R.color.black))
updatePadding(12.px, 0, 12.px, 0)
updatePadding(12.dp, 0, 12.dp, 0)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import androidx.appcompat.widget.AppCompatCheckBox
import androidx.core.content.ContextCompat
import androidx.core.view.updatePadding
import com.emmsale.R
import com.emmsale.presentation.utils.extension.px
import com.emmsale.presentation.utils.extension.dp

class EventTag : AppCompatCheckBox {
constructor(context: Context) : super(context)
Expand All @@ -25,7 +25,7 @@ class EventTag : AppCompatCheckBox {
minimumHeight = 0
background = ContextCompat.getDrawable(context, R.drawable.bg_event_tag)
setTextColor(ContextCompat.getColor(context, R.color.black))
updatePadding(12.px, 6.px, 12.px, 6.px)
updatePadding(12.dp, 6.dp, 12.dp, 6.dp)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.emmsale.presentation.ui.login

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
Expand All @@ -16,6 +17,7 @@ import com.emmsale.databinding.ActivityLoginBinding
import com.emmsale.presentation.common.extension.checkPostNotificationPermission
import com.emmsale.presentation.common.extension.showToast
import com.emmsale.presentation.ui.login.uistate.LoginUiState
import com.emmsale.presentation.ui.main.MainActivity
import com.emmsale.presentation.ui.onboarding.OnboardingActivity
import com.emmsale.presentation.utils.builder.uri
import com.google.android.material.snackbar.Snackbar
Expand Down Expand Up @@ -64,7 +66,7 @@ class LoginActivity : AppCompatActivity() {
}

private fun navigateToMain() {
// startActivity(MainActivity.getIntent(this))
MainActivity.startActivity(this)
finish()
}

Expand Down Expand Up @@ -118,6 +120,11 @@ class LoginActivity : AppCompatActivity() {

companion object {
private const val GITHUB_CODE_PARAMETER = "code"

fun startActivity(context: Context) {
val intent = Intent(context, LoginActivity::class.java)
context.startActivity(intent)
}
}

@SuppressLint("InlinedApi")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,65 @@
package com.emmsale.presentation.ui.main

import android.os.Bundle
import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.fragment.app.commit
import androidx.fragment.app.commitNow
import com.emmsale.R
import com.emmsale.databinding.ActivityMainBinding
import com.emmsale.presentation.ui.main.events.EventsFragment
import com.emmsale.presentation.ui.main.myProfile.MyProfileFragment

class MainActivity : AppCompatActivity() {

private val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setContentView(binding.root)

initBottomNavigationView()
}

private fun initBottomNavigationView() {
val mainBottomNavigationView = binding.bnvMain

addAllFragments()

mainBottomNavigationView.setOnItemSelectedListener {
when (it.itemId) {
R.id.mi_main_profile -> showFragment(MyProfileFragment.TAG)
R.id.mi_main_home -> showFragment(EventsFragment.TAG)
}
return@setOnItemSelectedListener true
}

mainBottomNavigationView.selectedItemId = R.id.mi_main_home
}

private fun addAllFragments() {
supportFragmentManager.commitNow {
add(R.id.fcv_main, MyProfileFragment(), MyProfileFragment.TAG)
add(R.id.fcv_main, EventsFragment(), EventsFragment.TAG)
}
}

private fun showFragment(tag: String) {
supportFragmentManager.commit {
val fragment = supportFragmentManager.findFragmentByTag(tag)
?: throw IllegalStateException("태그 ${tag}로 프래그먼트를 찾을 수 없습니다. 프래그먼트 초기화 로직을 다시 살펴보세요.")
supportFragmentManager.fragments.forEach { hide(it) }
show(fragment)
}
}

companion object {
fun startActivity(context: Context) {
val intent = Intent(context, MainActivity::class.java)
context.startActivity(intent)
}
}
}
Loading

0 comments on commit 69f1f4a

Please sign in to comment.