Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a776292
chore(): implemented rerouting and Ui components
Jul 11, 2025
879b624
chore(): implemented the links
Jul 11, 2025
d3c4473
chore(): fixed the toggle feature
Jul 11, 2025
62e72d2
chore(): added the nft id as name since there's no name data returned
Jul 11, 2025
d75c6a2
chore(): edited the actions font colot
Jul 11, 2025
58c1359
chore(): corrected the regex
Jul 11, 2025
4b19498
chore(): cleanup
Jul 14, 2025
2ff01cf
chore(): added logs to debug - would remove later
Jul 15, 2025
766b25a
chore(): fixed the nft details url
Jul 20, 2025
18b2e43
chore(): reverted deploymeny.json and added it to prettier ignore to …
Jul 21, 2025
6c2d612
chore(): implemented the changed requested from the PR review
Jul 23, 2025
b4d53f6
chore(): removed hardcoded data and getting the nft contract address …
Jul 25, 2025
a5d2dfe
chore(): removed incorrect comment
Jul 25, 2025
02841a1
chore(): cleanup on the useActivityLogData hook and moved specified f…
Jul 25, 2025
c4c7a60
Update packages/app/src/components/ActivityLog.tsx
L03TJ3 Jul 25, 2025
1463b67
refactor(ActivityLog): replace date formatting logic with utility fun…
Jul 25, 2025
0824247
Merge branch 'td-221-implement-action-summary' of https://github.com/…
Jul 25, 2025
6c4c731
fix(ActivityLog): update formatNftId to include creation timestamp fo…
Jul 25, 2025
6a1370a
refactor(ActivityLog): simplify user identifier logic by removing ful…
Jul 25, 2025
980e240
refactor(ActivityLog): optimize collective statistics calculation and…
Jul 25, 2025
f257ee0
refactor(ActivityLog): remove formatAmount utility and update payment…
Jul 25, 2025
8599d33
refactor(ActivityLog): replace formatDate utility with formatTime for…
Jul 25, 2025
75edd8c
refactor(ActivityLog): update display name logic, payment amount form…
Jul 25, 2025
2ba4d15
refactor(ActivityLog): enhance payment amount display format for clarity
Jul 25, 2025
c600a55
refactor(ActivityLog): update NFT details link to use nftHash for imp…
Jul 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
293 changes: 248 additions & 45 deletions packages/app/src/components/ActivityLog.tsx
Original file line number Diff line number Diff line change
@@ -1,78 +1,281 @@
import { Image, Text, View, StyleSheet } from 'react-native';
import { InterRegular, InterSemiBold } from '../utils/webFonts';
import { useState } from 'react';
import { StyleSheet, Text, TouchableOpacity, View, Linking } from 'react-native';
import { Colors } from '../utils/colors';

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> `;
import { InterMedium, InterSemiBold, InterSmall } from '../utils/webFonts';
import { ChevronDownIcon } from 'native-base';
Comment thread
EmekaManuel marked this conversation as resolved.

interface ActivityLogProps {
name: string;
id: string;
creationDate: string;
nftId?: string;
transactionHash?: string;
ipfsHash?: string;
collective?: string;
owner?: string;
hash?: string;
}

function ActivityLog({}: ActivityLogProps) {
function ActivityLog({
name,
creationDate,
nftId,
transactionHash,
collective,
ipfsHash,
owner,
hash,
}: ActivityLogProps) {
const [isExpanded, setIsExpanded] = useState(false);

const toggleExpanded = () => {
setIsExpanded(!isExpanded);
};

const openExternalLink = async (url: string) => {
try {
const supported = await Linking.canOpenURL(url);
if (supported) {
await Linking.openURL(url);
} else {
console.warn(`Don't know how to open URL: ${url}`);
}
} catch (error) {
console.error('Error opening URL:', error);
}
};

const handlePaymentTransactionPress = () => {
const txHash = transactionHash || hash;
if (txHash) {
openExternalLink(`https://celoscan.io/tx/${txHash}`);
} else if (owner) {
openExternalLink(`https://celoscan.io/address/${owner}`);
}
};

const handleNftDetailsPress = () => {
if (nftId && collective) {
openExternalLink(`https://celoscan.io/token/${collective}?a=${nftId}`);
} else if (nftId) {
openExternalLink(`https://celoscan.io/search?q=${nftId}`);
}
};

const truncatedName = name.length > 15 ? `${name.substring(0, 5)}...${name.substring(name.length - 5)}` : name;

