175 lines
6.9 KiB
Dart
175 lines
6.9 KiB
Dart
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<MyCard> cards;
|
|
final Map<String, List<ShareRecord>> shares;
|
|
final List<ShareRecord> resend;
|
|
final Map<String, Person> people;
|
|
final Map<String, String> 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<ShareRecord> shares;
|
|
final Map<String, Person> people;
|
|
final Map<String, String> 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<String>(
|
|
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', '새 친구에게 기본 공유'),
|
|
]),
|
|
]),
|
|
);
|
|
}
|
|
}
|