communitas 0.2.6

A diagnostic chat application for the P2P Foundation network
Documentation
import { invoke } from '@tauri-apps/api/core'
import React, { useState } from 'react'
import {
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
  Button,
  TextField,
  Typography,
  Box,
  CircularProgress,
  Alert,
  FormControlLabel,
  Checkbox,
  Stepper,
  Step,
  StepLabel,
  StepContent,
} from '@mui/material'
import {
  PersonAdd as PersonAddIcon,
  // Security as SecurityIcon,
  Check as CheckIcon,
} from '@mui/icons-material'
import { IdentityInfo, IdentityGenerationParams } from '../../types'

interface IdentitySetupProps {
  open: boolean
  onClose: () => void
  onIdentityCreated: (identity: IdentityInfo) => void
}

const IdentitySetup: React.FC<IdentitySetupProps> = ({
  open,
  onClose,
  onIdentityCreated,
}) => {
  const [activeStep, setActiveStep] = useState(0)
  const [displayName, setDisplayName] = useState('')
  const [useHardwareEntropy, setUseHardwareEntropy] = useState(true)
  const [powDifficulty, setPowDifficulty] = useState(8)
  const [isGenerating, setIsGenerating] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [generatedIdentity, setGeneratedIdentity] = useState<IdentityInfo | null>(null)

  const steps = [
    {
      label: 'Identity Information',
      description: 'Choose a display name for your identity',
    },
    {
      label: 'Security Settings',
      description: 'Configure security parameters',
    },
    {
      label: 'Generate Identity',
      description: 'Create your cryptographic identity',
    },
  ]

  const handleNext = () => {
    setActiveStep((prevActiveStep) => prevActiveStep + 1)
  }

  const handleBack = () => {
    setActiveStep((prevActiveStep) => prevActiveStep - 1)
  }

  const handleGenerateIdentity = async () => {
    setIsGenerating(true)
    setError(null)

    try {
      const params: IdentityGenerationParams = {
        display_name: displayName.trim() || undefined,
        use_hardware_entropy: useHardwareEntropy,
        pow_difficulty: powDifficulty,
        use_secure_storage: true,      }

      // Call the Tauri command
      const identity = await invoke<IdentityInfo>('generate_new_identity', {
        displayName: params.display_name,
      })

      setGeneratedIdentity(identity)
      handleNext()
    } catch (err) {
      setError(err as string)
    } finally {
      setIsGenerating(false)
    }
  }

  const handleComplete = () => {
    if (generatedIdentity) {
      onIdentityCreated(generatedIdentity)
    }
    onClose()
  }

  const handleClose = () => {
    if (!isGenerating) {
      onClose()
    }
  }

  const renderStepContent = (step: number) => {
    switch (step) {
      case 0:
        return (
          <Box sx={{ mt: 2 }}>
            <TextField
              fullWidth
              label="Display Name (Optional)"
              value={displayName}
              onChange={(e) => setDisplayName(e.target.value)}
              placeholder="e.g., Alice, Bob, or leave empty"
              helperText="This is how others will see your identity. You can change this later."
              sx={{ mb: 2 }}
            />
            <Alert severity="info" sx={{ mt: 2 }}>
              Your network address will be a unique 4-word combination like "warm-ocean-gentle-breeze"
              that others can use to connect to you.
            </Alert>
          </Box>
        )

      case 1:
        return (
          <Box sx={{ mt: 2 }}>
            <FormControlLabel
              control={
                <Checkbox
                  checked={useHardwareEntropy}
                  onChange={(e) => setUseHardwareEntropy(e.target.checked)}
                />
              }
              label="Use Hardware Entropy"
            />
            <Typography variant="body2" color="text.secondary" sx={{ ml: 4, mb: 2 }}>
              More secure but slightly slower identity generation
            </Typography>

            <Typography gutterBottom>
              Proof of Work Difficulty: {powDifficulty}
            </Typography>
            <Box sx={{ px: 2 }}>
              <input
                type="range"
                min="4"
                max="16"
                value={powDifficulty}
                onChange={(e) => setPowDifficulty(parseInt(e.target.value))}
                style={{ width: '100%' }}
              />
            </Box>
            <Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
              Higher difficulty provides better Sybil resistance but takes longer to generate
            </Typography>

            <Alert severity="warning" sx={{ mt: 2 }}>
              <Typography variant="body2">
                <strong>Security Note:</strong> Your cryptographic keys will be stored securely
                using your operating system's keychain. Make sure you have access to this device
                for future use.
              </Typography>
            </Alert>
          </Box>
        )

      case 2:
        if (isGenerating) {
          return (
            <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', mt: 3 }}>
              <CircularProgress size={60} sx={{ mb: 2 }} />
              <Typography variant="h6" gutterBottom>
                Generating Identity...
              </Typography>
              <Typography variant="body2" color="text.secondary" align="center">
                This may take a few moments depending on your security settings.
                Please don't close this window.
              </Typography>
            </Box>
          )
        }

        if (generatedIdentity) {
          return (
            <Box sx={{ mt: 2 }}>
              <Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
                <CheckIcon color="success" sx={{ mr: 1 }} />
                <Typography variant="h6" color="success.main">
                  Identity Created Successfully!
                </Typography>
              </Box>

              <Alert severity="success" sx={{ mb: 2 }}>
                <Typography variant="body1" sx={{ mb: 1 }}>
                  <strong>Your Network Address:</strong>
                </Typography>
                <Typography
                  variant="h5"
                  sx={{ fontFamily: 'monospace', color: 'success.main' }}
                >
                  {generatedIdentity.four_word_address}
                </Typography>
              </Alert>

              <Typography variant="body2" color="text.secondary">
                Your identity has been securely stored and is ready to use. You can now
                connect to the P2P network and start communicating with others.
              </Typography>
            </Box>
          )
        }

        return (
          <Box sx={{ mt: 2 }}>
            <Typography variant="body1" gutterBottom>
              Ready to generate your identity with the following settings:
            </Typography>
            <Box sx={{ ml: 2, mt: 2 }}>
              <Typography variant="body2">
                • Display Name: {displayName || 'None'}
              </Typography>
              <Typography variant="body2">
                • Hardware Entropy: {useHardwareEntropy ? 'Enabled' : 'Disabled'}
              </Typography>
              <Typography variant="body2">
                • Security Level: {powDifficulty}/16
              </Typography>
            </Box>
            {error && (
              <Alert severity="error" sx={{ mt: 2 }}>
                {error}
              </Alert>
            )}
          </Box>
        )

      default:
        return null
    }
  }

  return (
    <Dialog
      open={open}
      onClose={handleClose}
      maxWidth="md"
      fullWidth
      PaperProps={{
        sx: { minHeight: '500px' }
      }}
    >
      <DialogTitle>
        <Box display="flex" alignItems="center" gap={1}>
          <PersonAddIcon />
          Create New Identity
        </Box>
      </DialogTitle>

      <DialogContent>
        <Stepper activeStep={activeStep} orientation="vertical">
          {steps.map((step, index) => (
            <Step key={step.label}>
              <StepLabel>{step.label}</StepLabel>
              <StepContent>
                <Typography variant="body2" color="text.secondary" gutterBottom>
                  {step.description}
                </Typography>
                {renderStepContent(index)}
              </StepContent>
            </Step>
          ))}
        </Stepper>
      </DialogContent>

      <DialogActions>
        <Button onClick={handleClose} disabled={isGenerating}>
          Cancel
        </Button>
        
        {activeStep > 0 && activeStep < steps.length - 1 && (
          <Button onClick={handleBack} disabled={isGenerating}>
            Back
          </Button>
        )}
        
        {activeStep < steps.length - 1 && !generatedIdentity && (
          <Button
            onClick={activeStep === steps.length - 2 ? handleGenerateIdentity : handleNext}
            variant="contained"
            disabled={isGenerating}
            startIcon={isGenerating ? <CircularProgress size={16} /> : undefined}
          >
            {activeStep === steps.length - 2 ? 'Generate Identity' : 'Next'}
          </Button>
        )}
        
        {generatedIdentity && (
          <Button onClick={handleComplete} variant="contained" color="success">
            Complete Setup
          </Button>
        )}
      </DialogActions>
    </Dialog>
  )
}

export default IdentitySetup