Skip to content

Feature: Enable Social Sharing of Earned ROXN Rewards#66

Open
Brijeshthummar02 wants to merge 2 commits into
Roxonn-FutureTech:mainfrom
Brijeshthummar02:social_share
Open

Feature: Enable Social Sharing of Earned ROXN Rewards#66
Brijeshthummar02 wants to merge 2 commits into
Roxonn-FutureTech:mainfrom
Brijeshthummar02:social_share

Conversation

@Brijeshthummar02

@Brijeshthummar02 Brijeshthummar02 commented Dec 12, 2025

Copy link
Copy Markdown
Contributor

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

flowchart LR
  A["useSocialShare Hook"] -->|"generates messages"| B["Share Methods"]
  B -->|"Twitter/LinkedIn/Clipboard"| C["Social Platforms"]
  D["ShareAchievementModal"] -->|"uses"| A
  D -->|"displays"| E["Achievement Card"]
  F["DashboardPage"] -->|"integrates"| D
  F -->|"triggers"| G["Share Activity"]
Loading

File Walkthrough

Relevant files
Enhancement
use-social-share.ts
Social sharing hook with multi-platform support                   

client/src/hooks/use-social-share.ts

  • New custom hook for managing social sharing functionality
  • Implements message generation for Twitter, LinkedIn, and generic
    platforms
  • Provides methods for sharing on Twitter, LinkedIn, copying to
    clipboard
  • Includes native share API support and blockchain explorer integration
  • Integrates toast notifications for user feedback
+142/-0 
share-achievement-modal.tsx
Achievement sharing modal with styled preview                       

client/src/components/share-achievement-modal.tsx

  • New modal component for displaying achievement sharing interface
  • Implements currency-specific gradient styling for XDC, ROXN, USDC
  • Features animated achievement card preview with project information
  • Provides share buttons for Twitter and LinkedIn with hover effects
  • Includes copy-to-clipboard and blockchain explorer view options
  • Exports useShareAchievement hook for modal state management
+242/-0 
dashboard-page.tsx
Integrate social sharing into dashboard activity                 

client/src/pages/dashboard-page.tsx

  • Import ShareAchievementModal component and useShareAchievement hook
  • Add Share2 icon from lucide-react for share button
  • Extend ActivityItem component with issueId and onShare callback props
  • Add share button to activity items that appears on hover for reward
    type
  • Integrate share modal into dashboard with achievement data handling
  • Create handleShareActivity function to populate modal with activity
    data
+51/-5   

Summary by CodeRabbit

  • New Features
    • Share achievements to Twitter/X and LinkedIn
    • Copy achievement details to clipboard with confirmation feedback
    • View transaction details on a blockchain explorer when available
    • Share buttons added to the activity/feed for quick sharing
    • Styled achievement preview with currency-specific theming and presentation
    • Modal share flow integrated into the dashboard for streamlined sharing

✏️ Tip: You can customize this high-level summary in your review settings.

Signed-off-by: Brijeshthummar02 <brijeshthummar02@gmail.com>
Copilot AI review requested due to automatic review settings December 12, 2025 08:07
@coderabbitai

coderabbitai Bot commented Dec 12, 2025

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit 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.

Walkthrough

Adds 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

Cohort / File(s) Summary
Share Achievement Modal
client/src/components/share-achievement-modal.tsx
New React TypeScript component and hook export (ShareAchievementModalProps, ShareAchievementModal, useShareAchievement). Renders a themed achievement card, handles copy state, card ref, and exposes share actions and optional "View on Explorer".
Social Share Hook
client/src/hooks/use-social-share.ts
New hook useSocialShare() providing share message generation, shareOnTwitter, shareOnLinkedIn, copyToClipboard (with toasts), viewOnExplorer, and nativeShare (Web Share API). Uses toast feedback and returns a public API for share handlers.
Dashboard Integration
client/src/pages/dashboard-page.tsx
Integrates modal via useShareAchievement, adds per-activity onShare and issueId props, renders Share button for reward activities, and implements handleShareActivity to open modal with transaction and metadata. Minor layout adjustments for share UI.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas requiring extra attention:
    • Social message formatting: Twitter length/encoding and LinkedIn summary encoding.
    • Clipboard flow and copied-feedback timing/state handling in the modal.
    • Explorer URL construction and conditional availability when tx hash is absent.
    • Integration points: ensure toast dependency and useShareAchievement lifecycle are correct.
    • ActivityItem prop additions (issueId, onShare) and their propagation through activity mapping.

