Skip to content

Commit

Permalink
CMS-34: Add park name audio collection
Browse files Browse the repository at this point in the history
  • Loading branch information
ayumi-oxd committed Jan 6, 2025
1 parent dfe46db commit 98af06b
Show file tree
Hide file tree
Showing 13 changed files with 214 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"kind": "collectionType",
"collectionName": "park_name_audios",
"info": {
"singularName": "park-name-audio",
"pluralName": "park-name-audios",
"displayName": "Park-name-audio",
"description": ""
},
"options": {
"draftAndPublish": true
},
"attributes": {
"title": {
"type": "string",
"required": true
},
"url": {
"type": "string",
"required": true
},
"credit": {
"type": "string"
},
"transcript": {
"type": "text"
},
"protectedArea": {
"type": "relation",
"relation": "oneToOne",
"target": "api::protected-area.protected-area",
"inversedBy": "parkNameAudio"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

/**
* park-name-audio controller
*/

const { createCoreController } = require('@strapi/strapi').factories;

module.exports = createCoreController('api::park-name-audio.park-name-audio');
9 changes: 9 additions & 0 deletions src/cms/src/api/park-name-audio/routes/park-name-audio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

/**
* park-name-audio router
*/

const { createCoreRouter } = require('@strapi/strapi').factories;

module.exports = createCoreRouter('api::park-name-audio.park-name-audio');
9 changes: 9 additions & 0 deletions src/cms/src/api/park-name-audio/services/park-name-audio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

/**
* park-name-audio service
*/

const { createCoreService } = require('@strapi/strapi').factories;

module.exports = createCoreService('api::park-name-audio.park-name-audio');
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,12 @@
"relation": "manyToMany",
"target": "api::trail-report.trail-report",
"mappedBy": "protectedAreas"
},
"parkNameAudio": {
"type": "relation",
"relation": "oneToOne",
"target": "api::park-name-audio.park-name-audio",
"mappedBy": "protectedArea"
}
}
}
3 changes: 3 additions & 0 deletions src/gatsby/gatsby-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ module.exports = {
parkPhotos: {
fields: "*"
},
parkNameAudio: {
fields: "*"
},
trailReports: {
fields: "*"
},
Expand Down
86 changes: 86 additions & 0 deletions src/gatsby/src/components/audioButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { useState, useEffect, useRef } from "react"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import {
faVolumeHigh,
faChevronUp,
faChevronDown,
} from "@fortawesome/free-solid-svg-icons"
import "../styles/audioButton.scss"

export default function AudioButton({ audio }) {
const audioRef = useRef(null)
const [trackSrc, setTrackSrc] = useState("")
const [expanded, setExpanded] = useState(false)

const handlePlay = () => {
if (audioRef.current) {
audioRef.current.play()
}
}

useEffect(() => {
// convert transcript to vtt format
const vttContent = `WEBVTT\n\n1\n00:00:00.000 --> 00:00:10.000\n${audio.transcript}`
// create a blob from the vtt content
const blob = new Blob([vttContent], { type: "text/vtt" })
const url = URL.createObjectURL(blob)
setTrackSrc(url)
return () => {
URL.revokeObjectURL(url)
}
}, [audio.transcript])

return (
<div className="audio-container">
<div className="audio-container--left">
<button
aria-label="Play park name audio"
onClick={handlePlay}
className="btn-audio"
>
<FontAwesomeIcon icon={faVolumeHigh} />
</button>
<audio ref={audioRef} src={audio.url}>
<track kind="captions" srcLang="en" src={trackSrc} />
</audio>
</div>
<div className="audio-container--right">
<p>
<b>{audio.title}</b>
</p>
<p>
<small>
Spoken in {audio.credit} pronounced by {audio.credit}
</small>
</p>
{expanded && (
<div className="mb-3">
<p>
<small>
<b>Transcript</b>
</small>
</p>
<small>{audio.transcript}</small>
</div>
)}
{audio.transcript && (
<button
aria-label={expanded ? "Hide transcript" : "Show transcript"}
onClick={() => setExpanded(!expanded)}
className="btn btn-link expand-icon transcript-link"
>
{expanded ? (
<>
Hide transcript <FontAwesomeIcon icon={faChevronUp} />
</>
) : (
<>
Show transcript <FontAwesomeIcon icon={faChevronDown} />
</>
)}
</button>
)}
</div>
</div>
)
}
12 changes: 7 additions & 5 deletions src/gatsby/src/components/park/parkOverview.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@ import React, { useState, useEffect, useRef } from "react"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faChevronUp, faChevronDown } from "@fortawesome/free-solid-svg-icons"
import HtmlContent from "./htmlContent"
import AudioButton from "../audioButton"
import * as cheerio from 'cheerio';

