Skip to content

Merge release/1.1.3 to develop#255

Merged
DongChyeon merged 68 commits intodevelopfrom
release/1.1.3
Sep 16, 2025
Merged

Merge release/1.1.3 to develop#255
DongChyeon merged 68 commits intodevelopfrom
release/1.1.3

Conversation

@DongChyeon
Copy link
Member

@DongChyeon DongChyeon commented Sep 16, 2025

Related issue ๐Ÿ› 

closed #<issue_number>

์–ด๋–ค ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์žˆ์—ˆ๋‚˜์š”?

  • ๐Ÿž BugFix Something isn't working
  • ๐ŸŽจ Design Markup & styling
  • ๐Ÿ“ƒ Docs Documentation writing and editing (README.md, etc.)
  • โœจ Feature Feature
  • ๐Ÿ”จ Refactor Code refactoring
  • โš™๏ธ Setting Development environment setup
  • โœ… Test Test related (Junit, etc.)

CheckPoint โœ…

PR์ด ๋‹ค์Œ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

  • PR ์ปจ๋ฒค์…˜์— ๋งž๊ฒŒ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. (ํ•„์ˆ˜)
  • mergeํ•  ๋ธŒ๋žœ์น˜์˜ ์œ„์น˜๋ฅผ ํ™•์ธํ•ด ์ฃผ์„ธ์š”(mainโŒ/developโญ•) (ํ•„์ˆ˜)
  • Approve๋œ PR์€ assigner๊ฐ€ ๋จธ์ง€ํ•˜๊ณ , ์ˆ˜์ • ์š”์ฒญ์ด ์˜จ ๊ฒฝ์šฐ ์ˆ˜์ • ํ›„ ๋‹ค์‹œ push๋ฅผ ํ•ฉ๋‹ˆ๋‹ค. (ํ•„์ˆ˜)
  • BugFix์˜ ๊ฒฝ์šฐ, ๋ฒ„๊ทธ์˜ ์›์ธ์„ ํŒŒ์•…ํ•˜์˜€์Šต๋‹ˆ๋‹ค. (์„ ํƒ)

Work Description โœ๏ธ

  • ์ž‘์—… ๋‚ด์šฉ

Uncompleted Tasks ๐Ÿ˜…

  • Task1

To Reviewers ๐Ÿ“ข

