stuff
This commit is contained in:
+136
-21
@@ -1227,6 +1227,8 @@ export function ColossiansStudySectionPage({ content }: Props) {
|
|||||||
const [noteLoading, setNoteLoading] = useState(false)
|
const [noteLoading, setNoteLoading] = useState(false)
|
||||||
const [noteSaving, setNoteSaving] = useState(false)
|
const [noteSaving, setNoteSaving] = useState(false)
|
||||||
const [noteMessage, setNoteMessage] = useState('')
|
const [noteMessage, setNoteMessage] = useState('')
|
||||||
|
const [notesModalOpen, setNotesModalOpen] = useState(false)
|
||||||
|
const [contextNoteMenu, setContextNoteMenu] = useState<{ x: number; y: number; text: string } | null>(null)
|
||||||
const [completedSectionIds, setCompletedSectionIds] = useState<string[]>([])
|
const [completedSectionIds, setCompletedSectionIds] = useState<string[]>([])
|
||||||
const [progressLoading, setProgressLoading] = useState(false)
|
const [progressLoading, setProgressLoading] = useState(false)
|
||||||
const [progressSaving, setProgressSaving] = useState(false)
|
const [progressSaving, setProgressSaving] = useState(false)
|
||||||
@@ -1392,6 +1394,35 @@ export function ColossiansStudySectionPage({ content }: Props) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closeNotesModal() {
|
||||||
|
setNotesModalOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendSelectedTextToNotes(selectedText: string) {
|
||||||
|
if (!selectedText.trim()) return
|
||||||
|
setNoteText(prev => prev ? `${prev.trim()}\n\n${selectedText.trim()}` : selectedText.trim())
|
||||||
|
setNotesModalOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLessonContentContextMenu(e: React.MouseEvent<HTMLDivElement>) {
|
||||||
|
if (!auth.checked || !auth.authenticated || !isEnrolled || !study || !section) return
|
||||||
|
const selection = window.getSelection()
|
||||||
|
const selectedText = selection?.toString().trim() ?? ''
|
||||||
|
if (!selectedText) {
|
||||||
|
setContextNoteMenu(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.preventDefault()
|
||||||
|
setContextNoteMenu({ x: e.clientX + 4, y: e.clientY + 4, text: selectedText })
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!contextNoteMenu) return
|
||||||
|
const handleClick = () => setContextNoteMenu(null)
|
||||||
|
window.addEventListener('mousedown', handleClick)
|
||||||
|
return () => window.removeEventListener('mousedown', handleClick)
|
||||||
|
}, [contextNoteMenu])
|
||||||
|
|
||||||
async function markLessonComplete() {
|
async function markLessonComplete() {
|
||||||
if (!study || !section?.id || progressSaving) return
|
if (!study || !section?.id || progressSaving) return
|
||||||
setProgressSaving(true)
|
setProgressSaving(true)
|
||||||
@@ -1548,7 +1579,7 @@ export function ColossiansStudySectionPage({ content }: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="study-section-page" aria-label={section.title}>
|
<main className="study-section-page" aria-label={section.title}>
|
||||||
<section className="section-study-classroom">
|
<section className="section-study-classroom" onContextMenu={handleLessonContentContextMenu}>
|
||||||
<div className="section-inner study-classroom-shell">
|
<div className="section-inner study-classroom-shell">
|
||||||
<div className="study-classroom-main">
|
<div className="study-classroom-main">
|
||||||
<Link to={`/study/${study.slug}`} className="study-detail-back">Back to {study.title}</Link>
|
<Link to={`/study/${study.slug}`} className="study-detail-back">Back to {study.title}</Link>
|
||||||
@@ -1685,28 +1716,11 @@ export function ColossiansStudySectionPage({ content }: Props) {
|
|||||||
<div className="study-notes-box">
|
<div className="study-notes-box">
|
||||||
<p className="study-notes-user">Signed in as {auth.username}</p>
|
<p className="study-notes-user">Signed in as {auth.username}</p>
|
||||||
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap', marginBottom: '0.5rem' }}>
|
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap', marginBottom: '0.5rem' }}>
|
||||||
<Link to={`/study/${study.slug}/notes`} className="btn-secondary">Open My Notes</Link>
|
<button type="button" className="btn-primary" onClick={() => setNotesModalOpen(true)}>Open Notes</button>
|
||||||
<Link to="/study/account" className="btn-secondary">My Account</Link>
|
<Link to="/study/account" className="btn-secondary">My Account</Link>
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
<p style={{ margin: '0.5rem 0 0', color: '#b9b09b', fontSize: '0.9rem' }}>Highlight scripture, commentary, or Greek word text, right click, and add it to your notes.</p>
|
||||||
rows={8}
|
{noteLoading ? <p style={{ marginTop: '0.75rem' }}>Loading notes…</p> : null}
|
||||||
value={noteText}
|
|
||||||
onChange={e => setNoteText(e.target.value)}
|
|
||||||
placeholder="Write your lesson notes here..."
|
|
||||||
disabled={noteLoading || noteSaving}
|
|
||||||
/>
|
|
||||||
<div className="study-auth-actions" style={{ flexWrap: 'wrap', gap: '0.5rem' }}>
|
|
||||||
<button type="button" className="btn-primary" disabled={!canSaveNote} onClick={saveNote}>{noteSaving ? 'Saving...' : 'Save Notes'}</button>
|
|
||||||
<button type="button" className="btn-secondary" disabled={authBusy} onClick={logoutStudyUser}>Sign Out</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={lessonCompleted ? 'btn-secondary' : 'btn-primary'}
|
|
||||||
disabled={!isEnrolled || progressSaving || progressLoading}
|
|
||||||
onClick={lessonCompleted ? unmarkLessonCompletion : markLessonComplete}
|
|
||||||
>
|
|
||||||
{progressSaving ? 'Updating...' : lessonCompleted ? 'Mark Incomplete' : 'Mark Complete'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{(authMessage || noteMessage || progressMessage) && <p className="study-note-status">{authMessage || noteMessage || progressMessage}</p>}
|
{(authMessage || noteMessage || progressMessage) && <p className="study-note-status">{authMessage || noteMessage || progressMessage}</p>}
|
||||||
@@ -1726,6 +1740,107 @@ export function ColossiansStudySectionPage({ content }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
{contextNoteMenu && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'fixed',
|
||||||
|
left: contextNoteMenu.x,
|
||||||
|
top: contextNoteMenu.y,
|
||||||
|
zIndex: 50,
|
||||||
|
backgroundColor: '#1f1d19',
|
||||||
|
border: '1px solid #3f3b2f',
|
||||||
|
borderRadius: '12px',
|
||||||
|
padding: '0.75rem',
|
||||||
|
boxShadow: '0 10px 30px rgba(0,0,0,0.25)',
|
||||||
|
minWidth: '220px',
|
||||||
|
}}
|
||||||
|
onMouseDown={e => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<p style={{ margin: '0 0 0.75rem', color: '#ece3c6', fontSize: '0.95rem' }}>Add selected text to notes:</p>
|
||||||
|
<div style={{ marginBottom: '0.75rem', maxHeight: '100px', overflow: 'auto', color: '#f0ead8', fontSize: '0.9rem' }}>
|
||||||
|
{contextNoteMenu.text}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn-primary"
|
||||||
|
onClick={() => {
|
||||||
|
appendSelectedTextToNotes(contextNoteMenu.text)
|
||||||
|
setContextNoteMenu(null)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add to Notes
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{notesModalOpen && (
|
||||||
|
<div
|
||||||
|
className="study-modal-overlay"
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-label="Lesson notes editor"
|
||||||
|
onClick={closeNotesModal}
|
||||||
|
style={{
|
||||||
|
position: 'fixed',
|
||||||
|
inset: 0,
|
||||||
|
backgroundColor: 'rgba(12, 11, 10, 0.75)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
zIndex: 40,
|
||||||
|
padding: '1rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="study-modal"
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
style={{
|
||||||
|
width: 'min(760px, 100%)',
|
||||||
|
maxHeight: 'min(90vh, 900px)',
|
||||||
|
overflowY: 'auto',
|
||||||
|
backgroundColor: '#1b1a16',
|
||||||
|
border: '1px solid #3c3a31',
|
||||||
|
borderRadius: '18px',
|
||||||
|
padding: '1.5rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '1rem', marginBottom: '1rem' }}>
|
||||||
|
<div>
|
||||||
|
<h2 style={{ margin: 0 }}>Lesson Notes</h2>
|
||||||
|
<p style={{ margin: '0.5rem 0 0', color: '#b9b09b' }}>Save notes for this lesson and paste selections from the page.</p>
|
||||||
|
</div>
|
||||||
|
<button type="button" className="btn-secondary" onClick={closeNotesModal}>Close</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
rows={12}
|
||||||
|
value={noteText}
|
||||||
|
onChange={e => setNoteText(e.target.value)}
|
||||||
|
placeholder="Write your notes here..."
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
minHeight: '280px',
|
||||||
|
padding: '1rem',
|
||||||
|
borderRadius: '16px',
|
||||||
|
border: '1px solid #3f3b2f',
|
||||||
|
background: '#14130f',
|
||||||
|
color: '#f0ead8',
|
||||||
|
fontSize: '1rem',
|
||||||
|
lineHeight: '1.6',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', gap: '0.75rem', flexWrap: 'wrap', justifyContent: 'flex-end', marginTop: '1rem' }}>
|
||||||
|
<button type="button" className="btn-primary" onClick={saveNote} disabled={!canSaveNote}>
|
||||||
|
{noteSaving ? 'Saving...' : 'Save Notes'}
|
||||||
|
</button>
|
||||||
|
<button type="button" className="btn-secondary" onClick={closeNotesModal}>Cancel</button>
|
||||||
|
</div>
|
||||||
|
{(noteMessage || progressMessage) && <p className="study-note-status" style={{ marginTop: '1rem' }}>{noteMessage || progressMessage}</p>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
Reference in New Issue
Block a user