Generalize podcast-specific UI for study-only users

Other people using this app just want to do personal Bible study, not
produce a podcast episode, so the always-visible chrome (chunk
metadata, DOCX import, exports) now says "Session" instead of
"Episode" and marks podcast-only fields as optional. The "Prepare for
Podcast" prompt no longer hardcodes "Verse by Verse with Nate" for
every user — it now pulls from a new "Podcast / show name" field in
Account Settings, so it still works exactly as before once you set
yours, but produces a sensible generic prompt for anyone who hasn't.
Also added an explanatory blurb to the session-list import page and
loosened its docx parser to accept "Session N — Title" in addition to
"Ep. N — Title".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
nmemmert
2026-07-06 09:25:28 -04:00
parent 89e701bbda
commit 2735ef216c
4 changed files with 139 additions and 44 deletions
+19 -5
View File
@@ -7,7 +7,7 @@ import { dirname, join } from 'path';
import {
initDb, getAllProjects, getProject, upsertProject, deleteProject,
countUsers, createUser, getUserByEmail, getUserById, claimOrphanProjects,
enableTotp, disableTotp, setBackupCodeHashes,
enableTotp, disableTotp, setBackupCodeHashes, setPodcastName,
} from './db.js';
import { SqliteSessionStore } from './sessionStore.js';
import {
@@ -80,7 +80,7 @@ app.post('/api/auth/register', async (req, res) => {
req.session.regenerate((err) => {
if (err) return res.status(500).json({ error: 'Could not create session.' });
req.session.userId = user.id;
res.json({ id: user.id, email: user.email, totpEnabled: false });
res.json({ id: user.id, email: user.email, totpEnabled: false, podcastName: null });
});
} catch (err) {
console.error('POST /api/auth/register error:', err);
@@ -108,7 +108,7 @@ app.post('/api/auth/login', async (req, res) => {
return res.json({ mfaRequired: true });
}
req.session.userId = user.id;
res.json({ id: user.id, email: user.email, totpEnabled: false });
res.json({ id: user.id, email: user.email, totpEnabled: false, podcastName: user.podcastName ?? null });
});
} catch (err) {
console.error('POST /api/auth/login error:', err);
@@ -146,7 +146,7 @@ app.post('/api/auth/mfa/verify', async (req, res) => {
req.session.regenerate((err) => {
if (err) return res.status(500).json({ error: 'Could not create session.' });
req.session.userId = user.id;
res.json({ id: user.id, email: user.email, totpEnabled: true });
res.json({ id: user.id, email: user.email, totpEnabled: true, podcastName: user.podcastName ?? null });
});
} catch (err) {
console.error('POST /api/auth/mfa/verify error:', err);
@@ -164,7 +164,21 @@ app.post('/api/auth/logout', (req, res) => {
app.get('/api/auth/me', (req, res) => {
const user = req.session?.userId ? getUserById(req.session.userId) : null;
if (!user) return res.status(401).json({ error: 'Not signed in.' });
res.json({ id: user.id, email: user.email, totpEnabled: user.totpEnabled });
res.json({ id: user.id, email: user.email, totpEnabled: user.totpEnabled, podcastName: user.podcastName ?? null });
});
// ---------------------------------------------------------------------------
// PATCH /api/auth/profile — update account-level settings (currently just podcastName)
// ---------------------------------------------------------------------------
app.patch('/api/auth/profile', requireAuth, (req, res) => {
try {
const podcastName = String(req.body?.podcastName ?? '').trim().slice(0, 200);
setPodcastName(req.session.userId, podcastName || null);
res.json({ podcastName: podcastName || null });
} catch (err) {
console.error('PATCH /api/auth/profile error:', err);
res.status(500).json({ error: 'Failed to save profile.' });
}
});
// ---------------------------------------------------------------------------