feat(flutter): 함께 관리 화면을 보는 동안 내 초대 코드를 새로 읽기

일정 연동 코드는 한 번 쓰이면 서버가 바꾸므로, 함께 관리 화면을 열어 둔 동안 15초마다(들어올 때 한 번) 다시 읽어
보여 주는 코드·QR 이 낡지 않게 한다. 안내에 "초대 코드는 한 번 쓰면 새 코드로 바뀌어요."를 더한다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-11 23:21:04 +09:00
parent 02c7ec0079
commit aba3d801fc
@@ -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),
),