Overview
The useLicenseStatus hook provides a convenient way to manage and monitor VoiceTypr’s license and trial status in React components. It wraps the License Context and provides derived state for common license checks.
Import
import { useLicenseStatus } from '@/hooks/useLicenseStatus';
Usage
function MyComponent() {
const {
licenseStatus,
isValid,
isChecking,
checkLicense
} = useLicenseStatus();
return (
<div>
{isChecking ? (
<p>Checking license...</p>
) : isValid ? (
<p>License active</p>
) : (
<p>No active license</p>
)}
</div>
);
}
Return Values
The hook returns an object with the following properties:
Current license status object, or null if not yet loaded
Computed boolean indicating if user has valid access (trial or licensed)
True while license status is being checked from the backend
Function to manually trigger a license status check
LicenseStatus Type
interface LicenseStatus {
status: 'active' | 'trial' | 'expired' | 'none';
trial_days_left?: number;
license_type?: string;
license_key?: string;
expires_at?: string;
}
The status field uses 'active' in the frontend (mapped from 'licensed' in backend) for consistency with UI state naming.
Examples
Display Trial Countdown
function TrialBanner() {
const { licenseStatus, isValid } = useLicenseStatus();
if (licenseStatus?.status === 'trial' && licenseStatus.trial_days_left) {
return (
<div className="trial-banner">
{licenseStatus.trial_days_left} days left in trial
</div>
);
}
return null;
}
Conditional Feature Access
function PremiumFeature() {
const { isValid, licenseStatus } = useLicenseStatus();
if (!isValid) {
return (
<div className="locked-feature">
<p>This feature requires an active license</p>
<button onClick={() => invoke('open_purchase_page')}>
Purchase License
</button>
</div>
);
}
return <div>Premium feature content...</div>;
}
Manual License Check
function LicenseSettings() {
const { checkLicense, isChecking, licenseStatus } = useLicenseStatus();
const handleRefresh = async () => {
// Invalidate cache and recheck
await invoke('invalidate_license_cache');
await checkLicense();
};
return (
<div>
<p>Status: {licenseStatus?.status}</p>
<button onClick={handleRefresh} disabled={isChecking}>
{isChecking ? 'Checking...' : 'Refresh License'}
</button>
</div>
);
}
License Activation Flow
function LicenseActivation() {
const { checkLicense } = useLicenseStatus();
const [licenseKey, setLicenseKey] = useState('');
const [error, setError] = useState<string | null>(null);
const handleActivate = async () => {
try {
await invoke('activate_license', { licenseKey });
await checkLicense(); // Refresh status after activation
setError(null);
} catch (err) {
setError(err as string);
}
};
return (
<div>
<input
type="text"
value={licenseKey}
onChange={(e) => setLicenseKey(e.target.value)}
placeholder="Enter license key"
/>
<button onClick={handleActivate}>Activate</button>
{error && <p className="error">{error}</p>}
</div>
);
}
Show Expiration Warning
function ExpirationWarning() {
const { licenseStatus } = useLicenseStatus();
const showWarning = licenseStatus?.status === 'trial' &&
licenseStatus.trial_days_left !== undefined &&
licenseStatus.trial_days_left <= 3;
if (!showWarning) return null;
return (
<div className="warning">
<p>Your trial expires in {licenseStatus.trial_days_left} days</p>
<button onClick={() => invoke('open_purchase_page')}>
Purchase Now
</button>
</div>
);
}
Account Tab Integration
function AccountTab() {
const { licenseStatus, isValid, checkLicense } = useLicenseStatus();
const [activating, setActivating] = useState(false);
const handleDeactivate = async () => {
try {
await invoke('deactivate_license');
await checkLicense();
} catch (error) {
console.error('Deactivation failed:', error);
}
};
return (
<div className="account-tab">
<h2>License Status</h2>
{licenseStatus?.status === 'active' && (
<div>
<p>✅ Licensed</p>
<p>Type: {licenseStatus.license_type}</p>
<button onClick={handleDeactivate}>
Deactivate on this device
</button>
</div>
)}
{licenseStatus?.status === 'trial' && (
<div>
<p>🎯 Trial Mode</p>
<p>Days remaining: {licenseStatus.trial_days_left}</p>
<button onClick={() => invoke('open_purchase_page')}>
Upgrade to Full License
</button>
</div>
)}
{licenseStatus?.status === 'expired' && (
<div>
<p>⚠️ Trial Expired</p>
<button onClick={() => invoke('open_purchase_page')}>
Purchase License
</button>
</div>
)}
</div>
);
}
Implementation Details
The useLicenseStatus hook is a thin wrapper around the LicenseContext that:
- Accesses license state from React Context
- Computes derived values like
isValid
- Provides a cleaner API for component use
Source:
export function useLicenseStatus() {
const { status, isLoading, checkStatus } = useLicense();
const isValid = status ?
['active', 'trial'].includes(status.status) :
false;
return {
licenseStatus: status,
isValid,
isChecking: isLoading,
checkLicense: checkStatus
};
}
Context Provider
The hook requires the LicenseProvider to be present in the component tree:
import { LicenseProvider } from '@/contexts/LicenseContext';
function App() {
return (
<LicenseProvider>
<YourApp />
</LicenseProvider>
);
}
Automatic Status Checks
The License Context automatically:
- Checks license status on app startup
- Respects 8-hour cache TTL
- Validates against offline grace periods
- Updates status when license commands are invoked
See Also