import 'package:flutter/material.dart'; import 'package:friendly_map/modules/app/app.dart'; import 'package:friendly_map/modules/core/core.dart'; import 'package:friendly_map/modules/storage/storage.dart'; import '../app_scope.dart'; import '../theme.dart'; import '../widgets/async_view.dart'; import '../widgets/common.dart'; import '../widgets/labels.dart'; import 'card_edit_screen.dart'; import 'share_screen.dart'; import 'template_gallery_screen.dart'; class _WalletData { const _WalletData(this.cards, this.shares, this.resend, this.people, this.labels); final List cards; final Map> shares; final List resend; final Map people; final Map labels; } /// 내 명함첩: 용도별 여러 장. 길게 눌러 끌면 순서가 바뀌고, 고친 명함은 "다시 보내기"로 상대에게 보낸다. class CardWalletScreen extends StatelessWidget { const CardWalletScreen({super.key}); Future<_WalletData> _load(FriendlyApp app) async { final cards = await app.cards.mine(); return _WalletData( cards, {for (final c in cards) c.id: await app.cards.sharesOf(c.id)}, await app.cards.needingResend(), {for (final p in await app.friends.people()) p.id: p}, await fieldLabels(app), ); } @override Widget build(BuildContext context) { final app = AppScope.of(context).app; return Scaffold( appBar: AppBar( title: const Text('내 명함', style: TextStyle(fontWeight: FontWeight.w700)), actions: [ IconButton(tooltip: '명함 공유', onPressed: () => pushPage(context, const ShareScreen()), icon: const Icon(Icons.ios_share)), ], ), floatingActionButton: FloatingActionButton.extended( heroTag: null, onPressed: () => pushPage(context, const TemplateGalleryScreen()), icon: const Icon(Icons.add), label: const Text('명함 추가'), ), body: AsyncView<_WalletData>( load: _load, builder: (context, d) { return ListView(padding: const EdgeInsets.only(bottom: 100), children: [ if (d.resend.isNotEmpty) _ResendBanner(d), if (d.cards.isEmpty) const EmptyNote('명함이 없어요. 아래 버튼으로 만들어 보세요.'), Padding( padding: const EdgeInsets.fromLTRB(20, 4, 20, 8), child: Text('길게 눌러 끌면 순서를 바꿀 수 있어요', style: TextStyle(fontSize: 11, color: Pal.muted.withValues(alpha: 0.8))), ), ReorderableListView( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), buildDefaultDragHandles: true, onReorderItem: (from, to) => app.cards.move(d.cards[from].id, to), children: [ for (final c in d.cards) _CardTile(key: ValueKey(c.id), card: c, shares: d.shares[c.id] ?? const [], people: d.people, labels: d.labels), ], ), ]); }, ), ); } } class _ResendBanner extends StatelessWidget { const _ResendBanner(this.d); final _WalletData d; @override Widget build(BuildContext context) { final app = AppScope.of(context).app; return Panel( child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('고친 명함을 아직 보내지 않았어요', style: TextStyle(fontWeight: FontWeight.w700)), const SizedBox(height: 4), const Text('상대에게는 "다시 보내기"를 눌러야 새 내용이 가요.', style: TextStyle(fontSize: 12, color: Pal.muted)), const SizedBox(height: 8), for (final s in d.resend) Row(children: [ Expanded(child: Text('${d.people[s.peerId]?.name ?? '?'} · ${_title(s)}', overflow: TextOverflow.ellipsis)), TextButton( onPressed: () async { final ok = await app.cards.resend(s.id); if (context.mounted) showNote(context, ok ? '다시 보냈어요.' : '보내지 못했어요.'); }, child: const Text('다시 보내기'), ), ]), ]), ); } String _title(ShareRecord s) { for (final c in d.cards) { if (c.id == s.targetId) return c.title; } return '명함'; } } class _CardTile extends StatelessWidget { const _CardTile({super.key, required this.card, required this.shares, required this.people, required this.labels}); final MyCard card; final List shares; final Map people; final Map labels; @override Widget build(BuildContext context) { final app = AppScope.of(context).app; final lines = filledFields(decodeFields(card.fieldsJson), labels).take(3).toList(); final names = [for (final s in shares) people[s.peerId]?.name ?? '?']; return Panel( onTap: () => pushPage(context, CardEditScreen(cardId: card.id)), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Row(children: [ KindChip(card.kind, CardKinds.label(card.kind)), const SizedBox(width: 8), Expanded(child: Text(card.title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w700), overflow: TextOverflow.ellipsis)), PopupMenuButton( tooltip: '더 보기', onSelected: (v) async { if (v == 'edit') pushPage(context, CardEditScreen(cardId: card.id)); if (v == 'share') pushPage(context, ShareScreen(initialCardId: card.id)); if (v == 'delete') { final ok = await askConfirm(context, '명함을 지울까요?', '이미 상대에게 간 사본은 상대 기기에 남아요. 이후 공유는 끝나요.', ok: '지우기'); if (ok) await app.cards.delete(card.id); } }, itemBuilder: (_) => const [ PopupMenuItem(value: 'edit', child: Text('고치기')), PopupMenuItem(value: 'share', child: Text('공유')), PopupMenuItem(value: 'delete', child: Text('지우기')), ], ), ]), if (lines.isEmpty) const Text('내용을 채워 보세요', style: TextStyle(color: Pal.muted, fontSize: 13)), for (final e in lines) Text('${e.key} ${e.value}', style: const TextStyle(fontSize: 13, height: 1.5)), const SizedBox(height: 6), Row(children: [ const Icon(Icons.share_outlined, size: 14, color: Pal.muted), const SizedBox(width: 6), Expanded( child: Text( names.isEmpty ? '공유 안 함' : '${names.join(', ')} 에게 공유 중', style: const TextStyle(fontSize: 12, color: Pal.muted), overflow: TextOverflow.ellipsis, ), ), if (card.shareDefault) const KindChip('family', '새 친구에게 기본 공유'), ]), ]), ); } }