|
1 | | -import { Image, Text, View, StyleSheet } from 'react-native'; |
2 | | -import { InterRegular, InterSemiBold } from '../utils/webFonts'; |
| 1 | +import { useState } from 'react'; |
| 2 | +import { StyleSheet, Text, TouchableOpacity, View, Image, Linking } from 'react-native'; |
3 | 3 | import { Colors } from '../utils/colors'; |
| 4 | +import { ChevronDownIcon } from 'native-base'; |
| 5 | +import { InterMedium, InterSemiBold, InterSmall } from '../utils/webFonts'; |
| 6 | +import { getProvableNFTAddress } from '../models/constants'; |
| 7 | +import env from '../lib/env'; |
| 8 | +import { formatTime } from '../lib/formatTime'; |
4 | 9 |
|
5 | | -const ReceiveIconUri = `data:image/svg+xml;utf8,<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="32" height="32" rx="16" fill="#95EED8"/> <path d="M19.048 14.6186C18.7453 14.3159 18.2546 14.3159 17.952 14.6186L16.775 15.7956V9.33325C16.775 8.90523 16.428 8.55825 16 8.55825C15.572 8.55825 15.225 8.90523 15.225 9.33325V15.7956L14.048 14.6186C13.7453 14.3159 13.2546 14.3159 12.952 14.6186C12.6493 14.9212 12.6493 15.4119 12.952 15.7146L15.452 18.2146C15.7546 18.5172 16.2453 18.5172 16.548 18.2146L19.048 15.7146C19.3507 15.4119 19.3507 14.9212 19.048 14.6186ZM23.4417 15.9999C23.4417 15.5719 23.0947 15.2249 22.6667 15.2249C22.2386 15.2249 21.8917 15.5719 21.8917 15.9999C21.8917 19.2538 19.2539 21.8916 16 21.8916C12.7461 21.8916 10.1083 19.2538 10.1083 15.9999C10.1083 15.5719 9.76135 15.2249 9.33333 15.2249C8.90531 15.2249 8.55833 15.5719 8.55833 15.9999C8.55833 20.1098 11.8901 23.4416 16 23.4416C20.1099 23.4416 23.4417 20.1098 23.4417 15.9999Z" fill="#27564B" stroke="#5BBAA3" stroke-width="0.3"/> </svg> `; |
| 10 | +const ReceiveIconUri = |
| 11 | + 'data:image/svg+xml;utf8,<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="32" height="32" rx="16" fill="#95EED8"/> <path d="M19.048 14.6186C18.7453 14.3159 18.2546 14.3159 17.952 14.6186L16.775 15.7956V9.33325C16.775 8.90523 16.428 8.55825 16 8.55825C15.572 8.55825 15.225 8.90523 15.225 9.33325V15.7956L14.048 14.6186C13.7453 14.3159 13.2546 14.3159 12.952 14.6186C12.6493 14.9212 12.6493 15.4119 12.952 15.7146L15.452 18.2146C15.7546 18.5172 16.2453 18.5172 16.548 18.2146L19.048 15.7146C19.3507 15.4119 19.3507 14.9212 19.048 14.6186ZM23.4417 15.9999C23.4417 15.5719 23.0947 15.2249 22.6667 15.2249C22.2386 15.2249 21.8917 15.5719 21.8917 15.9999C21.8917 19.2538 19.2539 21.8916 16 21.8916C12.7461 21.8916 10.1083 19.2538 10.1083 15.9999C10.1083 15.5719 9.76135 15.2249 9.33333 15.2249C8.90531 15.2249 8.55833 15.5719 8.55833 15.9999C8.55833 20.1098 11.8901 23.4416 16 23.4416C20.1099 23.4416 23.4417 20.1098 23.4417 15.9999Z" fill="#27564B" stroke="#5BBAA3" stroke-width="0.3"/> </svg> '; |
6 | 12 |
|
7 | 13 | interface ActivityLogProps { |
8 | 14 | name: string; |
9 | 15 | id: string; |
10 | 16 | creationDate: string; |
| 17 | + nftId: string; |
| 18 | + transactionHash: string; |
| 19 | + ipfsHash: string; |
| 20 | + paymentAmount: string; |
| 21 | + collective: { |
| 22 | + id: string; |
| 23 | + name: string; |
| 24 | + }; |
| 25 | + nftHash: string; |
| 26 | + timestamp: number; |
11 | 27 | } |
12 | 28 |
|
13 | | -function ActivityLog({}: ActivityLogProps) { |
| 29 | +function ActivityLog({ |
| 30 | + name, |
| 31 | + nftId, |
| 32 | + transactionHash, |
| 33 | + collective, |
| 34 | + ipfsHash, |
| 35 | + nftHash, |
| 36 | + paymentAmount, |
| 37 | + timestamp, |
| 38 | +}: ActivityLogProps) { |
| 39 | + const networkName = env.REACT_APP_NETWORK || 'development-celo'; |
| 40 | + const NFT_CA = getProvableNFTAddress(networkName); |
| 41 | + const [isExpanded, setIsExpanded] = useState(false); |
| 42 | + |
| 43 | + const toggleExpanded = () => { |
| 44 | + setIsExpanded(!isExpanded); |
| 45 | + }; |
| 46 | + |
| 47 | + const openExternalLink = async (url: string) => { |
| 48 | + try { |
| 49 | + const supported = await Linking.canOpenURL(url); |
| 50 | + if (supported) { |
| 51 | + await Linking.openURL(url); |
| 52 | + } else { |
| 53 | + console.warn(`Don't know how to open URL: ${url}`); |
| 54 | + } |
| 55 | + } catch (error) { |
| 56 | + console.error('Error opening URL:', error); |
| 57 | + } |
| 58 | + }; |
| 59 | + |
| 60 | + const handlePaymentTransactionPress = () => { |
| 61 | + if (transactionHash) { |
| 62 | + openExternalLink(`https://celoscan.io/tx/${transactionHash}`); |
| 63 | + } |
| 64 | + }; |
| 65 | + |
| 66 | + const handleNftDetailsPress = () => { |
| 67 | + if (nftHash) { |
| 68 | + openExternalLink(`https://celoscan.io/nft/${NFT_CA}/${nftHash}`); |
| 69 | + } |
| 70 | + }; |
| 71 | + |
| 72 | + const displayName = nftHash || name; |
| 73 | + const truncatedName = |
| 74 | + displayName.length > 20 |
| 75 | + ? `${displayName.substring(0, 8)}...${displayName.substring(displayName.length - 8)}` |
| 76 | + : displayName; |
| 77 | + |
| 78 | + const handleIpfsPress = async () => { |
| 79 | + if (ipfsHash) { |
| 80 | + const cleanHash = ipfsHash.startsWith('0x') ? ipfsHash.slice(2) : ipfsHash; |
| 81 | + const gateways = [ |
| 82 | + `https://ipfs.io/ipfs/${cleanHash}`, |
| 83 | + `https://gateway.pinata.cloud/ipfs/${cleanHash}`, |
| 84 | + `https://cloudflare-ipfs.com/ipfs/${cleanHash}`, |
| 85 | + ]; |
| 86 | + |
| 87 | + try { |
| 88 | + await openExternalLink(gateways[0]); |
| 89 | + } catch (error) { |
| 90 | + try { |
| 91 | + await openExternalLink(gateways[1]); |
| 92 | + } catch (secondError) { |
| 93 | + console.warn('IPFS gateways failed'); |
| 94 | + } |
| 95 | + } |
| 96 | + } |
| 97 | + }; |
| 98 | + |
| 99 | + const displayDate = formatTime(timestamp); |
| 100 | + |
14 | 101 | return ( |
15 | 102 | <View style={styles.container}> |
16 | | - <View style={styles.row}> |
17 | | - <View style={styles.bar} /> |
18 | | - <Image style={styles.icon} source={{ uri: ReceiveIconUri }} /> |
19 | | - <Text style={styles.logProfileDetails}> |
20 | | - <Text style={styles.name}>Silvi Tree Claim {'\n'}</Text> |
21 | | - <Text style={styles.id}>0x723a86c93838c1facse.....</Text> |
22 | | - </Text> |
23 | | - <View style={styles.logDate}> |
24 | | - <Text style={styles.date}>July 3, 2023</Text> |
25 | | - </View> |
| 103 | + <View style={styles.leftBorder} /> |
| 104 | + |
| 105 | + <View style={styles.contentContainer}> |
| 106 | + <TouchableOpacity onPress={toggleExpanded} style={styles.actionRow}> |
| 107 | + <View style={styles.leftSection}> |
| 108 | + <View style={styles.iconContainer}> |
| 109 | + <Image style={styles.icon} source={{ uri: ReceiveIconUri }} /> |
| 110 | + </View> |
| 111 | + |
| 112 | + <View style={styles.actionInfo}> |
| 113 | + <Text style={styles.collectiveName}>{collective.name}</Text> |
| 114 | + |
| 115 | + <View style={styles.titleRow}> |
| 116 | + <Text style={styles.actionName}>{truncatedName}</Text> |
| 117 | + <Text style={styles.actionDate}>{displayDate}</Text> |
| 118 | + </View> |
| 119 | + |
| 120 | + {paymentAmount && <Text style={styles.paymentAmount}>G$ {paymentAmount.split(' ')[0]} transferred</Text>} |
| 121 | + |
| 122 | + <View style={styles.chevronContainer}> |
| 123 | + <ChevronDownIcon |
| 124 | + size={4} |
| 125 | + color={Colors.gray[200]} |
| 126 | + style={{ |
| 127 | + transform: [{ rotate: isExpanded ? '180deg' : '0deg' }], |
| 128 | + }} |
| 129 | + /> |
| 130 | + </View> |
| 131 | + </View> |
| 132 | + </View> |
| 133 | + </TouchableOpacity> |
| 134 | + |
| 135 | + {isExpanded && ( |
| 136 | + <View style={styles.expandedContent}> |
| 137 | + <TouchableOpacity |
| 138 | + style={styles.linkButton} |
| 139 | + onPress={handlePaymentTransactionPress} |
| 140 | + disabled={!transactionHash}> |
| 141 | + <Text style={transactionHash ? styles.linkText : styles.linkTextDisabled}>Payment Transaction</Text> |
| 142 | + </TouchableOpacity> |
| 143 | + |
| 144 | + <TouchableOpacity style={styles.linkButton} onPress={handleNftDetailsPress} disabled={!nftId}> |
| 145 | + <Text style={nftId ? styles.linkText : styles.linkTextDisabled}>NFT Details</Text> |
| 146 | + </TouchableOpacity> |
| 147 | + |
| 148 | + <TouchableOpacity style={styles.linkButton} onPress={handleIpfsPress} disabled={!ipfsHash}> |
| 149 | + <Text style={ipfsHash ? styles.linkText : styles.linkTextDisabled}> |
| 150 | + IPFS Claim and Proof of Verification |
| 151 | + </Text> |
| 152 | + </TouchableOpacity> |
| 153 | + |
| 154 | + <TouchableOpacity onPress={toggleExpanded} style={styles.collapseButton}> |
| 155 | + <Text style={styles.collapseIcon}>⌃</Text> |
| 156 | + </TouchableOpacity> |
| 157 | + </View> |
| 158 | + )} |
26 | 159 | </View> |
27 | 160 | </View> |
28 | 161 | ); |
29 | 162 | } |
30 | 163 |
|
31 | 164 | const styles = StyleSheet.create({ |
32 | 165 | container: { |
33 | | - width: '100%', |
34 | 166 | backgroundColor: Colors.white, |
35 | | - paddingLeft: 16, |
36 | | - paddingRight: 16, |
| 167 | + borderRadius: 12, |
| 168 | + marginBottom: 12, |
| 169 | + shadowColor: Colors.black, |
| 170 | + shadowOffset: { |
| 171 | + width: 0, |
| 172 | + height: 2, |
| 173 | + }, |
| 174 | + shadowOpacity: 0.1, |
| 175 | + shadowRadius: 4, |
| 176 | + elevation: 3, |
| 177 | + overflow: 'hidden', |
37 | 178 | }, |
38 | | - row: { |
39 | | - width: '100%', |
40 | | - flex: 1, |
41 | | - flexDirection: 'row', |
42 | | - gap: 8, |
43 | | - backgroundColor: Colors.white, |
| 179 | + |
| 180 | + contentContainer: { |
| 181 | + paddingLeft: 8, |
| 182 | + paddingRight: 8, |
| 183 | + paddingBottom: 8, |
| 184 | + paddingTop: 8, |
| 185 | + }, |
| 186 | + |
| 187 | + icon: { |
| 188 | + width: 32, |
| 189 | + height: 32, |
44 | 190 | }, |
45 | | - bar: { |
| 191 | + |
| 192 | + leftBorder: { |
| 193 | + position: 'absolute', |
| 194 | + left: 0, |
| 195 | + top: 0, |
| 196 | + bottom: 0, |
| 197 | + width: 4, |
46 | 198 | backgroundColor: Colors.green[100], |
47 | | - width: 6, |
48 | | - height: 40, |
49 | | - alignSelf: 'flex-start', |
| 199 | + borderTopLeftRadius: 12, |
| 200 | + borderBottomLeftRadius: 12, |
| 201 | + zIndex: 1, |
50 | 202 | }, |
51 | | - icon: { |
| 203 | + |
| 204 | + actionRow: { |
| 205 | + flexDirection: 'row', |
| 206 | + alignItems: 'center', |
| 207 | + justifyContent: 'space-between', |
| 208 | + padding: 16, |
| 209 | + }, |
| 210 | + |
| 211 | + leftSection: { |
| 212 | + flexDirection: 'row', |
| 213 | + alignItems: 'center', |
| 214 | + flex: 1, |
| 215 | + }, |
| 216 | + |
| 217 | + iconContainer: { |
52 | 218 | width: 32, |
53 | 219 | height: 32, |
| 220 | + borderRadius: 16, |
| 221 | + backgroundColor: Colors.green[100], |
| 222 | + justifyContent: 'center', |
| 223 | + alignItems: 'center', |
| 224 | + marginRight: 12, |
54 | 225 | }, |
55 | | - name: { |
56 | | - color: Colors.black, |
| 226 | + |
| 227 | + downloadIcon: { |
| 228 | + color: Colors.green[200], |
| 229 | + fontSize: 18, |
| 230 | + fontWeight: 'bold', |
| 231 | + }, |
| 232 | + |
| 233 | + actionInfo: { |
| 234 | + flex: 1, |
| 235 | + }, |
| 236 | + |
| 237 | + titleRow: { |
| 238 | + flexDirection: 'row', |
| 239 | + justifyContent: 'space-between', |
| 240 | + alignItems: 'center', |
| 241 | + width: '100%', |
| 242 | + marginBottom: 4, |
| 243 | + }, |
| 244 | + |
| 245 | + actionName: { |
57 | 246 | fontSize: 16, |
58 | | - lineHeight: 24, |
| 247 | + color: Colors.black, |
59 | 248 | ...InterSemiBold, |
60 | | - width: '100%', |
| 249 | + flex: 1, |
61 | 250 | }, |
62 | | - date: { |
63 | | - color: Colors.gray[100], |
| 251 | + |
| 252 | + actionDate: { |
| 253 | + fontSize: 12, |
| 254 | + color: Colors.gray[500], |
| 255 | + ...InterSmall, |
| 256 | + }, |
| 257 | + |
| 258 | + collectiveName: { |
64 | 259 | fontSize: 14, |
65 | | - lineHeight: 21, |
66 | | - textAlign: 'left', |
67 | | - width: '100%', |
68 | | - ...InterSemiBold, |
| 260 | + paddingBottom: 4, |
| 261 | + color: Colors.gray[200], |
| 262 | + ...InterMedium, |
| 263 | + marginBottom: 2, |
69 | 264 | }, |
70 | | - id: { |
71 | | - fontSize: 10, |
72 | | - lineHeight: 15, |
73 | | - ...InterRegular, |
| 265 | + |
| 266 | + paymentAmount: { |
| 267 | + fontSize: 12, |
| 268 | + color: Colors.blue[200], |
| 269 | + ...InterSmall, |
| 270 | + marginBottom: 4, |
| 271 | + }, |
| 272 | + |
| 273 | + chevronContainer: { |
| 274 | + padding: 0, |
| 275 | + display: 'flex', |
| 276 | + alignItems: 'center', |
| 277 | + justifyContent: 'center', |
| 278 | + marginLeft: 0, |
| 279 | + }, |
| 280 | + |
| 281 | + expandedContent: { |
| 282 | + borderTopWidth: 1, |
| 283 | + borderTopColor: Colors.gray[200], |
| 284 | + paddingVertical: 8, |
| 285 | + }, |
| 286 | + |
| 287 | + linkButton: { |
| 288 | + backgroundColor: 'transparent', |
| 289 | + paddingVertical: 8, |
| 290 | + paddingHorizontal: 16, |
| 291 | + }, |
| 292 | + |
| 293 | + linkText: { |
| 294 | + fontSize: 14, |
| 295 | + color: Colors.blue[200], |
| 296 | + textDecorationLine: 'underline', |
| 297 | + ...InterMedium, |
| 298 | + }, |
| 299 | + |
| 300 | + linkTextDisabled: { |
| 301 | + fontSize: 14, |
| 302 | + color: Colors.gray[400], |
| 303 | + ...InterMedium, |
| 304 | + }, |
| 305 | + |
| 306 | + collapseButton: { |
| 307 | + alignSelf: 'center', |
| 308 | + padding: 8, |
| 309 | + marginTop: 8, |
| 310 | + }, |
| 311 | + |
| 312 | + collapseIcon: { |
| 313 | + fontSize: 16, |
| 314 | + color: Colors.gray[400], |
74 | 315 | }, |
75 | | - logProfileDetails: { flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }, |
76 | | - logDate: { alignItems: 'flex-end' }, |
77 | 316 | }); |
| 317 | + |
78 | 318 | export default ActivityLog; |
0 commit comments