Skip to content

20260309 #51 todo list screen 캘린더 중심 재설계#52

Merged
EM-H20 merged 9 commits intomainfrom
20260309_#51_TodoListScreen_캘린더_중심_재설계
Mar 10, 2026

Hidden character warning

The head ref may contain hidden characters: "20260309_#51_TodoListScreen_\uce98\ub9b0\ub354_\uc911\uc2ec_\uc7ac\uc124\uacc4"
Merged

20260309 #51 todo list screen 캘린더 중심 재설계#52
EM-H20 merged 9 commits intomainfrom
20260309_#51_TodoListScreen_캘린더_중심_재설계

Conversation

@EM-H20
Copy link
Contributor

@EM-H20 EM-H20 commented Mar 9, 2026

✨ 변경 사항


✅ 테스트


  • 수동 테스트 완료
  • 테스트 코드 완료

Summary by CodeRabbit

  • 새로운 기능

    • 할일 뱅크 섹션 추가: 선택한 날짜에 할당되지 않은 할일을 하단 시트에서 보고 탭으로 해당 날짜에 추가 가능.
    • 슬라이더 기반 스와이프 UI 도입: 좌/우 스와이프로 카테고리 이동, 날짜에서 제거, 삭제 등 명시적 액션 제공.
  • 개선 사항

    • 할일에 공부 시간 라벨 표시(예: "30분 공부").
    • 타이머와 연동된 할일은 실수 조작을 막기 위해 일부 액션 제한 적용.

EM-H20 added 7 commits March 9, 2026 23:18
- flutter_slidable 패키지 추가
- 좌→우: 카테고리 이동, 우→좌: 날짜 제거 + 삭제
- CustomSlidableAction으로 반투명 배경 + 아이콘/텍스트 스타일 적용
- todosNotForDate provider 추가 (선택 날짜에 미배정 할일 필터)
- 홈 화면에 '할 일 추가' 섹션으로 todo bank 표시
- 탭하면 해당 날짜에 즉시 배정
- Slidable context 분리 (slidableContext vs buildContext)
- deleteTodo에 await 추가하여 에러 시 rollback 정상 작동
- 마지막 배정 날짜 제거 시 확인 다이얼로그 추가
- addDateToTodo를 TodoListNotifier로 이동 + 중복 날짜 체크
- 날짜 정규화를 TodoEntity.normalizeDate()로 통일
- TodoEntity.studyTimeLabel getter로 subtitle 중복 제거
- SizedBox(height: 4.h) → AppSpacing.s4
@coderabbitai
Copy link

coderabbitai bot commented Mar 9, 2026

Walkthrough

홈 화면에 할당되지 않은 할일을 보여주는 Todo Bank를 추가하고 Dismissible을 Slidable로 교체해 좌/우 스와이프 액션(카테고리 이동, 날짜 제거, 삭제)을 도입했습니다. provider에 todosNotForDate 가족과 addDateToTodo가 추가되고, TodoEntity에 studyTimeLabel 게터가 추가되었습니다.

Changes

