@@ -50,8 +48,7 @@ export default CommunityAudioRecorder
// ? Components
-function SpeedSetting() {
- const [speed, setSpeed] = useState(1)
+function SpeedSetting({ textSpeed, setTextSpeed }) {
const [isMounted, setIsMounted] = useState(false)
const { t } = useTranslation(['common'])
@@ -69,8 +66,8 @@ function SpeedSetting() {
@@ -290,70 +287,6 @@ function StopButton({ isRecording, stopRecording }) {
}
// ? Hooks
-function useAudioRecorder() {
- const [isRecording, setIsRecording] = useState(false)
- const [isPaused, setIsPaused] = useState(false)
- const [audioUrl, setAudioUrl] = useState(null)
- const mediaRecorder = useRef(null)
- const audioChunks = useRef([])
-
- const startRecording = useCallback(async () => {
- audioChunks.current = []
- try {
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
- mediaRecorder.current = new MediaRecorder(stream)
-
- mediaRecorder.current.ondataavailable = (event) => {
- audioChunks.current.push(event.data)
- }
-
- mediaRecorder.current.onstop = () => {
- const audioBlob = new Blob(audioChunks.current, { type: 'audio/wav' })
- const audioUrl = URL.createObjectURL(audioBlob)
- setAudioUrl(audioUrl)
- }
-
- mediaRecorder.current.start()
- setIsRecording(true)
- setIsPaused(false)
- } catch (error) {
- console.error('Error accessing microphone:', error)
- }
- }, [])
-
- const stopRecording = useCallback(() => {
- if (mediaRecorder.current) {
- mediaRecorder.current.stop()
- setIsRecording(false)
- setIsPaused(false)
- }
- }, [])
-
- const pauseRecording = useCallback(() => {
- if (mediaRecorder.current) {
- mediaRecorder.current.pause()
- setIsPaused(true)
- }
- }, [])
-
- const resumeRecording = useCallback(() => {
- if (mediaRecorder.current) {
- mediaRecorder.current.resume()
- setIsPaused(false)
- }
- }, [])
-
- return {
- isRecording,
- isPaused,
- audioUrl,
- startRecording,
- stopRecording,
- pauseRecording,
- resumeRecording,
- }
-}
-
function useAudioPreview(audioUrl) {
const [isPlaying, setIsPlaying] = useState(false)
const [preview, setPreview] = useState(null)
diff --git a/components/CommunityAudio/Teleprompter.js b/components/CommunityAudio/Teleprompter.js
new file mode 100644
index 000000000..71b12c3da
--- /dev/null
+++ b/components/CommunityAudio/Teleprompter.js
@@ -0,0 +1,215 @@
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+
+import { useRouter } from 'next/router'
+
+import { useTranslation } from 'next-i18next'
+
+import Breadcrumbs from 'components/Breadcrumbs'
+
+import { useAccess, useGetBooks, useProject } from 'utils/hooks'
+import { getVerseCount, getVerseCountOBS } from 'utils/helper'
+
+function Teleprompter({
+ verseObjects,
+ user,
+ reference,
+ isLoading,
+ isRecording,
+ isPaused,
+ stopRecording,
+ textProperties: { fontSize, textSpeed },
+}) {
+ const {
+ push,
+ query: { bookid, code },
+ } = useRouter()
+
+ const [{ isCoordinatorAccess }] = useAccess({
+ user_id: user?.id,
+ code,
+ })
+ const [project] = useProject({ code })
+ const { t } = useTranslation()
+ const [books] = useGetBooks({ code })
+
+ const [isPlaying, setIsPlaying] = useState(false)
+ const containerRef = useRef(null)
+ const animationRef = useRef(null)
+ const scrollPositionRef = useRef(0)
+
+ const verseCount = useMemo(() => {
+ if (project?.type === 'obs') {
+ return getVerseCountOBS(books, reference?.chapter)
+ } else {
+ return getVerseCount(books, bookid, reference?.chapter)
+ }
+ }, [books, project?.type, bookid, reference?.chapter])
+
+ const handleReset = useCallback(() => {
+ setIsPlaying(false)
+ scrollPositionRef.current = 0
+ if (containerRef.current) {
+ containerRef.current.scrollTop = 0
+ }
+ if (animationRef.current) {
+ cancelAnimationFrame(animationRef.current)
+ }
+ }, [])
+
+ const LoadingSection = () => (
+
+
+ {[...Array(22).keys()].map((el) => (
+
+ ))}
+
+ )
+
+ const NoContentSection = () => (
+ <>
+
{t('NoContent')}
+ {isCoordinatorAccess && (
+
+ push({
+ pathname: `/projects/${project?.code}`,
+ query: { properties: bookid, levels: true },
+ })
+ }
+ >
+ {t('CheckLinkResource')}
+
+ )}
+ >
+ )
+
+ useEffect(() => {
+ if (isRecording && !isPaused) {
+ setIsPlaying(true)
+ }
+ if (isPaused) {
+ setIsPlaying(false)
+ }
+
+ if (!isRecording) {
+ handleReset()
+ }
+ }, [fontSize, textSpeed, isRecording, isPaused])
+
+ useEffect(() => {
+ let previousTimestamp = null
+
+ const animate = (timestamp) => {
+ if (!previousTimestamp) previousTimestamp = timestamp
+ if (!containerRef.current) return
+
+ const elapsed = timestamp - previousTimestamp
+ const speed = textSpeed * 0.004
+
+ scrollPositionRef.current += elapsed * speed
+ containerRef.current.scrollTop = scrollPositionRef.current
+
+ previousTimestamp = timestamp
+
+ if (
+ containerRef.current.scrollTop >=
+ containerRef.current.scrollHeight - containerRef.current.clientHeight
+ ) {
+ stopRecording()
+ setIsPlaying(false)
+ handleReset()
+ return
+ }
+
+ if (isPlaying) {
+ animationRef.current = requestAnimationFrame(animate)
+ }
+ }
+
+ if (isPlaying) {
+ animationRef.current = requestAnimationFrame(animate)
+ }
+
+ return () => {
+ if (animationRef.current) {
+ cancelAnimationFrame(animationRef.current)
+ }
+ }
+ }, [isPlaying, textSpeed])
+
+ return (
+
+
+
+
+ {reference?.chapter && (
+
{`${t('books:' + bookid)} ${
+ reference?.chapter
+ }`}
+ )}
+
+
+
+
+ {!isLoading ? (
+ verseObjects ? (
+
+ {Array.from({ length: Math.min(verseCount + 1, 200) }).map((_, index) => {
+ const verseIndex = verseObjects?.verseObjects?.findIndex(
+ (verse) => parseInt(verse.verse) === index
+ )
+ const text =
+ verseObjects?.verseObjects && verseIndex !== -1
+ ? verseObjects.verseObjects[verseIndex].text
+ : ' '
+
+ return (
+
+ {index !== 0 &&
{index}}
+
{text}
+
+ )
+ })}
+ {verseObjects?.verseObjects && (
+
+ {verseObjects.verseObjects.find((verse) => verse.verse === 200)?.text}
+
+ )}
+
+ ) : (
+
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+ )
+}
+
+export default Teleprompter
diff --git a/components/CommunityAudio/Verses.jsx b/components/CommunityAudio/Verses.jsx
deleted file mode 100644
index f5946c404..000000000
--- a/components/CommunityAudio/Verses.jsx
+++ /dev/null
@@ -1,128 +0,0 @@
-import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'
-
-import { useRouter } from 'next/router'
-import Link from 'next/link'
-
-import { useTranslation } from 'next-i18next'
-
-import { Disclosure, Combobox, Tab, Transition } from '@headlessui/react'
-
-import Breadcrumbs from 'components/Breadcrumbs'
-
-import { useAccess, useGetBooks, useProject } from 'utils/hooks'
-
-import { getVerseCount, getVerseCountOBS } from 'utils/helper'
-
-import Gear from '/public/gear.svg'
-
-function Verses({ verseObjects, user, reference, isLoading, fontSize }) {
- const {
- push,
- query: { bookid, code },
- } = useRouter()
-
- const [{ isCoordinatorAccess }] = useAccess({
- user_id: user?.id,
- code,
- })
- const [project] = useProject({ code })
- const { t } = useTranslation()
- const [books] = useGetBooks({ code })
-
- const verseCount = useMemo(() => {
- if (project?.type === 'obs') {
- return getVerseCountOBS(books, reference?.chapter)
- } else {
- return getVerseCount(books, bookid, reference?.chapter)
- }
- }, [books, project?.type, bookid, reference?.chapter])
-
- return (
-
-
-
-
- {reference?.chapter && (
-
{`${t('books:' + bookid)} ${
- reference?.chapter
- }`}
- )}
-
- {/* */}
-
-
- {!isLoading ? (
- verseObjects ? (
- <>
- {Array.from({ length: Math.min(verseCount + 1, 200) }).map((_, index) => {
- const verseIndex = verseObjects?.verseObjects?.findIndex(
- (verse) => parseInt(verse.verse) === index
- )
- const text =
- verseObjects?.verseObjects && verseIndex !== -1
- ? verseObjects.verseObjects[verseIndex].text
- : ' '
-
- return (
-
- {index !== 0 &&
{index}}
-
{text}
-
- )
- })}
- {verseObjects?.verseObjects && (
-
- {verseObjects.verseObjects.find((verse) => verse.verse === 200)?.text}
-
- )}
- >
- ) : (
- <>
-
{t('NoContent')}
- {isCoordinatorAccess && (
-
- push({
- pathname: `/projects/${project?.code}`,
- query: {
- properties: bookid,
- levels: true,
- },
- })
- }
- >
- {t('CheckLinkResource')}
-
-
- )}
- >
- )
- ) : (
-
-
- {[...Array(22).keys()].map((el) => (
-
- ))}
-
- )}
-
-
- )
-}
-
-export default Verses
diff --git a/styles/globals.css b/styles/globals.css
index 34ff01c75..51602d3ec 100644
--- a/styles/globals.css
+++ b/styles/globals.css
@@ -421,3 +421,21 @@
.progress-light path {
@apply stroke-th-secondary-10
}
+
+.verse-container {
+ position: relative;
+ padding: 1rem;
+}
+
+.verse-line {
+ transition: all 0.3s ease;
+}
+
+.scrollbar-hide {
+ -ms-overflow-style: none; /* IE and Edge */
+ scrollbar-width: none; /* Firefox */
+}
+
+.scrollbar-hide::-webkit-scrollbar {
+ display: none; /* Chrome, Safari and Opera */
+}
From 4df1b183f4566c4de7d99ee4b5ed0ba69d357c17 Mon Sep 17 00:00:00 2001
From: BogdanLi
Date: Mon, 18 Nov 2024 18:46:47 +0500
Subject: [PATCH 11/23] feat: fixed conflict
---
components/CommunityAudio/BookListReader.js | 2 +-
components/CommunityAudio/CommunityAudio.js | 2 +-
components/Project/BookList/Testament.js | 15 +++++++++++++--
3 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/components/CommunityAudio/BookListReader.js b/components/CommunityAudio/BookListReader.js
index 2aa58d78f..7c8fa30e5 100644
--- a/components/CommunityAudio/BookListReader.js
+++ b/components/CommunityAudio/BookListReader.js
@@ -11,7 +11,7 @@ import { useGetChaptersTranslate } from 'utils/hooks'
import { checkChapterVersesExist } from 'utils/helper'
-import Down from '/public/arrow-down.svg'
+import Down from 'public/icons/arrow-down.svg'
function BookListReader({ books, setReference, reference, project }) {
const [currentBook, setCurrentBook] = useState(null)
diff --git a/components/CommunityAudio/CommunityAudio.js b/components/CommunityAudio/CommunityAudio.js
index 08038442d..d4f64a53d 100644
--- a/components/CommunityAudio/CommunityAudio.js
+++ b/components/CommunityAudio/CommunityAudio.js
@@ -19,7 +19,7 @@ import {
import { newTestamentList, oldTestamentList, usfmFileNames } from 'utils/config'
import { checkBookCodeExists, getVerseObjectsForBookAndChapter } from 'utils/helper'
-import Left from '/public/left.svg'
+import Left from 'public/icons/left.svg'
function CommunityAudio() {
const [fontSize, setFontSize] = useState(16)
diff --git a/components/Project/BookList/Testament.js b/components/Project/BookList/Testament.js
index fd2d98259..6256bb1e9 100644
--- a/components/Project/BookList/Testament.js
+++ b/components/Project/BookList/Testament.js
@@ -19,6 +19,7 @@ import DownloadIcon from 'public/icons/download.svg'
import Elipsis from 'public/icons/elipsis.svg'
import Gear from 'public/icons/gear.svg'
import Play from 'public/icons/play.svg'
+import Recorder from 'public/icons/recorder.svg'
function Testament({
bookList,
@@ -222,7 +223,7 @@ function Testament({
}}
/>
)}
- {isBookCreated && (
+ {/* {isBookCreated && (
@@ -232,7 +233,17 @@ function Testament({
})
}
/>
- )}
+ )} */}
+
+
+ push({
+ pathname: `/projects/${project?.code}/books/${book}/community-audio`,
+ shallow: true,
+ })
+ }
+ />
{isCoordinatorAccess && (
<>
{isBookCreated && (
From 8ec0fc0d3d346e44be43ef2bdd9cd4c36e22973b Mon Sep 17 00:00:00 2001
From: BogdanLi
Date: Tue, 19 Nov 2024 09:25:49 +0500
Subject: [PATCH 12/23] feat: max speed 15
---
components/CommunityAudio/CommunityAudioRecorder.js | 2 +-
components/CommunityAudio/Teleprompter.js | 2 +-
components/CommunityAudio/Teleprompter.jsx | 1 -
3 files changed, 2 insertions(+), 3 deletions(-)
delete mode 100644 components/CommunityAudio/Teleprompter.jsx
diff --git a/components/CommunityAudio/CommunityAudioRecorder.js b/components/CommunityAudio/CommunityAudioRecorder.js
index c19032edf..b4d2025ed 100644
--- a/components/CommunityAudio/CommunityAudioRecorder.js
+++ b/components/CommunityAudio/CommunityAudioRecorder.js
@@ -53,7 +53,7 @@ function SpeedSetting({ textSpeed, setTextSpeed }) {
const { t } = useTranslation(['common'])
const minSpeed = 1
- const maxSpeed = 50
+ const maxSpeed = 15
useEffect(() => {
setIsMounted(true)
diff --git a/components/CommunityAudio/Teleprompter.js b/components/CommunityAudio/Teleprompter.js
index 71b12c3da..e42ceb367 100644
--- a/components/CommunityAudio/Teleprompter.js
+++ b/components/CommunityAudio/Teleprompter.js
@@ -158,7 +158,7 @@ function Teleprompter({
}`}