feat: rename CryptID to enCryptID, improve Settings Menu styling
- Renamed all user-facing "CryptID" references to "enCryptID" - Updated emails, error messages, UI text, and onboarding tour - Enhanced BoardSettingsDropdown with section headers, icons, and alternating background colors for better visual hierarchy 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b8f179c9c1
commit
73d186e8e8
|
|
@ -325,44 +325,74 @@ const BoardSettingsDropdown: React.FC<BoardSettingsDropdownProps> = ({ className
|
||||||
Loading...
|
Loading...
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: '16px' }}>
|
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
|
|
||||||
{/* Board Info */}
|
{/* Board Info Section */}
|
||||||
<div>
|
<div style={{ padding: '14px', background: 'var(--color-muted-2)', borderBottom: '1px solid var(--color-panel-contrast)' }}>
|
||||||
<div style={{ fontSize: '11px', fontWeight: 600, color: 'var(--color-text-3)', marginBottom: '8px', textTransform: 'uppercase' }}>
|
<div style={{
|
||||||
|
fontSize: '11px',
|
||||||
|
fontWeight: 700,
|
||||||
|
color: 'var(--color-text)',
|
||||||
|
marginBottom: '10px',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: '0.5px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '6px',
|
||||||
|
}}>
|
||||||
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
|
<circle cx="12" cy="12" r="10"/>
|
||||||
|
<line x1="12" y1="16" x2="12" y2="12"/>
|
||||||
|
<line x1="12" y1="8" x2="12.01" y2="8"/>
|
||||||
|
</svg>
|
||||||
Board Info
|
Board Info
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: '12px', color: 'var(--color-text)' }}>
|
<div style={{ fontSize: '12px', color: 'var(--color-text-1)' }}>
|
||||||
<div style={{ marginBottom: '4px' }}>
|
<div style={{ marginBottom: '6px', display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||||
<strong>ID:</strong> {boardId}
|
<span style={{ color: 'var(--color-text-3)', minWidth: '50px' }}>ID:</span>
|
||||||
|
<span style={{ fontFamily: 'monospace', fontSize: '11px' }}>{boardId}</span>
|
||||||
</div>
|
</div>
|
||||||
{boardInfo?.ownerUsername && (
|
{boardInfo?.ownerUsername && (
|
||||||
<div style={{ marginBottom: '4px' }}>
|
<div style={{ marginBottom: '6px', display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||||
<strong>Owner:</strong> @{boardInfo.ownerUsername}
|
<span style={{ color: 'var(--color-text-3)', minWidth: '50px' }}>Owner:</span>
|
||||||
|
<span>@{boardInfo.ownerUsername}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||||
<strong>Status:</strong>
|
<span style={{ color: 'var(--color-text-3)', minWidth: '50px' }}>Status:</span>
|
||||||
<span style={{
|
<span style={{
|
||||||
padding: '2px 8px',
|
padding: '3px 10px',
|
||||||
borderRadius: '4px',
|
borderRadius: '4px',
|
||||||
fontSize: '11px',
|
fontSize: '11px',
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
background: boardInfo?.isProtected ? '#fef3c7' : '#d1fae5',
|
background: boardInfo?.isProtected ? '#fef3c7' : '#d1fae5',
|
||||||
color: boardInfo?.isProtected ? '#92400e' : '#065f46',
|
color: boardInfo?.isProtected ? '#92400e' : '#065f46',
|
||||||
}}>
|
}}>
|
||||||
{boardInfo?.isProtected ? 'Protected (View-only)' : 'Open (Anyone can edit)'}
|
{boardInfo?.isProtected ? 'Protected' : 'Open'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Admin Section */}
|
{/* Admin Section - Protection Settings */}
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<>
|
<>
|
||||||
<div style={{ borderTop: '1px solid var(--color-panel-contrast)', paddingTop: '12px' }}>
|
<div style={{ padding: '14px', background: 'var(--color-panel)', borderBottom: '1px solid var(--color-panel-contrast)' }}>
|
||||||
<div style={{ fontSize: '11px', fontWeight: 600, color: 'var(--color-text-3)', marginBottom: '8px', textTransform: 'uppercase' }}>
|
<div style={{
|
||||||
Protection Settings {isGlobalAdmin && <span style={{ color: '#3b82f6' }}>(Global Admin)</span>}
|
fontSize: '11px',
|
||||||
|
fontWeight: 700,
|
||||||
|
color: 'var(--color-text)',
|
||||||
|
marginBottom: '10px',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: '0.5px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '6px',
|
||||||
|
}}>
|
||||||
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
|
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||||||
|
</svg>
|
||||||
|
Protection {isGlobalAdmin && <span style={{ color: '#3b82f6', fontWeight: 500, fontSize: '10px' }}>(Global Admin)</span>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Protection Toggle */}
|
{/* Protection Toggle */}
|
||||||
|
|
@ -413,8 +443,24 @@ const BoardSettingsDropdown: React.FC<BoardSettingsDropdownProps> = ({ className
|
||||||
|
|
||||||
{/* Editor Management (only when protected) */}
|
{/* Editor Management (only when protected) */}
|
||||||
{boardInfo?.isProtected && (
|
{boardInfo?.isProtected && (
|
||||||
<div>
|
<div style={{ padding: '14px', background: 'var(--color-muted-2)', borderBottom: '1px solid var(--color-panel-contrast)' }}>
|
||||||
<div style={{ fontSize: '11px', fontWeight: 600, color: 'var(--color-text-3)', marginBottom: '8px', textTransform: 'uppercase' }}>
|
<div style={{
|
||||||
|
fontSize: '11px',
|
||||||
|
fontWeight: 700,
|
||||||
|
color: 'var(--color-text)',
|
||||||
|
marginBottom: '10px',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: '0.5px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '6px',
|
||||||
|
}}>
|
||||||
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
|
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||||
|
<circle cx="9" cy="7" r="4"/>
|
||||||
|
<path d="M23 21v-2a4 4 0 0 0-3-3.87"/>
|
||||||
|
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
|
||||||
|
</svg>
|
||||||
Editors ({editors.length})
|
Editors ({editors.length})
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -475,10 +521,10 @@ const BoardSettingsDropdown: React.FC<BoardSettingsDropdownProps> = ({ className
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
padding: '8px',
|
padding: '8px 10px',
|
||||||
borderRadius: '6px',
|
borderRadius: '6px',
|
||||||
marginBottom: '4px',
|
marginBottom: '4px',
|
||||||
background: 'var(--color-muted-2)',
|
background: 'var(--color-panel)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -514,7 +560,26 @@ const BoardSettingsDropdown: React.FC<BoardSettingsDropdownProps> = ({ className
|
||||||
|
|
||||||
{/* Request Admin Access (for non-admins) */}
|
{/* Request Admin Access (for non-admins) */}
|
||||||
{!isAdmin && session.authed && (
|
{!isAdmin && session.authed && (
|
||||||
<div style={{ borderTop: '1px solid var(--color-panel-contrast)', paddingTop: '12px' }}>
|
<div style={{ padding: '14px', background: 'var(--color-panel)', borderBottom: '1px solid var(--color-panel-contrast)' }}>
|
||||||
|
<div style={{
|
||||||
|
fontSize: '11px',
|
||||||
|
fontWeight: 700,
|
||||||
|
color: 'var(--color-text)',
|
||||||
|
marginBottom: '10px',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: '0.5px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '6px',
|
||||||
|
}}>
|
||||||
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
|
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||||
|
<circle cx="8.5" cy="7" r="4"/>
|
||||||
|
<line x1="20" y1="8" x2="20" y2="14"/>
|
||||||
|
<line x1="23" y1="11" x2="17" y2="11"/>
|
||||||
|
</svg>
|
||||||
|
Admin Access
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={requestAdminAccess}
|
onClick={requestAdminAccess}
|
||||||
disabled={requestingAdmin || adminRequestSent}
|
disabled={requestingAdmin || adminRequestSent}
|
||||||
|
|
@ -546,8 +611,10 @@ const BoardSettingsDropdown: React.FC<BoardSettingsDropdownProps> = ({ className
|
||||||
|
|
||||||
{/* Sign in prompt for anonymous users */}
|
{/* Sign in prompt for anonymous users */}
|
||||||
{!session.authed && (
|
{!session.authed && (
|
||||||
<div style={{ fontSize: '11px', color: 'var(--color-text-3)', textAlign: 'center', padding: '10px' }}>
|
<div style={{ padding: '14px', background: 'var(--color-muted-2)', textAlign: 'center' }}>
|
||||||
Sign in to access board settings
|
<div style={{ fontSize: '11px', color: 'var(--color-text-3)' }}>
|
||||||
|
Sign in to access board settings
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
/**
|
/**
|
||||||
* WalletLinkPanel - UI for connecting and linking Web3 wallets to CryptID
|
* WalletLinkPanel - UI for connecting and linking Web3 wallets to enCryptID
|
||||||
*
|
*
|
||||||
* Features:
|
* Features:
|
||||||
* - Connect wallet (MetaMask, WalletConnect, etc.)
|
* - Connect wallet (MetaMask, WalletConnect, etc.)
|
||||||
* - Link wallet to CryptID account via signature
|
* - Link wallet to enCryptID account via signature
|
||||||
* - View and manage linked wallets
|
* - View and manage linked wallets
|
||||||
* - Set primary wallet
|
* - Set primary wallet
|
||||||
* - Unlink wallets
|
* - Unlink wallets
|
||||||
|
|
@ -270,9 +270,9 @@ function LinkWalletSection({
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
return (
|
return (
|
||||||
<div style={styles.section}>
|
<div style={styles.section}>
|
||||||
<div style={styles.sectionTitle}>Link to CryptID</div>
|
<div style={styles.sectionTitle}>Link to enCryptID</div>
|
||||||
<div style={{ ...styles.card, color: '#6b7280' }}>
|
<div style={{ ...styles.card, color: '#6b7280' }}>
|
||||||
Please sign in with CryptID to link your wallet.
|
Please sign in with enCryptID to link your wallet.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -280,10 +280,10 @@ function LinkWalletSection({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={styles.section}>
|
<div style={styles.section}>
|
||||||
<div style={styles.sectionTitle}>Link to CryptID</div>
|
<div style={styles.sectionTitle}>Link to enCryptID</div>
|
||||||
<div style={styles.card}>
|
<div style={styles.card}>
|
||||||
<p style={{ fontSize: '13px', color: '#6b7280', marginBottom: '12px' }}>
|
<p style={{ fontSize: '13px', color: '#6b7280', marginBottom: '12px' }}>
|
||||||
Link this wallet to your CryptID account. You'll be asked to sign a message
|
Link this wallet to your enCryptID account. You'll be asked to sign a message
|
||||||
to prove ownership.
|
to prove ownership.
|
||||||
</p>
|
</p>
|
||||||
<input
|
<input
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ const AnonymousViewerBanner: React.FC<AnonymousViewerBannerProps> = ({
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<p className="banner-summary">
|
<p className="banner-summary">
|
||||||
Sign in with CryptID to edit
|
Sign in with enCryptID to edit
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ interface CryptIDProps {
|
||||||
type RegistrationStep = 'welcome' | 'username' | 'email' | 'success';
|
type RegistrationStep = 'welcome' | 'username' | 'email' | 'success';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CryptID - WebCryptoAPI-based authentication component
|
* enCryptID - WebCryptoAPI-based authentication component
|
||||||
* Enhanced with multi-step registration and email backup
|
* Enhanced with multi-step registration and email backup
|
||||||
*/
|
*/
|
||||||
const CryptID: React.FC<CryptIDProps> = ({ onSuccess, onCancel }) => {
|
const CryptID: React.FC<CryptIDProps> = ({ onSuccess, onCancel }) => {
|
||||||
|
|
@ -235,7 +235,7 @@ const CryptID: React.FC<CryptIDProps> = ({ onSuccess, onCancel }) => {
|
||||||
<p style={styles.description}>
|
<p style={styles.description}>
|
||||||
{!browserSupport.supported
|
{!browserSupport.supported
|
||||||
? 'Your browser does not support the required features for cryptographic authentication. Please use a modern browser.'
|
? 'Your browser does not support the required features for cryptographic authentication. Please use a modern browser.'
|
||||||
: 'CryptID requires a secure connection (HTTPS) to protect your cryptographic keys.'}
|
: 'enCryptID requires a secure connection (HTTPS) to protect your cryptographic keys.'}
|
||||||
</p>
|
</p>
|
||||||
{onCancel && (
|
{onCancel && (
|
||||||
<button onClick={onCancel} style={styles.secondaryButton}>
|
<button onClick={onCancel} style={styles.secondaryButton}>
|
||||||
|
|
@ -272,7 +272,7 @@ const CryptID: React.FC<CryptIDProps> = ({ onSuccess, onCancel }) => {
|
||||||
{registrationStep === 'welcome' && (
|
{registrationStep === 'welcome' && (
|
||||||
<div style={styles.card}>
|
<div style={styles.card}>
|
||||||
<div style={styles.iconLarge}>🔐</div>
|
<div style={styles.iconLarge}>🔐</div>
|
||||||
<h2 style={styles.title}>Welcome to CryptID</h2>
|
<h2 style={styles.title}>Welcome to enCryptID</h2>
|
||||||
<p style={styles.subtitle}>Passwordless, secure authentication</p>
|
<p style={styles.subtitle}>Passwordless, secure authentication</p>
|
||||||
|
|
||||||
<div style={styles.explainerBox}>
|
<div style={styles.explainerBox}>
|
||||||
|
|
@ -531,7 +531,7 @@ const CryptID: React.FC<CryptIDProps> = ({ onSuccess, onCancel }) => {
|
||||||
<div style={styles.card}>
|
<div style={styles.card}>
|
||||||
<div style={styles.successIcon}>✓</div>
|
<div style={styles.successIcon}>✓</div>
|
||||||
<h2 style={styles.title}>Welcome, {username}!</h2>
|
<h2 style={styles.title}>Welcome, {username}!</h2>
|
||||||
<p style={styles.subtitle}>Your CryptID account is ready</p>
|
<p style={styles.subtitle}>Your enCryptID account is ready</p>
|
||||||
|
|
||||||
<div style={styles.successBox}>
|
<div style={styles.successBox}>
|
||||||
<div style={styles.successItem}>
|
<div style={styles.successItem}>
|
||||||
|
|
@ -578,7 +578,7 @@ const CryptID: React.FC<CryptIDProps> = ({ onSuccess, onCancel }) => {
|
||||||
<div style={styles.container}>
|
<div style={styles.container}>
|
||||||
<div style={styles.card}>
|
<div style={styles.card}>
|
||||||
<div style={styles.iconLarge}>🔐</div>
|
<div style={styles.iconLarge}>🔐</div>
|
||||||
<h2 style={styles.title}>Sign In with CryptID</h2>
|
<h2 style={styles.title}>Sign In with enCryptID</h2>
|
||||||
|
|
||||||
{existingUsers.length > 0 ? (
|
{existingUsers.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -624,7 +624,7 @@ const CryptID: React.FC<CryptIDProps> = ({ onSuccess, onCancel }) => {
|
||||||
No accounts found on this device.
|
No accounts found on this device.
|
||||||
</p>
|
</p>
|
||||||
<p style={{ ...styles.hint, marginBottom: '20px' }}>
|
<p style={{ ...styles.hint, marginBottom: '20px' }}>
|
||||||
Create a new CryptID or use a backup link from another device to sign in here.
|
Create a new enCryptID or use a backup link from another device to sign in here.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -639,7 +639,7 @@ const CryptID: React.FC<CryptIDProps> = ({ onSuccess, onCancel }) => {
|
||||||
style={existingUsers.length > 0 ? { ...styles.linkButton, marginTop: '20px' } : styles.primaryButton}
|
style={existingUsers.length > 0 ? { ...styles.linkButton, marginTop: '20px' } : styles.primaryButton}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
{existingUsers.length > 0 ? 'Need an account? Create one' : 'Create a CryptID'}
|
{existingUsers.length > 0 ? 'Need an account? Create one' : 'Create an enCryptID'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { GoogleExportBrowser } from '../GoogleExportBrowser';
|
||||||
import { getFathomApiKey, saveFathomApiKey, removeFathomApiKey, isFathomApiKeyConfigured } from '../../lib/fathomApiKey';
|
import { getFathomApiKey, saveFathomApiKey, removeFathomApiKey, isFathomApiKeyConfigured } from '../../lib/fathomApiKey';
|
||||||
import { isMiroApiKeyConfigured } from '../../lib/miroApiKey';
|
import { isMiroApiKeyConfigured } from '../../lib/miroApiKey';
|
||||||
import { MiroIntegrationModal } from '../MiroIntegrationModal';
|
import { MiroIntegrationModal } from '../MiroIntegrationModal';
|
||||||
|
import { WalletLinkPanel } from '../WalletLinkPanel';
|
||||||
import { getMyConnections, createConnection, removeConnection, updateTrustLevel, updateEdgeMetadata } from '../../lib/networking/connectionService';
|
import { getMyConnections, createConnection, removeConnection, updateTrustLevel, updateEdgeMetadata } from '../../lib/networking/connectionService';
|
||||||
import { TRUST_LEVEL_COLORS, type TrustLevel, type UserConnectionWithProfile, type EdgeMetadata } from '../../lib/networking/types';
|
import { TRUST_LEVEL_COLORS, type TrustLevel, type UserConnectionWithProfile, type EdgeMetadata } from '../../lib/networking/types';
|
||||||
|
|
||||||
|
|
@ -26,6 +27,7 @@ const CryptIDDropdown: React.FC<CryptIDDropdownProps> = ({ isDarkMode = false })
|
||||||
const [showGoogleBrowser, setShowGoogleBrowser] = useState(false);
|
const [showGoogleBrowser, setShowGoogleBrowser] = useState(false);
|
||||||
const [showObsidianModal, setShowObsidianModal] = useState(false);
|
const [showObsidianModal, setShowObsidianModal] = useState(false);
|
||||||
const [showMiroModal, setShowMiroModal] = useState(false);
|
const [showMiroModal, setShowMiroModal] = useState(false);
|
||||||
|
const [showWalletModal, setShowWalletModal] = useState(false);
|
||||||
const [obsidianVaultUrl, setObsidianVaultUrl] = useState('');
|
const [obsidianVaultUrl, setObsidianVaultUrl] = useState('');
|
||||||
const [googleConnected, setGoogleConnected] = useState(false);
|
const [googleConnected, setGoogleConnected] = useState(false);
|
||||||
const [googleLoading, setGoogleLoading] = useState(false);
|
const [googleLoading, setGoogleLoading] = useState(false);
|
||||||
|
|
@ -335,7 +337,7 @@ const CryptIDDropdown: React.FC<CryptIDDropdownProps> = ({ isDarkMode = false })
|
||||||
transition: 'background 0.15s',
|
transition: 'background 0.15s',
|
||||||
pointerEvents: 'all',
|
pointerEvents: 'all',
|
||||||
}}
|
}}
|
||||||
title={session.authed ? session.username : 'Sign in with CryptID'}
|
title={session.authed ? session.username : 'Sign in with enCryptID'}
|
||||||
>
|
>
|
||||||
{session.authed ? (
|
{session.authed ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -429,7 +431,7 @@ const CryptIDDropdown: React.FC<CryptIDDropdownProps> = ({ isDarkMode = false })
|
||||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
||||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
||||||
</svg>
|
</svg>
|
||||||
CryptID secured
|
enCryptID secured
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -812,6 +814,63 @@ const CryptIDDropdown: React.FC<CryptIDDropdownProps> = ({ isDarkMode = false })
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Web3 Wallet */}
|
||||||
|
<div style={{ padding: '6px 10px', borderTop: '1px solid var(--color-grid)' }}>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' }}>
|
||||||
|
<div style={{
|
||||||
|
width: '24px',
|
||||||
|
height: '24px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
background: 'linear-gradient(135deg, #627eea 0%, #3b82f6 100%)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: '12px',
|
||||||
|
}}>
|
||||||
|
👛
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div style={{ fontSize: '13px', fontWeight: 500, color: 'var(--color-text)' }}>
|
||||||
|
Web3 Wallet
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: '11px', color: 'var(--color-text-2)' }}>
|
||||||
|
Link wallet to enCryptID
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setShowWalletModal(true);
|
||||||
|
setShowDropdown(false);
|
||||||
|
}}
|
||||||
|
onPointerDown={(e) => e.stopPropagation()}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '6px 12px',
|
||||||
|
fontSize: '12px',
|
||||||
|
fontWeight: 600,
|
||||||
|
borderRadius: '4px',
|
||||||
|
border: 'none',
|
||||||
|
background: 'linear-gradient(135deg, #627eea 0%, #3b82f6 100%)',
|
||||||
|
color: 'white',
|
||||||
|
cursor: 'pointer',
|
||||||
|
pointerEvents: 'all',
|
||||||
|
transition: 'all 0.15s',
|
||||||
|
boxShadow: '0 2px 4px rgba(99, 102, 241, 0.3)',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.background = 'linear-gradient(135deg, #4f46e5 0%, #2563eb 100%)';
|
||||||
|
e.currentTarget.style.boxShadow = '0 4px 8px rgba(99, 102, 241, 0.4)';
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.background = 'linear-gradient(135deg, #627eea 0%, #3b82f6 100%)';
|
||||||
|
e.currentTarget.style.boxShadow = '0 2px 4px rgba(99, 102, 241, 0.3)';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Manage Wallets
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Miro Board Import - Coming Soon */}
|
{/* Miro Board Import - Coming Soon */}
|
||||||
<div style={{ padding: '6px 10px', borderTop: '1px solid var(--color-grid)', opacity: 0.6 }}>
|
<div style={{ padding: '6px 10px', borderTop: '1px solid var(--color-grid)', opacity: 0.6 }}>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' }}>
|
||||||
|
|
@ -1160,6 +1219,107 @@ const CryptIDDropdown: React.FC<CryptIDDropdownProps> = ({ isDarkMode = false })
|
||||||
document.body
|
document.body
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Web3 Wallet Modal */}
|
||||||
|
{showWalletModal && createPortal(
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'fixed',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
zIndex: 100001,
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
if (e.target === e.currentTarget) {
|
||||||
|
setShowWalletModal(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: isDarkMode ? '#1f1f1f' : '#ffffff',
|
||||||
|
borderRadius: '16px',
|
||||||
|
padding: '0',
|
||||||
|
width: '420px',
|
||||||
|
maxWidth: '95vw',
|
||||||
|
maxHeight: '90vh',
|
||||||
|
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.3)',
|
||||||
|
overflow: 'auto',
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<div style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
padding: '16px 20px',
|
||||||
|
borderBottom: `1px solid ${isDarkMode ? '#333' : '#e5e7eb'}`,
|
||||||
|
}}>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
||||||
|
<div style={{
|
||||||
|
width: '36px',
|
||||||
|
height: '36px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
background: 'linear-gradient(135deg, #627eea 0%, #3b82f6 100%)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: '18px',
|
||||||
|
}}>
|
||||||
|
👛
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 style={{
|
||||||
|
margin: 0,
|
||||||
|
fontSize: '16px',
|
||||||
|
fontWeight: 600,
|
||||||
|
color: isDarkMode ? '#f4f4f5' : '#1f2937',
|
||||||
|
}}>
|
||||||
|
Web3 Wallet
|
||||||
|
</h3>
|
||||||
|
<p style={{
|
||||||
|
margin: 0,
|
||||||
|
fontSize: '12px',
|
||||||
|
color: isDarkMode ? '#a1a1aa' : '#6b7280',
|
||||||
|
}}>
|
||||||
|
Connect & link to enCryptID
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowWalletModal(false)}
|
||||||
|
style={{
|
||||||
|
background: isDarkMode ? '#333' : '#f3f4f6',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: '50%',
|
||||||
|
width: '32px',
|
||||||
|
height: '32px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
color: isDarkMode ? '#a1a1aa' : '#6b7280',
|
||||||
|
fontSize: '18px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* WalletLinkPanel content */}
|
||||||
|
<WalletLinkPanel />
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
)}
|
||||||
|
|
||||||
{/* CryptID Sign In Modal - rendered via portal */}
|
{/* CryptID Sign In Modal - rendered via portal */}
|
||||||
{showCryptIDModal && createPortal(
|
{showCryptIDModal && createPortal(
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ export const Profile: React.FC<ProfileProps> = ({ onLogout, onOpenVaultBrowser }
|
||||||
return (
|
return (
|
||||||
<div className="profile-container">
|
<div className="profile-container">
|
||||||
<div className="profile-header">
|
<div className="profile-header">
|
||||||
<h3>CryptID: {session.username}</h3>
|
<h3>enCryptID: {session.username}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="profile-settings">
|
<div className="profile-settings">
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* Provides functionality for:
|
* Provides functionality for:
|
||||||
* - Connecting/disconnecting wallets
|
* - Connecting/disconnecting wallets
|
||||||
* - Linking wallets to CryptID accounts
|
* - Linking wallets to enCryptID accounts
|
||||||
* - Managing linked wallets
|
* - Managing linked wallets
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ function generateLinkMessage(
|
||||||
timestamp: string,
|
timestamp: string,
|
||||||
nonce: string
|
nonce: string
|
||||||
): string {
|
): string {
|
||||||
return `Link wallet to CryptID
|
return `Link wallet to enCryptID
|
||||||
|
|
||||||
Account: ${username}
|
Account: ${username}
|
||||||
Wallet: ${address}
|
Wallet: ${address}
|
||||||
|
|
@ -120,7 +120,7 @@ export function useWalletConnection() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// useWalletLink - Link wallet to CryptID
|
// useWalletLink - Link wallet to enCryptID
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
export function useWalletLink() {
|
export function useWalletLink() {
|
||||||
|
|
@ -136,7 +136,7 @@ export function useWalletLink() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!session.authed || !session.username) {
|
if (!session.authed || !session.username) {
|
||||||
return { success: false, error: 'Not authenticated with CryptID' };
|
return { success: false, error: 'Not authenticated with enCryptID' };
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsLinking(true);
|
setIsLinking(true);
|
||||||
|
|
@ -159,7 +159,7 @@ export function useWalletLink() {
|
||||||
// Get public key for auth header
|
// Get public key for auth header
|
||||||
const publicKey = crypto.getPublicKey(session.username);
|
const publicKey = crypto.getPublicKey(session.username);
|
||||||
if (!publicKey) {
|
if (!publicKey) {
|
||||||
throw new Error('Could not get CryptID public key');
|
throw new Error('Could not get enCryptID public key');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send to backend for verification
|
// Send to backend for verification
|
||||||
|
|
@ -227,7 +227,7 @@ export function useLinkedWallets() {
|
||||||
|
|
||||||
const publicKey = crypto.getPublicKey(session.username);
|
const publicKey = crypto.getPublicKey(session.username);
|
||||||
if (!publicKey) {
|
if (!publicKey) {
|
||||||
setError('Could not get CryptID public key');
|
setError('Could not get enCryptID public key');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export const LinkDevice: React.FC = () => {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
setStatus('success');
|
setStatus('success');
|
||||||
setCryptidUsername(result.cryptidUsername || '');
|
setCryptidUsername(result.cryptidUsername || '');
|
||||||
setMessage('This device has been linked to your CryptID account!');
|
setMessage('This device has been linked to your enCryptID account!');
|
||||||
|
|
||||||
// Set the session - user is now logged in
|
// Set the session - user is now logged in
|
||||||
if (result.cryptidUsername) {
|
if (result.cryptidUsername) {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export const TOUR_STEPS: TourStep[] = [
|
||||||
{
|
{
|
||||||
id: 'cryptid-login',
|
id: 'cryptid-login',
|
||||||
title: 'Encrypted Identity',
|
title: 'Encrypted Identity',
|
||||||
content: 'Sign in with CryptID for end-to-end encrypted sync across devices. Your password never leaves your browser - we use cryptographic keys instead.',
|
content: 'Sign in with enCryptID for end-to-end encrypted sync across devices. Your password never leaves your browser - we use cryptographic keys instead.',
|
||||||
targetSelector: '.cryptid-dropdown-trigger',
|
targetSelector: '.cryptid-dropdown-trigger',
|
||||||
fallbackPosition: { top: 60, left: window.innerWidth - 200 },
|
fallbackPosition: { top: 60, left: window.innerWidth - 200 },
|
||||||
placement: 'bottom-left',
|
placement: 'bottom-left',
|
||||||
|
|
|
||||||
|
|
@ -412,9 +412,9 @@ export function UserSettingsModal({ onClose, isDarkMode, onToggleDarkMode }: Use
|
||||||
|
|
||||||
<div className="settings-divider" />
|
<div className="settings-divider" />
|
||||||
|
|
||||||
{/* CryptID Account Section */}
|
{/* enCryptID Account Section */}
|
||||||
<h3 style={{ fontSize: '14px', fontWeight: '600', marginBottom: '12px', color: colors.text }}>
|
<h3 style={{ fontSize: '14px', fontWeight: '600', marginBottom: '12px', color: colors.text }}>
|
||||||
CryptID Account
|
enCryptID Account
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{session.authed && session.username ? (
|
{session.authed && session.username ? (
|
||||||
|
|
@ -433,7 +433,7 @@ export function UserSettingsModal({ onClose, isDarkMode, onToggleDarkMode }: Use
|
||||||
{session.username}
|
{session.username}
|
||||||
</span>
|
</span>
|
||||||
<p style={{ fontSize: '11px', color: colors.textMuted, marginTop: '2px' }}>
|
<p style={{ fontSize: '11px', color: colors.textMuted, marginTop: '2px' }}>
|
||||||
Your CryptID username - cryptographically secured
|
Your enCryptID username - cryptographically secured
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -541,7 +541,7 @@ export function UserSettingsModal({ onClose, isDarkMode, onToggleDarkMode }: Use
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<p style={{ fontSize: '12px', color: colors.warningText }}>
|
<p style={{ fontSize: '12px', color: colors.warningText }}>
|
||||||
Sign in to manage your CryptID account settings
|
Sign in to manage your enCryptID account settings
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ async function sendEmail(
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
from: env.CRYPTID_EMAIL_FROM || 'CryptID <noreply@jeffemmett.com>',
|
from: env.CRYPTID_EMAIL_FROM || 'enCryptID <noreply@jeffemmett.com>',
|
||||||
to: [to],
|
to: [to],
|
||||||
subject,
|
subject,
|
||||||
html: htmlContent,
|
html: htmlContent,
|
||||||
|
|
@ -115,7 +115,7 @@ export async function handleLinkEmail(
|
||||||
|
|
||||||
if (existingUser && existingUser.cryptid_username !== cryptidUsername) {
|
if (existingUser && existingUser.cryptid_username !== cryptidUsername) {
|
||||||
return new Response(JSON.stringify({
|
return new Response(JSON.stringify({
|
||||||
error: 'Email already linked to a different CryptID account'
|
error: 'Email already linked to a different enCryptID account'
|
||||||
}), {
|
}), {
|
||||||
status: 409,
|
status: 409,
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
|
@ -175,10 +175,10 @@ export async function handleLinkEmail(
|
||||||
const emailSent = await sendEmail(
|
const emailSent = await sendEmail(
|
||||||
env,
|
env,
|
||||||
email,
|
email,
|
||||||
'Verify your CryptID email',
|
'Verify your enCryptID email',
|
||||||
`
|
`
|
||||||
<h2>Verify your CryptID email</h2>
|
<h2>Verify your enCryptID email</h2>
|
||||||
<p>Click the link below to verify your email address for CryptID: <strong>${cryptidUsername}</strong></p>
|
<p>Click the link below to verify your email address for enCryptID: <strong>${cryptidUsername}</strong></p>
|
||||||
<p><a href="${verifyUrl}" style="display: inline-block; padding: 12px 24px; background: #4f46e5; color: white; text-decoration: none; border-radius: 6px;">Verify Email</a></p>
|
<p><a href="${verifyUrl}" style="display: inline-block; padding: 12px 24px; background: #4f46e5; color: white; text-decoration: none; border-radius: 6px;">Verify Email</a></p>
|
||||||
<p>Or copy this link: ${verifyUrl}</p>
|
<p>Or copy this link: ${verifyUrl}</p>
|
||||||
<p>This link expires in 24 hours.</p>
|
<p>This link expires in 24 hours.</p>
|
||||||
|
|
@ -310,7 +310,7 @@ export async function handleRequestDeviceLink(
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new Response(JSON.stringify({
|
return new Response(JSON.stringify({
|
||||||
error: 'No verified CryptID account found for this email'
|
error: 'No verified enCryptID account found for this email'
|
||||||
}), {
|
}), {
|
||||||
status: 404,
|
status: 404,
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
|
@ -351,10 +351,10 @@ export async function handleRequestDeviceLink(
|
||||||
const emailSent = await sendEmail(
|
const emailSent = await sendEmail(
|
||||||
env,
|
env,
|
||||||
email,
|
email,
|
||||||
'Link new device to your CryptID',
|
'Link new device to your enCryptID',
|
||||||
`
|
`
|
||||||
<h2>New Device Link Request</h2>
|
<h2>New Device Link Request</h2>
|
||||||
<p>Someone is trying to link a new device to your CryptID: <strong>${user.cryptid_username}</strong></p>
|
<p>Someone is trying to link a new device to your enCryptID: <strong>${user.cryptid_username}</strong></p>
|
||||||
<p><strong>Device:</strong> ${deviceName || 'New Device'}</p>
|
<p><strong>Device:</strong> ${deviceName || 'New Device'}</p>
|
||||||
<p>If this was you, click the button below to approve:</p>
|
<p>If this was you, click the button below to approve:</p>
|
||||||
<p><a href="${linkUrl}" style="display: inline-block; padding: 12px 24px; background: #4f46e5; color: white; text-decoration: none; border-radius: 6px;">Approve Device</a></p>
|
<p><a href="${linkUrl}" style="display: inline-block; padding: 12px 24px; background: #4f46e5; color: white; text-decoration: none; border-radius: 6px;">Approve Device</a></p>
|
||||||
|
|
@ -527,7 +527,7 @@ export async function handleSendBackupEmail(
|
||||||
const emailSent = await sendEmail(
|
const emailSent = await sendEmail(
|
||||||
env,
|
env,
|
||||||
email,
|
email,
|
||||||
`Set up CryptID "${username}" on another device`,
|
`Set up enCryptID "${username}" on another device`,
|
||||||
`
|
`
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
|
@ -549,13 +549,13 @@ export async function handleSendBackupEmail(
|
||||||
<body>
|
<body>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="icon">🔐</div>
|
<div class="icon">🔐</div>
|
||||||
<h1>Welcome to CryptID</h1>
|
<h1>Welcome to enCryptID</h1>
|
||||||
<p>Your passwordless account is ready!</p>
|
<p>Your passwordless account is ready!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>Hi <strong>${username}</strong>,</p>
|
<p>Hi <strong>${username}</strong>,</p>
|
||||||
|
|
||||||
<p>Your CryptID account has been created on your current device. To access your account from another device (like your phone), follow these steps:</p>
|
<p>Your enCryptID account has been created on your current device. To access your account from another device (like your phone), follow these steps:</p>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="step">
|
<div class="step">
|
||||||
|
|
@ -590,9 +590,9 @@ export async function handleSendBackupEmail(
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<p>This email was sent because you created a CryptID account and opted for multi-device backup.</p>
|
<p>This email was sent because you created an enCryptID account and opted for multi-device backup.</p>
|
||||||
<p>If you didn't request this, you can safely ignore this email.</p>
|
<p>If you didn't request this, you can safely ignore this email.</p>
|
||||||
<p style="margin-top: 16px;">CryptID by <a href="${appUrl}" style="color: #8b5cf6;">jeffemmett.com</a></p>
|
<p style="margin-top: 16px;">enCryptID by <a href="${appUrl}" style="color: #8b5cf6;">jeffemmett.com</a></p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export function generateLinkMessage(
|
||||||
timestamp: string,
|
timestamp: string,
|
||||||
nonce: string
|
nonce: string
|
||||||
): string {
|
): string {
|
||||||
return `Link wallet to CryptID
|
return `Link wallet to enCryptID
|
||||||
|
|
||||||
Account: ${username}
|
Account: ${username}
|
||||||
Wallet: ${address}
|
Wallet: ${address}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue