cyb/src/pages/Mining/components/ReferralSection.tsx

import { useCallback, useState } from 'react';
import { Link } from 'react-router-dom';
import { LITIUM_REFER_CONTRACT } from 'src/constants/mining';
import { routes } from 'src/routes';
import { trimString } from 'src/utils/utils';
import Soft3MessageFactory from 'src/services/soft.js/api/msgs';
import useAutoSigner from '../hooks/useAutoSigner';
import useReferralInfo from '../hooks/useReferralInfo';
import { compactLi } from '../utils/formatLi';
import styles from '../Mining.module.scss';

const REFERRER_KEY = 'mining_referrer';

function loadReferrer(): string {
  try {
    return localStorage.getItem(REFERRER_KEY) || '';
  } catch {
    return '';
  }
}

function saveReferrer(value: string) {
  try {
    localStorage.setItem(REFERRER_KEY, value);
  } catch {
    // ignore
  }
}

type Props = {
  referrer: string;
  onReferrerChange: (value: string) => void;
};

function ReferralSection({ referrer, onReferrerChange }: Props) {
  const { signer, signingClient, address } = useAutoSigner();
  const { referralInfo, refetch } = useReferralInfo(address);

  const [inputValue, setInputValue] = useState(() => loadReferrer());
  const [busy, setBusy] = useState(false);
  const [status, setStatus] = useState<{ type: 'tx'; ok: boolean; txHash?: string; error?: string } | { type: 'info'; text: string } | null>(null);

  const boundReferrer = referralInfo?.referrer ?? null;
  const referralRewards = referralInfo
    ? Number(referralInfo.referral_rewards) / 1_000_000
    : 0;
  const referralsCount = referralInfo?.referrals_count ?? 0;

  const handleSetReferrer = useCallback(() => {
    const trimmed = inputValue.trim();
    if (!trimmed) return;
    if (address && trimmed === address) {
      setStatus({ type: 'info', text: 'Cannot refer yourself.' });
      return;
    }
    saveReferrer(trimmed);
    onReferrerChange(trimmed);
    setStatus({ type: 'info', text: 'Referrer saved. Will be included in your next proof submission.' });
  }, [inputValue, onReferrerChange, address]);

  const handleClaimReferralRewards = useCallback(async () => {
    if (!signer || !signingClient || !address) return;
    setBusy(true);
    setStatus(null);
    try {
      const [account] = await signer.getAccounts();
      const result = await signingClient.execute(
        account.address,
        LITIUM_REFER_CONTRACT,
        { claim_rewards: {} },
        Soft3MessageFactory.fee(8),
        ''
      );
      setStatus({ type: 'tx', ok: true, txHash: result.transactionHash });
      setTimeout(() => refetch(), 2000);
    } catch (err: any) {
      setStatus({ type: 'tx', ok: false, error: err?.message?.slice(0, 120) || 'Failed' });
    } finally {
      setBusy(false);
    }
  }, [signer, signingClient, address, refetch]);

  const handleCopyLink = useCallback(() => {
    if (address) {
      const origin = window.location.origin.startsWith('tauri:') ? 'https://cyb.ai' : window.location.origin;
      navigator.clipboard.writeText(`${origin}/mining?ref=${address}`);
      setStatus({ type: 'info', text: 'Referral link copied!' });
    }
  }, [address]);

  return (
    <div className={styles.sectionBox}>
      <span className={styles.sectionTitle}>Referral</span>

      {/* Referrer display / set */}
      <div className={styles.referralRow}>
        <span className={styles.referralLabel}>Your Referrer:</span>
        {boundReferrer ? (
          <span className={styles.referralValue}>
            {boundReferrer.slice(0, 16)}...{boundReferrer.slice(-6)}
          </span>
        ) : (
          <span className={styles.referralValue}>None (set before first proof)</span>
        )}
      </div>

      {!boundReferrer && (
        <div className={styles.stakingRow}>
          <input
            type="text"
            placeholder="bostrom1..."
            value={inputValue}
            onChange={(e) => setInputValue(e.target.value)}
            className={styles.stakingInput}
          />
          <button
            type="button"
            className={styles.stakingBtn}
            onClick={handleSetReferrer}
            disabled={!inputValue.trim()}
          >
            Set
          </button>
        </div>
      )}

      {/* Stats as referrer */}
      <div className={styles.statsGrid}>
        <div className={styles.statCard}>
          <span className={styles.statCardLabel}>Referrals</span>
          <span className={styles.statCardValue}>{referralsCount}</span>
        </div>
        <div className={styles.statCard}>
          <span className={styles.statCardLabel}>Rewards</span>
          <span className={styles.statCardValue}>
            {compactLi(referralRewards)}
            <span className={styles.statCardSuffix}> LI</span>
          </span>
        </div>
      </div>

      <div className={styles.stakingRow}>
        <button
          type="button"
          className={styles.stakingBtn}
          onClick={handleClaimReferralRewards}
          disabled={busy || referralRewards <= 0}
        >
          Claim Referral Rewards
        </button>
        <button
          type="button"
          className={styles.stakingBtn}
          onClick={handleCopyLink}
          disabled={!address}
        >
          Copy Referral Link
        </button>
      </div>

      {status && (
        <div className={styles.stakingStatus}>
          {status.type === 'tx' && status.ok && status.txHash ? (
            <Link to={routes.txExplorer.getLink(status.txHash)} style={{ color: '#36d6ae' }}>
              TX: {trimString(status.txHash, 10, 6)}
            </Link>
          ) : status.type === 'tx' && status.error ? (
            <span style={{ color: '#ef4444' }} title={status.error}>
              Error: {status.error.slice(0, 80)}
            </span>
          ) : status.type === 'info' ? (
            status.text
          ) : null}
        </div>
      )}
    </div>
  );
}

export { loadReferrer, saveReferrer };
export default ReferralSection;

Neighbours