// ShareView Component - Complete sharing & collaboration interface // Tabs: My Shares | Shared with Me | Groups function ShareView({ token, t, setActiveTab, setActionParams }) { const [activeShareTab, setActiveShareTab] = useState('shared-with-me'); const [myShares, setMyShares] = useState([]); const [sharedWithMe, setSharedWithMe] = useState([]); const [groups, setGroups] = useState([]); const [invitations, setInvitations] = useState([]); const [loading, setLoading] = useState(true); const [showShareModal, setShowShareModal] = useState(false); const [showGroupModal, setShowGroupModal] = useState(false); const [showInviteModal, setShowInviteModal] = useState(false); const [showViewNoteModal, setShowViewNoteModal] = useState(false); const [viewingNote, setViewingNote] = useState(null); const [viewingNoteDetails, setViewingNoteDetails] = useState(null); const [loadingNoteDetails, setLoadingNoteDetails] = useState(false); const [selectedNote, setSelectedNote] = useState(null); const [selectedGroup, setSelectedGroup] = useState(null); const [friends, setFriends] = useState([]); const [selectedFriends, setSelectedFriends] = useState([]); const [sharePermission, setSharePermission] = useState('view'); // Multi-step share modal states const [shareModalStep, setShareModalStep] = useState(1); // 1: select notes, 2: select friends const [allNotes, setAllNotes] = useState([]); const [selectedNotes, setSelectedNotes] = useState([]); const [noteSearchQuery, setNoteSearchQuery] = useState(''); const [loadingNotes, setLoadingNotes] = useState(false); const [sharingInProgress, setSharingInProgress] = useState(false); useEffect(() => { fetchData(); loadFriends(); }, [activeShareTab]); const fetchData = async () => { setLoading(true); try { if (activeShareTab === 'my-shares') { // Fetch only notes that user has shared with others const response = await fetch(API_URL + '/notes/my-shared-notes', { headers: { 'Authorization': `Bearer ${token}` } }); const data = await response.json(); setMyShares(data.notes || []); } else if (activeShareTab === 'shared-with-me') { const response = await fetch(API_URL + '/notes/shared-with-me', { headers: { 'Authorization': `Bearer ${token}` } }); const data = await response.json(); setSharedWithMe(data.notes || []); // Backend returns "notes", not "shared_notes" } else if (activeShareTab === 'groups') { // Fetch groups const [groupsRes, invitesRes] = await Promise.all([ fetch(API_URL + '/groups', { headers: { 'Authorization': `Bearer ${token}` } }), fetch(API_URL + '/invitations', { headers: { 'Authorization': `Bearer ${token}` } }) ]); const groupsData = await groupsRes.json(); const invitesData = await invitesRes.json(); setGroups(groupsData.groups || []); setInvitations(invitesData.invitations || []); } } catch (error) { console.error('Error fetching share data:', error); } finally { setLoading(false); } }; const loadFriends = async () => { try { const response = await fetch(API_URL + '/connections/my-friends', { headers: { 'Authorization': `Bearer ${token}` } }); const data = await response.json(); setFriends(data.friends || []); } catch (err) { console.error('Failed to load friends:', err); } }; // Share modal functions const openShareModal = async () => { setShowShareModal(true); setShareModalStep(1); setSelectedNotes([]); setSelectedFriends([]); setNoteSearchQuery(''); setSharePermission('view'); // Load all notes and friends setLoadingNotes(true); try { const response = await fetch(API_URL + '/notes', { headers: { 'Authorization': `Bearer ${token}` } }); const data = await response.json(); setAllNotes(data.notes || []); } catch (err) { console.error('Failed to load notes:', err); alert('Failed to load notes'); } finally { setLoadingNotes(false); } // Also load friends for step 2 await loadFriends(); }; const toggleNoteSelection = (noteId) => { setSelectedNotes(prev => prev.includes(noteId) ? prev.filter(id => id !== noteId) : [...prev, noteId] ); }; const toggleFriendSelection = (friendId) => { setSelectedFriends(prev => prev.includes(friendId) ? prev.filter(id => id !== friendId) : [...prev, friendId] ); }; const submitShare = async () => { if (selectedNotes.length === 0 || selectedFriends.length === 0) { alert('Please select at least one note and one friend'); return; } setSharingInProgress(true); try { // Share each selected note with all selected friends let successCount = 0; for (const noteId of selectedNotes) { const response = await fetch(API_URL + `/notes/${noteId}/share-with-users`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ user_ids: selectedFriends, permission_level: sharePermission }) }); if (response.ok) successCount++; } alert(`Successfully shared ${successCount} note(s) with ${selectedFriends.length} friend(s)!`); setShowShareModal(false); fetchData(); // Refresh the shared notes list } catch (err) { console.error('Share error:', err); alert('Failed to share notes. Please try again.'); } finally { setSharingInProgress(false); } }; // Filter notes based on search query const filteredNotes = allNotes.filter(note => note.title?.toLowerCase().includes(noteSearchQuery.toLowerCase()) || note.subject?.toLowerCase().includes(noteSearchQuery.toLowerCase()) || note.tags?.some(tag => tag.toLowerCase().includes(noteSearchQuery.toLowerCase())) ); const shareNote = async (noteId, permissionLevel) => { try { const response = await fetch(API_URL + `/notes/${noteId}/share`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ permission_level: permissionLevel, is_public: true, expires_in_days: null }) }); const data = await response.json(); // Copy link to clipboard const shareUrl = `${window.location.origin}${data.share_url}`; navigator.clipboard.writeText(shareUrl); alert('Share link copied to clipboard!'); setShowShareModal(false); } catch (error) { console.error('Error creating share link:', error); alert('Failed to create share link'); } }; const fetchNoteDetails = async (noteId) => { try { setLoadingNoteDetails(true); const response = await fetch(API_URL + `/notes/${noteId}/shared-view`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) { throw new Error('Failed to fetch note details'); } const data = await response.json(); console.log('Fetched note details:', data); setViewingNoteDetails(data); setViewingNote(data.note); // Keep for compatibility with existing modal setShowViewNoteModal(true); } catch (error) { console.error('Error fetching note details:', error); alert('❌ Failed to open note. Please try again.'); } finally { setLoadingNoteDetails(false); } }; const copyNote = async (noteId) => { try { setLoading(true); const response = await fetch(API_URL + `/notes/${noteId}/copy-to-my-notes`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) { throw new Error('Failed to copy note'); } const data = await response.json(); // Close modal if open setShowViewNoteModal(false); setViewingNote(null); setViewingNoteDetails(null); alert(`✅ Note copied successfully! ${data.blocks_copied} content blocks copied.`); // Navigate to notes tab to see the copied note setActiveTab('notes'); } catch (error) { console.error('Error copying note:', error); alert('❌ Failed to copy note. Please try again.'); } finally { setLoading(false); } }; const shareWithFriends = async () => { if (selectedFriends.length === 0) { alert('Please select at least one friend to share with'); return; } try { const response = await fetch(API_URL + `/notes/${selectedNote.id}/share-with-users`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ user_ids: selectedFriends, permission_level: sharePermission }) }); if (response.ok) { alert(`Note shared with ${selectedFriends.length} friend(s)!`); setShowShareModal(false); setSelectedFriends([]); setSharePermission('view'); } else { alert('Failed to share note'); } } catch (error) { console.error('Error sharing with friends:', error); alert('Failed to share note'); } }; const acceptInvitation = async (invitationId) => { try { const response = await fetch(API_URL + `/invitations/${invitationId}/accept`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }); alert('Invitation accepted!'); fetchData(); } catch (error) { console.error('Error accepting invitation:', error); alert('Failed to accept invitation'); } }; const declineInvitation = async (invitationId) => { try { await fetch(API_URL + `/invitations/${invitationId}/decline`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }); fetchData(); } catch (error) { console.error('Error declining invitation:', error); } }; const createGroup = async (e) => { e.preventDefault(); const formData = new FormData(e.target); const groupData = { name: formData.get('groupName'), description: formData.get('groupDescription'), is_private: formData.get('isPrivate') === 'on' }; try { const response = await fetch(API_URL + '/groups/create', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(groupData) }); const data = await response.json(); alert(`Group created! Join code: ${data.join_code}`); setShowGroupModal(false); fetchData(); } catch (error) { console.error('Error creating group:', error); alert('Failed to create group'); } }; const inviteMembers = async (e) => { e.preventDefault(); const formData = new FormData(e.target); const emailsText = formData.get('emails'); const emails = emailsText.split(',').map(e => e.trim()).filter(e => e); if (emails.length === 0) { alert('Please enter at least one email address'); return; } try { const response = await fetch(API_URL + `/groups/${selectedGroup.id}/invite`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ emails }) }); const data = await response.json(); alert(data.message); setShowInviteModal(false); setSelectedGroup(null); } catch (error) { console.error('Error inviting members:', error); alert('Failed to send invitations'); } }; if (loading) { return
Loading...
; } return (

Share Notes

{activeShareTab === 'groups' && ( )}
{/* Tab Navigation */}
{['shared-with-me', 'my-shares', 'groups', 'find-people'].map(tab => ( ))}
{/* Share Tab - Sharing Activity */} {activeShareTab === 'my-shares' && (
{/* Share New Notes Button */}

My Sharing Activity

{myShares.length === 0 ? (
📤

Start Sharing Your Knowledge

Share your notes with friends to collaborate and exchange ideas. Click the button above to get started!

) : (
{myShares.map(note => (

📝 {note.title}

{note.subject && ( {note.subject} )}
{/* Sharing stats */}
👥 Shared with {note.share_count || 0} {(note.share_count || 0) === 1 ? 'person' : 'people'}
📦 {note.block_count || 0} content blocks
{note.created_at && (
📅 Created {new Date(note.created_at).toLocaleDateString()}
)}
))}
)}
)} {/* Shared with Me Tab */} {activeShareTab === 'shared-with-me' && (
{sharedWithMe.length === 0 ? (
📥

No shared notes

Notes shared with you by others will appear here

) : (
{sharedWithMe.map(note => (
👤 {note.owner_name || note.owner_username || 'Someone'}

{note.title}

{note.description || 'No description'}

{note.group_name && (
📁 {note.group_name}
)}
))}
)}
)} {/* Groups Tab */} {activeShareTab === 'groups' && (
{/* Pending Invitations */} {invitations.length > 0 && (

Pending Invitations ({invitations.length})

{invitations.map(inv => (
{inv.group_name} invited by {inv.invited_by_name}
))}
)} {/* Groups List */} {groups.length === 0 && invitations.length === 0 ? (
👥

No groups yet

Create a group to collaborate with others

) : (
{groups.map(group => (

{group.name}

{group.role}

{group.description || 'No description'}

👥 {group.member_count} member{group.member_count !== 1 ? 's' : ''}
{(group.role === 'owner' || group.role === 'admin') && ( )}
))}
)}
)} {/* Find People Tab */} {activeShareTab === 'find-people' && ( )} {/* Share Modal */} {showShareModal && selectedNote && (

Share: {selectedNote.title}

Generate a shareable link for this note

)} {/* Group Creation Modal */} {showGroupModal && (

Create New Group