Summary by CodeRabbit

  • New Features

    • ํ™ˆ ์—…๋ฐ์ดํŠธ ์•ˆ๋‚ด ๋ฐฐ๋„ˆ ์ถ”๊ฐ€(๋‹ซ๊ธฐ/๋‹ค์‹œ ๋ณด์ง€ ์•Š๊ธฐ)
    • ํฌ์ถ˜ ๋”ฅ๋งํฌ ์ง€์›(orbitapp://fortune)
    • ์•Œ๋žŒ ๋ฐœ์ƒ ํ›„ ๋‹น์ผ 1ํšŒ ํฌ์ถ˜ ์ž๋™ ์ƒ์„ฑ(๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค์ผ€์ค„๋ง)
  • UI/UX

    • ํฌ์ถ˜ ๋กœ๋”ฉ ํ™”๋ฉด ๊ฐœํŽธ(์ƒˆ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ฐ ๋™์  ์—ฐ์ถœ)
    • ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ฐ” ์Šคํฌ๋ฆผ ๋ฐ ์‹œ์Šคํ…œ ๋ฐ” ํŒจ๋”ฉ ๋ฐ˜์˜
    • ์•Œ๋žŒ ์ƒํ˜ธ์ž‘์šฉยท์˜จ๋ณด๋”ฉยท์•Œ๋žŒ ํŽธ์ง‘ ๋ ˆ์ด์•„์›ƒ ๊ฐœ์„ 
    • ๋ฏธ์…˜ ์„ ํƒ UI ๊ฐœ์„  ๋ฐ ์„ ํƒ ์ƒํƒœ ํ‘œ์‹œ
  • Improvements

    • โ€œํ•˜๋ฃจ ์ฒซ ์•Œ๋žŒ ํ•ด์ œโ€ ํŒ๋ณ„ ๋กœ์ง ๊ฐœ์„ (์ •ํ™•๋„ ํ–ฅ์ƒ)
    • ์žฌ์˜ˆ์•ฝ ์‹œ ๋น„ํ™œ์„ฑ ์•Œ๋žŒ ์ œ์™ธ ์ฒ˜๋ฆฌ
    • ์ด๋ฏธ์ง€ ๋กœ๋”ฉ ์„ฑ๋Šฅ ๋ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—…๋ฐ์ดํŠธ
    • ์•ฑ ๋ฒ„์ „ ์—…๋ฐ์ดํŠธ: 1.1.3
  • Permissions

    • ๋„คํŠธ์›Œํฌ ์ƒํƒœ ๊ถŒํ•œ ์ถ”๊ฐ€
  • Chores

    • WorkManager/Hilt ํ†ตํ•ฉ ๋ฐ ์˜์กด์„ฑ ์ •๋น„ (Work, Hilt, Coil ๋“ฑ)

โ€ฆ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—… ์ฒ˜๋ฆฌ
โ€ฆ-status

[REFACTOR] ์šด์„ธ ์ƒ์„ฑ ์ƒํƒœ ๊ด€๋ฆฌ์šฉ FortuneCreateStatusFlow ๋„์ž…
โ€ฆom-sheet

[FEAT] ์—…๋ฐ์ดํŠธ ๊ณต์ง€ ๋ฐ”ํ…€์‹œํŠธ
@DongChyeon DongChyeon self-assigned this Sep 16, 2025
@DongChyeon DongChyeon added the โค๏ธโ€๐Ÿฉน CHORE ์ž‘์€ ์ˆ˜์ •(ํƒ€์ž…๋ณ€์ˆ˜, ํŒจํ‚ค์ง€๊ตฌ์กฐ ๋ณ€๊ฒฝ ๋“ฑ) label Sep 16, 2025
@coderabbitai
Copy link

coderabbitai bot commented Sep 16, 2025

Pre-merge checks and finishing touches

โŒ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage โš ๏ธ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
โœ… Passed checks (2 passed)
Check name Status Explanation
Description Check โœ… Passed Check skipped - CodeRabbitโ€™s high-level summary is enabled.
Title Check โœ… Passed PR ์ œ๋ชฉ 'Merge release/1.1.3 to develop'์€ ์†Œ์Šค์™€ ๋Œ€์ƒ ๋ธŒ๋žœ์น˜๋ฅผ ๋ช…ํ™•ํžˆ ํ‘œ์‹œํ•˜์—ฌ PR์˜ ํ•ต์‹ฌ ๋ชฉ์ (๋ฆด๋ฆฌ์Šค ๋ธŒ๋žœ์น˜ ๋ณ‘ํ•ฉ)์„ ์ •ํ™•ํ•˜๊ฒŒ ์š”์•ฝํ•ฉ๋‹ˆ๋‹ค. ๊ฐ„๊ฒฐํ•˜๊ณ  ํŒ๋…์„ฑ์ด ์ข‹์•„ ํžˆ์Šคํ† ๋ฆฌ ์Šค์บ” ์‹œ ๋ฌด์—‡์„ ์ˆ˜ํ–‰ํ–ˆ๋Š”์ง€ ๋ถ„๋ช…ํžˆ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์ œ๋ชฉ์ด ๋ณ€๊ฒฝ๋œ ์ฃผ์š” ๊ธฐ๋Šฅ์ด๋‚˜ ๋ฒ„๊ทธ ์ˆ˜์ •์„ ์„ค๋ช…ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ๋น ๋ฅธ ๋งฅ๋ฝ ํŒŒ์•…์„ ์œ„ํ•ด ๋” ์„œ์ˆ ์ ์ธ ์ œ๋ชฉ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.
โœจ Finishing touches
  • ๐Ÿ“ Generate Docstrings
๐Ÿงช Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch release/1.1.3

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

โค๏ธ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Sep 16, 2025

Codecov Report

โŒ Patch coverage is 1.16009% with 426 lines in your changes missing coverage. Please review.
โœ… Project coverage is 4.06%. Comparing base (374369f) to head (700e803).
โš ๏ธ Report is 70 commits behind head on develop.

Files with missing lines Patch % Lines
...e/component/bottomsheet/UpdateNoticeBottomSheet.kt 0.00% 86 Missing โš ๏ธ
...ure/home/src/main/java/com/yapp/home/HomeScreen.kt 0.00% 56 Missing โš ๏ธ
...larm/receivers/AlarmInteractionActivityReceiver.kt 0.00% 47 Missing โš ๏ธ
...m/component/bottomsheet/AlarmMissionBottomSheet.kt 0.00% 45 Missing โš ๏ธ
.../home/src/main/java/com/yapp/home/HomeViewModel.kt 0.00% 41 Missing โš ๏ธ
.../com/yapp/home/alarm/addedit/AlarmAddEditScreen.kt 0.00% 37 Missing โš ๏ธ
...ata/local/datasource/FortuneLocalDataSourceImpl.kt 0.00% 28 Missing โš ๏ธ
...home/component/bottomsheet/AlarmListBottomSheet.kt 0.00% 21 Missing โš ๏ธ
...ain/java/com/yapp/alarm/receivers/AlarmReceiver.kt 0.00% 20 Missing โš ๏ธ
.../main/java/com/yapp/alarm/AndroidAlarmScheduler.kt 0.00% 10 Missing โš ๏ธ
... and 6 more

โŒ Your project status has failed because the head coverage (4.06%) is below the target coverage (60.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##             develop    #255      +/-   ##
============================================
- Coverage       4.25%   4.06%   -0.19%     
  Complexity        53      53              
============================================
  Files             50      51       +1     
  Lines           4446    4670     +224     
  Branches         649     689      +40     
============================================
+ Hits             189     190       +1     
- Misses          4247    4470     +223     
  Partials          10      10              
Files with missing lines Coverage ฮ”
.../data/local/datasource/AlarmLocalDataSourceImpl.kt 0.00% <รธ> (รธ)
...om/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt 0.00% <รธ> (รธ)
...e/home/src/main/java/com/yapp/home/HomeContract.kt 0.00% <0.00%> (รธ)
.../main/java/com/yapp/alarm/services/AlarmService.kt 0.00% <0.00%> (รธ)
...yapp/data/repositoryimpl/UserInfoRepositoryImpl.kt 0.00% <0.00%> (รธ)
...p/data/local/datasource/UserLocalDataSourceImpl.kt 0.00% <0.00%> (รธ)
...om/yapp/alarm/receivers/RescheduleAlarmReceiver.kt 0.00% <0.00%> (รธ)
.../yapp/data/repositoryimpl/FortuneRepositoryImpl.kt 42.30% <35.71%> (+0.64%) โฌ†๏ธ
.../main/java/com/yapp/alarm/AndroidAlarmScheduler.kt 0.00% <0.00%> (รธ)
...ain/java/com/yapp/alarm/receivers/AlarmReceiver.kt 0.00% <0.00%> (รธ)
... and 8 more
๐Ÿš€ New features to boost your workflow:
  • โ„๏ธ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and canโ€™t be posted inline due to platform limitations.

โš ๏ธ Outside diff range comments (7)
core/network/src/main/java/com/yapp/network/di/NetworkModule.kt (2)

41-50: ํƒ€์ž„์•„์›ƒ 300์ดˆ๋Š” ๊ณผ๋„ โ€” ์‹คํŒจ ๊ฐ์ง€ ์ง€์—ฐ/๋ฆฌ์†Œ์Šค ์ ์œ  ์ฆ๊ฐ€

๋ณดํŽธ๊ฐ’์œผ๋กœ๋Š” ๊ณผํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜ API๋Š” ๋” ์งง๊ฒŒ, ๋Œ€์šฉ๋Ÿ‰ ์—…/๋‹ค์šด๋กœ๋“œ๋งŒ per-call๋กœ ๋Š˜๋ฆฌ๋Š” ๊ฒŒ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ˜ธ์ถœ ์ œํ•œ์„ ์œ„ํ•ด callTimeout๋„ ๊ณ ๋ คํ•ด ์ฃผ์„ธ์š”.

-            .readTimeout(300, TimeUnit.SECONDS)
-            .writeTimeout(300, TimeUnit.SECONDS)
-            .connectTimeout(300, TimeUnit.SECONDS)
+            .connectTimeout(15, TimeUnit.SECONDS)
+            .readTimeout(30, TimeUnit.SECONDS)
+            .writeTimeout(30, TimeUnit.SECONDS)
+            .callTimeout(60, TimeUnit.SECONDS)

41-50: ๊ธด๊ธ‰: ์ธ์ฆ Interceptor/Authenticator ๋ˆ„๋ฝ โ€” ๋ณดํ˜ธ API ์‹คํŒจ(๋ธ”๋กœ์ปค), ์ฆ‰์‹œ ๋ณต๊ตฌ ํ•„์š”

core/network/src/main/java/com/yapp/network/di/NetworkModule.kt์˜ provideHttpClient(๋ผ์ธ 41โ€“49)์—์„œ OkHttpClient์— loggingInterceptor๋งŒ ๋“ฑ๋ก๋˜์–ด ์žˆ์œผ๋ฉฐ, ์ €์žฅ์†Œ(.kt/.java) ์ „์ฒด ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ addInterceptor/.authenticator ํ˜ธ์ถœ ๋ฐ Authorization ํ† ํฐ ์ฃผ์ž… ํ”์ ์ด ๋ฐœ๊ฒฌ๋˜์ง€ ์•Š์Œ โ€” ๋ณดํ˜ธ API๋Š” Authorization ํ—ค๋”๊ฐ€ ์—†์œผ๋ฉด 401/์‹คํŒจ ๋ฐœ์ƒ.
์กฐ์น˜(๊ถŒ์žฅ): provideHttpClient์— ์ธ์ฆ Interceptor ๋ฐ/๋˜๋Š” Authenticator ์˜์กด์„ฑ ์ฃผ์ž… ํ›„ Builder์— ์ ์šฉ(์˜ˆ: optional authInterceptor/authenticator ํŒŒ๋ผ๋ฏธํ„ฐ ํ›„ addInterceptor/authenticator ํ˜ธ์ถœ) ๋ฐ ํ† ํฐ ์ฃผ์ž…ยท401 ์žฌ๋ฐœ๊ธ‰ ๋กœ์ง ์ •์ƒ ๋™์ž‘ ํ™•์ธ.

feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingViewModel.kt (1)

134-147: ๋นˆ ์‚ฌ์šด๋“œ ๋ชฉ๋ก ์‹œ IndexOutOfBoundsException ์œ„ํ—˜.

sounds๊ฐ€ ๋นˆ ๋ฆฌ์ŠคํŠธ๋ฉด index 0 ์ ‘๊ทผ์—์„œ ํฌ๋ž˜์‹œ๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค. ์•ˆ์ „ ๊ฐ€๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

๋‹ค์Œ์ฒ˜๋Ÿผ ๊ธฐ๋ณธ ์„ ํƒ ๋กœ์ง์„ ์•ˆ์ „ํ•˜๊ฒŒ ๋ฐ”๊พธ๋Š” ๊ฒƒ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

-        alarmUseCase.getAlarmSounds().onSuccess { sounds ->
-            val defaultSoundIndex = sounds.indexOfFirst { it.title == "Homecoming" }.takeIf { it >= 0 } ?: 0
-            val defaultSoundUri = sounds[defaultSoundIndex]
+        alarmUseCase.getAlarmSounds().onSuccess { sounds ->
+            val defaultSound = sounds.firstOrNull { it.title == "Homecoming" } ?: sounds.firstOrNull()
+            if (defaultSound == null) {
+                Log.w("OnboardingViewModel", "No alarm sounds available; skip creating alarm")
+                return@onSuccess
+            }

๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ๋ถ€:

-                soundUri = "${defaultSoundUri.uri}",
+                soundUri = defaultSound.uri,
feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingGenderScreen.kt (1)

73-79: ์„ ํƒ ์ด๋ฒคํŠธ ๋กœ๊น…์ด ๋ˆ„๋ฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค (logEvent ๋ฏธ์ฃผ์ž…).

OnboardingGenderScreen์˜ logEvent ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๊ธฐ๋ณธ๊ฐ’ ๋นˆ ๋žŒ๋‹ค์—ฌ์„œ ์„ฑ๋ณ„ ์„ ํƒ ๋กœ๊ทธ๊ฐ€ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค. Route์—์„œ ์ฃผ์ž…ํ•˜์„ธ์š”.

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฃผ์ž…์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

     OnboardingGenderScreen(
         state = state,
         bottomSheetState = bottomSheetState,
         currentStep = 5,
         totalSteps = 6,
         processAction = viewModel::processAction,
+        logEvent = analyticsHelper::logEvent,
     )
feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionActivity.kt (1)

102-105: API 33 ์ „์šฉ registerReceiver ์˜ค๋ฒ„๋กœ๋“œ ์‚ฌ์šฉ์œผ๋กœ ํ•˜์œ„ OS์—์„œ ํฌ๋ž˜์‹œ ๋ฐœ์ƒ

RECEIVER_EXPORTED ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฐ›๋Š” 3-์ธ์ž ์˜ค๋ฒ„๋กœ๋“œ๋Š” Android 13(API 33)+ ์ „์šฉ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ์ฝ”๋“œ ๊ทธ๋Œ€๋กœ๋ฉด API 32 ์ดํ•˜์—์„œ NoSuchMethodError๋กœ ํฌ๋ž˜์‹œ๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค. SDK ๊ฐ€๋“œ๋กœ ๋ถ„๊ธฐํ•ด ์ฃผ์„ธ์š”.

 private fun registerAlarmInteractionActivityCloseReceiver() {
     val filter = IntentFilter(AlarmConstants.ACTION_ALARM_INTERACTION_ACTIVITY_CLOSE)
-    registerReceiver(broadcastReceiver, filter, RECEIVER_EXPORTED)
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+        registerReceiver(broadcastReceiver, filter, RECEIVER_EXPORTED)
+    } else {
+        @Suppress("DEPRECATION")
+        registerReceiver(broadcastReceiver, filter)
+    }
 }
core/common/src/main/java/com/yapp/common/navigation/route/MissionRoute.kt (1)

3-3: ์ž˜๋ชป๋œ import๋กœ ์ปดํŒŒ์ผ ์‹คํŒจ ๊ฐ€๋Šฅ์„ฑ

MissionMode๋Š” com.yapp.domain.model ํŒจํ‚ค์ง€๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. import ๊ฒฝ๋กœ๋ฅผ ์ˆ˜์ •ํ•ด ์ฃผ์„ธ์š”.

-import com.yapp.domain.MissionMode
+import com.yapp.domain.model.MissionMode
feature/home/src/main/java/com/yapp/home/HomeScreen.kt (1)

295-299: LocalDensity ์บก์ฒ˜ ๋ˆ„๋ฝ์œผ๋กœ ์ปดํŒŒ์ผ ์—๋Ÿฌ ์œ ๋ฐœ.

์•„๋ž˜์—์„œ placeable.height.toDp()๋ฅผ ํ˜ธ์ถœํ•˜์ง€๋งŒ Density ์Šค์ฝ”ํ”„๊ฐ€ ์—†์–ด ์ปดํŒŒ์ผ์— ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. LocalDensity.current๋ฅผ ์บก์ฒ˜ํ•ด ์‚ฌ์šฉํ•ด ์ฃผ์„ธ์š”.

     var sheetHalfExpandHeight by remember { mutableStateOf(0.dp) }
 
+    val density = LocalDensity.current
๐Ÿงน Nitpick comments (70)
feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditScreen.kt (4)

308-316: ํ•˜๋“œ์ฝ”๋”ฉ๋œ 20.dp ๋ฐ˜๋ณต ์ตœ์†Œํ™”

๋™์ผํ•œ ์ˆ˜ํ‰ ํŒจ๋”ฉ ๊ฐ’์ด ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ๋ฐ˜๋ณต๋ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ์ƒ์ˆ˜๋กœ ์ถ”์ถœํ•ด ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ด๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜์ฒ˜๋Ÿผ ๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

@@
-    OrbitBottomSheetLayout(sheetState = bottomSheetState) {
-        Column(
+    OrbitBottomSheetLayout(sheetState = bottomSheetState) {
+        val contentHorizontalPadding = 20.dp
+        Column(
@@
-            AlarmAddEditSelectDaysSection(
-                modifier = Modifier.padding(horizontal = 20.dp),
+            AlarmAddEditSelectDaysSection(
+                modifier = Modifier.padding(horizontal = contentHorizontalPadding),
@@
-            AlarmAddEditSettingsSection(
-                modifier = Modifier.padding(horizontal = 20.dp),
+            AlarmAddEditSettingsSection(
+                modifier = Modifier.padding(horizontal = contentHorizontalPadding),
@@
-                    .padding(
-                        start = 20.dp,
-                        end = 20.dp,
+                    .padding(
+                        start = contentHorizontalPadding,
+                        end = contentHorizontalPadding,
                         bottom = 12.dp,
                     ),

Also applies to: 325-329


286-286: ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ” ํŒจ๋”ฉ ์ค‘๋ณต ์—ฌ์ง€

OrbitBottomSheetLayout์ด ๋‚ด๋ถ€์—์„œ navigationBarsPadding()์„ ์ ์šฉํ•˜๋ฏ€๋กœ, ์ €์žฅ ๋ฒ„ํŠผ์˜ bottom = 12.dp์™€ ํ•ฉ์ณ์ ธ ์ผ๋ถ€ ๊ธฐ๊ธฐ(3โ€‘button ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋“ฑ)์—์„œ ์ง€๋‚˜์น˜๊ฒŒ ํฐ ํ•˜๋‹จ ์—ฌ๋ฐฑ์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. UX์ธก ํ™•์ธ ํ›„ ํ•„์š” ์‹œ ๋ฒ„ํŠผ ์ปจํ…Œ์ด๋„ˆ์—๋Š” imePadding()๋งŒ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ํ•˜๋‹จ ์—ฌ๋ฐฑ์„ ์กฐ๊ฑด๋ถ€๋กœ ์ค„์ด๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด ์ฃผ์„ธ์š”.

Also applies to: 321-329


321-324: ์ €์žฅ ๋ฒ„ํŠผ ํ™œ์„ฑํ™” ์กฐ๊ฑด ์—ฐ๋™ ์ œ์•ˆ

ํ•ญ์ƒ enabled = true๋กœ ๋˜์–ด ์žˆ์–ด ๋น„์ •์ƒ ์ž…๋ ฅ/๋ฏธ๋ณ€๊ฒฝ ์ƒํƒœ์—์„œ๋„ ์ €์žฅ ๊ฐ€๋Šฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ํšจ์„ฑ/๋ณ€๊ฒฝ ์—ฌ๋ถ€ ํ”Œ๋ž˜๊ทธ๊ฐ€ ์žˆ๋‹ค๋ฉด ์—ฐ๋™์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: enabled = state.isSaveEnabled).


288-331: ์†Œํ˜• ํ™”๋ฉด/ํฐํŠธ ํ™•๋Œ€ํ•œ ํ™˜๊ฒฝ์—์„œ์˜ ์Šคํฌ๋กค ๊ฐ€๋Šฅ์„ฑ ๊ฒ€ํ† 

ํ˜„์žฌ ๋ฃจํŠธ Column์€ ์Šคํฌ๋กค์ด ์—†์–ด, ๋‚ด์šฉ์ด ๊ธธ์–ด์ง€๋Š” ์ƒํƒœ(์–ธ์–ด/ํฐํŠธ ์Šค์ผ€์ผ, ์‹œ์Šคํ…œ ์ œ์Šค์ฒ˜ ์˜์—ญ ํ•ฉ์‚ฐ)์—์„œ ํ•˜๋‹จ ๋ฒ„ํŠผ์ด ๊ฐ€๋ ค์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ์œ ๊ฐ€ ์žˆ๋‹ค๋ฉด LazyColumn ์ „ํ™˜ ํ˜น์€ ์„น์…˜๋ณ„ ์Šคํฌ๋กค ์ฒ˜๋ฆฌ ๊ฒ€ํ† ๋ฅผ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

feature/home/src/main/java/com/yapp/home/component/bottomsheet/AlarmListBottomSheet.kt (1)

326-331: ๋‘ ๊ฐœ์˜ DropdownMenu๊ฐ€ ๋™์‹œ์— ์—ด๋ฆด ์ˆ˜ ์žˆ๋Š” ์ƒํƒœ ๊ณต๊ฐ„

์ด์ œ ํ•ญ์ƒ Compose ํŠธ๋ฆฌ์— ์กด์žฌํ•˜๋ฏ€๋กœ menuExpanded์™€ sortDropDownMenuExpanded๊ฐ€ ๋™์‹œ์— true๊ฐ€ ๋˜๋ฉด ๋‘ ๋ฉ”๋‰ด๊ฐ€ ๊ฒน์ณ ์—ด๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜ธ์ถœ ์ธก ๋ถˆ๋ณ€์‹์œผ๋กœ ์ƒํ˜ธ ๋ฐฐํƒ€๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, ๋‹จ์ผ ์ƒํƒœ๋กœ ํ†ตํ•ฉํ•˜๋Š” ํŽธ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ„๋‹จ ์ œ์•ˆ:

  • ๋‹จ์ผ ์ƒํƒœ๋กœ ํ†ตํ•ฉ: enum class Menu { None, Main, Sort }๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  expanded = (menu == Main/Sort)๋กœ ์ œ์–ด.
  • ํ˜น์€ onClickSort์—์„œ ๋ฐ˜๋“œ์‹œ menuExpanded=false๋ฅผ ๋ณด์žฅ.

ํ˜ธ์ถœ๋ถ€์—์„œ ๋‘ ๋ถˆ๋ฆฐ์„ ๋™์‹œ์— true๋กœ ๋งŒ๋“ค์ง€ ์•Š๋Š” ๋กœ์ง์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด์ฃผ์„ธ์š”. ์—†์œผ๋ฉด ์œ„ ๋ฆฌํŒฉํ„ฐ๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

Also applies to: 333-338

data/src/main/java/com/yapp/data/remote/di/ServiceModule.kt (1)

16-17: ๋ฉ”์„œ๋“œ ๋„ค์ด๋ฐ ์ผ๊ด€์„ฑ(nit): provideโ€ฆ ํ˜•ํƒœ ๊ถŒ์žฅ

Hilt ๊ด€๋ก€์ƒ provideXxx๋กœ ๋งž์ถ”๋ฉด ๊ฐ€๋…์„ฑ์ด ์ข‹์•„์ง‘๋‹ˆ๋‹ค.

-    fun providesApiService(retrofit: Retrofit): ApiService =
+    fun provideApiService(retrofit: Retrofit): ApiService =
core/network/src/main/java/com/yapp/network/di/NetworkModule.kt (3)

54-63: kotlinx.serialization ์ปจ๋ฒ„ํ„ฐ Optโ€‘in ํ•„์š” ๊ฐ€๋Šฅ์„ฑ

ํ”„๋กœ์ ํŠธ ์„ค์ •์— ๋”ฐ๋ผ asConverterFactory ์‚ฌ์šฉ ์‹œ ExperimentalSerializationApi Optโ€‘in์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ ๊ฒฝ๊ณ /์—๋Ÿฌ๊ฐ€ ๋ณด์ธ๋‹ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ๋ช…์‹œ Optโ€‘in ํ•ด ์ฃผ์„ธ์š”.

-    fun provideRetrofit(
+    @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class)
+    fun provideRetrofit(
         okHttpClient: OkHttpClient,
         buildConfigFieldProvider: BuildConfigFieldProvider,
         json: Json,
     ): Retrofit =

54-63: ๋น„โ€‘JSON/๋นˆ ์‘๋‹ต ๋Œ€๋น„: Scalars ์ปจ๋ฒ„ํ„ฐ ์„ ๋“ฑ๋ก ๊ถŒ์žฅ

์„œ๋ฒ„๊ฐ€ text/plain ๋˜๋Š” ๋นˆ ๋ฐ”๋””(204/200 without body)๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—”๋“œํฌ์ธํŠธ๊ฐ€ ์žˆ๋‹ค๋ฉด JSON ์ปจ๋ฒ„ํ„ฐ๋งŒ์œผ๋กœ๋Š” ์‹คํŒจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•„์š” ์‹œ ScalarsConverterFactory๋ฅผ JSON๋ณด๋‹ค ๋จผ์ € ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”.

         Retrofit.Builder()
-            .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
+            .addConverterFactory(retrofit2.converter.scalars.ScalarsConverterFactory.create())
+            .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
             .baseUrl(buildConfigFieldProvider.get().baseUrl)
             .client(okHttpClient)
             .build()

54-63: baseUrl ํŠธ๋ ˆ์ผ๋ง ์Šฌ๋ž˜์‹œ ๋ณด์žฅ

Retrofit์€ baseUrl์ด ๋ฐ˜๋“œ์‹œ '/'๋กœ ๋๋‚˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. BuildConfig ๊ฐ’์ด ๋ณด์žฅ๋˜์ง€ ์•Š์œผ๋ฉด ๋Ÿฐํƒ€์ž„ ์˜ˆ์™ธ๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค. ๋ฐฉ์–ด ์ฝ”๋“œ ์ถ”๊ฐ€๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

-            .baseUrl(buildConfigFieldProvider.get().baseUrl)
+            .baseUrl(
+                buildConfigFieldProvider.get().baseUrl.let {
+                    if (it.endsWith("/")) it else "$it/"
+                }
+            )
feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingGenderScreen.kt (2)

168-168: ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๋ฒ„ํŠผ ๋ผ๋ฒจ i18n ์ฒ˜๋ฆฌ ํ•„์š”.

๋‹ค๊ตญ์–ด ๋ฐ ์ ‘๊ทผ์„ฑ ์ผ๊ด€์„ฑ์„ ์œ„ํ•ด stringResource ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

๋ฆฌ์†Œ์Šค ํ‚ค๋Š” ํ”„๋กœ์ ํŠธ ์ƒํ™ฉ์— ๋งž๊ฒŒ ๊ต์ฒดํ•˜์„ธ์š”.

-            buttonLabel = "๋‹ค์Œ",
+            buttonLabel = stringResource(id = R.string.common_button_next),

190-206: UI ๋ผ๋ฒจ์„ ์ƒํƒœ ๊ฐ’์œผ๋กœ ์ง์ ‘ ์ €์žฅ/๋น„๊ตํ•˜๋Š” ํŒจํ„ด์€ i18n/๋„๋ฉ”์ธ ์ผ๊ด€์„ฑ์— ์ทจ์•ฝํ•ฉ๋‹ˆ๋‹ค.

ํ‘œ์‹œ ๋ฌธ์ž์—ด(๋‚จ์„ฑ/์—ฌ์„ฑ) ๋Œ€์‹  ๋„๋ฉ”์ธ ์ฝ”๋“œ(์˜ˆ: "MALE"/"FEMALE" ๋˜๋Š” enum)๋ฅผ ์ƒํƒœ์— ๋ณด๊ด€ํ•˜๊ณ , ๋ผ๋ฒจ์€ stringResource๋กœ ๋งคํ•‘ํ•˜์„ธ์š”.

๊ตญ์ œํ™”์™€ ์„œ๋ฒ„ ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ์— ๊ฐ•ํ•œ ํ˜•ํƒœ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐœ์„ ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

-                    listOf("๋‚จ์„ฑ", "์—ฌ์„ฑ").forEach { gender ->
+                    listOf(
+                        "MALE" to stringResource(R.string.gender_male),
+                        "FEMALE" to stringResource(R.string.gender_female),
+                    ).forEach { (genderCode, genderLabel) ->
                         Box(modifier = Modifier.weight(1f)) {
                             OrbitGenderToggle(
-                                label = gender,
-                                isSelected = state.selectedGender == gender,
+                                label = genderLabel,
+                                isSelected = state.selectedGender == genderCode,
                                 onToggle = {
                                     logEvent(
                                         AnalyticsEvent(
                                             type = "onboarding_gender_select",
                                             properties = mapOf(
-                                                AnalyticsEvent.OnboardingPropertiesKeys.GENDER to gender,
+                                                AnalyticsEvent.OnboardingPropertiesKeys.GENDER to genderCode,
                                             ),
                                         ),
                                     )
-                                    processAction(OnboardingContract.Action.UpdateGender(gender))
+                                    processAction(OnboardingContract.Action.UpdateGender(genderCode))
                                 },
                             )
                         }
                     }

๋ฆฌ์†Œ์Šค ํ‚ค(R.string.gender_male/female)๋Š” ์‹ค์ œ ํ”„๋กœ์ ํŠธ ํ‚ค๋กœ ๊ต์ฒด ๋ฐ”๋ž๋‹ˆ๋‹ค.

feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/action/AlarmActionContract.kt (1)

17-18: Boolean? โ†’ Boolean์œผ๋กœ ๋‹จ์ˆœํ™”ํ•˜๊ณ  null ๊ฒŒ์ดํŒ… ์ œ๊ฑฐ ์ œ์•ˆ

์ดˆ๊ธฐ ๋กœ๋”ฉ์€ initialLoading๋กœ ์ด๋ฏธ ๊ฐ€๋ ค์ง€๋ฏ€๋กœ shouldShowMissionStart์˜ ์‚ผ์ค‘ ์ƒํƒœ(null/true/false)๋Š” ๋ถˆํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ถˆ๋ณ€(Boolean)๋กœ ์ขํ˜€ ํƒ€์ž… ์•ˆ์ •์„ฑ๊ณผ ๋ถ„๊ธฐ ๋‹จ์ˆœํ™”๋ฅผ ๊ฐ€์ ธ๊ฐ€๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜์ฒ˜๋Ÿผ ๋ณ€๊ฒฝ ์‹œ, ํ™”๋ฉด ์ชฝ์˜ isFirstMission != null ๋ถ„๊ธฐ๋„ ์ œ๊ฑฐ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

-        val shouldShowMissionStart: Boolean? = null,
+        val shouldShowMissionStart: Boolean = false,

ํ™”๋ฉด ๋ฐ˜์˜(๋‹ค๋ฅธ ํŒŒ์ผ)๋„ ํ•จ๊ป˜ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค:

-    isFirstMission: Boolean?,
+    shouldShowMissionStart: Boolean,

๋ฐ ๋ฒ„ํŠผ ๋ Œ๋”๋ง์—์„œ null ๋ถ„๊ธฐ ์ œ๊ฑฐ.

feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/action/AlarmActionScreen.kt (5)

115-126: ํŒŒ๋ผ๋ฏธํ„ฐ ๋ช…/ํƒ€์ž…์„ ์ƒํƒœ์™€ ์ผ์น˜ํ•˜๋„๋ก ์ •๋ฆฌ

์ปดํฌ์ €๋ธ” ํŒŒ๋ผ๋ฏธํ„ฐ isFirstMission: Boolean?๋Š” ์ƒํƒœ๋ช…(shouldShowMissionStart)๊ณผ ์˜๋ฏธ๊ฐ€ ๋‹ค๋ฅด๊ณ  nullable์ž…๋‹ˆ๋‹ค. ๋™์ผ ๋ช…์นญยท๋ถˆ๋ณ€(Boolean)์œผ๋กœ ์ •๋ฆฌํ•˜๋ฉด ์ดํ•ด์™€ ์‚ฌ์šฉ์ด ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.

-private fun AlarmActionContent(
+private fun AlarmActionContent(
     isAm: Boolean,
     hour: Int,
     minute: Int,
     todayDate: String,
     snoozeEnabled: Boolean,
     snoozeInterval: Int,
     snoozeCount: Int,
-    isFirstMission: Boolean?,
+    shouldShowMissionStart: Boolean,
     onSnoozeClick: () -> Unit,
     onDismissClick: () -> Unit,
 )

ํ˜ธ์ถœ๋ถ€๋„ ํ•จ๊ป˜:

-            isFirstMission = state.shouldShowMissionStart,
+            shouldShowMissionStart = state.shouldShowMissionStart,

170-189: ๋ฒ„ํŠผ ๋ Œ๋”๋ง์˜ null ๊ฒŒ์ดํŒ… ์ œ๊ฑฐ๋กœ UI ๋‹จ์ˆœํ™”

initialLoading ํ™”๋ฉด์ด ์žˆ์œผ๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ์˜ null ๋ถ„๊ธฐ๋Š” ๋ถˆํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํ•ญ์ƒ ๋ฒ„ํŠผ์„ ๋ Œ๋”๋งํ•˜๊ณ  ๋ผ๋ฒจ๋งŒ ๋ถ„๊ธฐํ•˜์„ธ์š”.

-        if (isFirstMission != null) {
-            OrbitButton(
-                label = if (isFirstMission) {
+        OrbitButton(
+            label = if (shouldShowMissionStart) {
                 stringResource(id = R.string.alarm_off_mission_start_btn)
             } else {
                 stringResource(id = R.string.alarm_off_btn)
-            },
-            enabled = true,
-            modifier = Modifier
-                .padding(
-                    start = 40.dp,
-                    end = 40.dp,
-                    bottom = 48.dp,
-                )
-                .height(62.dp),
-            onClick = onDismissClick,
-            )
-        } else {
-            Spacer(modifier = Modifier.height(62.dp))
-        }
+            },
+            enabled = true,
+            modifier = Modifier
+                .padding(start = 40.dp, end = 40.dp, bottom = 48.dp)
+                .height(62.dp),
+            onClick = onDismissClick,
+        )

150-154: contentDescription ํ•˜๋“œ์ฝ”๋”ฉ โ†’ stringResource๋กœ i18n

์ ‘๊ทผ์„ฑ ๋ฌธ์ž์—ด์€ ๋ฆฌ์†Œ์Šค๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋ฆฌ์†Œ์Šค ์ถ”๊ฐ€ ํ›„ ๊ต์ฒด๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ:

-            contentDescription = "Alarm Action Character",
+            contentDescription = stringResource(id = R.string.alarm_action_character_cd),

208-213: "์˜ค์ „/์˜คํ›„" ํ•˜๋“œ์ฝ”๋”ฉ ์ œ๊ฑฐ

๋‹ค๊ตญ์–ด ๋Œ€์‘์„ ์œ„ํ•ด ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ๋Œ€์‹  stringResource ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ:

text = stringResource(if (isAm) R.string.am else R.string.pm)

299-313: ํ”„๋ฆฌ๋ทฐ ์ผ€์ด์Šค ๋ณด๊ฐ•(๋ฏธ์…˜ ์œ ๋ฌด 2์ข…)

๋ฏธ์…˜ ์‹œ์ž‘/์ผ๋ฐ˜ ์ข…๋ฃŒ ๋ฒ„ํŠผ ๋ผ๋ฒจ์„ ๋ชจ๋‘ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋„๋ก ํ”„๋ฆฌ๋ทฐ๋ฅผ 2๊ฐœ๋กœ ๋‚˜๋ˆ„๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/action/AlarmActionViewModel.kt (3)

30-36: ์ดˆ๊ธฐ Intent ์ˆœ์„œ OK, ๋‹ค๋งŒ ์ดˆ๊ธฐ๋กœ๋”ฉ ํ•ด์ œ ํƒ€์ด๋ฐ๋งŒ ์ผ๊ด€ํ™” ์ œ์•ˆ

fetchShouldShowMissionStart() โ†’ initializeAlarmState() โ†’ startClock() ์ˆœ์„œ๋Š” ํ•ฉ๋ฆฌ์ ์ž…๋‹ˆ๋‹ค. initialLoading = false๋Š” ๋งค์ดˆ๊ฐ€ ์•„๋‹ˆ๋ผ ์ฒซ ํ‹ฑ์—์„œ๋งŒ ๋‚ด๋ฆฌ๋„๋ก ํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ์ƒํƒœ ๊ฐฑ์‹ ์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


64-82: ๋ฌดํ•œ ๋ฃจํ”„๋Š” ์ทจ์†Œ ์นœํ™”์ ์œผ๋กœ

์ทจ์†Œ ์‹œ๊ทธ๋„ ์ธ์ง€ ๋ช…ํ™•์„ฑ์„ ์œ„ํ•ด while (true) ๋Œ€์‹  coroutineContext.isActive ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

+import kotlinx.coroutines.isActive
@@
-    private fun startClock() = intent {
-        while (true) {
+    private fun startClock() = intent {
+        while (coroutineContext.isActive) {
             val now = LocalTime.now()
             ...
             delay(1000L)
         }
     }

84-98: Snooze ๋‹ค์ค‘ ํƒญ ๊ฐ€๋“œ

์—ฐ์† ํƒญ ์‹œ ์‚ฌ์ด๋“œ์ดํŽ™ํŠธ ์ค‘๋ณต ๋‚ด๋น„๊ฒŒ์ด์…˜ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ์‹œ์  ๋””์Šค์—์ด๋ธ” ํ”Œ๋ž˜๊ทธ๋ฅผ ์ƒํƒœ์— ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ์ฒ˜๋ฆฌ ์ค‘ ๊ฐ€๋“œ๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

feature/home/src/main/AndroidManifest.xml (1)

3-3: ๊ถŒํ•œ ์ค‘๋ณต ์„ ์–ธ ์ •๋ฆฌ ์ œ์•ˆ

ACCESS_NETWORK_STATE๋Š” ๋ณดํ†ต ์•ฑ ๋ชจ๋“ˆ ๋งค๋‹ˆํŽ˜์ŠคํŠธ์—์„œ๋งŒ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค. ํ”ผ์ฒ˜ ๋ชจ๋“ˆ ์„ ์–ธ์€ ๋ณ‘ํ•ฉ ์‹œ ์ค‘๋ณต์ด ๋˜๊ณ , ๋™์ /์˜จ๋””๋งจ๋“œ ๋ชจ๋“ˆ์ธ ๊ฒฝ์šฐ ๋ถˆํ•„์š” ๊ถŒํ•œ ๋…ธ์ถœ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•ฑ ๋งค๋‹ˆํŽ˜์ŠคํŠธ์—๋งŒ ์œ ์ง€ํ•˜๊ณ  ์—ฌ๊ธฐ์„œ๋Š” ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์ ์šฉ diff:

-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <!-- moved to app/src/main/AndroidManifest.xml -->
core/alarm/src/main/java/com/yapp/alarm/scheduler/PostFortuneTaskScheduler.kt (1)

3-5: ์Šค์ผ€์ค„๋ง ๊ฐ€์‹œ์„ฑ ํ–ฅ์ƒ์„ ์œ„ํ•œ ๋ฐ˜ํ™˜๊ฐ’/๋ฌธ์„œํ™” ์ œ์•ˆ

ํ˜ธ์ถœ ์ธก์—์„œ โ€œํ์ž‰ ์„ฑ๊ณต/์Šคํ‚ตโ€ ์—ฌ๋ถ€๋‚˜ ํŠธ๋ž˜ํ‚น ID๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Boolean(์„ฑ๊ณต/์Šคํ‚ต) ๋ฐ˜ํ™˜ ๋˜๋Š” KDoc๋กœ ๋™์ž‘ ๋ณด์žฅ์„ ๋ช…์‹œํ•˜๋Š” ๊ฐœ์„ ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ diff:

-interface PostFortuneTaskScheduler {
-    fun enqueueOnceForToday()
-}
+/**
+ * ์˜ค๋Š˜ ๋‚ ์งœ ๊ธฐ์ค€์œผ๋กœ 1ํšŒ๋งŒ ํ์ž‰ํ•ฉ๋‹ˆ๋‹ค(์ค‘๋ณต ๋ฐฉ์ง€).
+ * @return true๋ฉด ์ƒˆ๋กœ ํ์ž‰๋จ, false๋ฉด ์ด๋ฏธ ํ์ž‰๋˜์–ด ์Šคํ‚ต๋จ.
+ */
+interface PostFortuneTaskScheduler {
+    fun enqueueOnceForToday(): Boolean
+}
core/alarm/src/main/java/com/yapp/alarm/AndroidAlarmScheduler.kt (1)

20-27: ๋กœ๊ทธ ์˜ค๋ฒ„ํ—ค๋“œ ์ ˆ๊ฐ(์ŠคํƒํŠธ๋ ˆ์ด์Šค/๋ฆด๋ฆฌ์Šค ๋นŒ๋“œ ์ฐจ๋‹จ)

Throwable ์ŠคํƒํŠธ๋ ˆ์ด์Šค๋Š” ๋น„์šฉ์ด ํฝ๋‹ˆ๋‹ค. ๋””๋ฒ„๊ทธ/๋กœ๊ฑฐ ๊ฐ€๋“œ๋กœ ์ œํ•œํ•˜๊ณ  ์ŠคํƒํŠธ๋ ˆ์ด์Šค๋Š” ์ œ๊ฑฐํ•˜์„ธ์š”.

๊ถŒ์žฅ diff:

-    private fun logSchedule(tag: String, alarm: Alarm, triggerMillis: Long, extra: String = "") {
-        Log.d("ScheduleTrace", "scheduleAlarm Called", Throwable())
-        Log.d(
-            "AlarmSchedule",
-            "[$tag] id=${alarm.id}, repeatDays=${alarm.repeatDays}, " +
-                "time=${java.time.Instant.ofEpochMilli(triggerMillis)} $extra",
-        )
-    }
+    private fun logSchedule(tag: String, alarm: Alarm, triggerMillis: Long, extra: String = "") {
+        if (!BuildConfig.DEBUG && !Log.isLoggable("AlarmSchedule", Log.DEBUG)) return
+        Log.d(
+            "AlarmSchedule",
+            "[$tag] id=${alarm.id}, repeatDays=${alarm.repeatDays}, " +
+                "time=${java.time.Instant.ofEpochMilli(triggerMillis)} $extra"
+        )
+    }
core/ui/src/main/java/com/yapp/ui/component/navigation/NavigationBarScrim.kt (1)

16-26: ์žฌ์‚ฌ์šฉ์„ฑ ๊ฐœ์„ : ์ƒ‰์ƒ/๋ชจ๋””ํŒŒ์ด์–ด ํŒŒ๋ผ๋ฏธํ„ฐํ™” ์ œ์•ˆ

ํ…Œ๋งˆ ์Šคํฌ๋ฆผ ์ปฌ๋Ÿฌ ์‚ฌ์šฉ์ด๋‚˜ zIndex ์กฐ์ •์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์–ด ํŒŒ๋ผ๋ฏธํ„ฐํ™”ํ•˜๋ฉด ํ™œ์šฉ๋„๊ฐ€ ์˜ฌ๋ผ๊ฐ‘๋‹ˆ๋‹ค.

์˜ˆ์‹œ diff:

-@Composable
-fun BoxScope.NavigationBarScrim() {
-    Box(
-        modifier = Modifier
-            .align(Alignment.BottomCenter)
-            .fillMaxWidth()
-            .windowInsetsBottomHeight(WindowInsets.navigationBars)
-            .background(Color.Black)
-            .zIndex(1f),
-    )
-}
+@Composable
+fun BoxScope.NavigationBarScrim(
+    modifier: Modifier = Modifier,
+    color: Color = Color.Black,
+    zIndex: Float = 1f,
+) {
+    Box(
+        modifier = modifier
+            .align(Alignment.BottomCenter)
+            .fillMaxWidth()
+            .windowInsetsBottomHeight(WindowInsets.navigationBars)
+            .background(color)
+            .zIndex(zIndex),
+    )
+}
feature/home/src/main/res/values/strings.xml (2)

51-53: '์„ ํƒ๋จ' ๋ผ๋ฒจ์˜ ์‚ฌ์šฉ ๋งฅ๋ฝ์„ a11y/UX ๊ด€์ ์—์„œ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.

ํ•™์Šต ๋ฉ”๋ชจ์ƒ(selectedMissionType/Count๋Š” ๋‚ด๋ถ€ ์ƒํƒœ)๊ณผ ๊ฐ™์ด '์„ ํƒ๋จ'์ด ๊ฐ€์‹œ ํ…์ŠคํŠธ๋กœ ๋…ธ์ถœ๋˜๋ฉด ์ €์žฅ/ํ™•์ • ์ƒํƒœ๋กœ ์˜ค์ธ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฐ๋ฆฌ๋”์šฉ contentDescription๋กœ๋งŒ ์“ฐ์ด๊ฑฐ๋‚˜, UI์—์„œ๋Š” ์ฒดํฌ/์„ ํƒ ์ƒํƒœ๊ฐ€ ๋ช…ํ™•ํžˆ ์ „๋‹ฌ๋˜๋„๋ก ํ™•์ธ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.


128-129: '๋‹ค์‹œ ๋ณด์ง€ ์•Š๊ธฐ' ๋™์ž‘ ํƒ€์ž…(์ฒดํฌ๋ฐ•์Šค vs ๋ฒ„ํŠผ) ๋ช…ํ™•ํ™” ๋ฐ ํ…์ŠคํŠธ ์ผ๊ด€์„ฑ ํ™•์ธ

ํ•ด๋‹น ๋ฌธ์ž์—ด์ด ํ† ๊ธ€(์ฒดํฌ๋ฐ•์Šค)์ธ์ง€, 1ํšŒ์„ฑ ๋ฒ„ํŠผ์ธ์ง€์— ๋”ฐ๋ผ ์ ‘๊ทผ์„ฑ ๋ ˆ์ด๋ธ”/์ƒํƒœ ์•ˆ๋‚ด๊ฐ€ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ์œ„์ ฏ ํƒ€์ž…๊ณผ ์ผ์น˜ํ•˜๋„๋ก ๋ณด์กฐ ํ…์ŠคํŠธ(์˜ˆ: ์„ค๋ช…/์ ‘๊ทผ์„ฑ ๋ ˆ์ด๋ธ”)๋ฅผ ์ถ”๊ฐ€ํ• ์ง€ ๊ฒ€ํ† ํ•ด ์ฃผ์„ธ์š”.

app/src/main/java/com/yapp/orbit/di/AppVersionModule.kt (1)

11-18: @nAmed ๋Œ€์‹  ํƒ€์ž… ์„ธ์ดํ”„ํ•œ @qualifier ์‚ฌ์šฉ ์ œ์•ˆ

๋ฌธ์ž์—ด Qualifier๋Š” ์ถฉ๋Œ/์˜คํƒ€ ๋ฆฌ์Šคํฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ „์šฉ @qualifier๋ฅผ ๋„์ž…ํ•ด ํƒ€์ž… ์„ธ์ดํ”„ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•ด ์ฃผ์„ธ์š”.

 package com.yapp.orbit.di
@@
 import dagger.hilt.components.SingletonComponent
-import javax.inject.Named
 import javax.inject.Singleton
+import javax.inject.Qualifier
@@
-@Module
-@InstallIn(SingletonComponent::class)
-object AppVersionModule {
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class AppVersion
+
+@Module
+@InstallIn(SingletonComponent::class)
+object AppVersionModule {
     @Provides
     @Singleton
-    @Named("appVersion")
-    fun provideAppVersion(): String = BuildConfig.VERSION_NAME
+    @AppVersion
+    fun provideAppVersion(): String = BuildConfig.VERSION_NAME
 }
core/designsystem/src/main/res/raw/fortune_loading.json (1)

1-1: Lottie ์›๋ณธ ๋ผ์ด์„ ์Šค/ํฌ๊ธฐ ๊ฒ€์ฆ ํ•„์š”

  • ๋Œ€์šฉ๋Ÿ‰ base64 WebP ์—์…‹ ์ธ๋ผ์ธ์€ APK/๋ฉ”๋ชจ๋ฆฌ ํ’‹ํ”„๋ฆฐํŠธ๋ฅผ ๋Š˜๋ฆฝ๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•˜๋ฉด ๋ฒกํ„ฐํ™”/์—์…‹ ๋ถ„๋ฆฌ/์••์ถ•์„ ๊ฒ€ํ† ํ•˜์„ธ์š”.
  • ์›๋ณธ ๋ผ์ด์„ ์Šค(์ƒ์—…์  ์ด์šฉ/๋ณ€๊ฒฝ ํ—ˆ์šฉ ์—ฌ๋ถ€)์™€ ๊ท€์† ํ‘œ๊ธฐ๋ฅผ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.
domain/src/main/java/com/yapp/domain/model/AlarmDay.kt (1)

16-23: ์–‘๋ฐฉํ–ฅ ๋งคํ•‘ ๋กœ์ง์€ ๋งž์Šต๋‹ˆ๋‹ค๋งŒ, enum ์ˆœ์„œ ์˜์กด์„ฑ ์ œ๊ฑฐ ๊ถŒ์žฅ

ํ˜„์žฌ ๊ตฌํ˜„์€ entries/ordinal์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. enum ์ˆœ์„œ ๋ณ€๊ฒฝ์— ์ทจ์•ฝํ•˜๋ฏ€๋กœ ๋ช…์‹œ์  ๋งคํ•‘์œผ๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

-fun AlarmDay.toDayOfWeek(): DayOfWeek {
-    return DayOfWeek.of(((this.ordinal + 6) % 7) + 1)
-}
+fun AlarmDay.toDayOfWeek(): DayOfWeek = when (this) {
+    AlarmDay.SUN -> DayOfWeek.SUNDAY
+    AlarmDay.MON -> DayOfWeek.MONDAY
+    AlarmDay.TUE -> DayOfWeek.TUESDAY
+    AlarmDay.WED -> DayOfWeek.WEDNESDAY
+    AlarmDay.THU -> DayOfWeek.THURSDAY
+    AlarmDay.FRI -> DayOfWeek.FRIDAY
+    AlarmDay.SAT -> DayOfWeek.SATURDAY
+}
 
-fun DayOfWeek.toAlarmDay(): AlarmDay {
-    val index = (this.value % 7)
-    return AlarmDay.entries[index]
-}
+fun DayOfWeek.toAlarmDay(): AlarmDay = when (this) {
+    DayOfWeek.SUNDAY -> AlarmDay.SUN
+    DayOfWeek.MONDAY -> AlarmDay.MON
+    DayOfWeek.TUESDAY -> AlarmDay.TUE
+    DayOfWeek.WEDNESDAY -> AlarmDay.WED
+    DayOfWeek.THURSDAY -> AlarmDay.THU
+    DayOfWeek.FRIDAY -> AlarmDay.FRI
+    DayOfWeek.SATURDAY -> AlarmDay.SAT
+}
data/src/main/java/com/yapp/data/local/datasource/UserLocalDataSource.kt (1)

9-10: Epoch ๋‹จ์œ„/ํƒ€์ž„์กด ๋ช…์„ธ ํ•„์š”

  • updateNoticeLastShownDateEpochFlow์˜ ๋‹จ์œ„(์ดˆ/๋ฐ€๋ฆฌ์ดˆ)์™€ ๊ธฐ์ค€(UTC/local midnight) ๋ช…์‹œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋‚ ์งœ ๋น„๊ต ๋กœ์ง๊ณผ ๋ถˆ์ผ์น˜ ์‹œ ์˜ค๋™์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • markUpdateNoticeShownToday๊ฐ€ ์–ด๋–ค ๊ธฐ์ค€ ์‹œ๊ฐ์„ ์ €์žฅํ•˜๋Š”์ง€ KDoc์— ๋ช…ํ™•ํžˆ ๋‚จ๊ฒจ ์ฃผ์„ธ์š”.

Also applies to: 15-16

app/src/main/java/com/yapp/orbit/OrbitApplication.kt (1)

11-11: ๋นˆ ๊ธฐ๋ณธ ์ƒ์„ฑ์ž ์ œ๊ฑฐ ๊ฐ€๋Šฅ

static analysis ๋„๊ตฌ๊ฐ€ ์ง€์ ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋นˆ ๊ธฐ๋ณธ ์ƒ์„ฑ์ž ()๋Š” ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

-class OrbitApplication() : Application(), Configuration.Provider {
+class OrbitApplication : Application(), Configuration.Provider {
app/src/main/AndroidManifest.xml (1)

5-5: ACCESS_NETWORK_STATE ๊ถŒํ•œ ์ค‘๋ณต ์„ ์–ธ

android.permission.ACCESS_NETWORK_STATE ๊ถŒํ•œ์ด 5๋ฒˆ๊ณผ 14๋ฒˆ ๋ผ์ธ์— ์ค‘๋ณต ์„ ์–ธ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

14๋ฒˆ ๋ผ์ธ์˜ ์ค‘๋ณต๋œ ๊ถŒํ•œ ์„ ์–ธ์„ ์ œ๊ฑฐํ•˜์„ธ์š”:

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

Also applies to: 14-14

core/alarm/src/main/java/com/yapp/alarm/receivers/AlarmReceiver.kt (1)

114-119: ๋กœ์ปฌ ํ•จ์ˆ˜ ์„ ์–ธ ์œ„์น˜ ๊ฐœ์„  ํ•„์š”

ringsToday() ํ•จ์ˆ˜๊ฐ€ ์ฝ”๋ฃจํ‹ด ์Šค์ฝ”ํ”„ ๋‚ด๋ถ€์— ์„ ์–ธ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ์ˆœ์ˆ˜ ํ•จ์ˆ˜์ด๋ฏ€๋กœ ํด๋ž˜์Šค ๋ ˆ๋ฒจ์ด๋‚˜ ์ƒ๋‹จ์— ์„ ์–ธํ•˜๋Š” ๊ฒƒ์ด ๋” ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค.

ํ•จ์ˆ˜๋ฅผ ํด๋ž˜์Šค์˜ private ๋ฉ”์„œ๋“œ๋กœ ์ด๋™ํ•˜๊ฑฐ๋‚˜ companion object์— ์ถ”๊ฐ€ํ•˜์„ธ์š”:

-                fun Alarm.ringsToday(): Boolean {
-                    if (repeatDays == 0) return true
-
-                    val todayAlarmDay = LocalDate.now().dayOfWeek.toAlarmDay()
-                    return (repeatDays and todayAlarmDay.bitValue) != 0
-                }

ํด๋ž˜์Šค ๋ ˆ๋ฒจ์— ์ถ”๊ฐ€:

private fun Alarm.ringsToday(): Boolean {
    if (repeatDays == 0) return true
    
    val todayAlarmDay = LocalDate.now().dayOfWeek.toAlarmDay()
    return (repeatDays and todayAlarmDay.bitValue) != 0
}
domain/src/main/java/com/yapp/domain/repository/FortuneRepository.kt (2)

20-20: ์ด๋ฆ„ ๋ถˆ์ผ์น˜ ๊ฐ€๋Šฅ์„ฑ: markFortuneAsFailed vs markFortuneFailed

datastore(UserPreferences) ์ชฝ ๊ตฌํ˜„์€ markFortuneFailed๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ๋ ˆ์ด์–ด ๊ฐ„ ๋ช…๋ช… ํ†ต์ผ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ๋„๋ฉ”์ธ ๋ฉ”์„œ๋“œ๋ฅผ markFortuneFailed๋กœ ๋งž์ถ”๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ ๊ณ„์ธต์„ AsFailed๋กœ ๋งž์ถฐ ์ฃผ์„ธ์š”.

-    suspend fun markFortuneAsFailed()
+    suspend fun markFortuneFailed()

9-16: ํ™•์ธ ์™„๋ฃŒ โ€” ๋„๋ฉ”์ธ API ๋ณ€๊ฒฝ์ด ์ „ํŒŒ๋จ; KDoc ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€์ด๋“œ ์ถ”๊ฐ€ ๊ถŒ์žฅ

๊ฒ€์ฆ ๊ฒฐ๊ณผ: ์ด์ „ ์‹ฌ๋ณผ(fortuneDateFlow ๋“ฑ)์€ ์ฝ”๋“œ๋ฒ ์ด์Šค์—์„œ ๋ฐœ๊ฒฌ๋˜์ง€ ์•Š์•˜๊ณ , ์ƒˆ ์‹ฌ๋ณผ(fortuneDateEpochFlow, hasUnseenFortuneFlow, shouldShowFortuneToolTipFlow, isFirstAlarmDismissedTodayFlow, fortuneCreateStatusFlow)์ด domain/data/feature ๋ชจ๋“ˆ ์ „์—ญ์—์„œ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
ํ™•์ธ ํŒŒ์ผ(์˜ˆ): domain/src/main/java/com/yapp/domain/repository/FortuneRepository.kt, data/src/main/java/com/yapp/data/repositoryimpl/FortuneRepositoryImpl.kt, data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSourceImpl.kt, core/datastore/UserPreferences.kt, feature/* (Home/Mission/Fortune/Alarm).
์กฐ์น˜: ๊ฐ Flow์˜ ์˜๋ฏธ(ํ•˜๋ฃจ/epoch ๊ธฐ์ค€, reset ์กฐ๊ฑด)์™€ mark* ๊ณ„์—ด ๋ฉ”์„œ๋“œ์˜ ์ƒํƒœ ์ „์ด๋ฅผ ๊ฐ„๋‹จํ•œ KDoc์œผ๋กœ ๋ฌธ์„œํ™”ํ•˜์„ธ์š”.

core/datastore/src/main/java/com/yapp/datastore/UserPreferences.kt (2)

44-45: todayEpoch ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ/๊ฒฐ์ •์„ฑ ๊ฐœ์„  ์ œ์•ˆ

๋กœ์ปฌ ์‹œ๊ฐ„๋Œ€/์ž์ • ๊ฒฝ๊ณ„ ์ด์Šˆ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด Clock ์ฃผ์ž…(๋˜๋Š” provider ํ•จ์ˆ˜ ์ฃผ์ž…)์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ: ์ƒ์„ฑ์ž์— Clock ์ฃผ์ž… ํ›„ LocalDate.now(clock).toEpochDay() ์‚ฌ์šฉ.


108-116: FIRST_ALARM_DISMISSED_TODAY ํ”Œ๋ž˜๊ทธ ์ •๋ฆฌ(์˜ต์…˜)

ํ”Œ๋กœ์šฐ์—์„œ ๋‚ ์งœ ๋น„๊ต๋กœ ์•ˆ์ „ํ•˜์ง€๋งŒ, ๋ฐ์ดํ„ฐ ์ฒญ๊ฒฐ์„ ์œ„ํ•ด ๋‚ ์งœ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ํ”Œ๋ž˜๊ทธ๋ฅผ false๋กœ ์žฌ์„ค์ •ํ•˜๋Š” ์œ ์ง€๋ณด์ˆ˜์šฉ ์ •๋ฆฌ ๋กœ์ง์„ ๊ณ ๋ คํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionActivity.kt (1)

52-65: ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ” ํŒจ๋”ฉ + ์Šคํฌ๋ฆผ ์กฐํ•ฉ ์‚ฌ์šฉ ์‹œ ์ด์ค‘ ํŒจ๋”ฉ ์—ฌ๋ถ€ ํ™•์ธ ์š”์ฒญ

NavHost์— navigationBarsPadding์„ ์ฃผ๊ณ  ๋ฐ”๋‹ฅ์— ์Šคํฌ๋ฆผ์„ ๊น”์•˜์œผ๋‹ˆ ๋Œ€๋ถ€๋ถ„ OK์ž…๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๋‚ด๋ถ€ ํ™”๋ฉด๋“ค์ด ๋ณ„๋„๋กœ navigationBarsPadding/WindowInsets๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ํ•˜๋‹จ ์—ฌ๋ฐฑ์ด ๊ณผ๋‹คํ•ด์งˆ ์ˆ˜ ์žˆ์–ด ์‹ค์ œ ๋‹จ๋ง(์ œ์Šค์ฒ˜/3๋ฒ„ํŠผ ๋‚ด๋น„ ๋ชจ๋‘)์—์„œ ํ™•์ธ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ๋˜ํ•œ Box์— ํฌ๊ธฐ ์ œ์•ฝ์ด ๋ชจํ˜ธํ•œ ๊ฒฝ์šฐ๋ฅผ ๋ง‰์œผ๋ ค๋ฉด fillMaxSize๋ฅผ ๋ถ€์—ฌํ•˜๋Š” ๊ฒƒ๋„ ๊ณ ๋ คํ•ด ์ฃผ์„ธ์š”.

core/common/src/main/java/com/yapp/common/navigation/route/MissionRoute.kt (1)

8-11: ๋ฌธ์ž์—ด ๋Œ€์‹  ํƒ€์ž… ์„ธ์ดํ”„ enum ์‚ฌ์šฉ ๊ณ ๋ ค

missionMode: String ๋Œ€์‹  MissionMode ์ž์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์˜คํƒ€/์ŠคํŽ™ ๋ถˆ์ผ์น˜๋ฅผ ์ปดํŒŒ์ผ ํƒ€์ž„์— ์ฐจ๋‹จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(ํ•„์š” ์‹œ @serializable enum). ๋„ค๋น„๊ฒŒ์ด์…˜ ์ธ์ฝ”๋”ฉ/๋””์ฝ”๋”ฉ ์˜ํ–ฅ ๋ฒ”์œ„๋ฅผ ๊ฐ์•ˆํ•ด ํ›„์† ๋ฆฌํŒฉํ„ฐ๋กœ ๊ฒ€ํ† ํ•ด ์ฃผ์„ธ์š”.

feature/fortune/src/main/java/com/yapp/fortune/page/FortunePager.kt (1)

43-61: ํŽ˜์ด์ง€ ์ธ๋ฑ์Šค ๋งค์ง๋„˜๋ฒ„ ์ •๋ฆฌ ์ œ์•ˆ

in 1..4, 5 -> ๋“ฑ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ์ธ๋ฑ์Šค๋Š” ์œ ์ง€๋ณด์ˆ˜ ์‹œ ์˜ค๋ฅ˜๋ฅผ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค. ์ƒ์ˆ˜/ํŒŒ์ƒ ๊ฐ’์œผ๋กœ ์ค‘์•™์ง‘์ค‘ํ™”ํ•˜๊ฑฐ๋‚˜ state ๊ธฐ๋ฐ˜์œผ๋กœ ๊ณ„์‚ฐํ•˜๋„๋ก ์ •๋ฆฌํ•ด ์ฃผ์„ธ์š”.

feature/fortune/src/main/java/com/yapp/fortune/FortuneNavGraph.kt (1)

23-27: ๋”ฅ๋งํฌ URI ํŒจํ„ด ์ƒ์ˆ˜ํ™”

"orbitapp://fortune" ๋ฌธ์ž์—ด์„ ์ƒ์ˆ˜๋กœ ๋ถ„๋ฆฌํ•ด ์žฌ์‚ฌ์šฉํ•˜๋ฉด ์˜คํƒˆ์ž/์ค‘๋ณต ๊ด€๋ฆฌ๊ฐ€ ์ˆ˜์›”ํ•ฉ๋‹ˆ๋‹ค.

feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/snooze/AlarmSnoozeTimerViewModel.kt (1)

47-49: Clock ์ฃผ์ž…์œผ๋กœ ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ/์ผ๊ด€์„ฑ ํ–ฅ์ƒ ์ œ์•ˆ

LocalDate.now() ๋Œ€์‹  Clock์„ DI ๋ฐ›์•„ LocalDate.now(clock)์„ ์“ฐ๋ฉด ์˜คํ”„๋ผ์ธ/ํƒ€์ž„์กด/ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์•ˆ์ •์ ์ž…๋‹ˆ๋‹ค.

// ์˜ˆ์‹œ
class AlarmSnoozeTimerViewModel @Inject constructor(
    private val clock: Clock,
    ...
) : ViewModel(), ... {
    ...
    val todayDate = LocalDate.now(clock).toEpochDay()
}
feature/fortune/src/main/java/com/yapp/fortune/scheduler/WorkManagerPostFortuneTaskScheduler.kt (1)

21-31: ์ผ์ผ ๊ณ ์œ  ์›Œํฌ ์ด๋ฆ„ ์ „๋žต OK โ€” ํƒœ๊ทธ/๋ชจ๋‹ˆํ„ฐ๋ง ์ถ”๊ฐ€ ๊ถŒ์žฅ

์œ ๋‹ˆํฌ ๋„ค์ด๋ฐ+KEEP ์ •์ฑ…์€ ์˜๋„์— ๋ถ€ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์šด์˜ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ์œ„ํ•ด ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , ์‹คํŒจ/์žฌ์‹œ๋„ ์ถ”์ ์ด ํ•„์š”ํ•˜๋ฉด ๋กœ๊ทธ/๋ถ„์„ ์ด๋ฒคํŠธ๋ฅผ Worker์—์„œ ๋ฐœํ–‰ํ•˜์„ธ์š”.

 val req = OneTimeWorkRequestBuilder<PostFortuneWorker>()
     .setConstraints(constraints)
-    .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 15, TimeUnit.SECONDS)
+    .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 15, TimeUnit.SECONDS)
+    .addTag(POST_FORTUNE_TAG)
     .build()
// ํŒŒ์ผ ํ•˜๋‹จ ๋“ฑ
private const val POST_FORTUNE_TAG = "post_fortune"

๋˜ํ•œ, PostFortuneWorker๊ฐ€ @HiltWorker๋กœ ์„ ์–ธ๋˜๊ณ  HiltWorkerFactory๊ฐ€ Application์— ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.

domain/src/main/java/com/yapp/domain/repository/UserInfoRepository.kt (1)

11-12: ์ด๋ฆ„ ๋ช…ํ™•์„ฑ: Epoch โ†’ EpochDay ๊ถŒ์žฅ

updateNoticeLastShownDateEpochFlow๋Š” ๋ฐ€๋ฆฌ์ดˆ epoch๋กœ ์˜คํ•ด๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ ๋‹จ์œ„๋ผ๋ฉด ...EpochDayFlow์™€ ๊ฐ™์ด ๋ช…ํ™•ํžˆ ํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด ์ฃผ์„ธ์š”(๋„๋ฉ”์ธ ์ „๋ฐ˜ ๋™์‹œ ์ •๋ฆฌ ์ „์ œ).

feature/fortune/src/main/java/com/yapp/fortune/FortuneScreen.kt (4)

220-224: LottieAnimation์˜ width๊ฐ€ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค (๋‚ด๋ถ€์—์„œ fillMaxWidth ๊ณ ์ •).

ํ˜„์žฌ LottieAnimation ์ปดํฌ์ €๋ธ”์ด ๋‚ด๋ถ€์ ์œผ๋กœ modifier.fillMaxWidth()๋ฅผ ๊ฐ•์ œํ•˜๋ฏ€๋กœ, ํ˜ธ์ถœ๋ถ€์˜ .width(375.dp)๋Š” ํšจ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์˜๋„๋Œ€๋กœ ๊ฐ€๋กœ/์„ธ๋กœ ๋น„์œจ์„ ๋งž์ถ”๋ ค๋ฉด aspectRatio๋กœ ์ œ์–ดํ•˜๊ฑฐ๋‚˜ width๋ฅผ ์ œ๊ฑฐํ•˜์„ธ์š”.

์•„๋ž˜์ฒ˜๋Ÿผ ์ˆ˜์ • ๊ถŒ์žฅ:

-            LottieAnimation(
-                modifier = Modifier
-                    .width(375.dp)
-                    .height(267.dp),
-                resId = core.designsystem.R.raw.fortune_loading,
-            )
+            LottieAnimation(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .aspectRatio(375f / 267f),
+                resId = core.designsystem.R.raw.fortune_loading,
+            )

์ถ”๊ฐ€ import:

+import androidx.compose.foundation.layout.aspectRatio

174-181: ์Šฌ๋ผ์ด๋”ฉ ์ธ๋””์ผ€์ดํ„ฐ count ํ•˜๋“œ์ฝ”๋”ฉ(6) โ†’ ์‹ค์ œ ํŽ˜์ด์ง€ ์ˆ˜์™€ ๋™๊ธฐํ™” ํ•„์š”.

rememberPagerState(pageCount = { state.fortunePages.size + 2 })์™€ ๋ถˆ์ผ์น˜ ์‹œ ์ง€ํ‘œ ๊ฐœ์ˆ˜ ์˜ค๋ฅ˜๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค. ๋™์ผ ๊ณ„์‚ฐ์‹ ๋˜๋Š” pagerState.pageCount๋กœ ์น˜ํ™˜ํ•˜์„ธ์š”.

-                SlidingIndicator(
-                    currentIndex = pagerState.currentPage,
-                    count = 6,
+                SlidingIndicator(
+                    currentIndex = pagerState.currentPage,
+                    count = pagerState.pageCount, // ๋Œ€์•ˆ: state.fortunePages.size + 2

PagerState.pageCount ๊ฐ€์šฉ ์—ฌ๋ถ€ ํ™•์ธ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ๋ถˆ๊ฐ€ํ•˜๋ฉด ๋™์ผ ๋žŒ๋‹ค ๊ณ„์‚ฐ์‹์„ ์‚ฌ์šฉํ•˜์„ธ์š”.


83-91: ๋นˆ ์ด๋ฒคํŠธ ํƒ€์ž…("") ๋กœ๊น… ๋ฐฉ์ง€.

else -> ""๋กœ ์„ค์ •๋œ ๊ฒฝ์šฐ์—๋„ ๋กœ๊ทธ๋ฅผ ์Œ“์Šต๋‹ˆ๋‹ค. ๊ณต๋ฐฑ์ธ ๊ฒฝ์šฐ ๋กœ๊น…์„ ์Šคํ‚ตํ•˜์„ธ์š”.

-        analyticsHelper.logEvent(
-            AnalyticsEvent(
-                type = eventType,
-                properties = mapOf(
-                    AnalyticsEvent.FortunePropertiesKeys.FORTUNE_PAGE_NUMBER to pagerState.currentPage + 1,
-                ),
-            ),
-        )
+        if (eventType.isNotBlank()) {
+            analyticsHelper.logEvent(
+                AnalyticsEvent(
+                    type = eventType,
+                    properties = mapOf(
+                        AnalyticsEvent.FortunePropertiesKeys.FORTUNE_PAGE_NUMBER to pagerState.currentPage + 1,
+                    ),
+                ),
+            )
+        }

191-199: ๋ฌดํ•œ ๋ฃจํ”„ LaunchedEffect ๋‹จ์ˆœํ™” ๊ฐ€๋Šฅ.

while(true) + delay ๋Œ€์‹  rememberInfiniteTransition ๋“ฑ ์• ๋‹ˆ๋ฉ”์ด์…˜ API๋กœ ํ† ๊ธ€ํ•˜๋ฉด ๊ฐ€๋…์„ฑ๊ณผ ์ทจ์†Œ ์ฒ˜๋ฆฌ ๋ชจ๋‘ ๊ฐœ์„ ๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋Šฅ์€ ๋™์ผํ•˜๋ฏ€๋กœ ์„ ํƒ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค.

val transition = rememberInfiniteTransition(label = "deliverToggle")
val phase by transition.animateFloat(
    initialValue = 0f, targetValue = 1f,
    animationSpec = infiniteRepeatable(animation = tween(2000), repeatMode = RepeatMode.Reverse),
    label = "phase"
)
val isDelivering = phase > 0.5f
data/src/main/java/com/yapp/data/repositoryimpl/UserInfoRepositoryImpl.kt (1)

20-21: Epoch ๋‹จ์œ„ ๋ช…์‹œ ํ•„์š”(์ดˆ vs ๋ฐ€๋ฆฌ์ดˆ).

updateNoticeLastShownDateEpochFlow: Flow<Long?>์˜ ๋‹จ์œ„๊ฐ€ ๋ถˆ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค. ์ธํ„ฐํŽ˜์ด์Šค/๋„๋ฉ”์ธ ์ „๋ฐ˜์—์„œ epochMillis ๋“ฑ์œผ๋กœ ์ด๋ฆ„์„ ๋ช…์‹œํ•˜๊ฑฐ๋‚˜ KDoc์— ๋‹จ์œ„๋ฅผ ๊ณ ์ •ํ•ด ์ฃผ์„ธ์š”.

data/src/main/java/com/yapp/data/local/datasource/UserLocalDataSourceImpl.kt (1)

14-15: Epoch ๋‹จ์œ„ ๋ช…์‹œ ํ•„์š”(์ดˆ vs ๋ฐ€๋ฆฌ์ดˆ).

updateNoticeLastShownDateEpochFlow์˜ ๋‹จ์œ„๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค์™€ ์ผ๊ด€๋˜๊ฒŒ ๋ฌธ์„œํ™”/๋ช…๋ช…ํ•ด ์ฃผ์„ธ์š”. ์ƒ์œ„ ๋ ˆ์ด์–ด์™€ ๋™์ผ ์ œ์•ˆ ์ ์šฉ ๊ถŒ์žฅ.

feature/fortune/src/main/java/com/yapp/fortune/FortuneViewModel.kt (2)

53-72: ์ค‘๋ณต Success ๋ฐฉ์ง€: ๋™์ผ ์ƒํƒœ ์žฌ๋ฐฉ์ถœ ์‹œ ์ค‘๋ณต fetch ๊ฐ€๋Šฅ.

collect๋Š” ๋™์ผ Success ์žฌ๋ฐฉ์ถœ์—๋„ fetchAndUpdateFortune๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. distinctUntilChanged/collectLatest๋กœ ๋””๋ฐ”์šด์Šคํ•˜์„ธ์š”.

-import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.distinctUntilChanged
@@
-    private fun observeFortune() = intent {
-        fortuneRepository.fortuneCreateStatusFlow.collect { status ->
+    private fun observeFortune() = intent {
+        fortuneRepository.fortuneCreateStatusFlow
+            .distinctUntilChanged()
+            .collectLatest { status ->

99-103: ๋„คํŠธ์›Œํฌ ์‹คํŒจ ์‹œ UX ๋Œ€์‘ ํ•„์š”.

getFortune ์‹คํŒจ ์‹œ ๋กœ๋”ฉ๋งŒ ํ•ด์ œ๋˜๊ณ  ํ™”๋ฉด์ด ์ •์ง€ ์ƒํƒœ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ™ˆ ์ด๋™ ๋˜๋Š” ์žฌ์‹œ๋„/์•Œ๋ฆผ ํ† ์ŠคํŠธ ์ค‘ ํ•˜๋‚˜๋กœ ์ฒ˜๋ฆฌํ•˜์„ธ์š”.

         }.onFailure { error ->
             Log.e("FortuneViewModel", "์šด์„ธ ๋ฐ์ดํ„ฐ ์š”์ฒญ ์‹คํŒจ: ${error.message}")
             reduce { state.copy(isLoading = false) }
+            postSideEffect(FortuneContract.SideEffect.NavigateToHome)
         }
core/alarm/src/main/java/com/yapp/alarm/receivers/AlarmInteractionActivityReceiver.kt (2)

49-53: Broadcast ์‹œ๊ฐ„ ์ œํ•œ ๋Œ€๋น„: Flow first()์— ํƒ€์ž„์•„์›ƒ ๊ถŒ์žฅ.

๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ์ปจํ…์ŠคํŠธ์—์„œ๋Š” ์ง€์—ฐ์ด ๊ธธ์–ด์ง€๋ฉด OS๊ฐ€ ์ค‘๋‹จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. withTimeout์œผ๋กœ ์•ˆ์ „์žฅ์น˜ ์ถ”๊ฐ€๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

-import kotlinx.coroutines.withContext
+import kotlinx.coroutines.withContext
+import kotlinx.coroutines.withTimeout
@@
-                            val (fortuneCreateStatus, hasUnseenFortune) = withContext(Dispatchers.IO) {
-                                val status = fortuneRepository.fortuneCreateStatusFlow.first()
-                                val unseen = fortuneRepository.hasUnseenFortuneFlow.first()
-                                status to unseen
-                            }
+                            val (fortuneCreateStatus, hasUnseenFortune) = withContext(Dispatchers.IO) {
+                                withTimeout(3_000) {
+                                    val status = fortuneRepository.fortuneCreateStatusFlow.first()
+                                    val unseen = fortuneRepository.hasUnseenFortuneFlow.first()
+                                    status to unseen
+                                }
+                            }

81-82: Failure/Idle ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ ๋ช…์‹œ์ ์ž„.

์•„๋ฌด ๋™์ž‘ ์—†์Œ์ด ์˜๋„๋ผ๋ฉด OK์ž…๋‹ˆ๋‹ค. ํ•„์š” ์‹œ ๋กœ๊น…/ํŠธ๋ ˆ์ด์‹ฑ๋งŒ ์ถ”๊ฐ€ ๊ฒ€ํ† .

feature/fortune/src/main/java/com/yapp/fortune/worker/PostFortuneWorker.kt (1)

49-55: ์žฌ์‹œ๋„ ๊ธฐ์ค€์„ ๊ตฌ๋ถ„(์žฌ์‹œ๋„ ๊ฐ€๋Šฅ/๋ถˆ๊ฐ€)ํ•˜์„ธ์š”.

์„œ๋ฒ„ 4xx ๋“ฑ ์˜๊ตฌ ์‹คํŒจ์—์„œ๋„ Result.retry()๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ฐฑ์˜คํ”„ ์žฌ์‹œ๋„๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๊ณ„์†๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ €์žฅ์†Œ/๋„๋ฉ”์ธ ์—๋Ÿฌ ํƒ€์ž…์„ ๊ตฌ๋ถ„ํ•ด ์žฌ์‹œ๋„ ๋ถˆ๊ฐ€์ธ ๊ฒฝ์šฐ Result.failure()๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ถ„๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”.

-                        onFailure = {
-                            fortuneRepository.markFortuneAsFailed()
-                            // WM ๋ฐฑ์˜คํ”„ ๊ทœ์น™์— ๋”ฐ๋ผ ์žฌ์‹œ๋„
-                            Result.retry()
-                        },
+                        onFailure = { e ->
+                            fortuneRepository.markFortuneAsFailed()
+                            // ์˜ˆ: ๋„๋ฉ”์ธ ์˜ˆ์™ธ/HTTP ์ƒํƒœ์— ๋”ฐ๋ผ ์žฌ์‹œ๋„ ์—ฌ๋ถ€ ๊ฒฐ์ •
+                            if (e is TransientNetworkException /* or 5xx */) {
+                                Result.retry()
+                            } else {
+                                Result.failure()
+                            }
+                        },
feature/home/src/main/java/com/yapp/home/component/bottomsheet/UpdateNoticeBottomSheet.kt (3)

110-115: ์•ก์…˜ ์˜์—ญ์— ์ ‘๊ทผ์„ฑ Role ๋ถ€์—ฌ.

๋ฒ„ํŠผ ์—ญํ• ์„ ๋ช…์‹œํ•ด ์Šคํฌ๋ฆฐ๋ฆฌ๋” ํƒ์ƒ‰ ํ’ˆ์งˆ์„ ๋†’์—ฌ์ฃผ์„ธ์š”.

-                Box(
+                Box(
                     modifier = Modifier
                         .weight(1f)
-                        .clickable(onClick = onDontShowAgain)
+                        .clickable(role = androidx.compose.ui.semantics.Role.Button, onClick = onDontShowAgain)
                         .padding(vertical = 14.dp),
                     contentAlignment = Alignment.Center,
                 ) {
@@
-                Box(
+                Box(
                     modifier = Modifier
                         .weight(1f)
-                        .clickable(onClick = onClose)
+                        .clickable(role = androidx.compose.ui.semantics.Role.Button, onClick = onClose)
                         .padding(vertical = 14.dp),
                     contentAlignment = Alignment.Center,
                 ) {

Also applies to: 124-128


64-70: ์Šคํฌ๋ฆผ ํด๋ฆญ์— Ripple ์ œ๊ฑฐ ๋ฐ ๋ผ๋ฒจ ๋ถ€์—ฌ.

์ „์ฒด ์Šคํฌ๋ฆผ ํด๋ฆญ์— ๋ฌผ๊ฒฐ ํšจ๊ณผ๊ฐ€ ๋ถˆํ•„์š”ํ•˜๋ฉฐ, ์Šคํฌ๋ฆฐ๋ฆฌ๋” ๋ผ๋ฒจ์„ ๋ถ€์—ฌํ•˜๋ฉด ๋” ๋‚ซ์Šต๋‹ˆ๋‹ค.

-    Box(
+    Box(
         modifier = Modifier
             .fillMaxSize()
-            .background(Color(0xFF17191F).copy(alpha = 0.85f))
-            .clickable(onClick = onClose),
+            .background(Color(0xFF17191F).copy(alpha = 0.85f))
+            .clickable(
+                onClickLabel = stringResource(id = R.string.update_notice_bottom_sheet_close),
+                indication = null,
+                interactionSource = remember { MutableInteractionSource() },
+                onClick = onClose,
+            ),
         contentAlignment = Alignment.BottomCenter,
     ) {

93-101: ๋ฐฐ๋„ˆ ์ด๋ฏธ์ง€ ๋กœ๋”ฉ crossfade ์ถ”๊ฐ€๋กœ ์ „ํ™˜ ํ’ˆ์งˆ ๊ฐœ์„ .

๋„คํŠธ์›Œํฌ ๋กœ๋”ฉ์‹œ ๊นœ๋นก์ž„์„ ์ค„์ž…๋‹ˆ๋‹ค.

+import coil.request.ImageRequest
@@
-                AsyncImage(
-                    model = imageUrl,
+                AsyncImage(
+                    model = ImageRequest.Builder(context)
+                        .data(imageUrl)
+                        .crossfade(true)
+                        .build(),
                     contentDescription = null,
                     contentScale = ContentScale.Crop,
                     modifier = Modifier
                         .fillMaxWidth()
                         .aspectRatio(1f),
                 )
feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt (1)

142-149: ๋‘ Flow๋ฅผ ์ผ๊ด€๋œ ์Šค๋ƒ…์ƒท์œผ๋กœ ์ฝ์–ด ์กฐ๊ฑด ํŒ์ •ํ•˜์„ธ์š”.

์„œ๋กœ ๋‹ค๋ฅธ ์‹œ์ ์˜ first() ๊ฒฐ๊ณผ ์กฐํ•ฉ์œผ๋กœ ๊ฒฝ๊ณ„ ํƒ€์ด๋ฐ์—์„œ ์ž˜๋ชป๋œ ํŒ๋‹จ์ด ๊ฐ€๋Šฅ. combine์œผ๋กœ ์Šค๋ƒ…์ƒท์„ ๋งŒ๋“  ๋’ค first()๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

-        val fortuneCreateStatus = fortuneRepository.fortuneCreateStatusFlow.first()
-        val hasUnseenFortune = fortuneRepository.hasUnseenFortuneFlow.first()
+        val (fortuneCreateStatus, hasUnseenFortune) =
+            kotlinx.coroutines.flow.combine(
+                fortuneRepository.fortuneCreateStatusFlow,
+                fortuneRepository.hasUnseenFortuneFlow,
+            ) { a, b -> a to b }.first()
core/alarm/src/main/java/com/yapp/alarm/services/AlarmService.kt (2)

112-115: Notification ID์˜ Int ๋ณ€ํ™˜ ์•ˆ์ „ํ™”.

Long โ†’ Int ์บ์ŠคํŒ…์€ overflow ์œ„ํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ญ์ƒ ์–‘์˜ 32๋น„ํŠธ ๋ฒ”์œ„๋กœ ๋งคํ•‘ํ•˜์„ธ์š”.

-                startForeground(
-                    notificationId.toInt(),
-                    createNotification(alarm, shouldNavigateToMission(alarm.missionType)),
-                )
+                val notificationIdInt = (notificationId % Int.MAX_VALUE).toInt()
+                startForeground(
+                    notificationIdInt,
+                    createNotification(alarm, shouldNavigateToMission(alarm.missionType)),
+                )

199-206: ์„œ๋น„์Šค ์ƒ๋ช…์ฃผ๊ธฐ ์Šค์ฝ”ํ”„๋กœ ํ†ต์ผ.

๋ณ„๋„ CoroutineScope ๋Œ€์‹  serviceScope๋ฅผ ์‚ฌ์šฉํ•ด ์ทจ์†Œยท์ข…๋ฃŒ ์‹œ ์ผ๊ด€๋˜๊ฒŒ ์ •๋ฆฌ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

-        CoroutineScope(Dispatchers.IO).launch {
+        serviceScope.launch {
             alarmUseCase.updateAlarmActive(
                 id = alarmId,
                 active = false,
             )
         }
feature/home/src/main/java/com/yapp/home/HomeViewModel.kt (1)

457-464: ๋„คํŠธ์›Œํฌ ํŒ์ • ์™„ํ™”/์ฃผ์ž…ํ˜• Clock ๊ณ ๋ ค.

  • VALIDATED ๋ฏธ์ถฉ์กฑ(ํฌํ„ธ/์ดˆ๊ธฐ ์—ฐ๊ฒฐ)์—์„œ๋„ ํ‘œ์‹œ๋ฅผ ๋ง‰์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. INTERNET๋งŒ ๋งŒ์กฑ ์‹œ ์ผ๋‹จ ํ‘œ์‹œํ•˜๋„๋ก ์™„ํ™”ํ•˜๊ฑฐ๋‚˜, ์ตœ์†Œ ์ง€์—ฐ ํ›„ ์žฌํ‰๊ฐ€๋ฅผ ๊ณ ๋ คํ•˜์„ธ์š”.
  • LocalDate.now() ๊ธฐ๋ฐ˜ ๋กœ์ง์ด ๋งŽ์œผ๋ฏ€๋กœ ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ๊ณผ ๊ฒฝ๊ณ„ ์‹œ์ (์ž์ •) ์ œ์–ด๋ฅผ ์œ„ํ•ด Clock ์ฃผ์ž…๋„ ๊ณ ๋ คํ•ด ๋ณผ ๋งŒํ•ฉ๋‹ˆ๋‹ค.
data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSource.kt (2)

8-8: epoch ๋‹จ์œ„ ๋ช…ํ™•ํžˆ ํ•ด์ฃผ์„ธ์š”.

fortuneDateEpochFlow๊ฐ€ epoch day(Long, toEpochDay) ๊ธฐ๋ฐ˜์ด๋ฉด ์ด๋ฆ„์„ fortuneDateEpochDayFlow ๋“ฑ์œผ๋กœ ๋” ๊ตฌ์ฒดํ™”ํ•˜๊ฑฐ๋‚˜ KDoc์œผ๋กœ โ€œepoch milliseconds๊ฐ€ ์•„๋‹˜โ€์„ ๋ช…์‹œํ•ด ์ฃผ์„ธ์š”. ํ˜ผ๋™์œผ๋กœ ์ธํ•œ ๋ฒ„๊ทธ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


12-12: Tooltip ๋„ค์ด๋ฐ ์ผ๊ด€์„ฑ ๊นจ์ง(โ€œToolTipโ€ vs โ€œTooltipโ€).

shouldShowFortuneToolTipFlow์™€ markFortuneTooltipShown()์˜ ํ‘œ๊ธฐ๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์ผ๊ด€๋˜๊ฒŒ Tooltip๋กœ ๋งž์ถฐ ์ฃผ์„ธ์š”.

์ ์šฉ ์˜ˆ(์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ์ค€):

-    val shouldShowFortuneToolTipFlow: Flow<Boolean>
+    val shouldShowFortuneTooltipFlow: Flow<Boolean>

Also applies to: 21-21

feature/home/src/main/java/com/yapp/home/HomeScreen.kt (4)

416-422: API ๋ ˆ๋ฒจ ๋ถ„๊ธฐ ๋Œ€์‹  ์•ˆ์ „ ์˜์—ญ(WindowInsets.safeDrawing) ์‚ฌ์šฉ ๊ถŒ์žฅ.

SDK 35 ๋ถ„๊ธฐ๋Š” ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ์ด ํฌ๊ณ  ๊ธฐ๊ธฐ๋ณ„/์ œ์กฐ์‚ฌ๋ณ„ ์ธ์…‹ ์ฐจ์ด๋ฅผ ํก์ˆ˜ํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ์•ˆ์ „ํ•˜๊ฒŒ WindowInsets.safeDrawing ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ/ํ•˜๋‹จ ์ธ์…‹์„ ํ•ฉ์‚ฐํ•˜์„ธ์š”.

-                                val offset = if (Build.VERSION.SDK_INT < 35) {
-                                    0.dp
-                                } else {
-                                    statusBarHeight + navBarHeight
-                                }
-                                sheetHalfExpandHeight = screenHeight - contentHeight - offset
+                                val safeInsets = WindowInsets.safeDrawing.asPaddingValues()
+                                val systemBarsOffset = safeInsets.calculateTopPadding() + safeInsets.calculateBottomPadding()
+                                sheetHalfExpandHeight = screenHeight - contentHeight - systemBarsOffset

๋‹ค์–‘ํ•œ API(30/33/35) ๋ฐ ์ œ์Šค์ฒ˜ ๋‚ด๋น„๊ฒŒ์ด์…˜/3โ€‘button ๋‚ด๋น„๊ฒŒ์ด์…˜์—์„œ ์ ˆ๋ฐ˜ ํ™•์žฅ ์œ„์น˜๊ฐ€ ์˜๋„๋Œ€๋กœ์ธ์ง€ ๊ฒ€์ฆ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.


515-518: ์•„์ด์ฝ˜ ์ ‘๊ทผ์„ฑ ๋ผ๋ฒจ ํ˜„์ง€ํ™” ํ•„์š”.

contentDescription = "Mail" / "Setting"์€ ํ•˜๋“œ์ฝ”๋”ฉ/์˜๋ฌธ์ž…๋‹ˆ๋‹ค. stringResource๋กœ ํ˜„์ง€ํ™”๋œ ๋ผ๋ฒจ์„ ์ œ๊ณตํ•ด ์ฃผ์„ธ์š”.

์˜ˆ:

contentDescription = stringResource(id = R.string.cd_mail)
contentDescription = stringResource(id = R.string.cd_setting)

๋ฌธ์ž์—ด ๋ฆฌ์†Œ์Šค๊ฐ€ ์—†๋‹ค๋ฉด ์ถ”๊ฐ€ ํ•„์š”.

Also applies to: 556-560


604-607: ์žฅ์‹์šฉ ์ด๋ฏธ์ง€๋Š” contentDescription์„ null๋กœ.

SkyImage๋Š” ์žฅ์‹์šฉ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฐ๋ฆฌ๋” ์ค‘๋ณต ๋‚ญ๋…์„ ๋ฐฉ์ง€ํ•˜๋ ค๋ฉด contentDescription = null ์ฒ˜๋ฆฌํ•˜์„ธ์š”.

-        contentDescription = "IMG_MAIN_SKY",
+        contentDescription = null,

659-663: CD ์ผ๊ด€ํ™”: ์žฅ์‹์šฉ์€ null, ์˜๋ฏธ ์žˆ๋Š” ์ปจํŠธ๋กค์€ ๋ฆฌ์†Œ์Šค๋กœ.

  • ๋งํ’์„ /๋ณ„/๋นˆ ์ƒํƒœ ์ผ๋Ÿฌ์ŠคํŠธ ๋“ฑ ์žฅ์‹์šฉ์€ contentDescription = null ๊ถŒ์žฅ.
  • AddAlarmButton ๋‚ด ์•„์ด์ฝ˜์€ ์˜† ํ…์ŠคํŠธ๊ฐ€ ๋ผ๋ฒจ ์—ญํ• ์„ ํ•˜๋ฏ€๋กœ ์•„์ด์ฝ˜ CD๋Š” null์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค(์ค‘๋ณต ๋‚ญ๋… ๋ฐฉ์ง€).

์˜ˆ:

-                contentDescription = "IMG_MAIN_SPEECH_BUBBLE",
+                contentDescription = null,
-            contentDescription = "Add Alarm",
+            contentDescription = null,

Also applies to: 672-680, 748-758, 827-831

data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSourceImpl.kt (2)

22-35: ์˜ค๋Š˜ ํŒ์ • ๋กœ์ง์˜ ์‹œ๊ฐ„ ์†Œ์Šค ๋‹จ์ผํ™”/ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ ๊ฐœ์„ .

LocalDate.now() ๊ธฐ๋ฐ˜ todayEpoch()๋Š” ํ…Œ์ŠคํŠธ/ํƒ€์ž„์กด ๊ฒฝ๊ณ„(์ž์ • ์ „ํ›„)์—์„œ ์ทจ์•ฝํ•ฉ๋‹ˆ๋‹ค. ๊ณต์šฉ DateProvider/Clock๋ฅผ ์ฃผ์ž…ํ•˜๊ฑฐ๋‚˜ UserPreferences์˜ ๋™์ผ ๋กœ์ง์„ ๋‹จ์ผ ์†Œ์Šค๋กœ ๋…ธ์ถœํ•ด(์˜ˆ: todayEpochDay()), ์—ฌ๊ธฐ์„œ๋„ ๊ทธ ๊ฐ’์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋„๋ฉ”์ธ/๋ฐ์ดํ„ฐ์Šคํ† ์–ด ๊ฐ„ ๋ถˆ์ผ์น˜ ๊ฐ€๋Šฅ์„ฑ์„ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


36-36: ์ค‘๋ณต ์œ ํ‹ธ ์ œ๊ฑฐ ์ œ์•ˆ.

todayEpoch()๋Š” UserPreferences์™€์˜ ์ค‘๋ณต ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•œ ๊ณณ์œผ๋กœ ๋ชจ์œผ๊ณ  ์žฌ์‚ฌ์šฉํ•ด ์ฃผ์„ธ์š”.

๐Ÿ“œ Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 8852463 and f1fd6b6.

โ›” Files ignored due to path filters (8)
  • core/designsystem/src/main/res/drawable-xhdpi/ic_100_buble.png is excluded by !**/*.png
  • core/designsystem/src/main/res/drawable-xhdpi/ic_fortune_delivering_speech_bubble.png is excluded by !**/*.png
  • core/designsystem/src/main/res/drawable-xhdpi/ic_fortune_waiting_speech_bubble.png is excluded by !**/*.png
  • core/designsystem/src/main/res/drawable-xxhdpi/ic_100_buble.png is excluded by !**/*.png
  • core/designsystem/src/main/res/drawable-xxhdpi/ic_fortune_delivering_speech_bubble.png is excluded by !**/*.png
  • core/designsystem/src/main/res/drawable-xxhdpi/ic_fortune_waiting_speech_bubble.png is excluded by !**/*.png
  • core/designsystem/src/main/res/drawable/ic_100_buble.png is excluded by !**/*.png
  • project.dot.png is excluded by !**/*.png
๐Ÿ“’ Files selected for processing (70)
  • .github/workflows/android_ci.yml (0 hunks)
  • app/build.gradle.kts (2 hunks)
  • app/src/main/AndroidManifest.xml (3 hunks)
  • app/src/main/java/com/yapp/orbit/OrbitApplication.kt (1 hunks)
  • app/src/main/java/com/yapp/orbit/OrbitNavHost.kt (4 hunks)
  • app/src/main/java/com/yapp/orbit/di/AppVersionModule.kt (1 hunks)
  • build-logic/src/main/java/com/yapp/convention/HiltAndroid.kt (1 hunks)
  • build-logic/src/main/java/orbit.android.feature.gradle.kts (0 hunks)
  • core/alarm/src/main/java/com/yapp/alarm/AndroidAlarmScheduler.kt (5 hunks)
  • core/alarm/src/main/java/com/yapp/alarm/receivers/AlarmInteractionActivityReceiver.kt (2 hunks)
  • core/alarm/src/main/java/com/yapp/alarm/receivers/AlarmReceiver.kt (3 hunks)
  • core/alarm/src/main/java/com/yapp/alarm/receivers/RescheduleAlarmReceiver.kt (2 hunks)
  • core/alarm/src/main/java/com/yapp/alarm/scheduler/PostFortuneTaskScheduler.kt (1 hunks)
  • core/alarm/src/main/java/com/yapp/alarm/services/AlarmService.kt (5 hunks)
  • core/common/src/main/java/com/yapp/common/navigation/route/MissionRoute.kt (1 hunks)
  • core/datastore/src/main/java/com/yapp/datastore/UserPreferences.kt (3 hunks)
  • core/designsystem/src/main/res/raw/fortune_loading.json (1 hunks)
  • core/network/src/main/java/com/yapp/network/di/NetworkModule.kt (2 hunks)
  • core/network/src/main/java/com/yapp/network/di/Qualifier.kt (0 hunks)
  • core/ui/src/main/java/com/yapp/ui/component/navigation/NavigationBarScrim.kt (1 hunks)
  • data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSource.kt (0 hunks)
  • data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSourceImpl.kt (0 hunks)
  • data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSource.kt (1 hunks)
  • data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSourceImpl.kt (2 hunks)
  • data/src/main/java/com/yapp/data/local/datasource/UserLocalDataSource.kt (1 hunks)
  • data/src/main/java/com/yapp/data/local/datasource/UserLocalDataSourceImpl.kt (2 hunks)
  • data/src/main/java/com/yapp/data/remote/di/ServiceModule.kt (1 hunks)
  • data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt (0 hunks)
  • data/src/main/java/com/yapp/data/repositoryimpl/FortuneRepositoryImpl.kt (2 hunks)
  • data/src/main/java/com/yapp/data/repositoryimpl/UserInfoRepositoryImpl.kt (1 hunks)
  • domain/src/main/java/com/yapp/domain/model/AlarmDay.kt (2 hunks)
  • domain/src/main/java/com/yapp/domain/model/FortuneCreateStatus.kt (1 hunks)
  • domain/src/main/java/com/yapp/domain/model/MissionMode.kt (1 hunks)
  • domain/src/main/java/com/yapp/domain/repository/AlarmRepository.kt (0 hunks)
  • domain/src/main/java/com/yapp/domain/repository/FortuneRepository.kt (1 hunks)
  • domain/src/main/java/com/yapp/domain/repository/UserInfoRepository.kt (1 hunks)
  • feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionActivity.kt (2 hunks)
  • feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/action/AlarmActionContract.kt (1 hunks)
  • feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/action/AlarmActionScreen.kt (1 hunks)
  • feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/action/AlarmActionViewModel.kt (3 hunks)
  • feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/snooze/AlarmSnoozeTimerViewModel.kt (1 hunks)
  • feature/fortune/build.gradle.kts (1 hunks)
  • feature/fortune/src/main/java/com/yapp/fortune/FortuneNavGraph.kt (2 hunks)
  • feature/fortune/src/main/java/com/yapp/fortune/FortuneScreen.kt (3 hunks)
  • feature/fortune/src/main/java/com/yapp/fortune/FortuneViewModel.kt (4 hunks)
  • feature/fortune/src/main/java/com/yapp/fortune/di/SchedulerModule.kt (1 hunks)
  • feature/fortune/src/main/java/com/yapp/fortune/page/FortunePager.kt (1 hunks)
  • feature/fortune/src/main/java/com/yapp/fortune/scheduler/WorkManagerPostFortuneTaskScheduler.kt (1 hunks)
  • feature/fortune/src/main/java/com/yapp/fortune/worker/PostFortuneWorker.kt (1 hunks)
  • feature/home/build.gradle.kts (1 hunks)
  • feature/home/src/main/AndroidManifest.xml (1 hunks)
  • feature/home/src/main/java/com/yapp/home/HomeContract.kt (2 hunks)
  • feature/home/src/main/java/com/yapp/home/HomeScreen.kt (17 hunks)
  • feature/home/src/main/java/com/yapp/home/HomeViewModel.kt (9 hunks)
  • feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditScreen.kt (2 hunks)
  • feature/home/src/main/java/com/yapp/home/alarm/component/bottomsheet/AlarmMissionBottomSheet.kt (10 hunks)
  • feature/home/src/main/java/com/yapp/home/component/bottomsheet/AlarmListBottomSheet.kt (3 hunks)
  • feature/home/src/main/java/com/yapp/home/component/bottomsheet/UpdateNoticeBottomSheet.kt (1 hunks)
  • feature/home/src/main/res/values/strings.xml (2 hunks)
  • feature/mission/src/main/java/com/yapp/mission/MissionContract.kt (0 hunks)
  • feature/mission/src/main/java/com/yapp/mission/MissionNavGraph.kt (2 hunks)
  • feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt (0 hunks)
  • feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt (2 hunks)
  • feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingAccessScreen.kt (0 hunks)
  • feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingBirthdayScreen.kt (0 hunks)
  • feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingCompleteScreen2.kt (0 hunks)
  • feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingGenderScreen.kt (3 hunks)
  • feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingNavGraph.kt (1 hunks)
  • feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingViewModel.kt (1 hunks)
  • gradle/libs.versions.toml (4 hunks)
๐Ÿ’ค Files with no reviewable changes (12)
  • feature/mission/src/main/java/com/yapp/mission/MissionContract.kt
  • domain/src/main/java/com/yapp/domain/repository/AlarmRepository.kt
  • feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingBirthdayScreen.kt
  • .github/workflows/android_ci.yml
  • data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSource.kt
  • build-logic/src/main/java/orbit.android.feature.gradle.kts
  • data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt
  • feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingCompleteScreen2.kt
  • feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt
  • feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingAccessScreen.kt
  • data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSourceImpl.kt
  • core/network/src/main/java/com/yapp/network/di/Qualifier.kt
๐Ÿงฐ Additional context used
๐Ÿง  Learnings (4)
๐Ÿ“š Learning: 2025-07-27T15:20:35.256Z
Learnt from: DongChyeon
PR: YAPP-Github/Orbit-Android#238
File: feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt:118-120
Timestamp: 2025-07-27T15:20:35.256Z
Learning: MissionRoute์™€ MissionScreen์ด ๊ฐ™์€ ํŒŒ์ผ(feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt)์— ์žˆ์„ ๋•Œ, MissionRoute์—์„œ BackHandler๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŒŒ์ผ ๋ ˆ๋ฒจ์˜ import ๋ฌธ์€ ์œ ์ง€๋˜์–ด์•ผ ํ•จ.

Applied to files:

  • feature/mission/src/main/java/com/yapp/mission/MissionNavGraph.kt
  • core/common/src/main/java/com/yapp/common/navigation/route/MissionRoute.kt
๐Ÿ“š Learning: 2025-09-14T15:32:32.628Z
Learnt from: DongChyeon
PR: YAPP-Github/Orbit-Android#252
File: feature/fortune/src/main/java/com/yapp/fortune/worker/PostFortuneWorker.kt:24-56
Timestamp: 2025-09-14T15:32:32.628Z
Learning: ์šด์„ธ ์š”์ฒญ ๊ฐ„์—๋Š” ์ตœ์†Œ 1๋ถ„ ๊ฐ„๊ฒฉ์ด ๋ณด์žฅ๋˜์–ด ์žˆ์–ด์„œ PostFortuneWorker์—์„œ ๋™์‹œ์„ฑ/์›์ž์„ฑ ๋ฌธ์ œ๋ฅผ ๊ณ ๋ คํ•˜์ง€ ์•Š์•„๋„ ๋จ

Applied to files:

  • feature/fortune/src/main/java/com/yapp/fortune/worker/PostFortuneWorker.kt
๐Ÿ“š Learning: 2025-07-23T10:29:14.146Z
Learnt from: DongChyeon
PR: YAPP-Github/Orbit-Android#234
File: feature/home/src/main/java/com/yapp/home/alarm/component/bottomsheet/AlarmMissionBottomSheet.kt:73-76
Timestamp: 2025-07-23T10:29:14.146Z
Learning: AlarmMissionBottomSheet์—์„œ missionType/missionCount ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ํ˜„์žฌ ์ €์žฅ๋œ ๊ฐ’์„ UI์— ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๊ณ , selectedMissionType/selectedMissionCount๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ณ€๊ฒฝ ์ค‘์ธ ๋‚ด๋ถ€ ์ž‘์—… ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค. onDone์ด๋‚˜ onSave ์ฝœ๋ฐฑ์„ ํ†ตํ•ด ๋ช…์‹œ์ ์œผ๋กœ ์ €์žฅํ•  ๋•Œ๋งŒ ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ๋ฐ˜์˜๋˜๋Š” UX ํŒจํ„ด์ด๋‹ค.

Applied to files:

  • feature/home/src/main/res/values/strings.xml
  • feature/home/src/main/java/com/yapp/home/alarm/component/bottomsheet/AlarmMissionBottomSheet.kt
๐Ÿ“š Learning: 2025-09-15T07:43:50.275Z
Learnt from: DongChyeon
PR: YAPP-Github/Orbit-Android#254
File: data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSourceImpl.kt:22-34
Timestamp: 2025-09-15T07:43:50.275Z
Learning: FortuneCreateStatusFlow์—์„œ todayEpoch()๋ฅผ combine ๋‚ด๋ถ€์—์„œ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ์ถฉ๋ถ„ํ•œ ์ด์œ : ์šด์„ธ ์ƒ์„ฑํ•  ๋•Œ๋งˆ๋‹ค fortuneDateEpochFlow๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด combine์ด ์žฌํ‰๊ฐ€๋˜๋ฏ€๋กœ, ๊ทธ ์ˆœ๊ฐ„์˜ todayEpoch() ๊ณ„์‚ฐ์œผ๋กœ ์ถฉ๋ถ„ํ•จ. ์šด์„ธ ์š”์ฒญ ๊ฐ„๊ฒฉ ์ œ์•ฝ์œผ๋กœ ์ธํ•ด ์ž์ • ๋กค์˜ค๋ฒ„ ๋ฌธ์ œ๋Š” ์‹ค์šฉ์ ์œผ๋กœ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ.

Applied to files:

  • feature/home/src/main/java/com/yapp/home/HomeViewModel.kt
๐Ÿงฌ Code graph analysis (10)
feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionActivity.kt (2)
feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionNavGraph.kt (1)
  • alarmInteractionNavGraph (33-69)
core/ui/src/main/java/com/yapp/ui/component/navigation/NavigationBarScrim.kt (1)
  • NavigationBarScrim (16-26)
feature/fortune/src/main/java/com/yapp/fortune/page/FortunePager.kt (1)
feature/fortune/src/main/java/com/yapp/fortune/page/FortuneCompletePage.kt (1)
  • FortuneCompletePage (28-104)
feature/home/src/main/java/com/yapp/home/component/bottomsheet/UpdateNoticeBottomSheet.kt (1)
core/designsystem/src/main/java/com/yapp/designsystem/theme/Theme.kt (1)
  • OrbitTheme (12-28)
feature/fortune/src/main/java/com/yapp/fortune/FortuneScreen.kt (2)
core/ui/src/main/java/com/yapp/ui/component/lottie/LottieAnimation.kt (1)
  • LottieAnimation (22-85)
core/designsystem/src/main/java/com/yapp/designsystem/theme/Theme.kt (1)
  • OrbitTheme (12-28)
feature/home/src/main/java/com/yapp/home/HomeScreen.kt (2)
feature/home/src/main/java/com/yapp/home/HomeViewModel.kt (1)
  • processAction (55-87)
feature/home/src/main/java/com/yapp/home/component/bottomsheet/UpdateNoticeBottomSheet.kt (1)
  • UpdateNoticeBottomSheet (51-139)
feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditScreen.kt (1)
core/ui/src/main/java/com/yapp/ui/component/bottomsheet/OrbitBottomSheetLayout.kt (1)
  • OrbitBottomSheetLayout (31-59)
feature/home/src/main/java/com/yapp/home/component/bottomsheet/AlarmListBottomSheet.kt (2)
feature/home/src/main/java/com/yapp/home/component/AlarmListDropDownMenu.kt (1)
  • AlarmListDropDownMenu (43-72)
feature/home/src/main/java/com/yapp/home/component/AlarmSortDropDownMenu.kt (1)
  • AlarmSortDropDownMenu (36-67)
feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingGenderScreen.kt (4)
core/ui/src/main/java/com/yapp/ui/component/bottomsheet/OrbitBottomSheetLayout.kt (1)
  • OrbitBottomSheetLayout (31-59)
feature/onboarding/src/main/java/com/yapp/onboarding/OnBoardingScreen.kt (1)
  • OnboardingScreen (14-54)
feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingViewModel.kt (1)
  • processAction (49-67)
core/ui/src/main/java/com/yapp/ui/toggle/OrbitGenderToggle.kt (1)
  • OrbitGenderToggle (30-98)
core/datastore/src/main/java/com/yapp/datastore/UserPreferences.kt (1)
data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSourceImpl.kt (1)
  • todayEpoch (36-36)
data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSourceImpl.kt (1)
core/datastore/src/main/java/com/yapp/datastore/UserPreferences.kt (1)
  • todayEpoch (44-44)
๐Ÿช› detekt (1.23.8)
app/src/main/java/com/yapp/orbit/OrbitApplication.kt

[warning] 11-11: An empty default constructor can be removed.

(detekt.empty-blocks.EmptyDefaultConstructor)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

โ™ป๏ธ Duplicate comments (3)
feature/home/src/main/java/com/yapp/home/component/bottomsheet/AlarmListBottomSheet.kt (2)

102-114: snapshotFlow ์ค‘๋ณต ์ด๋ฒคํŠธ ๋ฐฉ์ง€ ๊ฐœ์„  ํ•„์š”

ํ˜„์žฌ ๊ตฌํ˜„์—์„œ๋Š” distinctUntilChanged()๋ฅผ ์ ์šฉํ–ˆ์ง€๋งŒ, LaunchedEffect์˜ key๊ฐ€ ์—ฌ์ „ํžˆ Unit์ž…๋‹ˆ๋‹ค. sheetState๋ฅผ key๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜์„ธ์š”:

-    LaunchedEffect(Unit) {
+    LaunchedEffect(sheetState) {
         snapshotFlow { sheetState.currentValue }
             .distinctUntilChanged()
             .collectLatest { value ->

98-98: Hidden ์ƒํƒœ ์ „์ด ๋ฐฉ์ง€ ํ•„์š”

confirmValueChange ์ œ๊ฑฐ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ๋“œ๋ž˜๊ทธํ•˜์—ฌ Hidden ์ƒํƒœ๋กœ ์ „์ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹œํŠธ๊ฐ€ ํ•ญ์ƒ ํ‘œ์‹œ๋˜์–ด์•ผ ํ•œ๋‹ค๋ฉด Hidden ์ƒํƒœ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ฐจ๋‹จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์ค‘ ํ•˜๋‚˜๋ฅผ ์ ์šฉํ•˜์„ธ์š”:

์˜ต์…˜ 1: Hidden ์ƒํƒœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ (๊ถŒ์žฅ)

-    val sheetState = rememberStandardBottomSheetState()
+    val sheetState = rememberStandardBottomSheetState(
+        skipHiddenState = true
+    )

์˜ต์…˜ 2: confirmValueChange๋กœ ๋ช…์‹œ์  ์ฐจ๋‹จ

-    val sheetState = rememberStandardBottomSheetState()
+    val sheetState = rememberStandardBottomSheetState(
+        confirmValueChange = { it != SheetValue.Hidden }
+    )
core/alarm/src/main/java/com/yapp/alarm/receivers/RescheduleAlarmReceiver.kt (1)

30-31: goAsync ์ ์šฉ ๊ตฟ + ์•ฑ ์—…๋ฐ์ดํŠธ ํ›„ ์žฌ์Šค์ผ€์ค„๋„ ํ•จ๊ป˜ ์ฒ˜๋ฆฌ ์ œ์•ˆ

๋ถ€ํŒ… ํ›„๋งŒ ์ฒ˜๋ฆฌํ•˜๋ฉด ์•ฑ ์—…๋ฐ์ดํŠธ ์งํ›„ ์•Œ๋žŒ ์žฌ์„ค์ •์ด ๋ˆ„๋ฝ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ACTION_MY_PACKAGE_REPLACED๋„ ํ•จ๊ป˜ ์ˆ˜์‹ ํ•ด ๋™์ผ ๋กœ์ง์„ ํƒœ์šฐ๋Š” ๊ฑธ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. (์ด์ „ ๋ฆฌ๋ทฐ ์ฝ”๋ฉ˜ํŠธ์™€ ๋™์ผ ์ œ์•ˆ)

-        if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
+        if (intent.action == Intent.ACTION_BOOT_COMPLETED || intent.action == Intent.ACTION_MY_PACKAGE_REPLACED) {
             val pending = goAsync()
             rescheduleAlarm(pending)
         }
๐Ÿงน Nitpick comments (1)
core/alarm/src/main/java/com/yapp/alarm/receivers/RescheduleAlarmReceiver.kt (1)

35-44: ์˜ˆ์™ธ ๋กœ๊น… ์ถ”๊ฐ€๋กœ ๋ฌธ์ œ ์ง„๋‹จ์„ฑ ๊ฐœ์„  ์ œ์•ˆ

ํ˜„์žฌ ์˜ˆ์™ธ๊ฐ€ ์‚ผ์ผœ์ ธ ์›์ธ ํŒŒ์•…์ด ์–ด๋ ต์Šต๋‹ˆ๋‹ค. catch์—์„œ ๋กœ๊น… ํ›„ finish ๋ณด์žฅ ์œ ์ง€ ๊ถŒ์žฅ.

-        CoroutineScope(Dispatchers.IO + SupervisorJob()).launch {
-            try {
-                val alarms = alarmUseCase.getAllAlarms().first()
-                alarms
-                    .filter { it.isAlarmActive }
-                    .forEach { alarm -> androidAlarmScheduler.scheduleAlarm(alarm) }
-            } finally {
-                pendingResult.finish()
-            }
-        }
+        CoroutineScope(Dispatchers.IO + SupervisorJob()).launch {
+            try {
+                val alarms = alarmUseCase.getAllAlarms().first()
+                alarms
+                    .filter { it.isAlarmActive }
+                    .forEach { alarm -> androidAlarmScheduler.scheduleAlarm(alarm) }
+            } catch (t: Throwable) {
+                android.util.Log.e("RescheduleAlarmReceiver", "Failed to reschedule alarms", t)
+            } finally {
+                pendingResult.finish()
+            }
+        }
๐Ÿ“œ Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between f1fd6b6 and 700e803.

๐Ÿ“’ Files selected for processing (2)
  • core/alarm/src/main/java/com/yapp/alarm/receivers/RescheduleAlarmReceiver.kt (2 hunks)
  • feature/home/src/main/java/com/yapp/home/component/bottomsheet/AlarmListBottomSheet.kt (4 hunks)
๐Ÿงฐ Additional context used
๐Ÿงฌ Code graph analysis (1)
feature/home/src/main/java/com/yapp/home/component/bottomsheet/AlarmListBottomSheet.kt (2)
feature/home/src/main/java/com/yapp/home/component/AlarmListDropDownMenu.kt (1)
  • AlarmListDropDownMenu (43-72)
feature/home/src/main/java/com/yapp/home/component/AlarmSortDropDownMenu.kt (1)
  • AlarmSortDropDownMenu (36-67)
โฐ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: Continuous Deployment
๐Ÿ”‡ Additional comments (4)
feature/home/src/main/java/com/yapp/home/component/bottomsheet/AlarmListBottomSheet.kt (2)

329-341: ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง ์ œ๊ฑฐ ํ™•์ธ

AlarmListDropDownMenu์™€ AlarmSortDropDownMenu๊ฐ€ ์ด์ œ ํ•ญ์ƒ ๋ Œ๋”๋ง๋˜๋ฉฐ, expanded ์ƒํƒœ๋งŒ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์„ฑ๋Šฅ์ƒ ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ, ๋‘ ๋ฉ”๋‰ด๊ฐ€ ๋™์‹œ์— ์—ด๋ฆด ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋‘ ๋ฉ”๋‰ด๊ฐ€ ๋™์‹œ์— ์—ด๋ฆฌ์ง€ ์•Š๋„๋ก ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ์ ์ ˆํžˆ ๋˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ menuExpanded์™€ sortDropDownMenuExpanded๊ฐ€ ์ƒํ˜ธ ๋ฐฐํƒ€์ ์œผ๋กœ ๊ด€๋ฆฌ๋˜๋Š”์ง€ ๊ฒ€์ฆํ•˜์„ธ์š”.


93-93: onExpanded ์ฝœ๋ฐฑ ์‚ฌ์šฉ ํ™•์ธ โ€” ํ•ด๊ฒฐ๋จ

feature/home/src/main/java/com/yapp/home/HomeScreen.kt (lines 394โ€“396)์—์„œ onExpanded๊ฐ€ ํ˜ธ์ถœ๋˜์–ด processAction(HomeContract.Action.HideToolTip)๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜์ • ๋ถˆํ•„์š”.

core/alarm/src/main/java/com/yapp/alarm/receivers/RescheduleAlarmReceiver.kt (2)

11-12: ํ•„์š” import ์ถ”๊ฐ€ ์ ์ ˆ

SupervisorJob, flow.first ๋„์ž… ๋ชจ๋‘ ์ƒํ™ฉ์— ๋งž๊ณ  ๐Ÿ‘


29-33: Manifest ์„ค์ • ํ™•์ธ: RECEIVE_BOOT_COMPLETEDยทintent-filterยทexported ํ™•์ธ๋จ

app/src/main/AndroidManifest.xml์— RECEIVE_BOOT_COMPLETED ๊ถŒํ•œ(๋ผ์ธ 11)๊ณผ RescheduleAlarmReceiver ๋ฆฌ์‹œ๋ฒ„ ์„ ์–ธ(๋ผ์ธ 72โ€“76) โ€” intent-filter(android.intent.action.BOOT_COMPLETED) ๋ฐ android:exported="true"(๋ผ์ธ 73)๊ฐ€ ์กด์žฌํ•˜๋ฏ€๋กœ BOOT_COMPLETED ์ˆ˜์‹  ์„ค์ •์€ ์ถฉ์กฑ๋ฉ๋‹ˆ๋‹ค.

@DongChyeon DongChyeon merged commit 94aeb45 into develop Sep 16, 2025
3 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

โค๏ธโ€๐Ÿฉน CHORE ์ž‘์€ ์ˆ˜์ •(ํƒ€์ž…๋ณ€์ˆ˜, ํŒจํ‚ค์ง€๊ตฌ์กฐ ๋ณ€๊ฒฝ ๋“ฑ)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant