Skip to content

Commit f75f3cd

Browse files
committed
work on #14新增配對紀錄頁面,讓使用者可以查看。配對紀錄在 Firebase 的資料表名稱為 matches
1 parent 067c6c1 commit f75f3cd

File tree

2 files changed

+342
-8
lines changed

2 files changed

+342
-8
lines changed

MATCHING_SYSTEM_ANALYSIS.md

Lines changed: 168 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,19 +177,19 @@ Row(
177177
heroTag: "reject",
178178
onPressed: _nextUser,
179179
backgroundColor: Colors.deepOrange,
180-
child: const Icon(Icons.close, color: Colors.white),
180+
child: const Icon(Icons.close),
181181
),
182182
FloatingActionButton(
183183
heroTag: "info",
184184
onPressed: () => _showUserInfoBottomSheet(context),
185185
backgroundColor: Colors.orange.shade600,
186-
child: const Icon(Icons.info, color: Colors.white),
186+
child: const Icon(Icons.info),
187187
),
188188
FloatingActionButton(
189189
heroTag: "accept",
190190
onPressed: _matchSuccess,
191191
backgroundColor: Colors.orange,
192-
child: const Icon(Icons.favorite, color: Colors.white),
192+
child: const Icon(Icons.favorite),
193193
),
194194
],
195195
)
@@ -410,4 +410,168 @@ Expanded(
410410
],
411411
),
412412
),
413-
),
413+
),
414+
415+
class _UserCard extends StatelessWidget {
416+
final UserModel user;
417+
final int matchScore;
418+
final String matchType;
419+
final VoidCallback onLike;
420+
final VoidCallback onDislike;
421+
const _UserCard({
422+
required this.user,
423+
required this.matchScore,
424+
required this.matchType,
425+
required this.onLike,
426+
required this.onDislike,
427+
});
428+
429+
String getAgeText(String learnerBirth) {
430+
try {
431+
final year = int.tryParse(learnerBirth.substring(0, 4));
432+
if (year != null) {
433+
final age = DateTime.now().year - year;
434+
return '年齡:約$age 歲';
435+
}
436+
} catch (_) {}
437+
return '年齡:未知';
438+
}
439+
440+
@override
441+
Widget build(BuildContext context) {
442+
return Card(
443+
elevation: 12,
444+
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
445+
margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
446+
child: Padding(
447+
padding: const EdgeInsets.all(24.0),
448+
child: Column(
449+
mainAxisSize: MainAxisSize.min,
450+
children: [
451+
// 頭像
452+
CircleAvatar(
453+
radius: 40,
454+
backgroundColor: Colors.orange.shade100,
455+
child: Text(user.name.isNotEmpty ? user.name[0] : '?',
456+
style: const TextStyle(fontSize: 36, color: Colors.orange)),
457+
),
458+
const SizedBox(height: 12),
459+
Text(user.name, style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
460+
const SizedBox(height: 4),
461+
Text(getAgeText(user.learnerBirth)),
462+
Text('地區:${user.address}'),
463+
const Divider(height: 24),
464+
Text('興趣:${user.habits.join(", ")}'),
465+
Text('可分享:${user.share.join(", ")}'),
466+
Text('想學習:${user.ask.join(", ")}'),
467+
const SizedBox(height: 12),
468+
// 配對分數
469+
if (matchScore >= 0)
470+
Chip(
471+
label: Text('$matchType配對分數:$matchScore',
472+
style: const TextStyle(color: Colors.white)),
473+
backgroundColor: Colors.orange,
474+
),
475+
const SizedBox(height: 16),
476+
Row(
477+
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
478+
children: [
479+
FloatingActionButton(
480+
heroTag: null,
481+
onPressed: onDislike,
482+
backgroundColor: Colors.red,
483+
child: const Icon(Icons.close),
484+
),
485+
FloatingActionButton(
486+
heroTag: null,
487+
onPressed: onLike,
488+
backgroundColor: Colors.green,
489+
child: const Icon(Icons.favorite),
490+
),
491+
],
492+
)
493+
],
494+
),
495+
),
496+
);
497+
}
498+
}
499+
500+
class MatchHistoryScreen extends StatefulWidget {
501+
const MatchHistoryScreen({super.key});
502+
@override
503+
State<MatchHistoryScreen> createState() => _MatchHistoryScreenState();
504+
}
505+
506+
class _MatchHistoryScreenState extends State<MatchHistoryScreen> {
507+
List<Map<String, dynamic>> records = [];
508+
bool isLoading = true;
509+
510+
@override
511+
void initState() {
512+
super.initState();
513+
_loadRecords();
514+
}
515+
516+
Future<void> _loadRecords() async {
517+
final user = FirebaseAuth.instance.currentUser;
518+
if (user == null) return;
519+
final ref = FirebaseDatabase.instance.ref('matches/${user.uid}');
520+
final snapshot = await ref.get();
521+
if (snapshot.exists && snapshot.value != null) {
522+
final data = Map<String, dynamic>.from(snapshot.value as Map);
523+
setState(() {
524+
records = data.entries.map((e) {
525+
final v = Map<String, dynamic>.from(e.value);
526+
v['uid'] = e.key;
527+
return v;
528+
}).toList();
529+
isLoading = false;
530+
});
531+
} else {
532+
setState(() {
533+
records = [];
534+
isLoading = false;
535+
});
536+
}
537+
}
538+
539+
@override
540+
Widget build(BuildContext context) {
541+
return Scaffold(
542+
appBar: AppBar(title: const Text('配對紀錄')),
543+
body: isLoading
544+
? const Center(child: CircularProgressIndicator())
545+
: ListView.separated(
546+
itemCount: records.length,
547+
separatorBuilder: (_, __) => const Divider(),
548+
itemBuilder: (context, i) {
549+
final r = records[i];
550+
return ListTile(
551+
leading: const Icon(Icons.person),
552+
title: Text('用戶ID: ${r['uid']}'),
553+
subtitle: Text('${r['matchType']}分數: ${r['score']}'),
554+
trailing: Text(
555+
DateTime.fromMillisecondsSinceEpoch(r['timestamp'])
556+
.toLocal()
557+
.toString()
558+
.substring(0, 16),
559+
),
560+
);
561+
},
562+
),
563+
);
564+
}
565+
}
566+
567+
actions: [
568+
IconButton(
569+
icon: const Icon(Icons.history),
570+
onPressed: () {
571+
Navigator.push(
572+
context,
573+
MaterialPageRoute(builder: (_) => const MatchHistoryScreen()),
574+
);
575+
},
576+
),
577+
],

0 commit comments

Comments
 (0)