Skip to content

Conversation

@doyou1
Copy link
Contributor

@doyou1 doyou1 commented Jan 17, 2026

Fixes #224

Summary

리딩챌린지의 퀴즈 확인, 딥타임, 퀴즈 풀기, 정답 확인 화면을 하나의 통합 화면으로 재구성하고, 각 화면의 UI/UX를 개선했습니다.

주요 변경사항

  • ReadingChallengeQuizWrapScreen 통합 화면 구현

    • PageView 기반 탭 네비게이션으로 퀴즈 확인 → 딥타임 → 퀴즈 풀기 → 정답 확인 플로우 통합
    • 각 탭별 상태 관리 및 화면 전환 로직 구현
    • CustomCountDownController를 활용한 딥타임 타이머 상태 관리
    • 잠금 기능으로 독서 중 화면 이탈 방지
  • 퀴즈 확인 화면 디자인 개선

    • 퀴즈 미리보기 카드 UI로 변경
    • 외곽 빛 효과(purple glow) 및 하단 그림자 추가
    • ic_quiz_check.png 아이콘 추가
    • "책 속에서 정답 찾기" CTA 버튼
  • 딥타임 화면 리팩토링

    • 그라데이션 배경 추가
    • 원형 아이콘 버튼 → 텍스트 버튼(CtaButtonS)으로 변경
    • Akira Expanded Demo 폰트 적용 (타이머 표시)
    • 외부 주입 콜백 구조로 개선
  • 퀴즈 풀기 화면 리팩토링

    • 컴포넌트 기반 구조로 변경
    • AutomaticKeepAliveClientMixin으로 상태 유지
  • 라우터 간소화

    • 3개 개별 라우트 → 1개 통합 라우트(/quiz/:chapterId/wrap)

기술적 개선

  • BaseScreen → ConsumerStatefulWidget 전환
  • AutomaticKeepAliveClientMixin 적용으로 탭 전환 시 상태 유지
  • 콜백 주입 패턴으로 화면 간 결합도 감소

Test Plan

  • 퀴즈 확인 화면에서 새로운 카드 UI 확인
  • "책 속에서 정답 찾기" 버튼 클릭 시 딥타임 화면으로 이동
  • 딥타임 화면에서 타이머 시작/일시정지/계속하기 동작 확인
  • 딥타임 타이머 폰트(Akira Expanded Demo) 적용 확인
  • 잠금 기능 활성화 시 뒤로가기 제한 확인
  • "퀴즈 풀러가기" 버튼 클릭 시 퀴즈 화면으로 이동
  • 퀴즈 풀기 화면에서 선택지 선택 및 제출 확인
  • 정답 확인 화면에서 정답/오답 표시 확인
  • 탭 전환 시 각 화면의 상태가 유지되는지 확인
0117-v2.mov
Before After

🤖 Generated with Claude Code

ReadingChallengeQuizWrapScreen 통합 화면 구현
- 퀴즈 확인, 딥타임, 퀴즈 풀기, 정답 확인을 하나의 화면에서 관리
- PageView 기반 탭 네비게이션 구조
- 각 탭별 상태 관리 및 화면 전환 로직 구현
- CustomCountDownController를 활용한 딥타임 타이머 상태 관리
- 잠금 기능(isLock)으로 독서 중 화면 이탈 방지

퀴즈 확인 화면 디자인 개선 (ReadingChallengeQuizCheckScreen)
- 기존 퀴즈 상세 화면에서 퀴즈 미리보기 UI로 변경
- "뒷내용이 궁금해지는 오늘의 퀴즈를 확인해 보세요" 안내 문구 추가
- 퀴즈 카드 디자인 개선
  - 외곽 빛 효과 (BoxShadow with purple glow)
  - 하단 그림자 효과
  - ic_quiz_check.png 아이콘 추가
  - 질문 텍스트만 표시 (선택지 제거)
- CTA 버튼 텍스트 변경: "책 속에서 정답 찾기"
- BaseScreen 구조 유지, 독립 실행 가능한 컴포넌트로 리팩토링
- 퀴즈 오류 신고 기능 제거 (통합 화면에서 관리)

딥타임 화면 리팩토리 (ReadingChallengeQuizDeepTimeScreen)
- BaseScreen에서 ConsumerStatefulWidget으로 변경
- 외부에서 controller, 상태, 콜백을 주입받는 구조로 개선
  - onStartTap, onPauseTap, onResumeTap, onStopTap
  - isLock, onLockToggle
  - CustomCountDownController controller
- AutomaticKeepAliveClientMixin 적용으로 탭 전환 시 상태 유지
- 그라데이션 배경 추가 (bottom to center purple gradient)
- 버튼 디자인 개선
  - 기존 원형 아이콘 버튼에서 CtaButtonS 텍스트 버튼으로 변경
  - "🚀독서 시작하기", "⏸️독서 일시정지", "🚀독서 계속하기", "🧩퀴즈 풀러가기"