export default function ParkOverview({ data: parkOverview, type }) {
export default function ParkOverview({ description, type, audio }) {
const [expanded, setExpanded] = useState(false)
const [height, setHeight] = useState(0)
const [sectionHeight, setSectionHeight] = useState(0)
const ref = useRef(null)
const isLong = height >= 300
const isMedium = height > 260 && height < 300

const $ = cheerio.load(parkOverview);
const $ = cheerio.load(description);
$('a').attr('tabindex', '-1')
const collapsedParkOverview = $.html()
const collapsedDescription = $.html()
const hasHr = $('hr').length > 0
const hrAtEnd = parkOverview.trim().endsWith('<hr>')
const hrAtEnd = description.trim().endsWith('<hr>')
const hasExpandCondition = (hasHr || isLong) && !isMedium && !hrAtEnd

useEffect(() => {
Expand Down Expand Up @@ -45,8 +46,9 @@ export default function ParkOverview({ data: parkOverview, type }) {
Highlights in this {type}
</h2>
<HtmlContent className="park-overview-html">
{expanded ? parkOverview : collapsedParkOverview}
{expanded ? description : collapsedDescription}
</HtmlContent>
<AudioButton audio={audio} />
</div>
{hasExpandCondition &&
<button
Expand Down
2 changes: 1 addition & 1 deletion src/gatsby/src/components/scrollToTop.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default function ScrollToTop() {
return (
isVisible && (
<button
aria-label="scroll to top"
aria-label="Scroll to top"
onClick={handleClick}
className="btn-scroll"
>
Expand Down
9 changes: 9 additions & 0 deletions src/gatsby/src/styles/audioButton.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@import "variables.scss";

.audio-container {
max-width: 600px;
display: flex;
&--left {
margin-right: 16px;
}
}
3 changes: 2 additions & 1 deletion src/gatsby/src/styles/cmsSnippets/parkInfoPage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@

.expand-link.expand-icon,
.park-overview-link.expand-icon,
.park-camping-link.expand-icon {
.park-camping-link.expand-icon,
.transcript-link.expand-icon {
display: inline-block;
color: $colorBlueMed !important;
text-decoration: none;
Expand Down
31 changes: 31 additions & 0 deletions src/gatsby/src/styles/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,37 @@ button.btn:not(.btn-link) {
}
}

.btn-audio {
height: 48px;
width: 48px;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
border-radius: 50%;
border: $colorGreyLight solid 1px;
padding: 0;
svg {
height: 28px;
width: 28px;
color: $colorBlue;
}
&:hover, &:focus {
background-color: $colorBlue;
border: $colorBlue solid 1px;
svg {
color: $colorWhite;
}
}
&:focus {
box-shadow: 0px 0px 4px 0px $colorBlack;
}
&:focus-visible {
border: $colorWhite solid 2px;
outline: $colorBlueMed solid 2px;
}
}

// breadcurmbs
nav.breadcrumbs {
ol {
Expand Down
8 changes: 7 additions & 1 deletion src/gatsby/src/templates/park.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ export default function ParkTemplate({ data }) {
<div className={`page-content has-nearby-parks--${hasNearbyParks} col-12 col-md-8`}>
{menuItems[0].visible && (
<div ref={parkOverviewRef} className="w-100">
<ParkOverview data={description} type={parkType} />
<ParkOverview description={description} type={parkType} audio={park.parkNameAudio} />
</div>
)}
{menuItems[1].visible && (
Expand Down Expand Up @@ -845,6 +845,12 @@ export const query = graphql`
}
}
}
parkNameAudio {
title
url
credit
transcript
}
}
featuredPhotos: allStrapiParkPhoto(
filter: {
Expand Down

0 comments on commit 98af06b

Please sign in to comment.