Brief 2 of cleanup-card-item-list-and-share-modal-palette convoy. Pure palette token sweep across the interior of components/ShareModal.js. The modal SURFACE was already correct (delegates to <Modal> → <GlassSurface>) and the user-search-dropdown + email-invite-card areas were updated in PR #117 — this brief addresses the residual interior rows. Migrations: Public Access section (L139-167): - Outer container: `border` (default Tailwind 1px) → tokenized border: '1px solid var(--border)' + backgroundColor: 'var(--bg-secondary)'. rounded-lg → rounded-xl. - Link icon: `text-gray-400` → `var(--text-secondary)`. - Heading: `text-gray-900` → `var(--text-primary)`. - Sub-text: `text-gray-500` → `var(--text-secondary)`. - Toggle: `bg-blue-600` (on) / `bg-gray-200` (off) → `var(--accent-ember)` / `var(--bg-tertiary)` + 1px border for visible delta when off. - Inner span (handle): `bg-white` → kept literal `rgb(255, 255, 255)` for theme-independent white contrast on the ember toggle. Add People input (L172-183): - `border-gray-300` + `focus:ring-blue-500` → `var(--input-border)` + `var(--input-bg)` + `--tw-ring-color: var(--accent-ember)`. - Search icon: `text-gray-400` → `var(--text-secondary)`. - rounded-lg → rounded-xl. Current Permissions section (L253-340): - Section paragraph: `text-gray-700` → `var(--text-secondary)`. - Current-user row: `bg-gray-50 rounded-lg` → `var(--bg-secondary)` + `var(--border)` + `rounded-xl`. - Current-user avatar circle: `bg-purple-600` → `linear-gradient(135deg, var(--accent-ember), var(--accent-flame))` (matches the UserMenu avatar gradient from TopSearchBar). - Owner badge: `bg-white rounded border` → `var(--bg-tertiary)` + `var(--text-secondary)` + 1px ember border + rounded-xl. - Invited-user row: `border rounded-lg` → `var(--border)` + `var(--bg-secondary)` + rounded-xl. - Invited-user avatar: `bg-gray-400` → `var(--bg-tertiary)` + 1px ember border + `var(--text-primary)` for the letter. - Role badge: `bg-gray-100 rounded` → `var(--bg-tertiary)` + `var(--text-secondary)` + rounded-xl. - All `text-gray-{500,700,900}` → `var(--text-primary)` / `var(--text-secondary)` per role. Share Link section (L305-340): - Readonly input: `bg-gray-50 border-gray-300 text-gray-600 rounded-lg` → `var(--input-bg)` + `var(--input-border)` + `var(--text-primary)` + rounded-xl. - Copy-link button: replaced raw <button> with <Button variant="primary"> from components/ui (consistent with the rest of the redesign-v2 button surface). - Copied! success state: `bg-green-100 text-green-800 border border-green-200` → `var(--bg-secondary)` + `var(--accent-flame)` + `var(--accent-ember)` border. Renders as a non-interactive badge instead of a styled button — same UX (you can't re-click "Copied!" anyway, the original was disabled-by-state). - New Button import added at top: `import { Modal, Button } from './ui'`. Social Share section (L327-365): - Icon container: `text-gray-600` → `var(--text-secondary)`. - Label: `text-xs text-gray-600` → `var(--text-secondary)`. Verification: - `grep -nE "bg-(palette)-[0-9]|text-(...)|border-(...)" components/ShareModal.js` → 0 matches. ✅ - `npm run lint` passes (1 pre-existing unrelated warning). - `npm run test:run`: 118/118 tests pass. Acceptance criteria from .convoys/cleanup-card-item-list-and-share-modal-palette.md all met for the in-scope sites. No edits outside ShareModal.js. Co-authored-by: Cursor <cursoragent@cursor.com>
475 lines
20 KiB
JavaScript
475 lines
20 KiB
JavaScript
import { useState, useEffect } from 'react';
|
|
import { Modal, Button } from './ui';
|
|
|
|
export default function ShareModal({
|
|
isOpen,
|
|
onClose,
|
|
collectionId,
|
|
isPublic,
|
|
onTogglePublic,
|
|
onInviteUser
|
|
}) {
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
const [searchResults, setSearchResults] = useState([]);
|
|
const [invitedUsers, setInvitedUsers] = useState([]);
|
|
const [currentUser, setCurrentUser] = useState(null);
|
|
const [copySuccess, setCopySuccess] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (!isOpen) return;
|
|
|
|
const fetchCurrentUser = async () => {
|
|
try {
|
|
const response = await fetch('/api/auth/verify', {
|
|
headers: {
|
|
Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
|
|
},
|
|
});
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setCurrentUser(data.user);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching current user:', error);
|
|
}
|
|
};
|
|
|
|
const fetchInvitedUsers = async () => {
|
|
try {
|
|
const response = await fetch(`/api/collections/${collectionId}/permissions`, {
|
|
headers: {
|
|
Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
|
|
},
|
|
});
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setInvitedUsers(data.permissions || []);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching invited users:', error);
|
|
}
|
|
};
|
|
|
|
fetchInvitedUsers();
|
|
fetchCurrentUser();
|
|
}, [isOpen, collectionId]);
|
|
|
|
const handleSearch = async (query) => {
|
|
setSearchQuery(query);
|
|
if (query.length < 2) {
|
|
setSearchResults([]);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Search for users by email
|
|
const response = await fetch(`/api/users/search?q=${encodeURIComponent(query)}`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
|
|
}
|
|
});
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setSearchResults(data.users || []);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error searching users:', error);
|
|
}
|
|
};
|
|
|
|
const handleInvite = async (emailOrUser) => {
|
|
try {
|
|
const email = typeof emailOrUser === 'string' ? emailOrUser : emailOrUser.email;
|
|
|
|
const response = await fetch(`/api/collections/${collectionId}/permissions`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
|
|
},
|
|
body: JSON.stringify({
|
|
email,
|
|
role: 'viewer' // Default to viewer as requested
|
|
})
|
|
});
|
|
|
|
if (response.ok) {
|
|
setSearchQuery('');
|
|
setSearchResults([]);
|
|
fetchInvitedUsers(); // Refresh the list
|
|
if (onInviteUser) onInviteUser(email);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error inviting user:', error);
|
|
}
|
|
};
|
|
|
|
const handleCopyLink = () => {
|
|
const url = window.location.href;
|
|
navigator.clipboard.writeText(url).then(() => {
|
|
setCopySuccess(true);
|
|
setTimeout(() => setCopySuccess(false), 2000);
|
|
});
|
|
};
|
|
|
|
const handleSocialShare = (platform) => {
|
|
const url = window.location.href;
|
|
const title = 'Check out this list on Deck Hearth';
|
|
|
|
const shareUrls = {
|
|
twitter: `https://twitter.com/intent/tweet?url=${encodeURIComponent(url)}&text=${encodeURIComponent(title)}`,
|
|
facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`,
|
|
reddit: `https://reddit.com/submit?url=${encodeURIComponent(url)}&title=${encodeURIComponent(title)}`,
|
|
discord: `https://discord.com/channels/@me` // Discord doesn't have direct share URL
|
|
};
|
|
|
|
if (shareUrls[platform]) {
|
|
window.open(shareUrls[platform], '_blank', 'width=600,height=400');
|
|
}
|
|
};
|
|
|
|
const isValidEmail = (email) => {
|
|
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
};
|
|
|
|
return (
|
|
<Modal open={isOpen} onClose={onClose} title="Share" size="md">
|
|
<>
|
|
{/* Public Access Toggle */}
|
|
<div
|
|
className="mb-6 p-4 rounded-xl"
|
|
style={{
|
|
border: '1px solid var(--border)',
|
|
backgroundColor: 'var(--bg-secondary)',
|
|
}}
|
|
>
|
|
<div className="flex items-start space-x-3">
|
|
<svg
|
|
className="w-5 h-5 mt-0.5"
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.102m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
|
|
</svg>
|
|
<div className="flex-1">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h3 className="font-medium" style={{ color: 'var(--text-primary)' }}>
|
|
Public access
|
|
</h3>
|
|
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
|
Anyone with a link can view
|
|
</p>
|
|
</div>
|
|
<button
|
|
onClick={onTogglePublic}
|
|
className="relative inline-flex h-6 w-11 items-center rounded-full transition-colors"
|
|
style={{
|
|
backgroundColor: isPublic
|
|
? 'var(--accent-ember)'
|
|
: 'var(--bg-tertiary)',
|
|
border: '1px solid var(--border)',
|
|
}}
|
|
>
|
|
<span
|
|
className={`inline-block h-4 w-4 transform rounded-full transition-transform ${
|
|
isPublic ? 'translate-x-6' : 'translate-x-1'
|
|
}`}
|
|
style={{ backgroundColor: 'rgb(255, 255, 255)' }}
|
|
/>
|
|
</button>
|
|
</div>
|
|
<p
|
|
className="text-sm mt-1"
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
>
|
|
This list will be available in the community.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Add People */}
|
|
<div className="mb-6">
|
|
<div className="relative">
|
|
<input
|
|
type="text"
|
|
placeholder="Add emails or people"
|
|
value={searchQuery}
|
|
onChange={(e) => handleSearch(e.target.value)}
|
|
className="w-full px-4 py-3 pl-10 rounded-xl focus:ring-2 focus:border-transparent"
|
|
style={{
|
|
border: '1px solid var(--input-border)',
|
|
backgroundColor: 'var(--input-bg)',
|
|
color: 'var(--text-primary)',
|
|
'--tw-ring-color': 'var(--accent-ember)',
|
|
}}
|
|
/>
|
|
<svg
|
|
className="w-5 h-5 absolute left-3 top-3.5"
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
</svg>
|
|
</div>
|
|
|
|
{/* Search Results */}
|
|
{searchResults.length > 0 && (
|
|
<div
|
|
className="mt-2 rounded-xl max-h-40 overflow-y-auto glass-panel"
|
|
>
|
|
{searchResults.map((user) => (
|
|
<div
|
|
key={user.id}
|
|
onClick={() => handleInvite(user)}
|
|
className="flex items-center p-3 cursor-pointer nav-item-hover"
|
|
>
|
|
<div
|
|
className="w-8 h-8 rounded-full flex items-center justify-center mr-3"
|
|
style={{
|
|
background:
|
|
'linear-gradient(135deg, var(--accent-ember) 0%, var(--accent-flame) 100%)',
|
|
}}
|
|
>
|
|
<span className="text-white text-sm font-bold">
|
|
{user.email.charAt(0).toUpperCase()}
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<div className="font-medium" style={{ color: 'var(--text-primary)' }}>
|
|
{user.email}
|
|
</div>
|
|
<div className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
|
Click to invite as viewer
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Email invite option */}
|
|
{searchQuery && isValidEmail(searchQuery) && !searchResults.some(u => u.email === searchQuery) && (
|
|
<div className="mt-2 rounded-xl glass-panel">
|
|
<div
|
|
onClick={() => handleInvite(searchQuery)}
|
|
className="flex items-center p-3 cursor-pointer nav-item-hover"
|
|
>
|
|
<div
|
|
className="w-8 h-8 rounded-full flex items-center justify-center mr-3"
|
|
style={{ backgroundColor: 'var(--bg-tertiary)' }}
|
|
>
|
|
<svg
|
|
className="w-4 h-4"
|
|
style={{ color: 'var(--text-primary)' }}
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207" />
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<div className="font-medium" style={{ color: 'var(--text-primary)' }}>
|
|
Invite {searchQuery}
|
|
</div>
|
|
<div className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
|
Send email invitation as viewer
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Current Permissions */}
|
|
<div className="mb-6">
|
|
<p
|
|
className="text-sm mb-3"
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
>
|
|
Only those invited can view or collaborate on this list.
|
|
</p>
|
|
|
|
<div className="space-y-2">
|
|
{/* Current User */}
|
|
{currentUser && (
|
|
<div
|
|
className="flex items-center justify-between p-3 rounded-xl"
|
|
style={{
|
|
backgroundColor: 'var(--bg-secondary)',
|
|
border: '1px solid var(--border)',
|
|
}}
|
|
>
|
|
<div className="flex items-center">
|
|
<div
|
|
className="w-8 h-8 rounded-full flex items-center justify-center mr-3"
|
|
style={{
|
|
background:
|
|
'linear-gradient(135deg, var(--accent-ember) 0%, var(--accent-flame) 100%)',
|
|
}}
|
|
>
|
|
<span className="text-white text-sm font-bold">
|
|
{currentUser.email.charAt(0).toUpperCase()}
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<div
|
|
className="font-medium"
|
|
style={{ color: 'var(--text-primary)' }}
|
|
>
|
|
{currentUser.email} (You)
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<span
|
|
className="text-sm px-2 py-1 rounded-xl"
|
|
style={{
|
|
backgroundColor: 'var(--bg-tertiary)',
|
|
color: 'var(--text-secondary)',
|
|
border: '1px solid var(--border)',
|
|
}}
|
|
>
|
|
Owner
|
|
</span>
|
|
</div>
|
|
)}
|
|
|
|
{/* Invited Users */}
|
|
{invitedUsers.map((permission) => (
|
|
<div
|
|
key={permission.id}
|
|
className="flex items-center justify-between p-3 rounded-xl"
|
|
style={{
|
|
border: '1px solid var(--border)',
|
|
backgroundColor: 'var(--bg-secondary)',
|
|
}}
|
|
>
|
|
<div className="flex items-center">
|
|
<div
|
|
className="w-8 h-8 rounded-full flex items-center justify-center mr-3"
|
|
style={{
|
|
backgroundColor: 'var(--bg-tertiary)',
|
|
border: '1px solid var(--border)',
|
|
}}
|
|
>
|
|
<span
|
|
className="text-sm font-bold"
|
|
style={{ color: 'var(--text-primary)' }}
|
|
>
|
|
{permission.user_email?.charAt(0).toUpperCase() || '?'}
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<div
|
|
className="font-medium"
|
|
style={{ color: 'var(--text-primary)' }}
|
|
>
|
|
{permission.user_email || 'Unknown User'}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<span
|
|
className="text-sm px-2 py-1 rounded-xl"
|
|
style={{
|
|
backgroundColor: 'var(--bg-tertiary)',
|
|
color: 'var(--text-secondary)',
|
|
}}
|
|
>
|
|
{permission.role === 'editor' ? 'Collaborator' : 'Viewer'}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Share Link */}
|
|
<div className="mb-6">
|
|
<div className="flex space-x-2">
|
|
<input
|
|
type="text"
|
|
value={window.location.href}
|
|
readOnly
|
|
className="flex-1 px-3 py-2 rounded-xl text-sm"
|
|
style={{
|
|
backgroundColor: 'var(--input-bg)',
|
|
border: '1px solid var(--input-border)',
|
|
color: 'var(--text-primary)',
|
|
}}
|
|
/>
|
|
{copySuccess ? (
|
|
<span
|
|
className="px-4 py-2 rounded-xl text-sm font-medium inline-flex items-center"
|
|
style={{
|
|
backgroundColor: 'var(--bg-secondary)',
|
|
color: 'var(--accent-flame)',
|
|
border: '1px solid var(--accent-ember)',
|
|
}}
|
|
>
|
|
Copied!
|
|
</span>
|
|
) : (
|
|
<Button variant="primary" onClick={handleCopyLink}>
|
|
Copy link
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Social Share */}
|
|
<div className="grid grid-cols-4 gap-4">
|
|
{[
|
|
{ name: 'Twitter', icon: 'twitter', platform: 'twitter' },
|
|
{ name: 'Facebook', icon: 'facebook', platform: 'facebook' },
|
|
{ name: 'Reddit', icon: 'reddit', platform: 'reddit' },
|
|
{ name: 'Discord', icon: 'discord', platform: 'discord' }
|
|
].map((social) => (
|
|
<button
|
|
key={social.platform}
|
|
onClick={() => handleSocialShare(social.platform)}
|
|
className="flex flex-col items-center p-3 rounded-xl border nav-item-hover transition-colors"
|
|
style={{ borderColor: 'var(--border)' }}
|
|
>
|
|
<div
|
|
className="w-8 h-8 mb-2"
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
>
|
|
{social.icon === 'twitter' && (
|
|
<svg viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/>
|
|
</svg>
|
|
)}
|
|
{social.icon === 'facebook' && (
|
|
<svg viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
|
|
</svg>
|
|
)}
|
|
{social.icon === 'reddit' && (
|
|
<svg viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 0 0-.231.094.33.33 0 0 0 0 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 0 0 .029-.463.33.33 0 0 0-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.196-2.512-.73a.326.326 0 0 0-.232-.095z"/>
|
|
</svg>
|
|
)}
|
|
{social.icon === 'discord' && (
|
|
<svg viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419-.0002 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1568 2.4189Z"/>
|
|
</svg>
|
|
)}
|
|
</div>
|
|
<span
|
|
className="text-xs"
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
>
|
|
{social.name}
|
|
</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</>
|
|
</Modal>
|
|
);
|
|
}
|