forked from FlowwStar/FlowStar
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuse-activity-feed.ts
More file actions
134 lines (114 loc) · 4.12 KB
/
Copy pathuse-activity-feed.ts
File metadata and controls
134 lines (114 loc) · 4.12 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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
'use client'
import { useState, useEffect, useCallback, useRef } from 'react'
import { useStreams } from '@/hooks/use-streams'
import type { StreamData } from '@/types/stream'
export type ActivityEventType =
| 'stream.created'
| 'stream.withdrawal'
| 'stream.cancelled'
| 'stream.completed'
| 'stream.topped_up'
| 'stream.transferred'
export interface ActivityEvent {
id: string
type: ActivityEventType
streamId: string
timestamp: number
role: 'sent' | 'received'
description: string
token: string
amount?: string
}
function deriveEvents(streams: StreamData[], walletAddress: string): ActivityEvent[] {
const events: ActivityEvent[] = []
const now = BigInt(Math.floor(Date.now() / 1000))
for (const s of streams) {
const role: 'sent' | 'received' = s.sender.toLowerCase() === walletAddress.toLowerCase()
? 'sent'
: 'received'
const fmt = (amount: bigint) =>
(Number(amount) / 10 ** s.token.decimals).toLocaleString(undefined, {
maximumFractionDigits: 4,
})
// Created event
events.push({
id: `created-${s.id}`,
type: 'stream.created',
streamId: s.id,
timestamp: Number(s.startTime) * 1000,
role,
description:
role === 'sent'
? `You started streaming ${fmt(s.depositedAmount)} ${s.token.symbol} to ${s.recipient.slice(0, 6)}…${s.recipient.slice(-4)}`
: `Incoming stream of ${fmt(s.depositedAmount)} ${s.token.symbol} from ${s.sender.slice(0, 6)}…${s.sender.slice(-4)}`,
token: s.token.symbol,
amount: fmt(s.depositedAmount),
})
// Withdrawal event (infer from withdrawn amount)
if (s.withdrawnAmount > 0n) {
events.push({
id: `withdrawal-${s.id}`,
type: 'stream.withdrawal',
streamId: s.id,
timestamp: Number(s.startTime) * 1000 + 1,
role,
description: `${role === 'received' ? 'You' : s.recipient.slice(0, 6) + '…'} withdrew ${fmt(s.withdrawnAmount)} ${s.token.symbol} from Stream #${s.id}`,
token: s.token.symbol,
amount: fmt(s.withdrawnAmount),
})
}
// Cancelled
if (s.cancelled) {
const returned = s.depositedAmount - s.withdrawnAmount
events.push({
id: `cancelled-${s.id}`,
type: 'stream.cancelled',
streamId: s.id,
timestamp: Number(s.endTime) * 1000,
role,
description:
role === 'sent'
? `You cancelled Stream #${s.id} — ${fmt(returned)} ${s.token.symbol} returned`
: `Stream #${s.id} was cancelled — ${fmt(returned)} ${s.token.symbol} returned`,
token: s.token.symbol,
amount: fmt(returned),
})
}
// Completed
if (!s.cancelled && now >= s.endTime && s.withdrawnAmount >= s.depositedAmount) {
events.push({
id: `completed-${s.id}`,
type: 'stream.completed',
streamId: s.id,
timestamp: Number(s.endTime) * 1000,
role,
description: `Stream #${s.id} fully unlocked — ${fmt(s.depositedAmount)} ${s.token.symbol}`,
token: s.token.symbol,
amount: fmt(s.depositedAmount),
})
}
}
return events.sort((a, b) => b.timestamp - a.timestamp)
}
export type ActivityFilter = {
eventType: ActivityEventType | 'all'
role: 'sent' | 'received' | 'all'
}
const PAGE_SIZE = 20
export function useActivityFeed(walletAddress: string | null) {
const { all } = useStreams()
const [filter, setFilter] = useState<ActivityFilter>({ eventType: 'all', role: 'all' })
const [page, setPage] = useState(1)
const allEvents = walletAddress ? deriveEvents(all, walletAddress) : []
const filtered = allEvents.filter((e) => {
if (filter.eventType !== 'all' && e.type !== filter.eventType) return false
if (filter.role !== 'all' && e.role !== filter.role) return false
return true
})
const visible = filtered.slice(0, page * PAGE_SIZE)
const hasMore = visible.length < filtered.length
const loadMore = useCallback(() => setPage((p) => p + 1), [])
// Reset page when filter changes
useEffect(() => setPage(1), [filter])
return { events: visible, hasMore, loadMore, filter, setFilter, total: filtered.length }
}