- 타이머 폰트 적용: Akira Expanded Demo (fontSize: 50, w900)
- 잠금 상태에 따른 뒤로가기 제어 제거 (wrap 화면에서 관리)

퀴즈 풀기 화면 리팩토링 (ReadingChallengeQuizScreen)
- BaseScreen에서 ConsumerStatefulWidget으로 변경
- 외부에서 onSubmitQuiz 콜백 주입받는 구조
- AutomaticKeepAliveClientMixin 적용
- CTA 버튼을 wrap 화면으로 이동
- 선택지 선택 로직을 wrap 화면과 공유
- 퀴즈 오류 신고 기능 제거

정답 확인 화면 개선 (ReadingChallengeQuizResultScreen)
- onNextChapterTap, onRetryTap 콜백 주입 구조
- CTA 버튼을 wrap 화면으로 이동
- 정답/오답 표시 UI 유지

라우터 설정 간소화
- 기존 3개 개별 라우트 통합
  - /reading-challenge/:challengeId/quiz/:chapterId/check (삭제)
  - /reading-challenge/:challengeId/quiz/:chapterId/deep-time (삭제)
  - /reading-challenge/:challengeId/quiz/:chapterId (삭제)
- 새로운 통합 라우트
  - /reading-challenge/:challengeId/quiz/:chapterId/wrap
- ReadingChallengeScreen, ReadingChallengeStartScreen 네비게이션 업데이트

폰트 및 에셋 추가
- Akira Expanded Demo 폰트 추가
  - assets/fonts/Akira Expanded Demo.otf
  - pubspec.yaml 폰트 패밀리 등록
  - main.dart에 FontLoader 추가
- ic_quiz_check.png 이미지 추가
- assets.gen.dart 자동 생성 파일 업데이트

ViewModel 업데이트
- OngoingChallengeViewModel에 resetQuizWrapState 메서드 추가
- 퀴즈 wrap 화면 상태 초기화 로직

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@doyou1 doyou1 self-assigned this Jan 17, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @doyou1, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 리딩 챌린지 퀴즈 경험을 전반적으로 개선하기 위해 기존의 분리된 퀴즈 관련 화면들을 하나의 통합된 플로우로 재구성합니다. 사용자 경험을 향상시키기 위한 UI/UX 디자인 업데이트와 함께, 화면 간 상태 유지 및 모듈화를 위한 기술적 리팩토링이 이루어졌습니다. 이를 통해 사용자는 더욱 매끄럽고 일관된 퀴즈 진행 경험을 할 수 있으며, 코드 베이스는 더 효율적이고 유지보수하기 쉬워졌습니다.

Highlights

  • 퀴즈 플로우 통합: 기존의 '퀴즈 확인', '딥타임', '퀴즈 풀기' 화면이 'ReadingChallengeQuizWrapScreen'이라는 하나의 통합 화면으로 재구성되었습니다. PageView 기반의 탭 네비게이션을 통해 각 단계를 순차적으로 진행할 수 있습니다.
  • UI/UX 개선: 퀴즈 확인 화면은 새로운 카드 UI와 외곽 빛 효과, 하단 그림자 등으로 디자인이 개선되었으며, 딥타임 화면에는 그라데이션 배경과 'Akira Expanded Demo' 폰트가 적용된 타이머, 텍스트 버튼이 도입되었습니다.
  • 기술적 리팩토링: 각 퀴즈 관련 화면들이 'BaseScreen'에서 'ConsumerStatefulWidget'으로 전환되었고, 'AutomaticKeepAliveClientMixin'을 적용하여 탭 전환 시 상태가 유지됩니다. 또한, 콜백 주입 패턴을 사용하여 화면 간 결합도를 낮췄습니다.
  • 라우터 간소화: 기존의 3개 개별 라우트가 '/quiz/:chapterId/wrap'이라는 하나의 통합 라우트로 간소화되어 라우팅 로직이 단순해졌습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

이번 PR은 리딩챌린지 관련 여러 화면을 하나의 통합된 화면으로 재구성하고 UI/UX를 개선하는 대규모 리팩토링을 포함하고 있습니다. PageViewTabController를 사용하여 탭 기반 네비게이션을 구현하고, 각 화면의 결합도를 낮추기 위해 콜백 패턴을 효과적으로 사용한 점이 인상적입니다. 전체적으로 코드 구조가 개선되고 사용자 경험이 향상될 것으로 기대됩니다.

몇 가지 개선점을 제안했습니다. main.dart에서 폰트를 잘못 로드하는 부분을 수정하여 앱 안정성을 높이고, 일부 위젯에서 불필요한 코드(사용되지 않는 속성, 중복된 setState 호출)를 제거하여 코드의 명확성과 성능을 개선할 수 있도록 제안했습니다.