Poem

🐰 I hopped to spread the joyous news,
A shiny badge, three playful views.
Tweet, link, or copy — take the floor,
Explorer clicks to see rewards galore.
— a rabbit cheering, sharing more 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and directly summarizes the primary change—adding social sharing functionality for earned ROXN rewards, which matches the main objective.
Linked Issues check ✅ Passed The PR implements all MVP acceptance criteria: share option post-claim, pre-filled Twitter/LinkedIn intents with customizable messages, Roxonn mentions, hashtags, and explorer links per issue #8.
Out of Scope Changes check ✅ Passed All changes are directly within scope: use-social-share hook, ShareAchievementModal component, and dashboard integration align with issue #8 requirements; no unrelated functionality introduced.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review

qodo-code-review Bot commented Dec 12, 2025

Copy link
Copy Markdown

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Reverse tabnabbing risk

Description: Using window.open to social intent URLs without feature restrictions can allow the new
window to access the opener context unless 'noopener' is enforced, which can enable
reverse tabnabbing.
use-social-share.ts [51-58]

Referred Code
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({
Clipboard context constraints

Description: Clipboard write is attempted without a user gesture check and fallback; while generally
safe, calling navigator.clipboard in insecure contexts can fail and may expose UX but not
security; primary concern is ensuring site is served over HTTPS.
use-social-share.ts [84-100]

Referred Code
const copyToClipboard = async (options: ShareOptions): Promise<boolean> => {
  try {
    const message = generateShareMessage(options, 'generic');
    await navigator.clipboard.writeText(message);
    toast({
      title: 'Copied to clipboard!',
      description: 'Share message copied successfully.',
    });
    return true;
  } catch (error) {
    toast({
      title: 'Failed to copy',
      description: 'Could not copy to clipboard.',
      variant: 'destructive',
    });
    return false;
  }
Ticket Compliance
🟡
🎫 #8
🔴 (Optional) Provide a simple shareable image/card summarizing the achievement.
Provide a "Share your achievement!" option after a successful ROXN reward claim.
On click, open a pre-filled social media intent for Twitter/X at minimum with a
customizable message.
Include Roxonn mention, project (if available), and relevant hashtags in the shared
message.
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing audit logs: New user-triggered share/copy/explorer actions are not recorded to any audit trail, making
it unclear which user performed these actions and when.

Referred Code
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!',
  });
};

/**
 * Share on LinkedIn
 */
const shareOnLinkedIn = (options: ShareOptions) => {
  const url = encodeURIComponent('https://app.roxonn.com');
  const summary = encodeURIComponent(generateShareMessage(options, 'linkedin'));
  window.open(
    `https://www.linkedin.com/sharing/share-offsite/?url=${url}&summary=${summary}`,


 ... (clipped 70 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Limited error handling: External share flows (window.open and navigator.share) lack error handling and user
feedback for failures or blocked popups, and clipboard copy errors return generic messages
without context.

Referred Code
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!',
  });
};

/**
 * Share on LinkedIn
 */
