diff --git a/renderer/src/components/EditorPage/AudioEditor/AudioEditor.js b/renderer/src/components/EditorPage/AudioEditor/AudioEditor.js index 793151c69..23d059a5c 100644 --- a/renderer/src/components/EditorPage/AudioEditor/AudioEditor.js +++ b/renderer/src/components/EditorPage/AudioEditor/AudioEditor.js @@ -66,7 +66,8 @@ const AudioEditor = ({ editor }) => { const _books = []; Object.entries(_data.type.flavorType.currentScope).forEach( async ([key]) => { - if (key === bookId.toUpperCase()) { + // Checking whether the selected book and chapter is in the scope or not + if (key === bookId.toUpperCase() && _data.type.flavorType.currentScope[key].includes(chapter)) { _books.push(bookId.toUpperCase()); const fs = window.require('fs'); const path = require('path'); diff --git a/renderer/src/components/EditorPage/Navigation/reference/SelectBook.js b/renderer/src/components/EditorPage/Navigation/reference/SelectBook.js index e0cde5603..40261083e 100644 --- a/renderer/src/components/EditorPage/Navigation/reference/SelectBook.js +++ b/renderer/src/components/EditorPage/Navigation/reference/SelectBook.js @@ -1,3 +1,4 @@ +/* eslint-disable no-nested-ternary */ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { Disclosure, Transition } from '@headlessui/react'; @@ -14,6 +15,8 @@ export default function SelectBook({ setSelectedBooks, scope, existingScope = [], + disableScope = {}, + call = '', }) { const [openNT, setOpenNT] = useState(true); const [openOT, setOpenOT] = useState(true); @@ -99,11 +102,13 @@ export default function SelectBook({ role="presentation" key={book.name} aria-label={`ot-${book.name}`} - onClick={(e) => ( - multiSelectBook + onClick={(e) => (call === 'audio-project' ? (Object.prototype.hasOwnProperty.call(disableScope, (book.key).toUpperCase()) + ? (multiSelectBook ? selectMultipleBooks(e, book.key, book.name) - : bookSelect(e, book.key, book.name))} - className={`${styles.bookSelect} ${selectedBooks.includes((book.key).toUpperCase()) ? styles.active : ''}`} + : bookSelect(e, book.key, book.name)) : '') : (multiSelectBook + ? selectMultipleBooks(e, book.key, book.name) + : bookSelect(e, book.key, book.name)))} + className={`${call === 'audio-project' && !Object.prototype.hasOwnProperty.call(disableScope, (book.key).toUpperCase()) ? styles.disabled : (selectedBooks.includes((book.key).toUpperCase()) ? (styles.bookSelect, styles.active) : styles.bookSelect)}`} > {book.name} @@ -138,10 +143,13 @@ export default function SelectBook({ key={book.name} role="presentation" aria-label={`nt-${book.name}`} - onClick={(e) => (multiSelectBook - ? selectMultipleBooks(e, book.key, book.name) - : bookSelect(e, book.key, book.name))} - className={`${styles.bookSelect} ${selectedBooks.includes((book.key).toUpperCase()) ? styles.active : ''}`} + onClick={(e) => (call === 'audio-project' ? (Object.prototype.hasOwnProperty.call(disableScope, (book.key).toUpperCase()) + ? (multiSelectBook + ? selectMultipleBooks(e, book.key, book.name) + : bookSelect(e, book.key, book.name)) : '') : (multiSelectBook + ? selectMultipleBooks(e, book.key, book.name) + : bookSelect(e, book.key, book.name)))} + className={`${call === 'audio-project' && !Object.prototype.hasOwnProperty.call(disableScope, (book.key).toUpperCase()) ? styles.disabled : (selectedBooks.includes((book.key).toUpperCase()) ? (styles.bookSelect, styles.active) : styles.bookSelect)}`} > {book.name} diff --git a/renderer/src/components/EditorPage/Navigation/reference/SelectReference.module.css b/renderer/src/components/EditorPage/Navigation/reference/SelectReference.module.css index 58c675f42..e1c09f06f 100644 --- a/renderer/src/components/EditorPage/Navigation/reference/SelectReference.module.css +++ b/renderer/src/components/EditorPage/Navigation/reference/SelectReference.module.css @@ -8,3 +8,7 @@ .active { @apply py-1 px-2 bg-primary text-white cursor-pointer rounded sm:w-10/12 lg:w-5/12; } + +.disabled { + @apply py-1 px-2 text-slate-400 rounded; +} \ No newline at end of file diff --git a/renderer/src/components/EditorPage/Navigation/reference/SelectVerse.js b/renderer/src/components/EditorPage/Navigation/reference/SelectVerse.js index 40fa0165a..ba564b1ce 100644 --- a/renderer/src/components/EditorPage/Navigation/reference/SelectVerse.js +++ b/renderer/src/components/EditorPage/Navigation/reference/SelectVerse.js @@ -1,3 +1,4 @@ +/* eslint-disable no-nested-ternary */ import { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { Disclosure, Transition } from '@headlessui/react'; @@ -23,6 +24,8 @@ export default function SelectVerse({ setVerseSelectActive, setChapterNumber, setVerseNumber, + scopedChapters, + call = '', }) { const [controlVerseSelect, setControlVerseSelect] = useState([]); const [openChapter, setOpenChapter] = useState(true); @@ -115,8 +118,8 @@ export default function SelectVerse({ key={chapter.key} role="presentation" id={`chapter-${chapter.name}`} - onClick={(e) => { onChapterSelect(e, chapter.key); }} - className={styles.select} + onClick={(e) => { call === 'audio-project' ? (scopedChapters.includes(chapter.name) ? onChapterSelect(e, chapter.key) : '') : onChapterSelect(e, chapter.key); }} + className={call === 'audio-project' ? (scopedChapters.includes(chapter.name) ? styles.select : styles.disabled) : styles.select} > {chapter.name} diff --git a/renderer/src/components/ProjectManagement/Common/Button/BookButton.jsx b/renderer/src/components/ProjectManagement/Common/Button/BookButton.jsx new file mode 100644 index 000000000..d06d90d33 --- /dev/null +++ b/renderer/src/components/ProjectManagement/Common/Button/BookButton.jsx @@ -0,0 +1,25 @@ +import React from 'react'; + +function BookButton({ + onClick, + children, + disabled = false, + className = '', + ...props +}) { + const buttonClasses = `py-1 px-2 hover:bg-primary hover:text-white hover:font-bold cursor-pointer rounded text-left ${className} `; + + return ( + + ); +} + +export default BookButton; diff --git a/renderer/src/components/ProjectManagement/Common/Button/Button.jsx b/renderer/src/components/ProjectManagement/Common/Button/Button.jsx new file mode 100644 index 000000000..9a32cbaf8 --- /dev/null +++ b/renderer/src/components/ProjectManagement/Common/Button/Button.jsx @@ -0,0 +1,25 @@ +import React from 'react'; + +function Button({ + children, + type = 'button', + onClick, + disabled = false, + ...props +}) { + return ( + + ); +} + +export default Button; diff --git a/renderer/src/components/ProjectManagement/ProjectManagement.js b/renderer/src/components/ProjectManagement/ProjectManagement.js new file mode 100644 index 000000000..54d8cc5a1 --- /dev/null +++ b/renderer/src/components/ProjectManagement/ProjectManagement.js @@ -0,0 +1,181 @@ +/* eslint-disable no-useless-escape */ +import React, { + useRef, Fragment, + useEffect, + useCallback, + useState, +} from 'react'; +import PropTypes from 'prop-types'; +import { Dialog, Transition } from '@headlessui/react'; +import { useTranslation } from 'react-i18next'; +import { SnackBar } from '@/components/SnackBar'; +// import { LoadingSpinner } from '@/components/LoadingSpinner'; +import CloseIcon from '@/illustrations/close-button-black.svg'; +import * as logger from '../../logger'; +import ScopeManagement from './scope-management/ScopeManagement'; +import { readProjectScope } from './utils/readProjectScope'; +import { LoadingSpinner } from '../LoadingSpinner'; +import { updateBurritoScope } from './utils/updateBurritoScope'; + +export default function ProjectMangement(props) { + const { + open, + closePopUp, + project, + } = props; + const { t } = useTranslation(); + const cancelButtonRef = useRef(null); + const [snackBar, setOpenSnackBar] = useState(false); + const [snackText, setSnackText] = useState(''); + const [notify, setNotify] = useState(); + const [currentScope, setCurrentScope] = useState({}); + const [metadata, setMetadata] = useState(''); + const [loading, setLoading] = useState(false); + const [backendScope, setBackendScope] = useState({}); + + const close = () => { + logger.debug('ProjectMangement.js', 'Closing the Dialog Box'); + setOpenSnackBar(true); + closePopUp(false); + setMetadata({}); + }; + + // load Metadata of the project + const getProjectMetadata = useCallback(async () => { + try { + setLoading(true); + const projectFullName = `${project?.name}_${project?.id?.[0]}`; + const projectMeta = await readProjectScope(projectFullName); + setMetadata(projectMeta.metadata); + setBackendScope(projectMeta.scope); + } catch (err) { + logger.error('ProjectMangement.js', `Read Meta : ${err}`); + } finally { + setLoading(false); + } + }, [project]); + function compareNumbers(a, b) { + return a - b; + } + const handleProject = () => { + logger.debug('ProjectMangement.js', 'Inside updateBurrito'); + let mergedScope = currentScope; + // Merge both existing and new scope, if any scope difference exists + if (Object.keys(backendScope).length > 0) { + Object.entries(backendScope).forEach((book) => { + // Checking whether the book in scope is available in currentscope + if (currentScope[book[0]]) { + // merging the chapters of existing and selected books + const scopeSet = backendScope[book[0]]; + const currentSet = currentScope[book[0]]; + const arr = [...scopeSet, ...currentSet]; + const mergedArr = [...new Set(arr)]; + mergedScope = { ...mergedScope, [book[0]]: mergedArr.sort(compareNumbers) }; + } else { + mergedScope = { ...mergedScope, [book[0]]: Object.values(backendScope[book[0]]) }; + } + }); + } + metadata.type.flavorType.currentScope = mergedScope; + const projectFullName = `${project?.name}_${project?.id?.[0]}`; + updateBurritoScope(projectFullName, metadata).then(() => { + setNotify('success'); + setSnackText('Scope updated successfully!'); + close(); + }); + }; + + useEffect(() => { + getProjectMetadata(); + }, [getProjectMetadata]); + + return ( + <> + + {}} + > + +
+
+
+ +
+
+ Project Management + : + {project?.name} +
+ +
+ +
+ +
+ {loading ? : } +
+ +
+ + +
+ +
+ +
+
+
+
+
+ + + ); +} +ProjectMangement.propTypes = { + open: PropTypes.bool, + closePopUp: PropTypes.func, + project: PropTypes.object, +}; diff --git a/renderer/src/components/ProjectManagement/scope-management/BookItem.jsx b/renderer/src/components/ProjectManagement/scope-management/BookItem.jsx new file mode 100644 index 000000000..ccf8f2221 --- /dev/null +++ b/renderer/src/components/ProjectManagement/scope-management/BookItem.jsx @@ -0,0 +1,34 @@ +/* eslint-disable no-nested-ternary */ +import React from 'react'; +import BookButton from '../Common/Button/BookButton'; +import XMark from '@/icons/Xelah/XMark.svg'; + +function BookItem({ + book, handleSelectBook, handleRemoveScope, isInScope, disable, +}) { + return ( +
+ handleSelectBook(e, book)} + > +
+ {book.name} +
+ + handleRemoveScope(e, book)} + /> +
+
+ ); +} + +export default BookItem; diff --git a/renderer/src/components/ProjectManagement/scope-management/BulkSelectionGroup.jsx b/renderer/src/components/ProjectManagement/scope-management/BulkSelectionGroup.jsx new file mode 100644 index 000000000..c3a740911 --- /dev/null +++ b/renderer/src/components/ProjectManagement/scope-management/BulkSelectionGroup.jsx @@ -0,0 +1,28 @@ +import React from 'react'; + +function BulkSelectionGroup({ + selectedOption = '', + handleSelect, + toggleOptions = [], +}) { + return ( +
+ {toggleOptions.map((option) => ( +
+ + +
+ ))} +
+ ); +} + +export default BulkSelectionGroup; diff --git a/renderer/src/components/ProjectManagement/scope-management/ScopeHead.jsx b/renderer/src/components/ProjectManagement/scope-management/ScopeHead.jsx new file mode 100644 index 000000000..fb3fe2edd --- /dev/null +++ b/renderer/src/components/ProjectManagement/scope-management/ScopeHead.jsx @@ -0,0 +1,9 @@ +import React from 'react'; + +function ScopeHead({ children }) { + return ( +

{children}

+ ); +} + +export default ScopeHead; diff --git a/renderer/src/components/ProjectManagement/scope-management/ScopeManagement.jsx b/renderer/src/components/ProjectManagement/scope-management/ScopeManagement.jsx new file mode 100644 index 000000000..87961a39a --- /dev/null +++ b/renderer/src/components/ProjectManagement/scope-management/ScopeManagement.jsx @@ -0,0 +1,292 @@ +import React, { useEffect, useState } from 'react'; +import { useBibleReference } from 'bible-reference-rcl'; +import ScopeHead from './ScopeHead'; +import TitleBar from './TitleBar'; +import BookButton from '../Common/Button/BookButton'; +import BulkSelectionGroup from './BulkSelectionGroup'; +import Button from '../Common/Button/Button'; +import BookItem from './BookItem'; +import * as logger from '../../../logger'; + +const initialBook = 'gen'; +const initialChapter = '1'; +const initialVerse = '1'; + +const ToggleBookOptions = [ + { key: 'all', name: 'All' }, + { key: 'old', name: 'Old' }, + { key: 'new', name: 'New' }, + { key: 'none', name: 'Deselect' }, +]; + +const ToggleChapterOptions = [ + { key: 'all', name: 'All' }, + { key: 'none', name: 'Deselect' }, +]; + +function ScopeManagement({ + metadata, currentScope, setCurrentScope, backendScope, +}) { + const [bookFilter, setBookFilter] = useState(''); + const [chapterFilter, setChapterFilter] = useState(''); + const [selectedChaptersSet, setSelectedChaptersSet] = useState(new Set([])); + + const { + state: { + // chapter, + // verse, + bookList, + chapterList, + // verseList, + bookName, + bookId, + }, actions: { + onChangeBook, + // onChangeChapter, + // onChangeVerse, + // applyBooksFilter, + }, + } = useBibleReference({ + initialBook, + initialChapter, + initialVerse, + }); + + const handleChangeBookToggle = (event) => { + setBookFilter(event.target.value); + const bookObj = {}; + if (event.target.value === 'all') { + bookList.forEach((book) => { + bookObj[book.key.toUpperCase()] = []; + }); + } else if (event.target.value === 'old') { + bookList?.slice(0, 39)?.forEach((book) => { + bookObj[book.key.toUpperCase()] = []; + }); + } else if (event.target.value === 'new') { + bookList?.slice(39)?.forEach((book) => { + bookObj[book.key.toUpperCase()] = []; + }); + } + const bookCode = Object.keys(bookObj)[0]; + onChangeBook(bookCode, bookCode); + setCurrentScope(bookObj); + }; + + const handleChangeChapterToggle = (event) => { + setChapterFilter(event.target.value); + let stringArray = []; + if (event.target.value === 'all') { + const numberArray = Array(chapterList.length).fill().map((_, idx) => 1 + idx); + stringArray = numberArray.map(String); + } + setCurrentScope((prev) => { + // check and change the selectedChapters + setSelectedChaptersSet(new Set(stringArray)); + return ({ ...prev, [bookId.toUpperCase()]: stringArray }); + }); + }; + + const handleSelectBook = (e, book) => { + if (bookFilter) { + setBookFilter(''); + } + const bookCode = book.key.toUpperCase(); + setCurrentScope((prev) => { + // check and change the selectedChapters + setSelectedChaptersSet(new Set(prev[bookCode]) || new Set([])); + return ({ ...prev, [bookCode]: prev[bookCode] || [] }); + }); + onChangeBook(book.key, book.key); + }; + + const handleChapterRangeSelection = (e) => { + e.preventDefault(); + let start = parseInt(e.target?.start?.value, 10) || null; + let end = parseInt(e.target?.end?.value, 10) || null; + // hanlde start greater and end smaller + if (start > end) { + const temp = start; + start = end; + end = temp; + } + const numberArray = Array(end - start + 1).fill().map((_, idx) => start + idx); + const stringArray = numberArray.map(String); + setCurrentScope((prev) => { + // check and change the selectedChapters + setSelectedChaptersSet(new Set(stringArray)); + return ({ ...prev, [bookId.toUpperCase()]: stringArray }); + }); + // e.target.start.value = ''; + // e.target.end.value = ''; + }; + + const handleRemoveScope = (e, book) => { + e.stopPropagation(); + if (bookFilter) { + setBookFilter(''); + } + const bukId = book.key.toUpperCase(); + const newScopeObj = { ...currentScope }; + delete newScopeObj[bukId]; + setCurrentScope(newScopeObj); + }; + + /** + * Fn to toggle chapter selection for the active book + */ + const handleChapterSelection = (e, chapter) => { + if (chapterFilter) { + setChapterFilter(''); + } + const bukId = bookId.toUpperCase(); + if (bukId in currentScope) { + setCurrentScope((prev) => { + const currentCh = new Set(prev[bukId] || new Set([])); + if (currentCh.has(chapter)) { + currentCh.delete(chapter); + } else { + currentCh.add(chapter); + } + setSelectedChaptersSet(currentCh); + return { + ...prev, + [bukId]: Array.from(currentCh), + }; + }); + } else { + logger.error('ScopeManagement.js', 'Active book is not in scope'); + } + }; + + // set current scope from meta + useEffect(() => { + if (metadata?.type?.flavorType?.currentScope) { + const scopeObj = metadata?.type?.flavorType?.currentScope; + // expect at least 1 scope - because creation and scope modification won't allow 0 scope + setSelectedChaptersSet(new Set(scopeObj[0]) || new Set([])); + const bookCode = Object.keys(scopeObj)[0].toUpperCase(); + onChangeBook(bookCode, bookCode); + setCurrentScope(scopeObj); + } else { + logger.error('ScopeManagement.js', 'Unable to read the scope from burrito'); + } + }, []); + + return ( +
+ Project Scope Management + +

Book Selection

+ +
+ +
+
+ {backendScope && bookList?.slice(0, 39)?.map((book) => { + const isScope = book?.key?.toUpperCase() in currentScope; + return ( + + ); + })} +
+ +
+ {backendScope && bookList?.slice(39)?.map((book) => { + const isScope = book?.key?.toUpperCase() in currentScope; + return ( + + ); + })} +
+
+ {bookName + && ( + +

+ Chapter Selection : + {bookName} +

+ +
+ )} + +
+
+ + +
+
+ + +
+ + +
+ +
+
+ {chapterList?.map(({ key, name }) => { + const isInScope = selectedChaptersSet.has(key); + const disable = backendScope[bookId.toUpperCase()]?.includes(key); + return ( + handleChapterSelection(e, name)} + key={key} + // eslint-disable-next-line no-nested-ternary + className={`border min-w-8 text-center ${disable ? 'bg-gray-400' : isInScope ? 'bg-primary text-white font-medium' : ''}`} + > + {name} + + ); + })} +
+
+ +
+ ); +} + +export default ScopeManagement; diff --git a/renderer/src/components/ProjectManagement/scope-management/TitleBar.jsx b/renderer/src/components/ProjectManagement/scope-management/TitleBar.jsx new file mode 100644 index 000000000..8811d9826 --- /dev/null +++ b/renderer/src/components/ProjectManagement/scope-management/TitleBar.jsx @@ -0,0 +1,9 @@ +import React from 'react'; + +function TitleBar({ children }) { + return ( +
{children}
+ ); +} + +export default TitleBar; diff --git a/renderer/src/components/ProjectManagement/utils/readProjectScope.js b/renderer/src/components/ProjectManagement/utils/readProjectScope.js new file mode 100644 index 000000000..ca1a6e799 --- /dev/null +++ b/renderer/src/components/ProjectManagement/utils/readProjectScope.js @@ -0,0 +1,62 @@ +import localForage from 'localforage'; +import * as logger from '../../../logger'; +import packageInfo from '../../../../../package.json'; + +function isDirEmpty(dirname, fs) { + return fs.promises.readdir(dirname).then((files) => files.length > 0); +} + +const getDirectories = (readdirSync, source) => readdirSync(source, { withFileTypes: true }) + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => dirent.name); + +// This function returns the Object of books & chapters which has atleast 1 audio file in it. +export const getScope = (project) => { + const path = require('path'); + const scope = {}; + const { readdirSync } = window.require('fs'); + const fs = window.require('fs'); + const list = getDirectories(readdirSync, project); + list.forEach((book) => { + const chapters = getDirectories(readdirSync, path.join(project, book)); + const chapterFilter = []; + chapters.forEach((chapter) => { + // Finding non empty directories/chapters + isDirEmpty(path.join(project, book, chapter), fs).then((value) => { + if (value === true) { + chapterFilter.push(chapter); + } + }); + }); + scope[book] = chapterFilter; + }); + return scope; +}; +export const readProjectScope = async (projectName) => { + try { + logger.debug('readProjectScope.js', `In read metadata - ${projectName}`); + const currentUser = await localForage.getItem('userProfile'); + const newpath = localStorage.getItem('userPath'); + const fs = window.require('fs'); + const path = require('path'); + const file = path.join(newpath, packageInfo.name, 'users', currentUser.username, 'projects'); + + const filePath = path.join(file, projectName, 'metadata.json'); + + if (fs.existsSync(filePath)) { + const metadataFile = await fs.readFileSync(filePath, 'utf-8'); + if (metadataFile) { + logger.debug('readProjectScope.js', `read metadata file successfully - ${projectName}`); + const project = path.join(file, projectName, 'audio', 'ingredients'); + const backendScope = getScope(project); + const json = await JSON.parse(metadataFile); + return { metadata: json, scope: backendScope }; + } + throw new Error(`failed to read settings file - ${projectName}`); + } + throw new Error(`failed to read metadata file - ${projectName}`); + } catch (err) { + logger.error('readProjectScope.js', `read metadata file successfully - ${projectName}`); + throw new Error(err?.message || err); + } +}; diff --git a/renderer/src/components/ProjectManagement/utils/updateBurritoScope.js b/renderer/src/components/ProjectManagement/utils/updateBurritoScope.js new file mode 100644 index 000000000..c7cb8985f --- /dev/null +++ b/renderer/src/components/ProjectManagement/utils/updateBurritoScope.js @@ -0,0 +1,24 @@ +import localForage from 'localforage'; +import * as logger from '../../../logger'; +import packageInfo from '../../../../../package.json'; + +export const updateBurritoScope = async (projectName, metadata) => { + try { + logger.debug('updateBurritoScope.js', `In update metadata - ${projectName}`); + const currentUser = await localForage.getItem('userProfile'); + const newpath = localStorage.getItem('userPath'); + const fs = window.require('fs'); + const path = require('path'); + const file = path.join(newpath, packageInfo.name, 'users', currentUser.username, 'projects'); + // Finally updating the scope in the metadata + const filePath = path.join(file, projectName, 'metadata.json'); + if (fs.existsSync(filePath)) { + fs.writeFileSync(filePath, JSON.stringify(metadata)); + return true; + } + throw new Error(`failed to read metadata file - ${projectName}`); + } catch (err) { + logger.error('updateBurritoScope.js', `read metadata file successfully - ${projectName}`); + throw new Error(err?.message || err); + } +}; diff --git a/renderer/src/components/Projects/ProjectList.js b/renderer/src/components/Projects/ProjectList.js index ece2c0528..8485de3bf 100644 --- a/renderer/src/components/Projects/ProjectList.js +++ b/renderer/src/components/Projects/ProjectList.js @@ -15,6 +15,7 @@ import NewProject from './NewProject'; import * as logger from '../../logger'; import ProjectRow from './ProjectRow'; import { ProjectContext } from '../context/ProjectContext'; +import ProjectMangement from '../ProjectManagement/ProjectManagement'; export default function ProjectList() { const { t } = useTranslation(); @@ -25,9 +26,7 @@ export default function ProjectList() { const filterList = ['name', 'language', 'type', 'date', 'view']; const { states: { - // starredProjects, projects, - // unstarredProjects, callEditProject, }, action: { @@ -36,7 +35,7 @@ export default function ProjectList() { FetchProjects, }, } = useContext(AutographaContext); - const { states: { openExportPopUp }, actions: { setOpenExportPopUp } } = useContext(ProjectContext); + const { states: { openExportPopUp, openManageProject }, actions: { setOpenExportPopUp, setOpenManageProject } } = useContext(ProjectContext); const [currentProject, setCurrentProject] = useState(); const [filteredProjects, setFilteredProjects] = useState(projects); const handleExportPopUp = (project) => { @@ -57,6 +56,13 @@ export default function ProjectList() { setCallEditProject(false); await FetchProjects(); }; + const manageProject = (project) => { + setCurrentProject(project); + setOpenManageProject(true); + }; + const closeManageProject = () => { + setOpenManageProject(false); + }; return ( <> {!callEditProject ? ( @@ -89,7 +95,7 @@ export default function ProjectList() { orderBy={orderBy} onRequestSort={handleRequestSort} /> - + {(!projects) &&
} @@ -102,6 +108,8 @@ export default function ProjectList() { ) : closeEditProject()} />} + {openManageProject + && } ); } diff --git a/renderer/src/components/Projects/ProjectRow.js b/renderer/src/components/Projects/ProjectRow.js index 472a0e48a..1dbbe7277 100644 --- a/renderer/src/components/Projects/ProjectRow.js +++ b/renderer/src/components/Projects/ProjectRow.js @@ -17,7 +17,7 @@ import NewProjectIcon from '@/icons/new.svg'; import { ProjectContext } from '../context/ProjectContext'; const ProjectRow = ({ - projects, order, orderBy, showArchived, openExportPopUp, handleClickStarred, setCurrentProject, filteredProjects, + projects, order, orderBy, showArchived, openExportPopUp, handleClickStarred, setCurrentProject, filteredProjects, manageProject, }) => { const router = useRouter(); const { t } = useTranslation(); @@ -233,6 +233,24 @@ const ProjectRow = ({ )} + { + project.type === 'Audio' + && ( + + {({ active }) => ( + + )} + + ) + } diff --git a/renderer/src/components/context/ProjectContext.js b/renderer/src/components/context/ProjectContext.js index cded3cd9c..8793de620 100644 --- a/renderer/src/components/context/ProjectContext.js +++ b/renderer/src/components/context/ProjectContext.js @@ -54,6 +54,7 @@ const ProjectContextProvider = ({ children }) => { const [sideBarTab, setSideBarTab] = useState(''); const [openImportPopUp, setOpenImportPopUp] = useState(false); const [openExportPopUp, setOpenExportPopUp] = useState(false); + const [openManageProject, setOpenManageProject] = useState(false); const handleProjectFields = (prop) => (event) => { setNewProjectFields({ ...newProjectFields, [prop]: event.target.value }); @@ -541,6 +542,7 @@ const ProjectContextProvider = ({ children }) => { sideBarTab, openImportPopUp, openExportPopUp, + openManageProject, selectedProjectMeta, importedBookCodes, }, @@ -566,6 +568,7 @@ const ProjectContextProvider = ({ children }) => { setSideBarTab, setOpenImportPopUp, setOpenExportPopUp, + setOpenManageProject, setSelectedProjectMeta, setImportedBookCodes, }, diff --git a/renderer/src/modules/biblenavigation/BibleNavigation.js b/renderer/src/modules/biblenavigation/BibleNavigation.js index a5cceef91..0be06ca39 100644 --- a/renderer/src/modules/biblenavigation/BibleNavigation.js +++ b/renderer/src/modules/biblenavigation/BibleNavigation.js @@ -10,15 +10,8 @@ import { import * as localforage from 'localforage'; import SelectBook from '@/components/EditorPage/Navigation/reference/SelectBook'; import SelectVerse from '@/components/EditorPage/Navigation/reference/SelectVerse'; - -// import { -// LockOpenIcon, -// LockClosedIcon, -// BookmarkIcon, -// CogIcon, -// ChatIcon, -// } from '@heroicons/react/24/outline'; import { ReferenceContext } from '@/components/context/ReferenceContext'; +import { splitStringByLastOccurence } from '@/util/splitStringByLastMarker'; import { isElectron } from '../../core/handleElectron'; export default function BibleNavigation(props) { @@ -58,7 +51,7 @@ export default function BibleNavigation(props) { const [selectedVerses, setSelectedVerses] = useState([]); const [selectedBooks, setSelectedBooks] = useState([]); const [verselectActive, setVerseSelectActive] = useState(false); - + const [existingScopes, setExistingScopes] = useState({}); function closeBooks() { setOpenBook(false); } @@ -79,6 +72,25 @@ export default function BibleNavigation(props) { if (multiSelectVerse) { setSelectedVerses([]); } } + const getProjectScope = () => { + localforage.getItem('currentProject').then(async (projectName) => { + const _projectname = await splitStringByLastOccurence(projectName, '_'); + localforage.getItem('projectmeta').then((value) => { + Object?.entries(value).forEach( + ([, _value]) => { + Object?.entries(_value).forEach( + ([, resources]) => { + if (resources.identification.name.en === _projectname[0]) { + setExistingScopes(resources.type.flavorType.currentScope); + } + }, + ); + }, + ); + }); + }); + }; + useEffect(() => { if (isElectron()) { localforage.getItem('refBibleBurrito') @@ -104,6 +116,13 @@ export default function BibleNavigation(props) { localforage.setItem('navigationHistory', [bookId, chapter]); }, [bookId, chapter]); + useEffect(() => { + if (Object.keys(existingScopes) < 1) { + getProjectScope(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { if (openBook === false && openVerse === false) { setCloseNavigation(true); @@ -179,6 +198,9 @@ export default function BibleNavigation(props) { // "scope" is added to disable the click on the book list. scope="Other" will only // allow to click/select the book. scope="Other" + existingScope={[]} + disableScope={existingScopes} + call="audio-project" >