Comment on lines 24 to 27
final fontLoader = FontLoader('BookkMyungjo');
fontLoader.addFont(rootBundle.load(Assets.fonts.bookkMyungjoBold));
fontLoader.addFont(rootBundle.load(Assets.fonts.akiraExpandedDemo));
await fontLoader.load();
Copy link
Contributor

Choose a reason for hiding this comment

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

high

새로운 폰트인 'AkiraExpandedDemo'를 로드하는 방식이 잘못되었습니다. FontLoader는 특정 폰트 패밀리에 대한 폰트들을 그룹화합니다. 현재 코드는 'AkiraExpandedDemo' 폰트를 'BookkMyungjo' 패밀리에 추가하고 있어, 의도한 대로 폰트가 적용되지 않을 수 있습니다.

'AkiraExpandedDemo' 폰트 패밀리에 대한 별도의 FontLoader를 생성하여 로드해야 합니다. 아래와 같이 수정하는 것을 제안합니다.

Suggested change
final fontLoader = FontLoader('BookkMyungjo');
fontLoader.addFont(rootBundle.load(Assets.fonts.bookkMyungjoBold));
fontLoader.addFont(rootBundle.load(Assets.fonts.akiraExpandedDemo));
await fontLoader.load();
final fontLoader = FontLoader('BookkMyungjo');
fontLoader.addFont(rootBundle.load(Assets.fonts.bookkMyungjoBold));
await fontLoader.load();
final akiraFontLoader = FontLoader('AkiraExpandedDemo');
akiraFontLoader.addFont(rootBundle.load(Assets.fonts.akiraExpandedDemo));
await akiraFontLoader.load();

Comment on lines +18 to +23
const ReadingChallengeQuizScreen({
super.key,
required this.chapterId,
required this.challengeId,
this.showAppBar = true,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

이 위젯이 ReadingChallengeWrapQuizScreen의 일부로 사용되면서 AppBar가 상위 위젯에서 관리되므로, showAppBar 속성은 더 이상 사용되지 않는 것 같습니다. 코드의 명확성을 위해 이 속성을 제거하는 것이 좋겠습니다.

  const ReadingChallengeQuizScreen({
    super.key,
    required this.chapterId,
    required this.challengeId,
  });

Comment on lines +158 to +165
onPauseTap: () {
_timerController.pause();
setState(() {});
},
onResumeTap: () {
_timerController.resume();
setState(() {});
},
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

onPauseTaponResumeTap 콜백에서 setState를 호출하는 것은 불필요해 보입니다. ReadingChallengeQuizDeepTimeScreen 내부의 버튼들은 CustomCountDownControllerValueNotifier를 통해 상태 변화를 감지하고 스스로 리빌드됩니다. 불필요한 setState 호출은 위젯 트리의 더 넓은 부분을 리빌드하여 성능에 미미한 영향을 줄 수 있으므로 제거하는 것을 권장합니다.

                  onPauseTap: () {
                    _timerController.pause();
                  },
                  onResumeTap: () {
                    _timerController.resume();
                  },

  BaseScreen에 PopScope 추가
  - PopScope 위젯으로 전체 Scaffold 감싸서 화면 pop 동작 제어
  - canPop() Hook 메서드 추가
    - 기본값: true (정상적으로 뒤로가기 허용)
    - 하위 클래스에서 오버라이드하여 조건부 제어 가능
  - onPopInvoked() Hook 메서드 추가
    - pop이 차단되었을 때(didPop = false) 실행되는 콜백
    - 필요시 사용자에게 알림 표시 등 추가 동작 구현 가능
  - PopScope.onPopInvokedWithResult 사용
    - didPop이 false일 때만 onPopInvoked() 호출

  ReadingChallengeWrapQuizScreen에 canPop() 구현
  - _isLock 상태에 따라 뒤로가기 동작 제어
  - _isLock = true: canPop() returns false
    - iOS edge swipe back 제스처 차단
    - Android 물리 뒤로가기 버튼 차단
    - AppBar 뒤로가기 버튼 비활성화 (기존 로직 유지)
  - _isLock = false: canPop() returns true
    - 모든 뒤로가기 동작 정상 작동

  기대 효과
  - 딥타임 잠금 활성화 시 사용자가 실수로 화면을 벗어나는 모든 경로 차단
    - iOS: edge swipe back 제스처
    - Android: 물리/소프트 뒤로가기 버튼
    - AppBar: 뒤로가기 버튼
  - 독서 집중 시간 중 의도치 않은 화면 이탈 방지
  - 사용자 경험 개선
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.

리딩챌린지: 퀴즈확인, 딥타임, 퀴즈풀기 ui 통합 및 디자인 개선

2 participants