QR Code Sharing Component

Overview

The QRCodeDialog component provides a simple way to share AI assistants via QR codes. Users can quickly generate QR codes that link directly to specific assistants, making it easy to share assistant configurations across devices and with other users.

Features

📱 QR Code Generation

  • Assistant Sharing: Generate QR codes that link directly to specific assistant instances
  • Multiple Formats: QR code display, downloadable PNG, and copyable URL
  • Cross-Device Access: Easy access to assistants across mobile and desktop devices
  • Native Sharing: Integrates with device’s native share functionality when available

🔗 Sharing Options

  • QR Code Display: Large, scannable QR code with 200px size
  • URL Copy: One-click copy of the assistant URL to clipboard
  • PNG Download: Download QR code as a high-quality PNG image (600x600px)
  • Native Share: Use device’s built-in share menu when supported

Component Interface

Props

interface QRCodeDialogProps {
  isOpen: boolean;              // Controls dialog visibility
  onOpenChange: (open: boolean) => void;  // Callback when dialog state changes
  assistantId: string;          // Unique identifier for the assistant
  assistantName: string;        // Display name of the assistant
}

Usage Example

import { QRCodeDialog } from "@/components/QRCodeDialog";

// In your component
const [qrDialogOpen, setQrDialogOpen] = useState(false);

<QRCodeDialog
  isOpen={qrDialogOpen}
  onOpenChange={setQrDialogOpen}
  assistantId="assistant-uuid-123"
  assistantName="My AI Assistant"
/>

Technical Implementation

Dependencies

// External dependencies
import { QRCodeSVG } from 'qrcode.react';

// Internal components
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { useToast } from "@/components/hooks/use-toast";

// Icons
import { Copy, Check, Download, Share2 } from "lucide-react";

URL Generation

// Generates the shareable URL for the assistant
const shareUrl = `${window.location.origin}/assistant/${assistantId}`;

QR Code Configuration

<QRCodeSVG 
  id="qr-code-svg"
  value={shareUrl} 
  size={200}
  level="M"              // Medium error correction
  includeMargin={true}
/>

Core Functionality

1. Copy to Clipboard

const handleCopy = async () => {
  try {
    await navigator.clipboard.writeText(shareUrl);
    setCopied(true);
    toast({
      title: "Copied!",
      description: "Share URL copied to clipboard"
    });
    setTimeout(() => setCopied(false), 2000);
  } catch (error) {
    // Error handling with toast notification
  }
};

2. Download as PNG

The download functionality converts the SVG QR code to a high-quality PNG:

const handleDownload = () => {
  // 1. Get the QR code SVG element
  const svg = document.querySelector('#qr-code-svg') as SVGElement;
  
  // 2. Clone and add white background
  const svgClone = svg.cloneNode(true) as SVGElement;
  const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
  rect.setAttribute('width', '100%');
  rect.setAttribute('height', '100%');
  rect.setAttribute('fill', 'white');
  svgClone.insertBefore(rect, svgClone.firstChild);
  
  // 3. Create high-resolution canvas (3x scale)
  const scale = 3;
  const canvas = document.createElement('canvas');
  canvas.width = 200 * scale;
  canvas.height = 200 * scale;
  
  // 4. Convert SVG to data URL and draw on canvas
  const svgData = new XMLSerializer().serializeToString(svgClone);
  const svgDataUrl = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgData);
  
  // 5. Trigger download with sanitized filename
  const filename = `${assistantName.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_qr_code.png`;
};

3. Native Share

const handleShare = async () => {
  if (navigator.share) {
    try {
      await navigator.share({
        title: assistantName,
        text: `Check out this AI assistant: ${assistantName}`,
        url: shareUrl,
      });
    } catch (error) {
      console.error("Failed to share:", error);
    }
  } else {
    // Fallback to copy URL if native share not available
    handleCopy();
  }
};

Integration

Used in SharingStatus Component

The QRCodeDialog is integrated into the SharingStatus component and appears when:

  • Assistant has a valid assistantId and assistantName
  • Assistant access control is NOT private (public, domain, group, or specific users)