const shareOnLinkedIn = (options: ShareOptions) => {
  const url = encodeURIComponent('https://app.roxonn.com');
  const summary = encodeURIComponent(generateShareMessage(options, 'linkedin'));
  window.open(
    `https://www.linkedin.com/sharing/share-offsite/?url=${url}&summary=${summary}`,


 ... (clipped 60 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unvalidated inputs: User-controlled fields (amount, projectName, issueTitle) are interpolated into share
messages and URLs without validation or sanitization, which may risk UX issues and
potential injection in downstream contexts.

Referred Code
const generateShareMessage = (
  options: ShareOptions,
  platform: 'twitter' | 'linkedin' | 'generic'
): string => {
  if (options.customMessage) {
    return options.customMessage;
  }

  const { amount, currency, projectName, issueNumber } = options;
  const projectText = projectName ? ` on ${projectName}` : '';
  const issueText = issueNumber ? ` (#${issueNumber})` : '';

  const baseMessage = `🎉 I just earned ${amount} ${currency} for contributing${projectText}${issueText}!`;

  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}`;


 ... (clipped 3 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review

qodo-code-review Bot commented Dec 12, 2025

Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Share a unique URL, not text

Instead of sharing pre-filled text, generate a unique URL for each achievement.
This URL should point to a page with Open Graph meta tags, enabling social media
platforms to display a rich, visual preview of the achievement card.

Examples:

client/src/hooks/use-social-share.ts [51-57]
  const shareOnTwitter = (options: ShareOptions) => {
    const text = encodeURIComponent(generateShareMessage(options, 'twitter'));
    window.open(
      `https://twitter.com/intent/tweet?text=${text}`,
      '_blank',
      'width=550,height=420'
    );
client/src/hooks/use-social-share.ts [67-74]
  const shareOnLinkedIn = (options: ShareOptions) => {
    const url = encodeURIComponent('https://app.roxonn.com');
    const summary = encodeURIComponent(generateShareMessage(options, 'linkedin'));
    window.open(
      `https://www.linkedin.com/sharing/share-offsite/?url=${url}&summary=${summary}`,
      '_blank',
      'width=550,height=520'
    );

Solution Walkthrough:

Before:

// client/src/hooks/use-social-share.ts

function useSocialShare() {
  const generateShareMessage = (options, platform) => {
    const baseMessage = `🎉 I just earned ${options.amount} ${options.currency}...`;
    // ... constructs a text-only message
    return `${baseMessage} @RoxonnPlatform ...`;
  };

  const shareOnTwitter = (options) => {
    const text = encodeURIComponent(generateShareMessage(options, 'twitter'));
    // Opens a new window to share only text.
    // The visual card from the modal is not included.
    window.open(`https://twitter.com/intent/tweet?text=${text}`);
  };

  return { shareOnTwitter, ... };
}

After:

// client/src/hooks/use-social-share.ts

function useSocialShare() {
  const shareOnTwitter = async (achievementData) => {
    // 1. (Concept) Generate a unique, public page for the achievement.
    // This could involve a backend call.
    const uniqueUrl = `https://app.roxonn.com/achievement/${achievementData.transactionHash}`;

    // 2. Share the URL. Social platforms will fetch this URL
    // and use its meta tags (og:image, etc.) to create a rich preview.
    const shareText = `I just earned a new achievement on @RoxonnPlatform!`;
    const encodedText = encodeURIComponent(shareText);
    const encodedUrl = encodeURIComponent(uniqueUrl);

    window.open(`https://twitter.com/intent/tweet?text=${encodedText}&url=${encodedUrl}`);
  };

  return { shareOnTwitter, ... };
}
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a major design flaw where the visually appealing achievement card is not actually shared, and proposes a standard, high-impact solution using unique URLs and Open Graph tags to enable rich media previews, which is critical for the feature's marketing goal.

High
Possible issue
Fix broken LinkedIn share functionality
Suggestion Impact:The commit removed the unsupported `summary` query parameter from the LinkedIn share URL (addressing the core breakage identified), but it did not implement the suggested clipboard-copy/manual-paste workaround and continued using the share-offsite endpoint.

code diff:

   const shareOnLinkedIn = (options: ShareOptions) => {
     const url = encodeURIComponent('https://app.roxonn.com');
-    const summary = encodeURIComponent(generateShareMessage(options, 'linkedin'));
     window.open(
-      `https://www.linkedin.com/sharing/share-offsite/?url=${url}&summary=${summary}`,
+      `https://www.linkedin.com/sharing/share-offsite/?url=${url}`,
       '_blank',
-      'width=550,height=520'
+      'noopener,noreferrer,width=550,height=520'
     );
     toast({
       title: 'Opening LinkedIn',
@@ -105,8 +104,11 @@

The LinkedIn share functionality is broken because the summary parameter is not
supported. Fix this by copying the share message to the clipboard and opening
LinkedIn for the user to manually paste it.

client/src/hooks/use-social-share.ts [67-79]

-const shareOnLinkedIn = (options: ShareOptions) => {
-  const url = encodeURIComponent('https://app.roxonn.com');
-  const summary = encodeURIComponent(generateShareMessage(options, 'linkedin'));
+const shareOnLinkedIn = async (options: ShareOptions) => {
+  const message = generateShareMessage(options, 'linkedin');
+  try {
+    await navigator.clipboard.writeText(message);
+    toast({
+      title: 'Message copied!',
+      description: 'Paste the message on LinkedIn to share your achievement.',
+    });
+  } catch (error) {
+    toast({
+      title: 'Failed to copy',
+      description: 'Could not copy message to clipboard.',
+      variant: 'destructive',
+    });
+  }
   window.open(
-    `https://www.linkedin.com/sharing/share-offsite/?url=${url}&summary=${summary}`,
+    'https://www.linkedin.com/feed/',
     '_blank',
     'width=550,height=520'
   );
-  toast({
-    title: 'Opening LinkedIn',
-    description: 'Share your professional achievement!',
-  });
 };

[Suggestion processed]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that the LinkedIn share implementation is non-functional due to an unsupported API parameter and provides a robust, standard workaround, fixing a key feature of the PR.

Medium
General
Add missing issue title to share

Pass the activity.title as issueTitle to the openShareModal function to include
the issue title on the shared achievement card, making it more descriptive.

client/src/pages/dashboard-page.tsx [328-336]

 const handleShareActivity = (activity: any) => {
   openShareModal({
     amount: activity.amount || '0',
     currency: (activity.currency || 'ROXN') as 'XDC' | 'ROXN' | 'USDC',
     projectName: activity.repoName,
+    issueTitle: activity.title,
     issueNumber: activity.issueId,
     transactionHash: activity.txHash,
   });
 };
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that the issueTitle is missing from the shared achievement data and proposes using the available activity.title, which significantly improves the context and completeness of the share card.

Medium
  • Update

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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: Normalize transactionHash before passing to the modal (xdc→0x), to match the explorer/link behavior.
ActivityItem.getLink() already does txHash.replace(/^xdc/, "0x"), but handleShareActivity() passes the raw activity.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 stabilizing onShare to avoid re-creating closures per row (optional).
Not a blocker, but if this list grows, a memoized handler (or passing activity id into a stable callback) can reduce churn.

client/src/components/share-achievement-modal.tsx (3)

21-38: Export ShareAchievementModalProps (or keep it private intentionally) but avoid “phantom API”.
Since this is a new component module, making the props type export is 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: Move useShareAchievement into client/src/hooks to match project guidelines.
This is a hook and will likely be reused; placing it under /src/hooks fits the repo rule. Based on learnings, prefer hooks from /src/hooks for client/src/**/*.{ts,tsx}.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2809015 and f4d32d7.

📒 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/hooks and UI components from shadcn/ui for frontend development

Files:

  • client/src/components/share-achievement-modal.tsx
  • client/src/hooks/use-social-share.ts
  • client/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.tsx
  • client/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.

Comment thread client/src/components/share-achievement-modal.tsx
Comment on lines +95 to +97
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="sm:max-w-md">

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +51 to +62
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!',
});
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Comment thread client/src/hooks/use-social-share.ts
Comment on lines +106 to +111
const viewOnExplorer = (transactionHash?: string) => {
if (transactionHash) {
const explorerUrl = `https://xdcscan.com/tx/${transactionHash}`;
window.open(explorerUrl, '_blank');
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Comment thread client/src/pages/dashboard-page.tsx Outdated
Comment on lines +328 to +336
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,
});
};

Copilot AI Dec 12, 2025

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +37 to +45
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}`;

Copilot AI Dec 12, 2025

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +174 to +189
<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" />
LinkedIn
</Button>

Copilot AI Dec 12, 2025

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +91
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'
};
}
};

Copilot AI Dec 12, 2025

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment thread client/src/pages/dashboard-page.tsx Outdated
Comment on lines +328 to +336
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,
});
};

Copilot AI Dec 12, 2025

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
} catch (error) {
toast({
title: 'Failed to copy',
description: 'Could not copy to clipboard.',

Copilot AI Dec 12, 2025

Copy link

Choose a reason for hiding this comment

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

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.

Suggested change
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.',

Copilot uses AI. Check for mistakes.
Comment thread client/src/hooks/use-social-share.ts Outdated
Comment on lines +106 to +109
const viewOnExplorer = (transactionHash?: string) => {
if (transactionHash) {
const explorerUrl = `https://xdcscan.com/tx/${transactionHash}`;
window.open(explorerUrl, '_blank');

Copilot AI Dec 12, 2025

Copy link

Choose a reason for hiding this comment

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

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.

Suggested change
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;

Copilot uses AI. Check for mistakes.
{time}
</p>
{/* Share button for reward type activities */}
{type === "reward" && amount && onShare && (

Copilot AI Dec 12, 2025

Copy link

Choose a reason for hiding this comment

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

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.

Suggested change
{type === "reward" && amount && onShare && (
{type === "reward" && Number(amount) > 0 && onShare && (

Copilot uses AI. Check for mistakes.
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';

Copilot AI Dec 12, 2025

Copy link

Choose a reason for hiding this comment

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

Unused import Card.

Suggested change
import { Card } from './ui/card';

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +18
import {
Twitter,
Linkedin,
Copy,
Download,
Share2,
Coins,
Award,
ExternalLink,
Check,
Sparkles
} from 'lucide-react';

Copilot AI Dec 12, 2025

Copy link

Choose a reason for hiding this comment

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

Unused imports Download, Share2.

Copilot uses AI. Check for mistakes.
@Brijeshthummar02

Copy link
Copy Markdown
Contributor Author

@dineshroxonn good to go?

@dineshroxonn dineshroxonn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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:

  1. Explorer URL - Wrong domain breaks "View on Explorer" functionality
  2. Security - Missing noopener,noreferrer is a security vulnerability
  3. 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';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[S] we should use all the capabilities of typescript

const [copied, setCopied] = useState<boolean>(false);

// Currency-specific styling
const getCurrencyStyle = () => {
switch (currency) {
case 'ROXN':

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 any type bypasses TypeScript's safety. The server defines an ActivityItem interface in server/services/activityService.ts that 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 viewOnExplorer call uses xdcscan.com without normalizing the xdc prefix to 0x, while the canonical pattern throughout the app is https://xdcscan.io/tx/${txHash.replace(/^xdc/, "0x")}. This issue exists in the useSocialShare hook.


92-93: onOpenChange should only trigger onClose when the dialog is closing.

Passing onClose directly to onOpenChange means onClose() may be invoked even when the dialog is opening (receiving true). 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 && onShare allows sharing when amount is "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 ActivityData interface 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: cardRef is assigned but not used functionally.

The cardRef is 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 adding aria-label attributes 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

📥 Commits

Reviewing files that changed from the base of the PR and between f4d32d7 and 0334cd4.

📒 Files selected for processing (3)
  • client/src/components/share-achievement-modal.tsx
  • client/src/hooks/use-social-share.ts
  • client/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/hooks and UI components from shadcn/ui for frontend development

Files:

  • client/src/components/share-achievement-modal.tsx
  • client/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 (issueId and onShare) are well-typed and extend the component interface cleanly without breaking existing usages.


246-247: LGTM!

Clean destructuring and proper use of the useShareAchievement hook 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 onShare callback correctly invokes handleShareActivity with the activity data. The any type issue is already noted above.

client/src/components/share-achievement-modal.tsx (5)

1-16: LGTM!

Imports are clean and well-organized. Correctly uses useSocialShare from /src/hooks and shadcn/ui components as per coding guidelines.


18-29: LGTM!

The interface is well-structured. Union types for currency are idiomatic in TypeScript. If the currency list grows significantly, consider extracting to a shared type definition.


62-88: LGTM!

The getCurrencyStyle function 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 useShareAchievement hook provides clean modal state management with sensible defaults. Collocating it with the modal component is a pragmatic choice for tightly coupled functionality.

@Brijeshthummar02

Copy link
Copy Markdown
Contributor Author

@dineshroxonn good to go?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Enable Social Sharing of Earned ROXN Rewards

4 participants