const handleIpfsPress = async () => {
if (ipfsHash) {
const cleanHash = ipfsHash.replace(/^(ipfs:\/\/|https?:\/\/[^/]+\/ipfs\/)/, '');
const gateways = [
`https://ipfs.io/ipfs/${cleanHash}`,
`https://gateway.pinata.cloud/ipfs/${cleanHash}`,
`https://cloudflare-ipfs.com/ipfs/${cleanHash}`,
];

try {
Comment on lines +67 to +87

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): IPFS gateway fallback logic may not work as intended due to openExternalLink always resolving.

Since openExternalLink only verifies if a URL can be opened, not if the resource exists, the fallback gateways may never be used even if the first is down. Consider checking resource availability with a fetch or HEAD request before attempting to open the link, or document that this is a best-effort fallback.

await openExternalLink(gateways[0]);
} catch (error) {
try {
await openExternalLink(gateways[1]);
} catch (secondError) {
console.warn('IPFS gateways failed');
}
}
}
};

const hasPaymentData = Boolean(transactionHash || hash || owner);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question (bug_risk): hasPaymentData logic may enable Payment Transaction button even if only owner is present.

Currently, the button is enabled when only owner is present, which may not correspond to a transaction. Should the button be disabled unless a transaction hash exists?


return (
<View style={styles.container}>
<View style={styles.row}>
<View style={styles.bar} />
<Image style={styles.icon} source={{ uri: ReceiveIconUri }} />
<Text style={styles.logProfileDetails}>
<Text style={styles.name}>Silvi Tree Claim {'\n'}</Text>
<Text style={styles.id}>0x723a86c93838c1facse.....</Text>
</Text>
<View style={styles.logDate}>
<Text style={styles.date}>July 3, 2023</Text>
</View>
<View style={styles.leftBorder} />

<View style={styles.contentContainer}>
<TouchableOpacity onPress={toggleExpanded} style={styles.actionRow}>
<View style={styles.leftSection}>
<View style={styles.iconContainer}>
<Text style={styles.downloadIcon}>↓</Text>
</View>

<View style={styles.actionInfo}>
<View style={styles.titleRow}>
<Text style={styles.actionName}>NFT ID: {truncatedName}</Text>

<Text style={styles.actionDate}>{creationDate}</Text>
</View>
<View style={styles.chevronContainer}>
<ChevronDownIcon
size={4}
color={Colors.gray[200]}
style={{
transform: [{ rotate: isExpanded ? '180deg' : '0deg' }],
}}
/>
</View>
</View>
</View>
</TouchableOpacity>

{isExpanded && (
<View style={styles.expandedContent}>
<TouchableOpacity
style={styles.linkButton}
onPress={handlePaymentTransactionPress}
disabled={!hasPaymentData}>
<Text style={hasPaymentData ? styles.linkText : styles.linkTextDisabled}>Payment Transaction</Text>
</TouchableOpacity>

<TouchableOpacity style={styles.linkButton} onPress={handleNftDetailsPress} disabled={!nftId}>
<Text style={nftId ? styles.linkText : styles.linkTextDisabled}>NFT Details</Text>
</TouchableOpacity>

<TouchableOpacity style={styles.linkButton} onPress={handleIpfsPress} disabled={!ipfsHash}>
<Text style={ipfsHash ? styles.linkText : styles.linkTextDisabled}>
IPFS Claim and Proof of Verification
</Text>
</TouchableOpacity>

<TouchableOpacity onPress={toggleExpanded} style={styles.collapseButton}>
<Text style={styles.collapseIcon}>⌃</Text>
</TouchableOpacity>
</View>
)}
</View>
</View>
);
}

