Feature: Enable Social Sharing of Earned ROXN Rewards#66
Feature: Enable Social Sharing of Earned ROXN Rewards#66Brijeshthummar02 wants to merge 2 commits into
Conversation
Signed-off-by: Brijeshthummar02 <brijeshthummar02@gmail.com>
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds a ShareAchievementModal component and useSocialShare hook, integrates a per-activity Share flow into the dashboard, and wires share actions (Twitter/X, LinkedIn, clipboard, explorer) through the new hook and modal. Changes
Sequence DiagramsequenceDiagram
actor User
participant Dashboard as Dashboard UI
participant Activity as ActivityItem
participant Modal as ShareAchievementModal
participant Hook as useSocialShare
participant Platform as Social Platform / Clipboard / Explorer
participant Toast as Toasts
User->>Activity: Clicks "Share" on activity
Activity->>Dashboard: invoke onShare()
Dashboard->>Modal: open(with achievement data)
Modal->>User: display achievement card
alt Twitter/X
User->>Modal: Click Twitter button
Modal->>Hook: shareOnTwitter(options)
Hook->>Hook: generateShareMessage('twitter')
Hook->>Platform: open Twitter Web Intent
Hook->>Toast: show success toast
else LinkedIn
User->>Modal: Click LinkedIn button
Modal->>Hook: shareOnLinkedIn(options)
Hook->>Hook: generateShareMessage('linkedin')
Hook->>Platform: open LinkedIn share dialog
Hook->>Toast: show success toast
else Copy
User->>Modal: Click Copy button
Modal->>Hook: copyToClipboard(options)
Hook->>Hook: generateShareMessage('generic')
Hook->>Platform: write to clipboard
Hook->>Toast: show success/failure toast
Modal->>Modal: update copied feedback state
else View on Explorer
User->>Modal: Click "View on Explorer"
Modal->>Hook: viewOnExplorer(txHash)
Hook->>Platform: open explorer URL
end
User->>Modal: Close modal
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
|||||||||||||||||||||||||||||||
PR Code Suggestions ✨Explore these optional code suggestions:
|
|||||||||||||||
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
client/src/pages/dashboard-page.tsx (2)
115-137: Share button visibility needs keyboard-accessible focus styling.
Right now the button is effectively “hidden until hover” (opacity-0 group-hover:opacity-100), which makes keyboard navigation awkward.- className="h-6 px-2 text-xs gap-1 opacity-0 group-hover:opacity-100 transition-opacity text-violet-500 hover:text-violet-400 hover:bg-violet-500/10" + className="h-6 px-2 text-xs gap-1 opacity-0 group-hover:opacity-100 focus:opacity-100 focus-visible:opacity-100 transition-opacity text-violet-500 hover:text-violet-400 hover:bg-violet-500/10"Also applies to: 176-196
315-336: NormalizetransactionHashbefore passing to the modal (xdc→0x), to match the explorer/link behavior.
ActivityItem.getLink()already doestxHash.replace(/^xdc/, "0x"), buthandleShareActivity()passes the rawactivity.txHash, so “View on Explorer” can diverge/break depending on the incoming format.const handleShareActivity = (activity: any) => { + const normalizedTxHash = + typeof activity.txHash === 'string' ? activity.txHash.replace(/^xdc/i, '0x') : undefined; + openShareModal({ amount: activity.amount || '0', currency: (activity.currency || 'ROXN') as 'XDC' | 'ROXN' | 'USDC', projectName: activity.repoName, issueNumber: activity.issueId, - transactionHash: activity.txHash, + transactionHash: normalizedTxHash, }); };
🧹 Nitpick comments (4)
client/src/pages/dashboard-page.tsx (1)
460-466: Consider stabilizingonShareto avoid re-creating closures per row (optional).
Not a blocker, but if this list grows, a memoized handler (or passingactivityid into a stable callback) can reduce churn.client/src/components/share-achievement-modal.tsx (3)
21-38: ExportShareAchievementModalProps(or keep it private intentionally) but avoid “phantom API”.
Since this is a new component module, making the props typeexportis helpful if other call sites will type-check against it.
53-59: Clear the “copied” timeout on unmount to avoid stray state updates.
Minor, but easy to harden (especially if the modal is closed quickly).
219-242: MoveuseShareAchievementintoclient/src/hooksto match project guidelines.
This is a hook and will likely be reused; placing it under/src/hooksfits the repo rule. Based on learnings, prefer hooks from/src/hooksforclient/src/**/*.{ts,tsx}.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
client/src/components/share-achievement-modal.tsx(1 hunks)client/src/hooks/use-social-share.ts(1 hunks)client/src/pages/dashboard-page.tsx(8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
client/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use React hooks from
/src/hooksand UI components from shadcn/ui for frontend development
Files:
client/src/components/share-achievement-modal.tsxclient/src/hooks/use-social-share.tsclient/src/pages/dashboard-page.tsx
🧠 Learnings (1)
📚 Learning: 2025-12-11T16:19:54.207Z
Learnt from: CR
Repo: Roxonn-FutureTech/Roxonn-Platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-11T16:19:54.207Z
Learning: Applies to client/src/**/*.{ts,tsx} : Use React hooks from `/src/hooks` and UI components from shadcn/ui for frontend development
Applied to files:
client/src/components/share-achievement-modal.tsxclient/src/hooks/use-social-share.ts
🧬 Code graph analysis (2)
client/src/components/share-achievement-modal.tsx (1)
client/src/hooks/use-social-share.ts (1)
useSocialShare(17-142)
client/src/pages/dashboard-page.tsx (2)
client/src/components/share-achievement-modal.tsx (2)
useShareAchievement(220-242)ShareAchievementModal(34-217)server/services/activityService.ts (1)
ActivityItem(14-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Agent
🔇 Additional comments (3)
client/src/pages/dashboard-page.tsx (2)
11-12: Keep the share integration import tidy (modal + icon) and ensure tree-shaking stays clean.
No functional concerns here; just noting the new dependency surface.Also applies to: 28-29
339-346: Modal mount + controlled state wiring looks good.
Placing the modal once at the page root and driving it through a hook is a clean pattern.client/src/hooks/use-social-share.ts (1)
84-101: Clipboard path is nicely handled (success/failure feedback).
Good user feedback and error fallback.
| return ( | ||
| <Dialog open={isOpen} onOpenChange={onClose}> | ||
| <DialogContent className="sm:max-w-md"> |
There was a problem hiding this comment.
Dialog controlled close handler should respect the open boolean.
Passing onClose directly to onOpenChange will call onClose() even when the dialog is opening (depending on the shadcn Dialog behavior). Safer to close only when open === false.
- <Dialog open={isOpen} onOpenChange={onClose}>
+ <Dialog
+ open={isOpen}
+ onOpenChange={(open) => {
+ if (!open) onClose();
+ }}
+ >🤖 Prompt for AI Agents
In client/src/components/share-achievement-modal.tsx around lines 95 to 97, the
Dialog's onOpenChange is wired directly to onClose which can run when the dialog
opens; update the handler to accept the open boolean and only call onClose when
the boolean is false (e.g., onOpenChange={(open) => !open && onClose()}),
ensuring you preserve types and any existing props.
| const shareOnTwitter = (options: ShareOptions) => { | ||
| const text = encodeURIComponent(generateShareMessage(options, 'twitter')); | ||
| window.open( | ||
| `https://twitter.com/intent/tweet?text=${text}`, | ||
| '_blank', | ||
| 'width=550,height=420' | ||
| ); | ||
| toast({ | ||
| title: 'Opening Twitter', | ||
| description: 'Share your achievement with the world!', | ||
| }); | ||
| }; |
There was a problem hiding this comment.
Harden window.open against reverse-tabnabbing (missing noopener).
window.open(..., '_blank', ...) can allow the new page to access window.opener unless you explicitly disable it.
const shareOnTwitter = (options: ShareOptions) => {
const text = encodeURIComponent(generateShareMessage(options, 'twitter'));
- window.open(
+ const w = window.open(
`https://twitter.com/intent/tweet?text=${text}`,
'_blank',
- 'width=550,height=420'
+ 'noopener,noreferrer,width=550,height=420'
);
+ if (w) w.opener = null;
toast({
title: 'Opening Twitter',
description: 'Share your achievement with the world!',
});
};
const shareOnLinkedIn = (options: ShareOptions) => {
const url = encodeURIComponent('https://app.roxonn.com');
const summary = encodeURIComponent(generateShareMessage(options, 'linkedin'));
- window.open(
+ const w = window.open(
`https://www.linkedin.com/sharing/share-offsite/?url=${url}&summary=${summary}`,
'_blank',
- 'width=550,height=520'
+ 'noopener,noreferrer,width=550,height=520'
);
+ if (w) w.opener = null;
toast({
title: 'Opening LinkedIn',
description: 'Share your professional achievement!',
});
};Also applies to: 67-79
🤖 Prompt for AI Agents
In client/src/hooks/use-social-share.ts around lines 51-62 (and also apply the
same change to 67-79), window.open is currently called with '_blank' which
allows the opened page to access window.opener; update the call to include
'noopener,noreferrer' in the feature string and after opening set the returned
window's opener to null when non-null. Ensure both the Twitter and the other
social share calls use the same pattern (feature string 'noopener,noreferrer'
and a check to set newWindow.opener = null) to prevent reverse-tabnabbing and
suppress referrer leakage.
| const viewOnExplorer = (transactionHash?: string) => { | ||
| if (transactionHash) { | ||
| const explorerUrl = `https://xdcscan.com/tx/${transactionHash}`; | ||
| window.open(explorerUrl, '_blank'); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Explorer URL host/format is inconsistent with the dashboard.
Dashboard links use xdcscan.io/tx/... and normalize xdc→0x, while this uses xdcscan.com/tx/... without normalization. Recommend centralizing this (single helper) and using it everywhere.
🤖 Prompt for AI Agents
In client/src/hooks/use-social-share.ts around lines 106 to 111, the explorer
link uses the wrong host and format (https://xdcscan.com/tx/...) and does not
normalize XDC addresses to 0x, causing inconsistency with the dashboard; replace
this logic by calling a centralized helper that builds explorer transaction URLs
(ensure it returns https://xdcscan.io/tx/{txHash}) and make that helper perform
address normalization from xdc... to 0x... (or accept already-normalized
addresses), then update this function to use that helper and replace any other
ad-hoc explorer URL construction sites to use the same helper so all links are
consistent.
There was a problem hiding this comment.
Pull request overview
This pull request implements social sharing functionality for earned ROXN rewards, enabling users to share their achievements on Twitter/X and LinkedIn, or copy share text to clipboard. The feature adds a share button to reward activities in the dashboard that opens a modal with a visually appealing achievement card and multiple sharing options.
Key Changes:
- Added a share button to reward-type activities in the dashboard with hover-reveal animation
- Created a reusable social sharing hook with support for Twitter, LinkedIn, clipboard, and native share API
- Implemented an achievement modal component with currency-specific theming and blockchain verification display
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 12 comments.
| File | Description |
|---|---|
| client/src/pages/dashboard-page.tsx | Integrated share functionality into ActivityItem component and added modal for sharing rewards |
| client/src/hooks/use-social-share.ts | Created custom hook providing social media sharing functions and message generation |
| client/src/components/share-achievement-modal.tsx | Implemented modal component with achievement card preview and social sharing buttons |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const handleShareActivity = (activity: any) => { | ||
| openShareModal({ | ||
| amount: activity.amount || '0', | ||
| currency: (activity.currency || 'ROXN') as 'XDC' | 'ROXN' | 'USDC', | ||
| projectName: activity.repoName, | ||
| issueNumber: activity.issueId, | ||
| transactionHash: activity.txHash, | ||
| }); | ||
| }; |
There was a problem hiding this comment.
The parameter type 'any' lacks type safety. Consider defining a proper type for the activity parameter to ensure type checking and better IDE support. You could create an interface or type alias that matches the structure of the activity items being passed.
| const hashtags = '#Roxonn #XDC #OpenSource #Web3 #Bounty'; | ||
| const link = 'https://app.roxonn.com'; | ||
|
|
||
| if (platform === 'twitter') { | ||
| return `${baseMessage}\n\n${hashtags}\n\n🚀 Start earning with @RoxonnPlatform:\n${link}`; | ||
| } else if (platform === 'linkedin') { | ||
| return `${baseMessage}\n\nRoxonn is revolutionizing open-source contributions with blockchain-powered rewards. Join the future of collaborative development!\n\n${link}\n\n${hashtags}`; | ||
| } | ||
| return `${baseMessage}\n\n${hashtags}\n\n${link}`; |
There was a problem hiding this comment.
The hashtags, link, and platform-specific text are hardcoded as magic strings within the function. Consider extracting these as constants at the module level or in a configuration object to improve maintainability and make them easier to update in the future.
| <Button | ||
| onClick={handleShareTwitter} | ||
| variant="outline" | ||
| className="gap-2 hover:bg-[#1DA1F2]/10 hover:border-[#1DA1F2]/50 hover:text-[#1DA1F2]" | ||
| > | ||
| <Twitter className="h-4 w-4" /> | ||
| Share on X | ||
| </Button> | ||
| <Button | ||
| onClick={handleShareLinkedIn} | ||
| variant="outline" | ||
| className="gap-2 hover:bg-[#0077B5]/10 hover:border-[#0077B5]/50 hover:text-[#0077B5]" | ||
| > | ||
| <Linkedin className="h-4 w-4" /> | ||
| </Button> |
There was a problem hiding this comment.
The share buttons lack accessible labels that explain what will happen when clicked. Consider adding aria-label attributes that clarify these buttons will open new windows to share on Twitter/X and LinkedIn respectively, which would help users with screen readers understand the action.
| const getCurrencyStyle = () => { | ||
| switch (currency) { | ||
| case 'ROXN': | ||
| return { | ||
| gradient: 'from-violet-500 via-purple-500 to-fuchsia-500', | ||
| bgGradient: 'from-violet-500/20 via-purple-500/10 to-fuchsia-500/20', | ||
| text: 'text-violet-400', | ||
| border: 'border-violet-500/30' | ||
| }; | ||
| case 'USDC': | ||
| return { | ||
| gradient: 'from-blue-500 via-cyan-500 to-teal-500', | ||
| bgGradient: 'from-blue-500/20 via-cyan-500/10 to-teal-500/20', | ||
| text: 'text-cyan-400', | ||
| border: 'border-cyan-500/30' | ||
| }; | ||
| case 'XDC': | ||
| default: | ||
| return { | ||
| gradient: 'from-cyan-500 via-emerald-500 to-green-500', | ||
| bgGradient: 'from-cyan-500/20 via-emerald-500/10 to-green-500/20', | ||
| text: 'text-emerald-400', | ||
| border: 'border-emerald-500/30' | ||
| }; | ||
| } | ||
| }; |
There was a problem hiding this comment.
The function name 'getCurrencyStyle' could be more descriptive. Consider renaming it to 'getCurrencyStyleConfig' or 'getCurrencyTheme' to better convey that it returns a configuration object with multiple style properties rather than a single style value.
| const handleShareActivity = (activity: any) => { | ||
| openShareModal({ | ||
| amount: activity.amount || '0', | ||
| currency: (activity.currency || 'ROXN') as 'XDC' | 'ROXN' | 'USDC', | ||
| projectName: activity.repoName, | ||
| issueNumber: activity.issueId, | ||
| transactionHash: activity.txHash, | ||
| }); | ||
| }; |
There was a problem hiding this comment.
The function doesn't handle the case where required fields are missing. For example, if activity.amount is undefined or null, it defaults to '0' which may not be the intended behavior. Consider adding validation or early returns for cases where essential data is missing to prevent misleading share messages.
| } catch (error) { | ||
| toast({ | ||
| title: 'Failed to copy', | ||
| description: 'Could not copy to clipboard.', |
There was a problem hiding this comment.
The error message 'Could not copy to clipboard' is generic and doesn't help users understand why the operation failed. Consider providing more context or guidance, such as checking browser permissions or suggesting alternative methods to share the content.
| description: 'Could not copy to clipboard.', | |
| description: 'Could not copy to clipboard. This may be due to browser permissions or unsupported features. Please check your browser settings or try copying the text manually.', |
| const viewOnExplorer = (transactionHash?: string) => { | ||
| if (transactionHash) { | ||
| const explorerUrl = `https://xdcscan.com/tx/${transactionHash}`; | ||
| window.open(explorerUrl, '_blank'); |
There was a problem hiding this comment.
The function silently does nothing when transactionHash is not provided. Consider returning a boolean to indicate success/failure, or showing a user-facing message to inform them when the explorer link is unavailable, consistent with other methods in this hook.
| const viewOnExplorer = (transactionHash?: string) => { | |
| if (transactionHash) { | |
| const explorerUrl = `https://xdcscan.com/tx/${transactionHash}`; | |
| window.open(explorerUrl, '_blank'); | |
| const viewOnExplorer = (transactionHash?: string): boolean => { | |
| if (transactionHash) { | |
| const explorerUrl = `https://xdcscan.com/tx/${transactionHash}`; | |
| window.open(explorerUrl, '_blank'); | |
| return true; | |
| } else { | |
| toast({ | |
| title: 'Explorer link unavailable', | |
| description: 'No transaction hash provided.', | |
| variant: 'destructive', | |
| }); | |
| return false; |
| {time} | ||
| </p> | ||
| {/* Share button for reward type activities */} | ||
| {type === "reward" && amount && onShare && ( |
There was a problem hiding this comment.
The condition checks for multiple props (type === "reward" && amount && onShare) but doesn't validate if the amount is a valid non-zero value. Consider adding a check to ensure the amount is meaningful before showing the share button, as sharing "0 ROXN" may not be useful.
| {type === "reward" && amount && onShare && ( | |
| {type === "reward" && Number(amount) > 0 && onShare && ( |
| import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from './ui/dialog'; | ||
| import { Button } from './ui/button'; | ||
| import { Badge } from './ui/badge'; | ||
| import { Card } from './ui/card'; |
There was a problem hiding this comment.
Unused import Card.
| import { Card } from './ui/card'; |
| import { | ||
| Twitter, | ||
| Linkedin, | ||
| Copy, | ||
| Download, | ||
| Share2, | ||
| Coins, | ||
| Award, | ||
| ExternalLink, | ||
| Check, | ||
| Sparkles | ||
| } from 'lucide-react'; |
There was a problem hiding this comment.
Unused imports Download, Share2.
|
@dineshroxonn good to go? |
dineshroxonn
left a comment
There was a problem hiding this comment.
PR Review: Social Sharing Feature (#66 → Fixes #8)
Issue #8 Acceptance Criteria Validation
| Criteria | Status | Notes |
|---|---|---|
| "Share" option after bounty claim | ✅ Met | Shows on hover in activity feed |
| Pre-filled Twitter intent | ✅ Met | Uses Twitter Web Intent API |
| Roxonn mention, project, hashtags | ✅ Met | Includes @RoxonnPlatform, project name, #Roxonn #XDC etc. |
The MVP acceptance criteria are satisfied. Great work on the UI/UX with currency-specific styling!
Critical Issues Requiring Changes
1. 🔴 Explorer URL - Wrong Domain (use-social-share.ts:102)
// Current (BROKEN)
const explorerUrl = `https://xdcscan.com/tx/${transactionHash}`;
// Fix
const normalizedHash = transactionHash.startsWith('xdc')
? '0x' + transactionHash.slice(3)
: transactionHash;
const explorerUrl = `https://xdcscan.io/tx/${normalizedHash}`;Issue: xdcscan.com is not the correct domain. XDC explorer is at xdcscan.io. Also needs xdc→0x hash prefix normalization.
2. 🔴 Security: Missing noopener,noreferrer (use-social-share.ts:57-60, 72-75, 102-103)
// Current (VULNERABLE to tabnabbing)
window.open(`https://twitter.com/intent/tweet?text=${text}`, '_blank', 'width=550,height=420');
// Fix
window.open(`https://twitter.com/intent/tweet?text=${text}`, '_blank', 'noopener,noreferrer,width=550,height=420');Issue: All window.open calls need noopener,noreferrer to prevent reverse tabnabbing attacks.
3. 🔴 LinkedIn Share Broken (use-social-share.ts:71-75)
// Current (BROKEN - summary param not supported)
window.open(
`https://www.linkedin.com/sharing/share-offsite/?url=${url}&summary=${summary}`,
...
);
// Fix - LinkedIn only accepts url parameter
window.open(
`https://www.linkedin.com/sharing/share-offsite/?url=${url}`,
'_blank',
'noopener,noreferrer,width=550,height=520'
);Issue: LinkedIn's share-offsite API only accepts the url parameter. The summary parameter is ignored. Consider using a landing page URL with Open Graph meta tags instead.
Medium Priority Issues
4. 🟠 Silent Failure in nativeShare (use-social-share.ts:118-130)
// Current - silently swallows errors
} catch (error) {
// User cancelled or share failed
return false;
}
// Suggested - at least log for debugging
} catch (error) {
console.debug('Native share failed:', error);
return false;
}5. 🟠 Type Safety - any type usage (dashboard-page.tsx:333)
// Current
const handleShareActivity = (activity: any) => { ... }
// Suggested - define proper type
interface ActivityData {
amount?: string;
currency?: 'XDC' | 'ROXN' | 'USDC';
repoName?: string;
issueId?: number;
txHash?: string;
}
const handleShareActivity = (activity: ActivityData) => { ... }6. 🟠 Unused Imports (share-achievement-modal.tsx)
Remove unused imports: Card, Download, Share2
Summary
The feature implementation is solid and meets all Issue #8 acceptance criteria. The UI is well-designed with currency-specific theming. However, 3 critical issues must be fixed before merge:
- Explorer URL - Wrong domain breaks "View on Explorer" functionality
- Security - Missing
noopener,noreferreris a security vulnerability - LinkedIn - Broken due to unsupported API parameter
Please address the critical issues and this will be ready to merge. 🚀
| onClose: () => void; | ||
| achievementData: { | ||
| amount: string; | ||
| currency: 'XDC' | 'ROXN' | 'USDC'; |
There was a problem hiding this comment.
we can write this hardcoded strings values as enum so they can be extensible at one place and while comparing no need to match exact string by writting hardcode instead of that we can use enum . this will reduce the typo error and there will change at one place rule .
enum currencyEnum { XDC = "XDC" , ROXN = "ROXN" ,USDC = "USDC" }
interface ShareAchievementModalProps {
achievementData : {
currency : currencyEnum
}
| onClose, | ||
| achievementData | ||
| }: ShareAchievementModalProps) { | ||
| const [copied, setCopied] = useState(false); |
There was a problem hiding this comment.
[S] we should use all the capabilities of typescript
const [copied, setCopied] = useState<boolean>(false);
| // Currency-specific styling | ||
| const getCurrencyStyle = () => { | ||
| switch (currency) { | ||
| case 'ROXN': |
There was a problem hiding this comment.
Can use above mentioned Enum value as case compare and its better if we pass currency as parameter , it will be more descriptive as unit ( as , i don't have to know check from where it is coming , once i see function , will know currency is parameter , probably , it is passing from where it called )
Signed-off-by: Brijeshthummar02 <brijeshthummar02@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
client/src/pages/dashboard-page.tsx (1)
315-315: Add type annotation for activity items in the map callback.Using
anytype bypasses TypeScript's safety. The server defines anActivityIteminterface inserver/services/activityService.tsthat could be shared or mirrored client-side.🔎 Suggested approach
Create a shared type or import from a common types module:
interface ActivityItem { id: string; type: 'reward' | 'contribution' | 'subscription' | 'referral'; title: string; description: string; timestamp: string; metadata?: { amount?: string; currency?: 'XDC' | 'ROXN' | 'USDC'; repoName?: string; txHash?: string; issueId?: number; }; }Then use it in the map:
- const recentActivity = activityData?.activities?.map((activity: any) => ({ + const recentActivity = activityData?.activities?.map((activity: ActivityItem) => ({client/src/components/share-achievement-modal.tsx (2)
58-60: Explorer URL handling is inconsistent with the rest of the codebase.The
viewOnExplorercall usesxdcscan.comwithout normalizing thexdcprefix to0x, while the canonical pattern throughout the app ishttps://xdcscan.io/tx/${txHash.replace(/^xdc/, "0x")}. This issue exists in theuseSocialSharehook.
92-93:onOpenChangeshould only triggeronClosewhen the dialog is closing.Passing
onClosedirectly toonOpenChangemeansonClose()may be invoked even when the dialog is opening (receivingtrue). The handler should guard against this.🔎 Suggested fix
- <Dialog open={isOpen} onOpenChange={onClose}> + <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
🧹 Nitpick comments (4)
client/src/pages/dashboard-page.tsx (2)
182-195: Consider validating for a meaningful amount before showing the share button.The current condition
amount && onShareallows sharing whenamountis"0". Sharing "0 ROXN" may not be useful. A stricter check would improve UX.🔎 Suggested fix
- {type === "reward" && amount && onShare && ( + {type === "reward" && amount && Number(amount) > 0 && onShare && (
328-344: Move interface to module level and consider validation for missing data.The
ActivityDatainterface is defined inside the component body; move it to the top of the file for clarity. Also, defaulting to'0'for missing amounts could produce misleading share messages.🔎 Suggested refactor
+// Move to top of file, after imports +interface ActivityData { + amount?: string; + currency?: 'XDC' | 'ROXN' | 'USDC'; + repoName?: string; + issueId?: number; + txHash?: string; +} + export default function DashboardPage() { // ... existing code ... - interface ActivityData { - amount?: string; - currency?: 'XDC' | 'ROXN' | 'USDC'; - repoName?: string; - issueId?: number; - txHash?: string; - }client/src/components/share-achievement-modal.tsx (2)
37-37: Unused ref:cardRefis assigned but not used functionally.The
cardRefis attached to the card div but isn't used for any operations (e.g., screenshot export). If planned for future use, consider adding a TODO comment; otherwise, remove it.
170-186: Consider addingaria-labelattributes for improved accessibility.Screen reader users would benefit from knowing these buttons open external windows. Adding descriptive labels improves accessibility.
🔎 Suggested improvement
<Button onClick={handleShareTwitter} variant="outline" + aria-label="Share on X (opens in new window)" className="gap-2 hover:bg-[#1DA1F2]/10 hover:border-[#1DA1F2]/50 hover:text-[#1DA1F2]" > <Twitter className="h-4 w-4" /> Share on X </Button> <Button onClick={handleShareLinkedIn} variant="outline" + aria-label="Share on LinkedIn (opens in new window)" className="gap-2 hover:bg-[#0077B5]/10 hover:border-[#0077B5]/50 hover:text-[#0077B5]" >
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
client/src/components/share-achievement-modal.tsxclient/src/hooks/use-social-share.tsclient/src/pages/dashboard-page.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- client/src/hooks/use-social-share.ts
🧰 Additional context used
📓 Path-based instructions (1)
client/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use React hooks from
/src/hooksand UI components from shadcn/ui for frontend development
Files:
client/src/components/share-achievement-modal.tsxclient/src/pages/dashboard-page.tsx
🧠 Learnings (1)
📚 Learning: 2025-12-11T16:19:54.216Z
Learnt from: CR
Repo: Roxonn-FutureTech/Roxonn-Platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-11T16:19:54.216Z
Learning: Applies to client/src/**/*.{ts,tsx} : Use React hooks from `/src/hooks` and UI components from shadcn/ui for frontend development
Applied to files:
client/src/components/share-achievement-modal.tsx
🧬 Code graph analysis (1)
client/src/pages/dashboard-page.tsx (2)
client/src/components/share-achievement-modal.tsx (2)
useShareAchievement(217-239)ShareAchievementModal(31-214)server/services/activityService.ts (1)
ActivityItem(14-27)
🔇 Additional comments (10)
client/src/pages/dashboard-page.tsx (5)
11-11: LGTM!The import correctly pulls in both the modal component and its companion hook from the same module, following the colocation pattern.
124-136: LGTM!The new optional props (
issueIdandonShare) are well-typed and extend the component interface cleanly without breaking existing usages.
246-247: LGTM!Clean destructuring and proper use of the
useShareAchievementhook for modal state management.
348-353: LGTM!The modal is correctly positioned at the top level and properly wired to the hook's state management.
468-473: LGTM on the share callback wiring.The
onSharecallback correctly invokeshandleShareActivitywith the activity data. Theanytype issue is already noted above.client/src/components/share-achievement-modal.tsx (5)
1-16: LGTM!Imports are clean and well-organized. Correctly uses
useSocialSharefrom/src/hooksand shadcn/ui components as per coding guidelines.
18-29: LGTM!The interface is well-structured. Union types for
currencyare idiomatic in TypeScript. If the currency list grows significantly, consider extracting to a shared type definition.
62-88: LGTM!The
getCurrencyStylefunction cleanly maps currencies to their visual themes. The switch-case structure with a default is appropriate.
105-167: LGTM!The achievement card preview is well-structured with appropriate conditional rendering and smooth animations via Framer Motion.
216-239: LGTM!The
useShareAchievementhook provides clean modal state management with sensible defaults. Collocating it with the modal component is a pragmatic choice for tightly coupled functionality.
|
@dineshroxonn good to go? |
User description
Fixes #8
PR Type
Enhancement
Description
Add social sharing functionality for earned ROXN rewards
Create reusable hook for Twitter, LinkedIn, clipboard sharing
Build achievement modal component with currency-specific styling
Integrate share button into dashboard activity feed
Diagram Walkthrough
File Walkthrough
use-social-share.ts
Social sharing hook with multi-platform supportclient/src/hooks/use-social-share.ts
platforms
clipboard
share-achievement-modal.tsx
Achievement sharing modal with styled previewclient/src/components/share-achievement-modal.tsx
useShareAchievementhook for modal state managementdashboard-page.tsx
Integrate social sharing into dashboard activityclient/src/pages/dashboard-page.tsx
type
data
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.