// From SharingStatus.tsx
{assistantId && assistantName && accessControl.type !== 'private' && (
  <TooltipProvider>
    <Tooltip delayDuration={300}>
      <TooltipTrigger asChild>
        <Button
          variant="ghost"
          size="sm"
          onClick={() => setQrDialogOpen(true)}
          className="h-8 w-8 p-0 text-muted-foreground hover:text-foreground"
        >
          <QrCode className="h-4 w-4" />
        </Button>
      </TooltipTrigger>
      <TooltipContent>
        Show QR code for sharing
      </TooltipContent>
    </Tooltip>
  </TooltipProvider>
)}

User Experience

Dialog Layout

<Dialog open={isOpen} onOpenChange={onOpenChange}>
  <DialogContent className="sm:max-w-md">
    <DialogHeader>
      <DialogTitle className="text-center">Share QR Code</DialogTitle>
    </DialogHeader>
    
    <div className="flex flex-col items-center space-y-6 py-4">
      {/* Assistant Name */}
      <div className="text-center">
        <h3 className="font-semibold text-lg">{assistantName}</h3>
        <p className="text-sm text-muted-foreground">Scan to access this assistant</p>
      </div>

      {/* QR Code with styling */}
      <div className="bg-white p-6 rounded-lg shadow-lg border-2 border-muted">
        <QRCodeSVG {...qrCodeProps} />
      </div>

      {/* URL Display and Copy */}
      <div className="w-full space-y-2">
        <label className="text-sm font-medium text-muted-foreground">Share URL:</label>
        <div className="flex gap-2">
          <input type="text" value={shareUrl} readOnly />
          <Button onClick={handleCopy}>
            {copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
          </Button>
        </div>
      </div>

      {/* Action Buttons */}
      <div className="flex flex-wrap justify-center gap-3 w-full">
        <Button onClick={handleDownload}>
          <Download className="w-4 h-4 mr-2" />
          Download
        </Button>
        <Button onClick={handleShare}>
          <Share2 className="w-4 h-4 mr-2" />
          Share
        </Button>
      </div>
    </div>
  </DialogContent>
</Dialog>

Visual Design

  • QR Code Styling: White background with padding, rounded corners, and shadow
  • Responsive Design: Works on both desktop and mobile devices
  • Button States: Visual feedback for copy action (checkmark when copied)
  • Accessibility: Proper ARIA labels and keyboard navigation

Security Considerations

URL Security

  • Origin-based URLs: Uses window.location.origin to generate appropriate URLs
  • No sensitive data: QR codes only contain the public assistant URL
  • Access control: Respects existing assistant access permissions

File Downloads

  • Safe filenames: Assistant names are sanitized for filesystem compatibility
  • CSP compliance: Uses data URLs that comply with Content Security Policy
  • High resolution: 3x scale provides crisp images while maintaining reasonable file size

Error Handling

Copy Functionality

try {
  await navigator.clipboard.writeText(shareUrl);
  // Success feedback
} catch (error) {
  console.error("Failed to copy URL:", error);
  toast({
    title: "Copy failed",
    description: "Failed to copy URL to clipboard",
    variant: "destructive"
  });
}

Download Functionality

// Multiple error checks throughout download process
if (!svg) {
  console.error("QR code SVG not found");
  return;
}

if (!ctx) {
  console.error("Canvas context not available");
  return;
}

img.onerror = () => {
  console.error("Failed to load SVG image");
  toast({
    title: "Download failed",
    description: "Failed to process QR code image",
    variant: "destructive"
  });
};

Browser Compatibility

Required Features

  • QR Code Generation: SVG support (all modern browsers)
  • Clipboard API: navigator.clipboard (HTTPS required)
  • Canvas API: For PNG download functionality
  • File Download: URL.createObjectURL and blob support

Fallback Behavior

  • Share API: Falls back to copy-to-clipboard if navigator.share unavailable
  • Manual copy: Users can manually select and copy the URL if clipboard API fails
  • Mobile QR scanning: Relies on device camera and QR code reader apps

File Locations

  • Component: src/components/QRCodeDialog.tsx
  • Integration: src/components/SharingStatus.tsx
  • Documentation: docs/features/QR_CODE_SHARING.md
  • SharingStatus: Displays assistant sharing status and QR code trigger
  • Dialog components: Uses shadcn/ui dialog system
  • Toast system: Provides user feedback for actions