const styles = StyleSheet.create({
container: {
width: '100%',
backgroundColor: Colors.white,
paddingLeft: 16,
paddingRight: 16,
borderRadius: 12,
marginBottom: 12,
shadowColor: Colors.black,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
overflow: 'hidden',
},
row: {
width: '100%',
flex: 1,
flexDirection: 'row',
gap: 8,
backgroundColor: Colors.white,

contentContainer: {
paddingLeft: 8,
paddingRight: 8,
paddingBottom: 8,
paddingTop: 8,
},
bar: {

leftBorder: {
position: 'absolute',
left: 0,
top: 0,
bottom: 0,
width: 4,
backgroundColor: Colors.green[100],
width: 6,
height: 40,
alignSelf: 'flex-start',
borderTopLeftRadius: 12,
borderBottomLeftRadius: 12,
zIndex: 1,
},

actionRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 16,
},

leftSection: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
},
icon: {

iconContainer: {
width: 32,
height: 32,
borderRadius: 16,
backgroundColor: Colors.green[100],
justifyContent: 'center',
alignItems: 'center',
marginRight: 12,
},
name: {
color: Colors.black,

downloadIcon: {
color: Colors.green[200],
fontSize: 18,
fontWeight: 'bold',
},

actionInfo: {
flex: 1,
},

titleRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
width: '100%',
},

actionName: {
fontSize: 16,
lineHeight: 24,
color: Colors.black,
...InterSemiBold,
width: '100%',
},
date: {
color: Colors.gray[100],

actionDate: {
fontSize: 12,
color: Colors.gray[500],
...InterSmall,
},

chevronContainer: {
padding: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginLeft: 0,
},

expandedContent: {
borderTopWidth: 1,
borderTopColor: Colors.gray[200],
paddingVertical: 8,
},

linkButton: {
backgroundColor: 'transparent',
paddingVertical: 8,
paddingHorizontal: 16,
},

linkText: {
fontSize: 14,
lineHeight: 21,
textAlign: 'left',
width: '100%',
...InterSemiBold,
color: Colors.blue[200],
textDecorationLine: 'underline',
...InterMedium,
},
id: {
fontSize: 10,
lineHeight: 15,
...InterRegular,

linkTextDisabled: {
fontSize: 14,
color: Colors.gray[400],
...InterMedium,
},

collapseButton: {
alignSelf: 'center',
padding: 8,
marginTop: 8,
},

collapseIcon: {
fontSize: 16,
color: Colors.gray[400],
},
logProfileDetails: { flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
logDate: { alignItems: 'flex-end' },
});

export default ActivityLog;
34 changes: 25 additions & 9 deletions packages/app/src/components/WalletCards/StewardCollectiveCard.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Text, View, Image } from 'react-native';
import RoundedButton from '../RoundedButton';
import useCrossNavigate from '../../routes/useCrossNavigate';
import { IpfsCollective, StewardCollective } from '../../models/models';
import { styles } from './styles';
import { Image, Text, TouchableOpacity, View } from 'react-native';
import { InfoIcon, StewardOrange } from '../../assets';
import { calculateGoodDollarAmounts } from '../../lib/calculateGoodDollarAmounts';
import { defaultInfoLabel } from '../../models/constants';
import { IpfsCollective, StewardCollective } from '../../models/models';
import useCrossNavigate from '../../routes/useCrossNavigate';
import { GoodDollarAmount } from '../GoodDollarAmount';
import RoundedButton from '../RoundedButton';
import { styles } from './styles';

interface StewardCollectiveCardProps {
collective: StewardCollective;
Expand All @@ -15,9 +15,16 @@ interface StewardCollectiveCardProps {
tokenPrice?: number;
containerStyle?: Record<string, any>;
isDesktopResolution: boolean;
stewardAddress?: string;
}

function StewardCollectiveCard({ ipfsCollective, collective, ensName, tokenPrice }: StewardCollectiveCardProps) {
function StewardCollectiveCard({
ipfsCollective,
collective,
ensName,
tokenPrice,
stewardAddress,
}: StewardCollectiveCardProps) {
const { navigate } = useCrossNavigate();
const userName = ensName ?? 'This wallet';

Expand All @@ -29,6 +36,12 @@ function StewardCollectiveCard({ ipfsCollective, collective, ensName, tokenPrice

const infoLabel = ipfsCollective.rewardDescription ?? defaultInfoLabel;

const handleActionsClick = () => {
if (stewardAddress) {
navigate(`/profile/${stewardAddress}/activity`);
}
};

return (
<View style={[styles.cardContainer, styles.elevation]}>
<View style={styles.cardContentContainer}>
Expand All @@ -46,9 +59,12 @@ function StewardCollectiveCard({ ipfsCollective, collective, ensName, tokenPrice
{userName} has {ipfsCollective.pooltype === 'UBI' ? 'claimed' : 'performed'}
</Text>
<View style={styles.row}>
<Text style={styles.orangeBoldUnderline}>
{collective.actions} {ipfsCollective.pooltype === 'UBI' ? 'times' : 'actions'}
</Text>
{/* Make the actions count clickable */}
<TouchableOpacity onPress={handleActionsClick}>
<Text style={[styles.orangeBoldUnderline, { textDecorationLine: 'underline' }]}>
{collective.actions} {ipfsCollective.pooltype === 'UBI' ? 'times' : 'actions'}
</Text>
</TouchableOpacity>
</View>
</View>

Expand Down
Loading
Loading