From aba3d801fcb0022ad9fb0b4926d4c8e28e83f47f Mon Sep 17 00:00:00 2001 From: TtiPo Date: Sun, 11 Oct 2026 23:21:04 +0900 Subject: [PATCH] =?UTF-8?q?feat(flutter):=20=ED=95=A8=EA=BB=98=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=ED=99=94=EB=A9=B4=EC=9D=84=20=EB=B3=B4=EB=8A=94=20?= =?UTF-8?q?=EB=8F=99=EC=95=88=20=EB=82=B4=20=EC=B4=88=EB=8C=80=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=EB=A5=BC=20=EC=83=88=EB=A1=9C=20=EC=9D=BD=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 일정 연동 코드는 한 번 쓰이면 서버가 바꾸므로, 함께 관리 화면을 열어 둔 동안 15초마다(들어올 때 한 번) 다시 읽어 보여 주는 코드·QR 이 낡지 않게 한다. 안내에 "초대 코드는 한 번 쓰면 새 코드로 바뀌어요."를 더한다. Co-Authored-By: Claude Opus 5.5 --- .../pages/share_calendar_page.dart | 104 ++++++++++++------ 1 file changed, 73 insertions(+), 31 deletions(-) diff --git a/flutter/lib/features/schedule/presentation/pages/share_calendar_page.dart b/flutter/lib/features/schedule/presentation/pages/share_calendar_page.dart index 559ace2..4b2312c 100644 --- a/flutter/lib/features/schedule/presentation/pages/share_calendar_page.dart +++ b/flutter/lib/features/schedule/presentation/pages/share_calendar_page.dart @@ -33,6 +33,9 @@ const double _kInviteQr = 130; const double _kInviteRowHeight = _kInviteQr + 11 * 2 + 2; const double _kSheetQr = 196; +/// 이 화면을 보는 동안 연동 상태를 다시 읽는 간격 — 내 코드로 누가 연동하면 코드가 바뀌고 사람이 늘어난다 +const Duration _kPageRefresh = Duration(seconds: 15); + /// 펼쳐지는 영역 (시안 max-height .45s cubic-bezier(.4,0,.2,1)) const Duration _kExpand = Duration(milliseconds: 450); @@ -68,37 +71,39 @@ class ShareCalendarPage extends ConsumerWidget { final linked = sharing?.linked ?? false; final on = linked || ref.watch(calendarSharingOnProvider); - return Scaffold( - body: SafeArea( - bottom: false, - child: SingleChildScrollView( - padding: EdgeInsets.fromLTRB( - AppSpacing.xl, - AppSpacing.xs, - AppSpacing.xl, - MediaQuery.paddingOf(context).bottom + AppSpacing.lg, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - SubPageHeader( - title: '함께 관리', - subtitle: '가족·친구와 같은 달력을 함께 써요', - onBack: () => context.pop(), - ), - _ToggleCard( - on: on, - onChanged: sharing == null ? null : (v) => unawaited(_toggle(context, ref, sharing, v)), - ), - if (!on) const _OffHint(), - // 켜면 펼쳐진다 - AnimatedSize( - duration: _kExpand, - curve: const Cubic(.4, 0, .2, 1), - alignment: Alignment.topCenter, - child: on && sharing != null ? _OnBody(sharing: sharing) : const SizedBox(width: double.infinity), - ), - ], + return _WhileOpenRefresh( + child: Scaffold( + body: SafeArea( + bottom: false, + child: SingleChildScrollView( + padding: EdgeInsets.fromLTRB( + AppSpacing.xl, + AppSpacing.xs, + AppSpacing.xl, + MediaQuery.paddingOf(context).bottom + AppSpacing.lg, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SubPageHeader( + title: '함께 관리', + subtitle: '가족·친구와 같은 달력을 함께 써요', + onBack: () => context.pop(), + ), + _ToggleCard( + on: on, + onChanged: sharing == null ? null : (v) => unawaited(_toggle(context, ref, sharing, v)), + ), + if (!on) const _OffHint(), + // 켜면 펼쳐진다 + AnimatedSize( + duration: _kExpand, + curve: const Cubic(.4, 0, .2, 1), + alignment: Alignment.topCenter, + child: on && sharing != null ? _OnBody(sharing: sharing) : const SizedBox(width: double.infinity), + ), + ], + ), ), ), ), @@ -106,6 +111,41 @@ class ShareCalendarPage extends ConsumerWidget { } } +/// 이 화면을 보는 동안 연동 상태를 자주 다시 읽는다 — 초대 코드는 한 번 쓰면 바뀌므로 보여 주는 코드가 낡지 않게 +class _WhileOpenRefresh extends ConsumerStatefulWidget { + const _WhileOpenRefresh({required this.child}); + + final Widget child; + + @override + ConsumerState<_WhileOpenRefresh> createState() => _WhileOpenRefreshState(); +} + +class _WhileOpenRefreshState extends ConsumerState<_WhileOpenRefresh> { + Timer? _timer; + + @override + void initState() { + super.initState(); + _timer = Timer.periodic(_kPageRefresh, (_) { + if (mounted) unawaited(succeeded(ref.read(calendarSharingProvider.notifier).refresh(quiet: true))); + }); + // 들어올 때 한 번 — 다른 화면에 있는 동안 바뀌었을 수 있다 + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) unawaited(succeeded(ref.read(calendarSharingProvider.notifier).refresh(quiet: true))); + }); + } + + @override + void dispose() { + _timer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) => widget.child; +} + /// 일정 연동 켜기·끄기 카드 class _ToggleCard extends StatelessWidget { const _ToggleCard({required this.on, required this.onChanged}); @@ -252,6 +292,8 @@ class _OnBody extends ConsumerWidget { const TextSpan(children: [ TextSpan(text: '연동을 하게 되면 '), TextSpan(text: '연동된 사람과 모든 일정이 공유됩니다.', style: TextStyle(fontWeight: FontWeight.w700)), + // 한 번 연동에 쓴 코드는 서버가 바꾼다 — 받은 사람이 다시 쓰거나 남에게 넘길 수 없다 + TextSpan(text: ' 초대 코드는 한 번 쓰면 새 코드로 바뀌어요.'), ]), style: _ui(context, 12.5, FontWeight.w400, AppColors.ink2, height: 1.55), ),