UNPKG

@churchapps/helpers

Version:

Library of helper functions not specific to any one ChurchApps project or framework.

237 lines 9.74 kB
import { ApiHelper } from "../ApiHelper.js"; export class LessonsContentProvider { providerId = "lessons"; lessonsUrl; constructor(lessonsUrl = "https://lessons.church") { this.lessonsUrl = lessonsUrl; } canHandle(plan, planItem) { // Handles: lessonAction, lessonAddOn, lessonSection, and items with relatedId when plan has lesson const lessonTypes = ["lessonAction", "lessonAddOn", "lessonSection"]; const hasLessonPlan = plan?.contentType === "venue" || plan?.contentType === "externalVenue"; if (lessonTypes.includes(planItem.itemType) && planItem.relatedId) return true; if (planItem.itemType === "item" && planItem.relatedId && hasLessonPlan) return true; return false; } async fetchContent(plan, planItems) { const result = new Map(); // Group by type for efficient batching const actions = planItems.filter(p => p.itemType === "lessonAction" && p.relatedId); const addOns = planItems.filter(p => p.itemType === "lessonAddOn" && p.relatedId); const sections = planItems.filter(p => (p.itemType === "lessonSection" || p.itemType === "item") && p.relatedId); const externalRef = this.getExternalRef(plan); // Build embed URLs for each item for (const item of actions) { result.set(item.id, { provider: this.providerId, embedUrl: externalRef ? `${this.lessonsUrl}/embed/external/${externalRef.externalProviderId}/action/${item.relatedId}` : `${this.lessonsUrl}/embed/action/${item.relatedId}` }); } for (const item of addOns) { result.set(item.id, { provider: this.providerId, embedUrl: externalRef ? `${this.lessonsUrl}/embed/external/${externalRef.externalProviderId}/addon/${item.relatedId}` : `${this.lessonsUrl}/embed/addon/${item.relatedId}` }); } for (const item of sections) { result.set(item.id, { provider: this.providerId, embedUrl: externalRef ? `${this.lessonsUrl}/embed/external/${externalRef.externalProviderId}/section/${item.relatedId}` : `${this.lessonsUrl}/embed/section/${item.relatedId}` }); } return result; } // ============================================ // Plan/Lesson Association Methods // ============================================ hasAssociatedLesson(plan) { return (plan?.contentType === "venue" || plan?.contentType === "externalVenue") && !!plan?.contentId; } isExternalVenue(plan) { return plan?.contentType === "externalVenue"; } getExternalRef(plan) { if (!this.isExternalVenue(plan) || !plan?.contentId) return null; try { return JSON.parse(plan.contentId); } catch { return null; } } getVenueId(plan) { if (!this.hasAssociatedLesson(plan)) return null; if (this.isExternalVenue(plan)) { return this.getExternalRef(plan)?.venueId || null; } return plan.contentId || null; } // ============================================ // API Fetch Methods // ============================================ /** * Fetch venue plan items - the basic hierarchical structure * Returns: headers with children (sections), but sections don't have their actions * Use this for preview mode display */ async fetchVenuePlanItems(plan) { if (!this.hasAssociatedLesson(plan)) return { items: [] }; const externalRef = this.getExternalRef(plan); if (externalRef) { return await ApiHelper.getAnonymous(`/externalProviders/${externalRef.externalProviderId}/venue/${externalRef.venueId}/planItems`, "LessonsApi"); } return await ApiHelper.getAnonymous(`/venues/public/planItems/${plan.contentId}`, "LessonsApi"); } /** * Fetch venue actions - sections with their full action lists * Use this for action selection dialogs and full expansion */ async fetchVenueActions(plan) { if (!this.hasAssociatedLesson(plan)) return { sections: [] }; const externalRef = this.getExternalRef(plan); if (externalRef) { return await ApiHelper.getAnonymous(`/externalProviders/${externalRef.externalProviderId}/venue/${externalRef.venueId}/actions`, "LessonsApi"); } return await ApiHelper.getAnonymous(`/venues/public/actions/${plan.contentId}`, "LessonsApi"); } /** * Fetch the full lesson tree for browsing (programs -> studies -> lessons -> venues) */ async fetchLessonTree() { return await ApiHelper.getAnonymous("/lessons/public/tree", "LessonsApi"); } /** * Fetch the action tree for action selection (includes actions in each venue section) */ async fetchActionTree() { return await ApiHelper.getAnonymous("/lessons/public/actionTree", "LessonsApi"); } // ============================================ // Display List Methods (for preview/display without full actions) // ============================================ /** * Get the display list - hierarchical items suitable for preview * Structure: headers -> sections (no actions expanded) * This is the lightweight version for showing what a lesson contains */ async getDisplayList(plan) { const response = await this.fetchVenuePlanItems(plan); return response?.items || []; } /** * Get display list with sections only (strip actions from children) * Use this when importing a lesson as editable plan items */ async getSectionsOnlyList(plan) { const response = await this.fetchVenuePlanItems(plan); if (!response?.items) return []; return response.items.map(item => ({ ...item, children: item.children?.map(section => ({ ...section, children: undefined // Remove actions from sections })) })); } // ============================================ // Expanded List Methods (with full actions) // ============================================ /** * Get the fully expanded list - items with all actions populated in sections * Merges fetchVenuePlanItems with fetchVenueActions to get complete data */ async getExpandedList(plan) { const [planItemsResponse, actionsResponse] = await Promise.all([ this.fetchVenuePlanItems(plan), this.fetchVenueActions(plan) ]); if (!planItemsResponse?.items) return []; // Create a map of section ID -> actions const sectionActionsMap = new Map(); if (actionsResponse?.sections) { for (const section of actionsResponse.sections) { if (section.id && section.actions) { sectionActionsMap.set(section.id, section.actions.map(action => ({ itemType: "lessonAction", relatedId: action.id, label: action.name, description: action.actionType, seconds: action.seconds }))); } } } // Recursively expand sections with their actions const expandItem = (item) => { if (!item.children) return item; return { ...item, children: item.children.map(child => { // If this is a section (has relatedId), try to get its actions if (child.relatedId && sectionActionsMap.has(child.relatedId)) { return { ...child, children: sectionActionsMap.get(child.relatedId) }; } // Otherwise recursively process return expandItem(child); }) }; }; return planItemsResponse.items.map(expandItem); } // ============================================ // Embed URL Helpers // ============================================ /** * Get embed URL for an action */ getActionEmbedUrl(actionId, externalProviderId) { if (externalProviderId) { return `${this.lessonsUrl}/embed/external/${externalProviderId}/action/${actionId}`; } return `${this.lessonsUrl}/embed/action/${actionId}`; } /** * Get embed URL for an add-on */ getAddOnEmbedUrl(addOnId, externalProviderId) { if (externalProviderId) { return `${this.lessonsUrl}/embed/external/${externalProviderId}/addon/${addOnId}`; } return `${this.lessonsUrl}/embed/addon/${addOnId}`; } /** * Get embed URL for a section */ getSectionEmbedUrl(sectionId, externalProviderId) { if (externalProviderId) { return `${this.lessonsUrl}/embed/external/${externalProviderId}/section/${sectionId}`; } return `${this.lessonsUrl}/embed/section/${sectionId}`; } /** * Get the external provider ID from a plan (if external) */ getExternalProviderId(plan) { const externalRef = this.getExternalRef(plan); return externalRef?.externalProviderId || null; } } //# sourceMappingURL=LessonsContentProvider.js.map