forked from StellarSplit/StellarSplit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLiveActivityFeed.tsx
More file actions
91 lines (84 loc) · 4.08 KB
/
Copy pathLiveActivityFeed.tsx
File metadata and controls
91 lines (84 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import { useCollaboration } from '../../hooks/useCollaboration';
import { Activity, CreditCard, PlusCircle, Trash, RefreshCw } from 'lucide-react';
export interface ActivityFeedItem {
id: string;
type: string;
userId?: string;
userName: string;
message: string;
timestamp: Date | string;
splitId?: string;
}
interface LiveActivityFeedProps {
activities?: ActivityFeedItem[];
}
export function LiveActivityFeed({ activities: initialActivities }: LiveActivityFeedProps) {
const { activities: liveActivities } = useCollaboration();
const activities = initialActivities && initialActivities.length > 0
? [...initialActivities, ...liveActivities].filter(
(activity, index, collection) =>
collection.findIndex((candidate) => candidate.id === activity.id) === index
).sort(
(left, right) =>
new Date(right.timestamp).getTime() - new Date(left.timestamp).getTime()
)
: liveActivities;
if (activities.length === 0) {
return (
<div className="bg-white rounded-lg shadow p-6 text-center" id="live-activity-feed">
<Activity className="w-8 h-8 text-gray-400 mx-auto mb-3" />
<h3 className="text-sm font-medium text-gray-900">No recent activity</h3>
<p className="text-xs text-gray-500 mt-1">Updates will appear here live</p>
</div>
);
}
const getEventIcon = (type: string) => {
switch (type) {
case 'payment-status': return <CreditCard className="w-4 h-4 text-green-500" />;
case 'item-added': return <PlusCircle className="w-4 h-4 text-blue-500" />;
case 'item-deleted': return <Trash className="w-4 h-4 text-red-500" />;
case 'item-updated': return <RefreshCw className="w-4 h-4 text-purple-500" />;
default: return <Activity className="w-4 h-4 text-gray-500" />;
}
};
const getTimeAgo = (date: Date | string) => {
const seconds = Math.floor((new Date().getTime() - new Date(date).getTime()) / 1000);
if (seconds < 60) return 'Just now';
const mins = Math.floor(seconds / 60);
if (mins < 60) return `${mins}m ago`;
const hours = Math.floor(mins / 60);
return `${hours}h ago`;
};
return (
<div className="bg-white rounded-lg shadow p-0 overflow-hidden" id="live-activity-feed">
<div className="px-6 py-4 border-b border-gray-100 flex items-center justify-between">
<h3 className="text-md font-bold text-gray-900 flex items-center gap-2">
<Activity className="w-5 h-5 text-purple-500" /> Live Feed
</h3>
<span className="flex h-2 w-2 relative">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-purple-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-purple-500"></span>
</span>
</div>
<div className="p-4 max-h-64 overflow-y-auto w-full">
<ul className="space-y-4">
{activities.map((activity, idx) => (
<li key={activity.id || idx} className="flex gap-3 animate-in slide-in-from-left-2 duration-300">
<div className="mt-0.5 bg-gray-50 p-1.5 rounded-full border border-gray-100">
{getEventIcon(activity.type)}
</div>
<div className="flex-1">
<p className="text-sm text-gray-800">
<span className="font-semibold">{activity.userName}</span> {activity.message}
</p>
<p className="text-xs text-gray-400 mt-0.5">
{getTimeAgo(activity.timestamp)}
</p>
</div>
</li>
))}
</ul>
</div>
</div>
);
}