Cohort / File(s) Summary
문서 및 계획
docs/plans/2026-03-09-todo-list-calendar-redesign.md
Todo Bank 및 캘린더-투두 재설계 계획 문서 추가.
홈 화면 UI
lib/features/home/presentation/screens/home_screen.dart
TodoBank 섹션 _buildTodoBankSection_addTodoToDate 헬퍼 추가, bottom sheet 레이아웃 통합.
Todo 프로바이더 (핵심)
lib/features/todo/presentation/providers/todo_provider.dart, lib/features/todo/presentation/providers/todo_provider.g.dart
addDateToTodo 메서드 추가, todosNotForDate(Ref, DateTime) 프로바이더 가족 추가, 날짜 정규화로 todosForDate/todosByDateMap 변경 및 생성 코드(해시·가족형 프로바이더) 확장.
Todo 엔티티
lib/features/todo/domain/entities/todo_entity.dart
String? get studyTimeLabel 게터 추가 (actualMinutes 기반).
Todo 항목 위젯
lib/features/todo/presentation/widgets/dismissible_todo_item.dart
Dismissible → Slidable 전환; start/end ActionPane 구현(카테고리 이동, 날짜 제거, 삭제), 타이머 연동 가드 추가, 액션 핸들러로 리팩토링.
자동생성 프로바이더 해시 업데이트
lib/features/*/presentation/providers/*.g.dart
lib/features/auth/..., lib/features/badge/..., lib/features/exploration/..., lib/features/settings/..., lib/features/timer/...
여러 자동 생성 provider 파일의 debug hash 상수 값 갱신(동작 변화 없음).
종속성
pubspec.yaml
flutter_slidable: ^4.0.3 의존성 추가.

Sequence Diagram(s)

sequenceDiagram
    participant User as 사용자
    participant Home as HomeScreen
    participant Slidable as Slidable Widget
    participant TodoProv as TodoProvider
    participant DB as 데이터베이스

    User->>Home: 홈 진입 및 날짜 선택
    Home->>TodoProv: todosNotForDate(selectedDate) 요청
    TodoProv->>DB: 미할당 할일 조회
    DB-->>TodoProv: 미할당 할일 반환
    TodoProv-->>Home: 미할당 할일 전달 (Todo Bank 표시)

    rect rgba(100,150,200,0.5)
    User->>Home: Todo Bank 항목 탭 (할일 추가)
    Home->>TodoProv: addDateToTodo(todo, date)
    TodoProv->>DB: todo.scheduledDates에 정규화된 날짜 추가
    DB-->>TodoProv: 저장 완료
    TodoProv-->>Home: 상태 갱신
    end

    rect rgba(200,150,100,0.5)
    User->>Slidable: 할일 좌/우 스와이프
    Slidable->>Home: 액션 트리거 (카테고리 이동/날짜 제거/삭제)
    Home->>TodoProv: 해당 업데이트 요청
    TodoProv->>DB: 변경 저장/삭제
    DB-->>TodoProv: 완료
    TodoProv-->>Home: UI 갱신
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 주요 변경 사항(Todo 목록 화면의 캘린더 중심 재설계)을 명확하게 설명하고 있으며, 실제 변경 내용(Todo Bank 섹션 추가, flutter_slidable 스와이프 액션 개선 등)과 부합합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 20260309_#51_TodoListScreen_캘린더_중심_재설계

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.

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: 4

🧹 Nitpick comments (1)
lib/features/home/presentation/screens/home_screen.dart (1)

513-515: 새 bank row 패딩은 AppPadding preset으로 맞춰 주세요.

여기서 다시 EdgeInsets.fromLTRB(...)를 쓰면 리스트 행 간격 규칙이 파일마다 흩어집니다. AppPadding.listItemPadding 같은 preset을 쓰거나 필요한 preset을 상수로 추가하는 편이 맞습니다.

As per coding guidelines Never hardcode padding — use AppPadding presets (all, horizontal, vertical) and specialized presets (screenPadding, cardPadding, listItemPadding, buttonPadding) from core/constants/app_padding.dart.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/home/presentation/screens/home_screen.dart` around lines 513 -
515, The Padding around the TodoItem in HomeScreen uses a hardcoded
EdgeInsets.fromLTRB which violates the no-hardcode-padding rule; replace that
with the AppPadding preset (e.g., AppPadding.listItemPadding) or add a new
preset in core/constants/app_padding.dart if one doesn’t exist, then use that
preset for the Padding around TodoItem to keep list row spacing consistent
across files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/features/home/presentation/screens/home_screen.dart`:
- Around line 435-436: The empty-state message is shown even when there are bank
items available; update the rendering in home_screen.dart so the empty-state
card is only shown when both the selected date's todos are empty AND the
todo-bank is empty, otherwise render the todo-bank section
(_buildTodoBankSection(selectedKey)) above or instead of the empty state;
alternatively, if you prefer to keep the empty-state visible when bank exists,
change its copy to a date-scoped message like "이 날짜에 배정된 할 일이 없어요" to avoid
suggesting adding via + when bank items are available.
- Around line 498-500: The Home screen is directly coupled to todo presentation
providers — replace direct uses of todosNotForDateProvider and
todoListNotifierProvider in _buildTodoBankSection and _addTodoToDate with a
domain/application-level abstraction (e.g., a TodoUseCase or HomeTodoFacade
provider) that exposes only the required methods/data (e.g.,
fetchTodosNotForDate(selectedDate) and addTodoToDate(todo, date)); update calls
to use ref.watch/ref.read of that new facade instead of
todosNotForDateProvider/todoListNotifierProvider so the home presentation only
depends on the domain/application abstraction per the Clean 3-Layer guideline
and dependency inversion.

In `@lib/features/todo/presentation/widgets/dismissible_todo_item.dart`:
- Around line 100-104: The onToggle currently computes date = contextDate ??
DateTime.now() and calls toggleTodoForDate(todo, date), causing a mismatch when
contextDate is null; change this to either (A) disable toggling when contextDate
== null by returning early from the onToggle handler so the UI and mutation
align, or (B) add a dedicated global toggle mutation on the notifier (e.g.,
toggleTodoGlobal(Todo todo)) and call
ref.read(todoListNotifierProvider.notifier).toggleTodoGlobal(todo) when
contextDate == null so the mutation uses the same global completion semantics as
the UI; update the onToggle branch in dismissible_todo_item.dart to check
contextDate and call the appropriate path.
- Around line 233-236: The _isLinkedToTimer function in
dismissible_todo_item.dart directly reads timerNotifierProvider (timerState) and
thus couples the todo presentation to timer presentation; instead remove this
direct provider read and accept the linkage as an injected dependency (either a
boolean parameter like isLinkedToTimer on the DismissibleTodoItem widget or an
abstracted domain/service interface such as TodoTimerLinker.isLinked(todo.id)
provided by the parent). Update the parent that constructs DismissibleTodoItem
to compute the flag (e.g., watch timerNotifierProvider there or use the domain
abstraction) and pass it down, or register and inject the domain-level
abstraction through providers so _isLinkedToTimer is eliminated and the widget
no longer calls timerNotifierProvider directly.

---

Nitpick comments:
In `@lib/features/home/presentation/screens/home_screen.dart`:
- Around line 513-515: The Padding around the TodoItem in HomeScreen uses a
hardcoded EdgeInsets.fromLTRB which violates the no-hardcode-padding rule;
replace that with the AppPadding preset (e.g., AppPadding.listItemPadding) or
add a new preset in core/constants/app_padding.dart if one doesn’t exist, then
use that preset for the Padding around TodoItem to keep list row spacing
consistent across files.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cab9b651-8267-47ec-874f-659cd44832dc

📥 Commits

Reviewing files that changed from the base of the PR and between a39b95f and 5187582.

⛔ Files ignored due to path filters (1)
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • docs/plans/2026-03-09-todo-list-calendar-redesign.md
  • lib/features/auth/presentation/providers/auth_provider.g.dart
  • lib/features/badge/presentation/providers/badge_provider.g.dart
  • lib/features/exploration/presentation/providers/exploration_provider.g.dart
  • lib/features/home/presentation/screens/home_screen.dart
  • lib/features/settings/presentation/providers/settings_provider.g.dart
  • lib/features/timer/presentation/providers/timer_provider.g.dart
  • lib/features/todo/domain/entities/todo_entity.dart
  • lib/features/todo/presentation/providers/todo_provider.dart
  • lib/features/todo/presentation/providers/todo_provider.g.dart
  • lib/features/todo/presentation/widgets/dismissible_todo_item.dart
  • pubspec.yaml

Comment on lines +498 to +500
final todosNotForDate = ref.watch(
todosNotForDateProvider(selectedDate),
);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

홈 화면이 todo presentation provider에 직접 결합돼 있습니다.

_buildTodoBankSection_addTodoToDatetodosNotForDateProvider/todoListNotifierProvider를 바로 읽으면서 home feature가 todo feature의 presentation layer를 알아야 합니다. 이 읽기/쓰기 경로는 domain/usecase나 상위 application provider 뒤로 숨겨서 화면은 그 추상화만 보게 하는 편이 경계가 덜 새어 나갑니다.

As per coding guidelines Use Clean 3-Layer Architecture with Dependency Inversion Principle (Presentation → Domain ← Data) — Domain layer must be at the center and reverse dependencies are forbidden.

Also applies to: 535-536

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/home/presentation/screens/home_screen.dart` around lines 498 -
500, The Home screen is directly coupled to todo presentation providers —
replace direct uses of todosNotForDateProvider and todoListNotifierProvider in
_buildTodoBankSection and _addTodoToDate with a domain/application-level
abstraction (e.g., a TodoUseCase or HomeTodoFacade provider) that exposes only
the required methods/data (e.g., fetchTodosNotForDate(selectedDate) and
addTodoToDate(todo, date)); update calls to use ref.watch/ref.read of that new
facade instead of todosNotForDateProvider/todoListNotifierProvider so the home
presentation only depends on the domain/application abstraction per the Clean
3-Layer guideline and dependency inversion.

Comment on lines +233 to +236
bool _isLinkedToTimer(WidgetRef ref) {
final timerState = ref.read(timerNotifierProvider);
return timerState.status != TimerStatus.idle &&
timerState.linkedTodoId == todo.id;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

todo 위젯이 timer presentation provider를 직접 읽고 있습니다.

_isLinkedToTimertimerNotifierProvider를 바로 읽으면서 todo presentation이 timer presentation에 결합됐습니다. 이 플래그는 상위에서 주입하거나 domain/application 레벨의 추상화로 노출하는 편이 기능 경계를 지키기 쉽습니다.

As per coding guidelines Use Clean 3-Layer Architecture with Dependency Inversion Principle (Presentation → Domain ← Data) — Domain layer must be at the center and reverse dependencies are forbidden.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/todo/presentation/widgets/dismissible_todo_item.dart` around
lines 233 - 236, The _isLinkedToTimer function in dismissible_todo_item.dart
directly reads timerNotifierProvider (timerState) and thus couples the todo
presentation to timer presentation; instead remove this direct provider read and
accept the linkage as an injected dependency (either a boolean parameter like
isLinkedToTimer on the DismissibleTodoItem widget or an abstracted
domain/service interface such as TodoTimerLinker.isLinked(todo.id) provided by
the parent). Update the parent that constructs DismissibleTodoItem to compute
the flag (e.g., watch timerNotifierProvider there or use the domain abstraction)
and pass it down, or register and inject the domain-level abstraction through
providers so _isLinkedToTimer is eliminated and the widget no longer calls
timerNotifierProvider directly.

- bank에 할일이 있으면 빈 상태 카드 숨기고 bank만 표시
- 불필요한 Consumer 래핑 제거 (상위에서 watch한 데이터 전달)
- contextDate null일 때 토글 no-op 처리하여 표시/저장 기준 불일치 해결
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: 2

🧹 Nitpick comments (2)
lib/features/todo/presentation/widgets/dismissible_todo_item.dart (1)

53-56: Slidable 액션 배경색도 AppColors 경로로 통일해 주세요.

세 곳 모두 Colors.transparent를 직접 넣고 있어서 이 파일만 색상 예외 규칙이 생깁니다. 필요하면 transparent 토큰을 AppColors에 추가해서라도 한 경로로 맞추는 편이 좋습니다.

As per coding guidelines Never hardcode colors — use AppColors constants from core/constants/app_colors.dart.

Also applies to: 72-75, 82-85

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/todo/presentation/widgets/dismissible_todo_item.dart` around
lines 53 - 56, Replace the three hardcoded Colors.transparent usages in this
file (used as backgroundColor for CustomSlidableAction instances) with the
AppColors token from core/constants/app_colors.dart (e.g.,
AppColors.transparent); if AppColors does not yet expose a transparent token,
add one there first and then import and use it in dismissible_todo_item.dart so
all Slidable actions (including the ones tied to _moveCategory and the other two
CustomSlidableAction calls) use the centralized AppColors constant instead of a
hardcoded color.
lib/features/home/presentation/screens/home_screen.dart (1)

512-513: bank 항목 패딩도 AppPadding 토큰으로 통일해 주세요.

여기서 새 EdgeInsets.fromLTRB(20.w, 0, 20.w, AppSpacing.s8)를 만들면 같은 화면 안에서도 padding 규칙이 다시 갈라집니다. 이미 위에서 쓰는 preset처럼 AppPadding 기반으로 맞추는 편이 낫습니다.

As per coding guidelines Never hardcode padding — use AppPadding presets (all, horizontal, vertical) and specialized presets (screenPadding, cardPadding, listItemPadding, buttonPadding) from core/constants/app_padding.dart.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/home/presentation/screens/home_screen.dart` around lines 512 -
513, Replace the hardcoded EdgeInsets.fromLTRB(20.w, 0, 20.w, AppSpacing.s8)
used in the bank item Padding with the AppPadding preset and then add the bottom
spacing; e.g. use AppPadding.horizontal.add(EdgeInsets.only(bottom:
AppSpacing.s8)) (or the appropriate AppPadding preset like screenPadding if that
better matches the surrounding presets) so the bank item's padding follows the
shared AppPadding tokens instead of a hardcoded EdgeInsets.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/features/home/presentation/screens/home_screen.dart`:
- Around line 523-524: 함수 _addTodoToDate를 void에서 Future<void>로 변경하고 내부에서
addDateToTodo 비동기 호출을 await로 수행한 뒤 try-catch로 에러를 잡아 사용자 피드백(예: 스낵바/토스트/로깅)을
호출하도록 수정하세요; 또한 이 헬퍼를 호출하는 곳(onToggle/onTap 익명 콜백)도 async로 바꾸고 await
_addTodoToDate(ref, todo, selectedDate)로 호출하도록 업데이트해 비동기 실패가 누락되지 않게 하세요.

In `@lib/features/todo/presentation/widgets/dismissible_todo_item.dart`:
- Around line 150-154: The calls to updateTodo (in the branch that sets
categoryIds and the other location that updates categories) must be awaited and
error-handled: change
ref.read(todoListNotifierProvider.notifier).updateTodo(...) to await the call,
wrap it in try { await updateTodo(...) } catch (e, st) { if (context.mounted)
ScaffoldMessenger.of(context).showSnackBar(...) } matching the pattern used in
removeDateFromTodo/deleteTodo so failures surface to the user; ensure you
preserve context.mounted checks before showing the SnackBar and keep the same
notifier method signature (updateTodo) and todo.copyWith(...) usage.

---

Nitpick comments:
In `@lib/features/home/presentation/screens/home_screen.dart`:
- Around line 512-513: Replace the hardcoded EdgeInsets.fromLTRB(20.w, 0, 20.w,
AppSpacing.s8) used in the bank item Padding with the AppPadding preset and then
add the bottom spacing; e.g. use
AppPadding.horizontal.add(EdgeInsets.only(bottom: AppSpacing.s8)) (or the
appropriate AppPadding preset like screenPadding if that better matches the
surrounding presets) so the bank item's padding follows the shared AppPadding
tokens instead of a hardcoded EdgeInsets.

In `@lib/features/todo/presentation/widgets/dismissible_todo_item.dart`:
- Around line 53-56: Replace the three hardcoded Colors.transparent usages in
this file (used as backgroundColor for CustomSlidableAction instances) with the
AppColors token from core/constants/app_colors.dart (e.g.,
AppColors.transparent); if AppColors does not yet expose a transparent token,
add one there first and then import and use it in dismissible_todo_item.dart so
all Slidable actions (including the ones tied to _moveCategory and the other two
CustomSlidableAction calls) use the centralized AppColors constant instead of a
hardcoded color.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f4c96349-2418-4316-b49c-ae82933f0827

📥 Commits

Reviewing files that changed from the base of the PR and between 5187582 and 60de94e.

📒 Files selected for processing (2)
  • lib/features/home/presentation/screens/home_screen.dart
  • lib/features/todo/presentation/widgets/dismissible_todo_item.dart

Comment on lines +523 to +524
onToggle: () => _addTodoToDate(ref, todo, selectedDate),
onTap: () => _addTodoToDate(ref, todo, selectedDate),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find the home_screen.dart file and check its size
fd -t f "home_screen.dart" lib/features/home/presentation/screens/

# Then read the relevant sections

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 131


🏁 Script executed:

# Get the file location and inspect lines around 523-524 and 532-534
cat -n lib/features/home/presentation/screens/home_screen.dart | sed -n '515,545p'

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 1222


🏁 Script executed:

# Also search for the _addTodoToDate method definition
grep -n "_addTodoToDate" lib/features/home/presentation/screens/home_screen.dart

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 297


🏁 Script executed:

# Search for the addDateToTodo mutation definition to verify it's async
grep -n "addDateToTodo" lib/features/home/presentation/screens/home_screen.dart | head -20

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 154


🏁 Script executed:

# Find the todoListNotifierProvider
fd -t f "todo_list.*provider" lib/features/home/

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 58


🏁 Script executed:

# Search for todoListNotifierProvider definition
rg "todoListNotifierProvider" lib/features/home/ -A 5 | head -40

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 1237


🏁 Script executed:

# Search for addDateToTodo method definition
rg "addDateToTodo" lib/features/home/ -B 3 -A 10

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 1270


🏁 Script executed:

# Search across entire lib directory for todoListNotifierProvider
rg "final todoListNotifierProvider" lib/ -A 10

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 1117


🏁 Script executed:

# Search for the notifier class that has addDateToTodo method
rg "class.*Notifier.*extends" lib/features/home/ -A 20 | grep -A 20 "TodoList\|addDateToTodo"

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 58


🏁 Script executed:

# Find all provider files in home feature
fd -t f "provider" lib/features/home/

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 58


🏁 Script executed:

# List all files in home presentation directory
ls -la lib/features/home/presentation/

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 296


🏁 Script executed:

# Find the source provider file (not generated)
fd -t f "todo_provider.dart" lib/features/todo/

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 135


🏁 Script executed:

# Read the TodoListNotifier class implementation
rg "class TodoListNotifier" lib/features/todo/ -A 100 | head -150

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 9215


🏁 Script executed:

# Search specifically for addDateToTodo method
rg "addDateToTodo" lib/features/todo/ -B 3 -A 15

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 1747


🏁 Script executed:

# Check if there are other usages of _addTodoToDate at lines around 532-534
cat -n lib/features/home/presentation/screens/home_screen.dart | sed -n '525,540p'

Repository: SpaceStudyShip/SpaceStudyShip-FE

Length of output: 550


_addTodoToDate()Future<void>로 변경하고 await/try-catch로 에러 처리 필수

addDateToTodo()가 비동기 mutation인데 _addTodoToDate()void 반환형이고 await 없이 호출되므로, 저장 실패 시 에러가 처리되지 않은 채 비동기로 흘러갑니다. 이 헬퍼를 Future<void>로 변경하고 내부에서 await/try-catch로 사용자 피드백을 처리해 주세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/home/presentation/screens/home_screen.dart` around lines 523 -
524, 함수 _addTodoToDate를 void에서 Future<void>로 변경하고 내부에서 addDateToTodo 비동기 호출을
await로 수행한 뒤 try-catch로 에러를 잡아 사용자 피드백(예: 스낵바/토스트/로깅)을 호출하도록 수정하세요; 또한 이 헬퍼를
호출하는 곳(onToggle/onTap 익명 콜백)도 async로 바꾸고 await _addTodoToDate(ref, todo,
selectedDate)로 호출하도록 업데이트해 비동기 실패가 누락되지 않게 하세요.

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

Caution

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

⚠️ Outside diff range comments (1)
lib/features/todo/presentation/widgets/dismissible_todo_item.dart (1)

218-229: ⚠️ Potential issue | 🟡 Minor

_openEditSheetupdateTodo() 호출에도 동일한 에러 핸들링이 필요합니다.

_moveCategory와 마찬가지로, 할일 수정 저장 실패 시 사용자에게 피드백을 제공해야 합니다.

🛡️ 에러 핸들링 추가 제안
  if (result != null && context.mounted) {
-   await ref
-       .read(todoListNotifierProvider.notifier)
-       .updateTodo(
-         todo.copyWith(
-           title: result['title'] as String,
-           categoryIds: (result['categoryIds'] as List<String>?) ?? [],
-           scheduledDates:
-               (result['scheduledDates'] as List<DateTime>?) ?? [],
-         ),
-       );
+   try {
+     await ref
+         .read(todoListNotifierProvider.notifier)
+         .updateTodo(
+           todo.copyWith(
+             title: result['title'] as String,
+             categoryIds: (result['categoryIds'] as List<String>?) ?? [],
+             scheduledDates:
+                 (result['scheduledDates'] as List<DateTime>?) ?? [],
+           ),
+         );
+   } catch (e) {
+     if (context.mounted) {
+       AppSnackBar.error(context, '할일 수정에 실패했습니다');
+     }
+   }
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/todo/presentation/widgets/dismissible_todo_item.dart` around
lines 218 - 229, The call to updateTodo inside _openEditSheet needs the same
error handling as _moveCategory: wrap the await
ref.read(todoListNotifierProvider.notifier).updateTodo(...) in a try/catch,
catch and log the exception, and present user feedback (e.g.,
ScaffoldMessenger.of(context).showSnackBar or an error dialog) only if
context.mounted; keep using todo.copyWith(...) for the update payload and ensure
any caught error does not crash the UI.
♻️ Duplicate comments (1)
lib/features/todo/presentation/widgets/dismissible_todo_item.dart (1)

12-13: 🛠️ Refactor suggestion | 🟠 Major

todo presentation이 timer presentation을 직접 import하고 있습니다.

timerNotifierProviderTimerState를 직접 import하여 _isLinkedToTimer 메서드에서 사용하고 있어, todo feature가 timer feature의 presentation 레이어에 결합됩니다.

이전 리뷰에서도 언급되었듯이, 이 의존성은 상위 위젯에서 isLinkedToTimer boolean 파라미터로 주입하거나 domain 레벨의 추상화를 통해 제공하는 것이 Clean Architecture 원칙에 부합합니다.

As per coding guidelines: Use Clean 3-Layer Architecture with Dependency Inversion Principle (Presentation → Domain ← Data) — Domain layer must be at the center and reverse dependencies are forbidden.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/todo/presentation/widgets/dismissible_todo_item.dart` around
lines 12 - 13, The DismissibleTodoItem widget currently imports
timerNotifierProvider and TimerState and uses them in _isLinkedToTimer, coupling
todo presentation to timer presentation; remove the direct imports and stop
using timerNotifierProvider/TimerState inside _isLinkedToTimer, instead accept
an injected boolean (e.g., isLinkedToTimer) on the DismissibleTodoItem
constructor or obtain linkage via a domain-level abstraction passed in (e.g., an
ITaskTimerLinkChecker interface) and update _isLinkedToTimer to use that
injected value/method; ensure all callers (parent widgets) pass the
isLinkedToTimer boolean or the domain abstraction rather than letting the todo
widget depend on timer presentation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/features/todo/presentation/widgets/dismissible_todo_item.dart`:
- Around line 150-154: The call to updateTodo (via
ref.read(todoListNotifierProvider.notifier).updateTodo) is awaited but lacks
error handling; wrap that await in a try-catch like the patterns used in
_removeFromDate and _deleteTodo, catch and log the exception, and show user
feedback (e.g., Snackbar/ScaffoldMessenger or the same feedback helper used
elsewhere) if the update fails; ensure you still check context.mounted before
showing UI feedback and keep the existing newCategoryIds null check.

---

Outside diff comments:
In `@lib/features/todo/presentation/widgets/dismissible_todo_item.dart`:
- Around line 218-229: The call to updateTodo inside _openEditSheet needs the
same error handling as _moveCategory: wrap the await
ref.read(todoListNotifierProvider.notifier).updateTodo(...) in a try/catch,
catch and log the exception, and present user feedback (e.g.,
ScaffoldMessenger.of(context).showSnackBar or an error dialog) only if
context.mounted; keep using todo.copyWith(...) for the update payload and ensure
any caught error does not crash the UI.

---

Duplicate comments:
In `@lib/features/todo/presentation/widgets/dismissible_todo_item.dart`:
- Around line 12-13: The DismissibleTodoItem widget currently imports
timerNotifierProvider and TimerState and uses them in _isLinkedToTimer, coupling
todo presentation to timer presentation; remove the direct imports and stop
using timerNotifierProvider/TimerState inside _isLinkedToTimer, instead accept
an injected boolean (e.g., isLinkedToTimer) on the DismissibleTodoItem
constructor or obtain linkage via a domain-level abstraction passed in (e.g., an
ITaskTimerLinkChecker interface) and update _isLinkedToTimer to use that
injected value/method; ensure all callers (parent widgets) pass the
isLinkedToTimer boolean or the domain abstraction rather than letting the todo
widget depend on timer presentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e02f04bb-ccb1-4d93-8c5c-bc26dcc95c71

📥 Commits

Reviewing files that changed from the base of the PR and between 60de94e and 0e2d138.

📒 Files selected for processing (2)
  • lib/features/home/presentation/screens/home_screen.dart
  • lib/features/todo/presentation/widgets/dismissible_todo_item.dart
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/features/home/presentation/screens/home_screen.dart

Comment on lines +150 to +154
if (newCategoryIds != null && context.mounted) {
await ref
.read(todoListNotifierProvider.notifier)
.updateTodo(todo.copyWith(categoryIds: newCategoryIds));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

updateTodo() 호출에 에러 핸들링을 추가하세요.

await가 추가되었지만, _removeFromDate_deleteTodo의 패턴처럼 try-catch로 감싸서 실패 시 사용자에게 피드백을 제공해야 합니다.

🛡️ 에러 핸들링 추가 제안
  if (newCategoryIds != null && context.mounted) {
-   await ref
-       .read(todoListNotifierProvider.notifier)
-       .updateTodo(todo.copyWith(categoryIds: newCategoryIds));
+   try {
+     await ref
+         .read(todoListNotifierProvider.notifier)
+         .updateTodo(todo.copyWith(categoryIds: newCategoryIds));
+   } catch (e) {
+     if (context.mounted) {
+       AppSnackBar.error(context, '카테고리 변경에 실패했습니다');
+     }
+   }
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/todo/presentation/widgets/dismissible_todo_item.dart` around
lines 150 - 154, The call to updateTodo (via
ref.read(todoListNotifierProvider.notifier).updateTodo) is awaited but lacks
error handling; wrap that await in a try-catch like the patterns used in
_removeFromDate and _deleteTodo, catch and log the exception, and show user
feedback (e.g., Snackbar/ScaffoldMessenger or the same feedback helper used
elsewhere) if the update fails; ensure you still check context.mounted before
showing UI feedback and keep the existing newCategoryIds null check.

@EM-H20 EM-H20 merged commit f28d6c8 into main Mar 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant