Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,10 @@ data class NewImageUrl(
data class ImageData(
val image: Any,
val description: String
)

data class CommonImageData(
val image: Any,
val type: String,
val description: String
Comment on lines 14 to +22
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

ImageData vs CommonImageData 중복 – 모델 정리 필요

두 데이터 클래스가 image·description 을 공유하고 CommonImageData 에만 type 필드가 추가되었습니다. 모델 중복은 유지보수 비용을 키우므로 다음을 고려해 주세요.

  1. type 을 nullable 로 두고 단일 ImageData 로 통합
  2. sealed class/인터페이스 로 공통 필드를 추상화
  3. UI 전용 모델은 ui.model 패키지로 이동하여 API 요청용 DTO 와 분리

또한 image: Any 는 타입 안정성이 없습니다. Uri | Int 등 실제 사용 타입을 sealed 로 좁히는 편이 안전합니다.

빠른 의견 부탁드립니다. 필요하면 리팩터 제안 PR 드리겠습니다.


🏁 Script executed:

#!/bin/bash
# 프로젝트 내에서 ImageData와 CommonImageData의 사용처 검색
rg -n "ImageData" -n .
rg -n "CommonImageData" -n .

Length of output: 6327


중복된 ImageData 모델 통합 및 타입 안전성 확보 필요

공통 필드(image, description)를 갖는 ImageData와 CommonImageData가 중복되어 유지보수 비용이 상승하고 있습니다. 아래 항목을 참고해 리팩터링을 권장합니다.

  • 앱 API DTO (app/src/main/java/com/project/drinkly_admin/api/request/image/StoreImageRequest.kt)
    ImageDataCommonImageData를 단일 ImageData(image, type?, description) 또는 sealed class/인터페이스로 추상화
    image: AnyImageSource(File/Uri/String 등) 같은 sealed class로 구체화
  • UI 계층 모델 분리
    • StoreDetailAvailableDrinksFragment.kt, AvailableDrinkAdapter.kt 등에서 API DTO가 아닌 UI 전용 모델(ui.model 패키지) 사용

예시 diff:

--- a/app/src/main/java/com/project/drinkly_admin/api/request/image/StoreImageRequest.kt
-data class ImageData(
-    val image: Any,
-    val description: String
-)
-
-data class CommonImageData(
-    val image: Any,
-    val type: String,
-    val description: String
-)
+data class ImageData(
+    val image: ImageSource,
+    val type: String? = null,
+    val description: String
+)

ImageSource sealed class 정의 및 UI-DTO 분리를 통해 명확한 계층 구조를 갖추면 유지보수가 용이해집니다.

🤖 Prompt for AI Agents
In
app/src/main/java/com/project/drinkly_admin/api/request/image/StoreImageRequest.kt
around lines 14 to 22, the ImageData and CommonImageData classes have
overlapping fields causing redundancy and maintenance overhead. Refactor by
merging these into a single ImageData class with an optional type field or use a
sealed class/interface abstraction. Replace the generic image: Any type with a
sealed class ImageSource representing specific image types like File, Uri, or
String to improve type safety. Additionally, separate UI models from API DTOs by
creating dedicated UI model classes in a ui.model package for use in UI
components like StoreDetailAvailableDrinksFragment.kt and
AvailableDrinkAdapter.kt.

)
63 changes: 63 additions & 0 deletions app/src/main/java/com/project/drinkly_admin/ui/BasicToast.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.project.drinkly_admin.ui

import android.content.Context
import android.content.res.Resources
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.PopupWindow
import androidx.databinding.DataBindingUtil
import com.project.drinkly_admin.R
import com.project.drinkly_admin.databinding.ToastBasicBinding
import com.project.drinkly_admin.util.MainUtil.toPx

object BasicToast {
fun showBasicToast(context: Context, message: String, icon: Int, anchorView: View) {
val inflater = LayoutInflater.from(context)
val binding: ToastBasicBinding =
DataBindingUtil.inflate(inflater, R.layout.toast_basic, null, false)

binding.run {
textViewTooltip.text = message
imageViewTooltip.setImageResource(icon)
}

val screenWidth = Resources.getSystem().displayMetrics.widthPixels
val marginPx = 20.toPx()
val popupWidth = screenWidth - marginPx * 2

val popupWindow = PopupWindow(binding.root,
popupWidth,
ViewGroup.LayoutParams.WRAP_CONTENT,
false
)

anchorView.post {
binding.root.measure(
View.MeasureSpec.UNSPECIFIED,
View.MeasureSpec.UNSPECIFIED
)
val popupHeight = binding.root.measuredHeight

val yOffset = -(anchorView.height + popupHeight + 8.toPx())

popupWindow.showAsDropDown(anchorView, 0, yOffset)

// 뷰가 detach될 때 PopupWindow 해제
anchorView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {}
override fun onViewDetachedFromWindow(v: View) {
if (popupWindow.isShowing) {
popupWindow.dismiss()
}
}
})

binding.root.postDelayed({
if (popupWindow.isShowing) {
popupWindow.dismiss()
}
}, 2000)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class AvailableDrinkAdapter(

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
with(holder.binding) {
textViewDrink.text = images?.get(position)?.description
textViewDrink.setText(images?.get(position)?.description)
Glide.with(activity)
.load(images?.get(position)?.image)
.into(imageViewAvailableDrink)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class StoreAvailableDrinksBottomSheetFragment() : DialogFragment() {

fun checkComplete() {
binding.run {
if(isImageUpload && editTextAvailableDrink.text.isNotEmpty()) {
if(editTextAvailableDrink.text.isNotEmpty()) {
buttonAdd.isEnabled = true
} else {
buttonAdd.isEnabled = false
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.bumptech.glide.Glide
import com.project.drinkly_admin.R
import com.project.drinkly_admin.api.request.store.StoreDetailRequest
import com.project.drinkly_admin.databinding.FragmentStoreDetailInfoBinding
import com.project.drinkly_admin.ui.BasicToast
import com.project.drinkly_admin.ui.MainActivity
import com.project.drinkly_admin.util.MainUtil.updateViewPositionForKeyboard
import com.project.drinkly_admin.util.MyApplication
Expand Down Expand Up @@ -168,13 +169,18 @@ class StoreDetailInfoFragment : Fragment() {
binding.run {
editTextStoreDescription.setText(viewModel.storeDetailInfo.value?.storeDescription)
editTextStoreInstagram.setText(viewModel.storeDetailInfo.value?.instagramUrl)
if(viewModel.storeDetailInfo.value?.instagramUrl.isNullOrEmpty()) {
BasicToast.showBasicToast(requireContext(), "대표 이미지를 등록하면 홍보에 더 도움이 돼요!", R.drawable.ic_check, binding.buttonSave)
}

if(viewModel.storeDetailInfo.value?.storeMainImageUrl != null) {
Glide.with(mainActivity).load(viewModel.storeDetailInfo.value?.storeMainImageUrl).into(imageViewStoreImage)
layoutStoreImage.visibility = View.INVISIBLE
} else {
layoutStoreImage.visibility = View.VISIBLE
}


toolbar.run {
textViewTitle.text = "매장 정보"
buttonBack.setOnClickListener {
Expand All @@ -201,7 +207,7 @@ class StoreDetailInfoFragment : Fragment() {
}

buttonSave.isEnabled =
(storeMainImage != null || viewModel.storeDetailInfo.value?.storeMainImageUrl != null) && editTextStoreDescription.text.isNotEmpty()
(storeMainImage != null || viewModel.storeDetailInfo.value?.storeMainImageUrl != null) || editTextStoreDescription.text.isNotEmpty() || editTextStoreInstagram.text.isNotEmpty()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.project.drinkly_admin.api.request.store.StoreDetailRequest
import com.project.drinkly_admin.api.response.home.StoreDetailResponse
import com.project.drinkly_admin.databinding.FragmentStoreDetailInfoMainBinding
import com.project.drinkly_admin.ui.MainActivity
import com.project.drinkly_admin.ui.home.HomeFragment
import com.project.drinkly_admin.util.MyApplication
import com.project.drinkly_admin.viewModel.StoreViewModel

Expand All @@ -23,8 +24,6 @@ class StoreDetailInfoMainFragment : Fragment() {
ViewModelProvider(requireActivity())[StoreViewModel::class.java]
}

private var isSaveInfo = MutableList(5) { false }

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
Expand Down Expand Up @@ -80,12 +79,9 @@ class StoreDetailInfoMainFragment : Fragment() {
}

buttonNext.setOnClickListener {
var storeInfo =
StoreDetailRequest(
isReady = true
)

viewModel.editStoreInfo(mainActivity, MyApplication.storeId, storeInfo)
mainActivity.supportFragmentManager.beginTransaction()
.replace(R.id.fragmentContainerView_main, HomeFragment())
.commit()
}
}

Expand All @@ -107,67 +103,59 @@ class StoreDetailInfoMainFragment : Fragment() {

fun checkInfo(storeInfo: StoreDetailResponse?) {
binding.run {
// 매장 정보
if(storeInfo?.storeDescription != null) {
isSaveInfo[0] = true
buttonStoreInfo.setBackgroundResource(R.drawable.background_primary10_radius10)
// 제공하는 주류
if(storeInfo?.availableDrinkImageUrls?.size != 0) {
buttonAvailableDrink.setBackgroundResource(R.drawable.background_primary10_radius10)
imageViewInfoNext1.setImageResource(R.drawable.ic_check_circle_checked)
} else {
isSaveInfo[0] = false
buttonStoreInfo.setBackgroundResource(R.drawable.background_white_radius10)
buttonAvailableDrink.setBackgroundResource(R.drawable.background_white_radius10)
imageViewInfoNext1.setImageResource(R.drawable.ic_next)
}
// 제공하는 주류
if(storeInfo?.availableDrinkImageUrls?.size != 0) {
isSaveInfo[1] = true
buttonAvailableDrink.setBackgroundResource(R.drawable.background_primary10_radius10)
// 멤버십 이용 가능 요일
if(storeInfo?.availableDays != null) {
buttonAvailableDate.setBackgroundResource(R.drawable.background_primary10_radius10)
imageViewInfoNext2.setImageResource(R.drawable.ic_check_circle_checked)
} else {
isSaveInfo[1] = false
buttonAvailableDrink.setBackgroundResource(R.drawable.background_white_radius10)
buttonAvailableDate.setBackgroundResource(R.drawable.background_white_radius10)
imageViewInfoNext2.setImageResource(R.drawable.ic_next)
}
// 메뉴판
if(storeInfo?.menuImageUrls?.size != 0) {
isSaveInfo[2] = true
buttonMenu.setBackgroundResource(R.drawable.background_primary10_radius10)
// 영업시간
if(storeInfo?.openingHours != null) {
buttonOpenTime.setBackgroundResource(R.drawable.background_primary10_radius10)
imageViewInfoNext3.setImageResource(R.drawable.ic_check_circle_checked)
} else {
isSaveInfo[2] = false
buttonMenu.setBackgroundResource(R.drawable.background_white_radius10)
buttonOpenTime.setBackgroundResource(R.drawable.background_white_radius10)
imageViewInfoNext3.setImageResource(R.drawable.ic_next)
}
// 영업시간
if(storeInfo?.openingHours != null) {
isSaveInfo[3] = true
buttonOpenTime.setBackgroundResource(R.drawable.background_primary10_radius10)

// 매장 정보
if(storeInfo?.storeDescription != null) {
buttonStoreInfo.setBackgroundResource(R.drawable.background_primary10_radius10)
imageViewInfoNext4.setImageResource(R.drawable.ic_check_circle_checked)
} else {
isSaveInfo[3] = false
buttonOpenTime.setBackgroundResource(R.drawable.background_white_radius10)
buttonStoreInfo.setBackgroundResource(R.drawable.background_white_radius10)
imageViewInfoNext4.setImageResource(R.drawable.ic_next)
}
// 멤버십 이용 가능 요일
if(storeInfo?.availableDays != null) {
isSaveInfo[4] = true
buttonAvailableDate.setBackgroundResource(R.drawable.background_primary10_radius10)
// 메뉴판
if(storeInfo?.menuImageUrls?.size != 0) {
buttonMenu.setBackgroundResource(R.drawable.background_primary10_radius10)
imageViewInfoNext5.setImageResource(R.drawable.ic_check_circle_checked)
} else {
isSaveInfo[4] = false
buttonAvailableDate.setBackgroundResource(R.drawable.background_white_radius10)
buttonMenu.setBackgroundResource(R.drawable.background_white_radius10)
imageViewInfoNext5.setImageResource(R.drawable.ic_next)
}
}
}

fun initView() {
binding.run {
if(isSaveInfo.all { it }) {
if(storeInfo?.isReady == true) {
buttonNext.visibility = View.VISIBLE
} else {
buttonNext.visibility = View.GONE
}

}
}

fun initView() {
binding.run {
toolbar.run {
textViewTitle.text = "매장 세부 정보 등록"
buttonBack.setOnClickListener {
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/java/com/project/drinkly_admin/util/MainUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ import java.util.Calendar
import java.util.Date
import java.util.Locale

import android.content.res.Resources
import kotlin.math.roundToInt

object MainUtil {
fun Int.toPx(): Int = (this * Resources.getSystem().displayMetrics.density).roundToInt()
}
// 투명한 status bar
fun Activity.setStatusBarTransparent() {
// 상태바를 투명하게 설정하고, 레이아웃을 상태바까지 확장
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,10 @@ class StoreViewModel : ViewModel() {
Log.d("DrinklyViewModel", "onResponse 성공: " + result?.toString())

MyApplication.storeId = result?.payload?.storeId ?: 0
storeDetailInfo.value = result?.payload

if(storeInfo.isReady != null) {
activity.supportFragmentManager.beginTransaction()
.replace(R.id.fragmentContainerView_main, HomeFragment())
.commit()
} else {
storeDetailInfo.value = result?.payload
activity.supportFragmentManager.popBackStack()

activity.supportFragmentManager.popBackStack()
}
} else {
// 통신이 실패한 경우(응답코드 3xx, 4xx 등)
var result: BaseResponse<StoreDetailResponse>? = response.body()
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/background_gray3_radius16_top.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

<solid android:color="@color/gray3"/>

<corners
android:topRightRadius="16dp"
android:topLeftRadius="16dp"/>
</shape>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

<solid android:color="@color/white"/>

<stroke
android:color="@color/primary_30"
android:width="1dp"/>

<corners android:radius="10dp"/>
</shape>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

<solid android:color="@color/white"/>

<stroke
android:color="@color/yellow"
android:width="1dp"/>

<corners android:radius="10dp"/>
</shape>
7 changes: 7 additions & 0 deletions app/src/main/res/drawable/background_white_radius12.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

<solid android:color="@color/white"/>

<corners android:radius="12dp"/>
</shape>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

<solid android:color="@color/white"/>

<corners
android:bottomRightRadius="12dp"
android:bottomLeftRadius="12dp"/>
</shape>
16 changes: 16 additions & 0 deletions app/src/main/res/drawable/ic_check.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M10,10m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
android:fillColor="#5168EC"/>
<path
android:pathData="M5,9.543L8.611,13.5L15,6.5"
android:strokeLineJoin="round"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>
16 changes: 16 additions & 0 deletions app/src/main/res/drawable/ic_edit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="17dp"
android:height="16dp"
android:viewportWidth="17"
android:viewportHeight="16">
<path
android:strokeWidth="1"
android:pathData="M8.369,3.563L3.911,3.644C3.131,3.658 2.512,4.338 2.527,5.163L2.661,12.632C2.676,13.458 3.32,14.115 4.099,14.101L11.859,13.961C12.639,13.947 13.258,13.267 13.243,12.442L13.145,6.975"
android:fillColor="#00000000"
android:strokeColor="#71788C"
android:strokeLineCap="round"/>
<path
android:pathData="M14.48,2.262C14.408,2.082 14.304,1.916 14.171,1.774C14.041,1.635 13.884,1.524 13.71,1.447C13.538,1.372 13.353,1.333 13.165,1.333C12.978,1.333 12.792,1.372 12.621,1.447C12.446,1.524 12.289,1.635 12.159,1.774L11.774,2.182L13.786,4.313L14.171,3.904C14.305,3.763 14.41,3.597 14.48,3.416C14.625,3.045 14.625,2.633 14.48,2.262ZM12.789,5.369L10.776,3.237L7.406,6.808C7.356,6.861 7.322,6.927 7.309,6.998L6.931,9.002C6.881,9.263 7.1,9.493 7.346,9.441L9.238,9.041C9.307,9.025 9.37,8.99 9.418,8.938L12.789,5.369Z"
android:fillColor="#71788C"
android:fillType="evenOdd"/>
</vector>
Binary file added app/src/main/res/drawable/img_basic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/src/main/res/drawable/img_beer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable/img_draft_beer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable/img_highball.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/src/main/res/drawable/img_soju.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading