diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 1a6571bea..000000000 --- a/.babelrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "presets": ["next/babel"] -} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9a8404139..91bdc8c22 100644 --- a/.gitignore +++ b/.gitignore @@ -47,7 +47,7 @@ yarn-debug.log* yarn-error.log* # local env files - +.env .env.local .env.development.local .env.test.local diff --git a/app/favicon.ico b/app/favicon.ico new file mode 100644 index 000000000..f443de1d8 Binary files /dev/null and b/app/favicon.ico differ diff --git a/app/head.js b/app/head.js new file mode 100644 index 000000000..4094cccc9 --- /dev/null +++ b/app/head.js @@ -0,0 +1,13 @@ +// import i18n from '../src/translations/i18n'; +import '../styles/nprogress.css'; + +export default function Head() { + return ( + <> + + + + Scribe Scripture editor + + ); +} diff --git a/app/home/layout.jsx b/app/home/layout.jsx new file mode 100644 index 000000000..b2072ec06 --- /dev/null +++ b/app/home/layout.jsx @@ -0,0 +1,22 @@ +'use client'; + +import PropTypes from 'prop-types'; +import SubMenuBar from '@/layouts/editor/SubMenuBar'; +import MenuBar from '@/layouts/editor/WebMenuBar'; + +export default function EditorLayout(props) { + const { children } = props; + + return ( + <> + + + +
{children}
+ + ); +} + +EditorLayout.propTypes = { + children: PropTypes.any, +}; diff --git a/app/home/page.jsx b/app/home/page.jsx new file mode 100644 index 000000000..0187a3615 --- /dev/null +++ b/app/home/page.jsx @@ -0,0 +1,12 @@ +'use client'; + +import SectionContainer from '@/layouts/editor/WebSectionContainer'; +import ProtectedRoute from '@/components/Protected'; + +export default function page() { + return ( + + + + ); +} diff --git a/app/layout.js b/app/layout.js new file mode 100644 index 000000000..addf47cf9 --- /dev/null +++ b/app/layout.js @@ -0,0 +1,16 @@ +import { Providers } from './providers'; +import '../styles/nprogress.css'; +import '../styles/globals.css'; +// import { initializeParse } from '@parse/react-ssr'; +import 'usfm-editor/dist/style.css'; +import '../styles/style-override.lazy.css'; + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ); +} diff --git a/app/loading.js b/app/loading.js new file mode 100644 index 000000000..c9d5ef850 --- /dev/null +++ b/app/loading.js @@ -0,0 +1,7 @@ +import LoadingScreen from '@/components/Loading/LoadingScreen'; + +const Loading = () => ( + +); + +export default Loading; diff --git a/app/login/page.jsx b/app/login/page.jsx new file mode 100644 index 000000000..40a3f7e7f --- /dev/null +++ b/app/login/page.jsx @@ -0,0 +1,5 @@ +import WebLogin from '@/components/Login/WebLogin'; + +const login = () => ; + +export default login; diff --git a/app/newproject/page.js b/app/newproject/page.js new file mode 100644 index 000000000..617bebfc2 --- /dev/null +++ b/app/newproject/page.js @@ -0,0 +1,10 @@ +import ProtectedRoute from '@/components/Protected'; +import NewWebProject from '@/modules/projects/NewWebProject'; + +const newproject = () => ( + + + +); + +export default newproject; diff --git a/app/page.js b/app/page.js new file mode 100644 index 000000000..3ac175a66 --- /dev/null +++ b/app/page.js @@ -0,0 +1,7 @@ +import WebHome from '../renderer/src/WebHome'; + +const index = () => ( + +); + +export default index; diff --git a/app/profile/page.js b/app/profile/page.js new file mode 100644 index 000000000..bb48f9fb9 --- /dev/null +++ b/app/profile/page.js @@ -0,0 +1,12 @@ +import React from 'react'; +// import Profile from '@/modules/projects/Profile'; +import ProtectedRoute from '@/components/Protected'; + +const ProfilePage = () => ( + + Profile page + {/* */} + +); + +export default ProfilePage; diff --git a/app/projects/page.jsx b/app/projects/page.jsx new file mode 100644 index 000000000..c938d1b54 --- /dev/null +++ b/app/projects/page.jsx @@ -0,0 +1,10 @@ +import ProjectList from '@/modules/projects/WebProjectList'; +import ProtectedRoute from '@/components/Protected'; + +const projects = () => ( + + + +); + +export default projects; diff --git a/app/providers.js b/app/providers.js new file mode 100644 index 000000000..00e1edd97 --- /dev/null +++ b/app/providers.js @@ -0,0 +1,26 @@ +'use client'; + +import SyncContextProvider from '@/components/Sync/SyncContextProvider'; +import ScribexContextProvider from '@/components/context/ScribexContext'; +import ProjectContextProvider from '../renderer/src/components/context/ProjectContext'; +import ReferenceContextProvider from '../renderer/src/components/context/ReferenceContext'; +import AuthenticationContextProvider from '../renderer/src/components/Login/AuthenticationContextProvider'; +import AutographaContextProvider from '../renderer/src/components/context/AutographaContext'; + +export function Providers({ children }) { + return ( + + + + + + + {children} + + + + + + + ); +} diff --git a/app/resource/page.jsx b/app/resource/page.jsx new file mode 100644 index 000000000..9498e94e4 --- /dev/null +++ b/app/resource/page.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +// import SectionPlaceholder1 from '../../renderer/src/layouts/editor/SectionPlaceholder1'; +// import SectionPlaceholder2 from '../../renderer/src/layouts/editor/SectionPlaceholder2'; + +const Page = () => ( +
+ {/* + */} +
+); + +export default Page; diff --git a/app/signup/page.jsx b/app/signup/page.jsx new file mode 100644 index 000000000..441ea4b7b --- /dev/null +++ b/app/signup/page.jsx @@ -0,0 +1,8 @@ +/* eslint-disable no-tabs */ +import SignupPage from '@/components/Signup/WebSignup'; + +function Page() { + return ; +} + +export default Page; diff --git a/app/sync/page.js b/app/sync/page.js new file mode 100644 index 000000000..db8ccf987 --- /dev/null +++ b/app/sync/page.js @@ -0,0 +1,11 @@ +import ProtectedRoute from '@/components/Protected'; +// import Sync from '@/modules/projects/Sync'; + +const sync = () => ( + + Sync page + {/* */} + +); + +export default sync; diff --git a/docs/Architecture/CodeStructure.html b/docs/Architecture/CodeStructure.html index 094031570..0ef32d374 100644 --- a/docs/Architecture/CodeStructure.html +++ b/docs/Architecture/CodeStructure.html @@ -1,25 +1,1677 @@ - - - - -Markmap - - - - - - - + + + + + Markmap + + + + + + + + diff --git a/jsconfig.json b/jsconfig.json index d716d235c..a19b392f1 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -27,4 +27,4 @@ ], } } -} \ No newline at end of file +} diff --git a/main/index.js b/main/index.js index b278544a7..158d55538 100644 --- a/main/index.js +++ b/main/index.js @@ -2,6 +2,8 @@ require('@electron/remote/main').initialize(); const { join } = require('path'); const { format } = require('url'); +const config = require("dotenv"); +config.config(); // Packages const { BrowserWindow, app, ipcMain } = require('electron'); diff --git a/package.json b/package.json index b8e33ced0..146e690a8 100644 --- a/package.json +++ b/package.json @@ -129,6 +129,7 @@ "@ory/kratos-client": "^0.10.1", "@radix-ui/react-dialog": "^1.0.3", "@radix-ui/react-dropdown-menu": "^2.0.4", + "@supabase/supabase-js": "^2.26.0", "@tailwindcss/forms": "^0.5.2", "@tailwindcss/typography": "^0.5.9", "@xelah/type-perf-html": "^1.0.1", @@ -137,6 +138,7 @@ "bible-reference-rcl": "1.1.0", "clsx": "1.1.1", "crypto-js": "^4.1.1", + "dotenv": "^16.3.1", "electron-is-dev": "^2.0.0", "electron-log": "4.4.7", "electron-next": "^3.1.5", @@ -158,7 +160,7 @@ "markdown-translatable": "1.3.0", "md5": "^2.3.0", "moment": "^2.29.3", - "next": "^13.0.0", + "next": "^13.4.10", "next-images": "^1.8.4", "next-on-netlify": "^3.0.1", "next-optimized-images": "^2.6.2", @@ -202,4 +204,4 @@ "word-aligner": "$word-aligner", "@mui/lab": "$@mui/lab" } -} +} \ No newline at end of file diff --git a/pages/_app.js b/pages/_app.js deleted file mode 100644 index d5d1a416a..000000000 --- a/pages/_app.js +++ /dev/null @@ -1,25 +0,0 @@ -/* eslint-disable react/prop-types */ -import NProgress from 'nprogress'; -import Router from 'next/router'; -import '../styles/nprogress.css'; -import '../styles/globals.css'; -// import { initializeParse } from '@parse/react-ssr'; -// import { environment } from '../renderer/environment'; - -// initializeParse( -// environment.SERVER_URL, -// environment.APPLICATION_ID, -// environment.MASTER_KEY, -// ); - -Router.events.on('routeChangeStart', () => NProgress.start()); -Router.events.on('routeChangeComplete', () => NProgress.done()); -Router.events.on('routeChangeError', () => NProgress.done()); - -function Autographa({ Component, pageProps }) { - return ( - - ); -} - -export default Autographa; diff --git a/pages/_document.js b/pages/_document.js deleted file mode 100644 index 0ae83bd52..000000000 --- a/pages/_document.js +++ /dev/null @@ -1,22 +0,0 @@ -import Document, { - Html, Head, NextScript, Main, -} from 'next/document'; - -export default class MyDocument extends Document { - static async getInitialProps(ctx) { - const initialProps = await Document.getInitialProps(ctx); - return { ...initialProps }; - } - - render() { - return ( - - - -
- - - - ); - } -} diff --git a/pages/editor.js b/pages/editor.js deleted file mode 100644 index 4c12c602f..000000000 --- a/pages/editor.js +++ /dev/null @@ -1,28 +0,0 @@ -import AuthenticationContextProvider from '@/components/Login/AuthenticationContextProvider'; -import ProjectContextProvider from '@/components/context/ProjectContext'; -import ReferenceContextProvider from '@/components/context/ReferenceContext'; -import CustomNavigationContextProvider from '@/components/context/CustomNavigationContext'; -import EditorLayout from '@/layouts/editor/Layout'; -import Editor from '@/modules/editor/Editor'; - -export default function ReferenceSelector() { - return ( - - - - - -
-
column 1
-
column 2
-
- -
-
-
-
-
-
-
- ); -} diff --git a/pages/error.js b/pages/error.js deleted file mode 100644 index df3dd2968..000000000 --- a/pages/error.js +++ /dev/null @@ -1,7 +0,0 @@ -import Error from '../renderer/src/components/Error/Error'; - -const error = () => ( - -); - -export default error; diff --git a/pages/home.js b/pages/home.js deleted file mode 100644 index 47e855866..000000000 --- a/pages/home.js +++ /dev/null @@ -1,43 +0,0 @@ -import ProjectContextProvider from '@/components/context/ProjectContext'; -import ReferenceContextProvider from '@/components/context/ReferenceContext'; -import EditorLayout from '@/layouts/editor/Layout'; -import AuthenticationContextProvider from '@/components/Login/AuthenticationContextProvider'; -import dynamic from 'next/dynamic'; -import CustomNavigationContextProvider from '@/components/context/CustomNavigationContext'; -import SectionPlaceholder from '@/layouts/editor/SectionPlaceholder1'; - -import AutographaContextProvider from '@/components/context/AutographaContext'; -import ScribexContextProvider from '@/components/context/ScribexContext'; -import ScribeX from '@/components/EditorPage/Scribex/ScribeX'; - -const Scribex = dynamic( - () => import('@/components/EditorPage/Scribex/Scribex'), - { ssr: false }, -); - -const home = () => ( - <> - - - - - - -
- -
- {/* */} -
-
-
-
-
-
-
-
- - {/* */} - -); - -export default home; diff --git a/pages/index.js b/pages/index.js deleted file mode 100644 index 4956cabff..000000000 --- a/pages/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import Meta from '../renderer/src/Meta'; - -const index = () => ( -
- - {/* Commented for development purpose */} - - {/* */} -
-); - -export default index; diff --git a/pages/login.js b/pages/login.js deleted file mode 100644 index 25972006f..000000000 --- a/pages/login.js +++ /dev/null @@ -1,10 +0,0 @@ -import Login from '@/components/Login/Login'; -import AuthenticationContextProvider from '@/components/Login/AuthenticationContextProvider'; - -const login = () => ( - - - -); - -export default login; diff --git a/pages/main.js b/pages/main.js deleted file mode 100644 index acea3de48..000000000 --- a/pages/main.js +++ /dev/null @@ -1,10 +0,0 @@ -// import { environment } from '../renderer/environment'; -import Main from '../renderer/src/components/main'; - -const main = () => ( -
-
-
-); - -export default main; diff --git a/pages/newproject.js b/pages/newproject.js deleted file mode 100644 index 4d8ff71f1..000000000 --- a/pages/newproject.js +++ /dev/null @@ -1,16 +0,0 @@ -import NewProject from '@/modules/projects/NewProject'; -import AuthenticationContextProvider from '@/components/Login/AuthenticationContextProvider'; -import ProjectContextProvider from '@/components/context/ProjectContext'; -import ReferenceContextProvider from '@/components/context/ReferenceContext'; - -const newproject = () => ( - - - - - - - -); - -export default newproject; diff --git a/pages/profile.js b/pages/profile.js deleted file mode 100644 index 65e794ee3..000000000 --- a/pages/profile.js +++ /dev/null @@ -1,10 +0,0 @@ -import Profile from '@/modules/projects/Profile'; -import AuthenticationContextProvider from '@/components/Login/AuthenticationContextProvider'; - -const profile = () => ( - - - -); - -export default profile; diff --git a/pages/projects.js b/pages/projects.js deleted file mode 100644 index f0754cccb..000000000 --- a/pages/projects.js +++ /dev/null @@ -1,16 +0,0 @@ -import ProjectList from '@/modules/projects/ProjectList'; -import AuthenticationContextProvider from '@/components/Login/AuthenticationContextProvider'; -import AutographaContextProvider from '@/components/context/AutographaContext'; -import ReferenceContextProvider from '@/components/context/ReferenceContext'; - -const projects = () => ( - - - - - - - -); - -export default projects; diff --git a/pages/signup.js b/pages/signup.js deleted file mode 100644 index d3512d831..000000000 --- a/pages/signup.js +++ /dev/null @@ -1,11 +0,0 @@ -import Signup from '../renderer/src/components/Signup/Signup'; -import Meta from '../renderer/src/Meta'; - -const signup = () => ( -
- - -
-); - -export default signup; diff --git a/pages/sync.js b/pages/sync.js deleted file mode 100644 index 7749780f2..000000000 --- a/pages/sync.js +++ /dev/null @@ -1,13 +0,0 @@ -import Sync from '@/modules/projects/Sync'; -import AuthenticationContextProvider from '@/components/Login/AuthenticationContextProvider'; -import SyncContextProvider from '@/components/Sync/SyncContextProvider'; - -const projects = () => ( - - - - - -); - -export default projects; diff --git a/pages/wip/ColumnLayout.js b/pages/wip/ColumnLayout.js deleted file mode 100644 index 0f4838280..000000000 --- a/pages/wip/ColumnLayout.js +++ /dev/null @@ -1,76 +0,0 @@ -import React, { useState } from 'react'; -import PropTypes from 'prop-types'; -import Editor from '@/modules/editor/Editor'; -import RowSection from './RowSection'; - -export default function ColumnSection(props) { - const { colNumber } = props; - const maxRows = 3; - const minRows = 1; - const [showColOne, setShowColOne] = useState([true]); - const [showColTwo, setShowColTwo] = useState([true]); - - function addRow(col, index) { - const rows = col === 'colone' ? { ...showColOne } : { ...showColTwo }; - if (index < maxRows - 1) { rows[index + 1] = true; } - if (col === 'colone') { setShowColOne({ ...rows }); } else { setShowColTwo({ ...rows }); } - } - - function removeRow(col, index) { - const rows = col === 'colone' ? { ...showColOne } : { ...showColTwo }; - if (index > minRows - 1) { rows[index] = false; } - if (col === 'colone') { setShowColOne({ ...rows }); } else { setShowColTwo({ ...rows }); } - } - - function expandRow(col, index) { - const rows = col === 'colone' ? { ...showColOne } : { ...showColTwo }; - if (index < maxRows - 1) { rows[index + 1] = true; } - if (col === 'colone') { setShowColOne({ ...rows }); } else { setShowColTwo({ ...rows }); } - } - - return ( - <> - {colNumber === 3 - && ( -
- { - [...Array(maxRows)].map((e, i) => ( - addRow('colone', i)} - removeRow={() => removeRow('colone', i)} - /> -)) - } -
- )} - - {colNumber >= 2 - && ( -
- { - [...Array(maxRows)].map((e, i) => ( - addRow('coltwo', i)} - expandRow={() => expandRow('coltwo', i)} - removeRow={() => removeRow('coltwo', i)} - /> -)) - } -
- )} - -
- -
- - ); -} -ColumnSection.propTypes = { - colNumber: PropTypes.number, -}; diff --git a/pages/wip/RowSection.js b/pages/wip/RowSection.js deleted file mode 100644 index e787b7d1b..000000000 --- a/pages/wip/RowSection.js +++ /dev/null @@ -1,101 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { SquaresPlusIcon, CogIcon, XMarkIcon } from '@heroicons/react/24/outline'; - -import { classNames } from '@/util/classNames'; - -export default function ColumnOne(props) { - const { - - rowCount, - ishidden, - expandRow, - addRow, - removeRow, - } = props; - - return ( -
- -
-
-
-
- Bible -
- -
- - - - - -
-
-
-
- -
- -
-

asf

-

asf

-

asf

-

asf

-

asf

-

asf

-

asf

-
- -
-
-
- Load a Module - {rowCount} -
- -
-
- -
- - - -
- ); -} - -ColumnOne.propTypes = { - rowCount: PropTypes.string, - ishidden: PropTypes.bool, - expandRow: PropTypes.func, - addRow: PropTypes.func, - removeRow: PropTypes.func, -}; diff --git a/pages/wip/WaveForm.js b/pages/wip/WaveForm.js deleted file mode 100644 index b5bd2469d..000000000 --- a/pages/wip/WaveForm.js +++ /dev/null @@ -1,94 +0,0 @@ -import { useEffect, useRef, useState } from 'react'; -import PropTypes from 'prop-types'; - -import { PlayIcon, PauseIcon } from '@heroicons/react/24/solid'; - -export default function AudioWaveForm(props) { - const { - height, - waveColor, - btnColor, - barGap, - barWidth, - barRadius, - } = props; - - const formWaveSurferOptions = (ref) => ({ - container: ref, - waveColor, - progressColor: '#0073E5', - cursorColor: 'OrangeRed', - barWidth: barWidth ?? 1, - barRadius: barRadius ?? 1, - barGap: barGap ?? 2, - responsive: true, - height, - normalize: true, - partialRender: true, - hideScrollbar: true, - }); - - const waveformRef = useRef(null); - const wavesurfer = useRef(null); - const [playing, setPlaying] = useState(false); - - const url = 'https://www.mfiles.co.uk/mp3-downloads/brahms-st-anthony-chorale-theme-two-pianos.mp3'; - - const create = async () => { - const WaveSurfer = (await import('wavesurfer.js')).default; - - const options = formWaveSurferOptions(waveformRef.current); - wavesurfer.current = WaveSurfer.create(options); - - wavesurfer.current.load(url); - }; - - useEffect(() => { - create(); - - return () => { - if (wavesurfer.current) { - wavesurfer.current.destroy(); - } - }; - }, []); - - const handlePlayPause = () => { - setPlaying(!playing); - wavesurfer.current.playPause(); - }; - - return ( -
-
-
-
- -
- ); -} - -AudioWaveForm.propTypes = { - height: PropTypes.number, - waveColor: PropTypes.string, - btnColor: PropTypes.string, - barGap: PropTypes.number, - barWidth: PropTypes.number, - barRadius: PropTypes.number, -}; diff --git a/pages/wip/editor-audio.js b/pages/wip/editor-audio.js deleted file mode 100644 index bf4263693..000000000 --- a/pages/wip/editor-audio.js +++ /dev/null @@ -1,539 +0,0 @@ -import dynamic from 'next/dynamic'; - -import { - TrashIcon, - MicrophoneIcon, - ChatIcon, - RefreshIcon, - ChevronDownIcon, - CogIcon, - MinusIcon, - PlusIcon, - ChatBubbleBottomCenterTextIcon, - SpeakerphoneIcon, - ArrowNarrowRightIcon, - CheckIcon, - XMarkIcon, - PencilIcon, -} from '@heroicons/react/24/outline'; - -import AuthenticationContextProvider from '@/components/Login/AuthenticationContextProvider'; -import ProjectContextProvider from '@/components/context/ProjectContext'; -import ReferenceContextProvider from '@/components/context/ReferenceContext'; -import CustomNavigationContextProvider from '@/components/context/CustomNavigationContext'; -import AutographaContextProvider from '@/components/context/AutographaContext'; -import EditorLayout from '@/layouts/editor/Layout'; -import Editor from '@/modules/editor/Editor'; -import BibleNavigation from '@/modules/biblenavigation/BibleNavigation'; - -import { classNames } from '@/util/classNames'; - -import PlayIcon from '@/icons/basil/Outline/Media/Play.svg'; -import PauseIcon from '@/icons/basil/Outline/Media/Pause.svg'; - -const AudioWaveform = dynamic(() => import('./WaveForm'), { ssr: false }); - -const verses = [ - { - id: 1, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-01.jpg', - text: 'Then God said, “Let there be light!” And there was light. God saw that the light was good and called it “day.” He separated it from the darkness, which he called “night.” God created the light on the first day of creation.', - }, - { - id: 2, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-02.jpg', - text: 'On the second day of creation, God said, “Let there be an expanse above the waters.” And there was an expanse. God called this expanse “sky.”', - }, - { - id: 3, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-03.jpg', - text: 'On the third day, God said, “Let the water come together in one place and the dry land appear.” He called the dry land “earth,” and he called the water “seas.” God saw that what he had created was good.', - }, - { - id: 4, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-04.jpg', - text: 'Then God said, “Let the earth produce all kinds of trees and plants.” And that is what happened. God saw that what he had created was good.', - }, - { - id: 5, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-05.jpg', - text: 'On the fourth day of creation, God said, “Let there be lights in the sky.” And the sun, the moon, and the stars appeared. God made them to give light to the earth and to mark day and night, seasons and years. God saw that what he had created was good.', - }, - { - id: 6, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-06.jpg', - text: 'On the fifth day, God said, “Let living things fill the waters, and birds fly in the sky.” This is how he made everything that swims in the water and all the birds. God saw that it was good, and he blessed them.', - }, - { - id: 7, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-07.jpg', - text: 'On the sixth day of creation, God said, “Let there be all kinds of land animals!” And it happened just like God said. Some were farm animals, some crawled on the ground, and some were wild. And God saw that it was good.', - }, - { - id: 8, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-08.jpg', - text: 'Then God said, “Let us make human beings in our image to be like us. They will rule over the earth and all the animals.”', - }, - { - id: 9, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-09.jpg', - text: 'So God took some soil, formed it into a man, and breathed life into him. This man’s name was Adam. God planted a large garden where Adam could live, and put him there to care for it.', - }, - { - id: 10, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-10.jpg', - text: 'In the middle of the garden, God planted two special trees—the tree of life and the tree of the knowledge of good and evil. God told Adam that he could eat from any tree in the garden except from the tree of the knowledge of good and evil. If he ate from this tree, he would die.', - }, -]; - -const mainChunk = { - id: 1, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-01.jpg', - text: 'आदि में परमेश्वर ने आकाश एवं पृथ्वी को रचा. पृथ्वी बिना आकार के तथा खाली थी, और पानी के ऊपर अंधकार था तथा परमेश्वर का आत्मा जल के ऊपर मंडरा रहा था. उसके बाद परमेश्वर ने कहा, “प्रकाश हो जाए,” और प्रकाश हो गया. परमेश्वर ने प्रकाश को देखा कि अच्छा है. परमेश्वर ने प्रकाश को अंधकार से अलग किया.', -}; - -const chunks = [ - { - id: 1, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-02.jpg', - text: 'परमेश्वर ने प्रकाश को “दिन” तथा अंधकार को “रात” कहा और शाम हुई, फिर सुबह हुई—इस प्रकार पहला दिन हो गया.”', - }, - { - id: 2, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-03.jpg', - text: 'फिर परमेश्वर ने कहा, “जल के बीच ऐसा विभाजन हो कि जल', - }, - { - id: 3, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-04.jpg', - text: 'इसलिये परमेश्वर ने नीचे के जल और ऊपर के जल को अलग किया. यह वैसा ही हो गया.', - }, - { - id: 4, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-05.jpg', - text: 'परमेश्वर ने इस अंतर को “आकाश” नाम दिया. और शाम हुई, फिर सुबह हुई—इस प्रकार दूसरा दिन हो गया.', - }, - { - id: 5, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-06.jpg', - text: 'फिर परमेश्वर ने कहा, “आकाश के नीचे का पानी एक जगह इकट्ठा हो जाए और सूखी भूमि दिखाई दे” और वैसा ही हो गया.', - }, - { - id: 6, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-07.jpg', - text: 'परमेश्वर ने सूखी भूमि को “धरती” तथा जो जल इकट्ठा हुआ उसको “सागर” कहा और परमेश्वर ने देखा कि वह अच्छा है.', - }, - { - id: 7, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-08.jpg', - text: 'फिर परमेश्वर ने कहा, “पृथ्वी से हरी घास तथा पेड़ उगने लगें: और पृथ्वी पर फलदाई वृक्षों में फल लगने लगें.” और वैसा हो गया.”', - }, -]; - -export default function ReferenceSelector() { - return ( - - - - - - -
-
-
- -
- Source -
-
-
- {verses.map((story, index) => ( -
-
-
-
- {index + 1} -
-
-

- {story.text} -

-
-
- -
-
- ))} -
-
-
- -
-
-
- {2} -
-

- {mainChunk.text} -

- - -
- -
- -
-
- - Automatic Speech to text - suggestion - - -
-
- - -
-
-
- {chunks.map((story, index) => ( -
-
-
-
- -
- {1 + index} -
-

- {story.text} -

-
-
-
- - {(index === 0 - || index - === 1) && ( - - )} -
- -
-
- a -
-
- b -
-
- c -
-
-
-
- -
-
- ))} -
-
-
- -
-
-
-
- audio -
- -
- -
-
- speed -
- -
- -
-
-
- rewind -
- -
- -
-
- record -
- -
- -
-
- play -
- -
- -
-
- pause -
- -
- -
-
- stop -
- -
- -
-
- Volume -
-
-
-
- -
-
- Takes -
-
-
- a -
-
- b -
-
- c -
-
-
- -
-
- settings -
-
- -
-
-
- -
- -
-
-
- - - - - - - ); -} diff --git a/pages/wip/editor-obs.js b/pages/wip/editor-obs.js deleted file mode 100644 index 78ef90430..000000000 --- a/pages/wip/editor-obs.js +++ /dev/null @@ -1,150 +0,0 @@ -import React, { useState } from 'react'; - -import { EyeIcon, EyeOffIcon } from '@heroicons/react/24/outline'; -import AuthenticationContextProvider from '@/components/Login/AuthenticationContextProvider'; -import ProjectContextProvider from '@/components/context/ProjectContext'; -import ReferenceContextProvider from '@/components/context/ReferenceContext'; -import CustomNavigationContextProvider from '@/components/context/CustomNavigationContext'; -import AutographaContextProvider from '@/components/context/AutographaContext'; - -import EditorLayout from '@/layouts/editor/Layout'; -import Editor from '@/modules/editor/Editor'; -import { classNames } from '@/util/classNames'; -import RowSection from './RowSection'; - -const stories = [ - { - id: 1, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-01.jpg', - text: 'Then God said, “Let there be light!” And there was light. God saw that the light was good and called it “day.” He separated it from the darkness, which he called “night.” God created the light on the first day of creation.', - }, - { - id: 2, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-02.jpg', - text: 'On the second day of creation, God said, “Let there be an expanse above the waters.” And there was an expanse. God called this expanse “sky.”', - }, - { - id: 3, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-03.jpg', - text: 'On the third day, God said, “Let the water come together in one place and the dry land appear.” He called the dry land “earth,” and he called the water “seas.” God saw that what he had created was good.', - }, - { - id: 4, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-04.jpg', - text: 'Then God said, “Let the earth produce all kinds of trees and plants.” And that is what happened. God saw that what he had created was good.', - }, - { - id: 5, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-05.jpg', - text: 'On the fourth day of creation, God said, “Let there be lights in the sky.” And the sun, the moon, and the stars appeared. God made them to give light to the earth and to mark day and night, seasons and years. God saw that what he had created was good.', - }, - { - id: 6, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-06.jpg', - text: 'On the fifth day, God said, “Let living things fill the waters, and birds fly in the sky.” This is how he made everything that swims in the water and all the birds. God saw that it was good, and he blessed them.', - }, - { - id: 7, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-07.jpg', - text: 'On the sixth day of creation, God said, “Let there be all kinds of land animals!” And it happened just like God said. Some were farm animals, some crawled on the ground, and some were wild. And God saw that it was good.', - }, - { - id: 8, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-08.jpg', - text: 'Then God said, “Let us make human beings in our image to be like us. They will rule over the earth and all the animals.”', - }, - { - id: 9, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-09.jpg', - text: 'So God took some soil, formed it into a man, and breathed life into him. This man’s name was Adam. God planted a large garden where Adam could live, and put him there to care for it.', - }, - { - id: 10, - img: 'https://cdn.door43.org/obs/jpg/360px/obs-en-01-10.jpg', - text: 'In the middle of the garden, God planted two special trees—the tree of life and the tree of the knowledge of good and evil. God told Adam that he could eat from any tree in the garden except from the tree of the knowledge of good and evil. If he ate from this tree, he would die.', - }, -]; - -export default function ReferenceSelector() { - const [blur, setBlur] = useState(false); - - return ( - - - - - - -
- -
- -
- -
- -
-
-
-
- Bible -
- -
- -
-
-
-
- -
- { - stories.map((story) => ( -
- -

- {story.text} -

-
- )) - } -
-
- -
- - { - stories.map((story) => ( -
-

- {story.text} -

-
- )) - } -
-
-
-
-
-
-
-
-
- ); -} diff --git a/pages/wip/folder.svg b/pages/wip/folder.svg deleted file mode 100644 index 5156c9037..000000000 --- a/pages/wip/folder.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/pages/wip/newproject.js b/pages/wip/newproject.js deleted file mode 100644 index 40c38b2d8..000000000 --- a/pages/wip/newproject.js +++ /dev/null @@ -1,224 +0,0 @@ -import { useState } from 'react'; - -import ProjectsLayout from '@/layouts/projects/Layout'; -import AuthenticationContextProvider from '@/components/Login/AuthenticationContextProvider'; -import ProjectContextProvider from '@/components/context/ProjectContext'; -import ReferenceContextProvider from '@/components/context/ReferenceContext'; - -import { - CloudArrowDownIcon, - CloudArrowUpIcon, - MagnifyingGlassIcon, -} from '@heroicons/react/24/outline'; -import { classNames } from '@/util/classNames'; -import FolderIcon from './folder.svg'; - -function GridRow({ - title, lastSync, selected, isUpload, uploadPercentage = 10, -}) { - return ( - <> -
-
- -
- - - {title} - - - {lastSync} - -
- {isUpload && ( -
-
-
- )} - - ); -} - -export default function NewProject() { - return ( - - - - -
-
-
- Local Projects - -
-
-
- Autographa Project -
-
- Last Sync -
-
-
- - - - - -
-
- -
-
- Cloud PROJECTS - - - - -
-
-
- -
- - -
-
- -
- - -
- Arpit Jacob -
-
-
-
- - - - - -
-
-
-
-
-
-
- ); -} diff --git a/pages/wip/row-column-example.js b/pages/wip/row-column-example.js deleted file mode 100644 index b53c73a97..000000000 --- a/pages/wip/row-column-example.js +++ /dev/null @@ -1,49 +0,0 @@ -import React, { useState } from 'react'; -import AuthenticationContextProvider from '@/components/Login/AuthenticationContextProvider'; -import ProjectContextProvider from '@/components/context/ProjectContext'; -import ReferenceContextProvider from '@/components/context/ReferenceContext'; -import CustomNavigationContextProvider from '@/components/context/CustomNavigationContext'; -import EditorLayout from '@/layouts/editor/Layout'; -import AutographaContextProvider - from '@/components/context/AutographaContext'; -import ColumnLayout from './ColumnLayout'; - -export default function ReferenceSelector() { - const [colNumber, setColNumber] = useState(1); - - function addColumn() { - const col = colNumber < 3 ? colNumber + 1 : 1; - // console.log(col); - setColNumber(col); - } - - return ( - - - - - - - - - -
- -
- -
-
-
-
-
-
- ); -} diff --git a/public/brands/scribe.png b/public/brands/scribe.png new file mode 100644 index 000000000..98f7d16d8 Binary files /dev/null and b/public/brands/scribe.png differ diff --git a/public/icons/Common/AdjustmentsVertical.svg b/public/icons/Common/AdjustmentsVertical.svg new file mode 100644 index 000000000..6bcba685b --- /dev/null +++ b/public/icons/Common/AdjustmentsVertical.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/ArchiveBox.svg b/public/icons/Common/ArchiveBox.svg new file mode 100644 index 000000000..5fc675e5b --- /dev/null +++ b/public/icons/Common/ArchiveBox.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/ArrowLeft.svg b/public/icons/Common/ArrowLeft.svg new file mode 100644 index 000000000..949d08a54 --- /dev/null +++ b/public/icons/Common/ArrowLeft.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/ArrowPath.svg b/public/icons/Common/ArrowPath.svg new file mode 100644 index 000000000..e1945758d --- /dev/null +++ b/public/icons/Common/ArrowPath.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/Check.svg b/public/icons/Common/Check.svg new file mode 100644 index 000000000..cc6e0f655 --- /dev/null +++ b/public/icons/Common/Check.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/ChevronDown.svg b/public/icons/Common/ChevronDown.svg new file mode 100644 index 000000000..ac802d948 --- /dev/null +++ b/public/icons/Common/ChevronDown.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/ChevronRight.svg b/public/icons/Common/ChevronRight.svg new file mode 100644 index 000000000..5b8b31419 --- /dev/null +++ b/public/icons/Common/ChevronRight.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/ChevronUp.svg b/public/icons/Common/ChevronUp.svg new file mode 100644 index 000000000..48bf4b209 --- /dev/null +++ b/public/icons/Common/ChevronUp.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/ChevronUpDown.svg b/public/icons/Common/ChevronUpDown.svg new file mode 100644 index 000000000..3c3c4a829 --- /dev/null +++ b/public/icons/Common/ChevronUpDown.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/Cog.svg b/public/icons/Common/Cog.svg new file mode 100644 index 000000000..4db4769ed --- /dev/null +++ b/public/icons/Common/Cog.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/ComputerDesktop.svg b/public/icons/Common/ComputerDesktop.svg new file mode 100644 index 000000000..2f82c0a90 --- /dev/null +++ b/public/icons/Common/ComputerDesktop.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/EllipsisVertical.svg b/public/icons/Common/EllipsisVertical.svg new file mode 100644 index 000000000..8afdc9169 --- /dev/null +++ b/public/icons/Common/EllipsisVertical.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/ExclamationTriangle.svg b/public/icons/Common/ExclamationTriangle.svg new file mode 100644 index 000000000..2e0edf308 --- /dev/null +++ b/public/icons/Common/ExclamationTriangle.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/InformationCircle.svg b/public/icons/Common/InformationCircle.svg new file mode 100644 index 000000000..9c1994aa9 --- /dev/null +++ b/public/icons/Common/InformationCircle.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/MagnifyingGlass.svg b/public/icons/Common/MagnifyingGlass.svg new file mode 100644 index 000000000..d7cafa1b6 --- /dev/null +++ b/public/icons/Common/MagnifyingGlass.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/Minus.svg b/public/icons/Common/Minus.svg new file mode 100644 index 000000000..57a3715e0 --- /dev/null +++ b/public/icons/Common/Minus.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/Pencil.svg b/public/icons/Common/Pencil.svg new file mode 100644 index 000000000..4d22810d3 --- /dev/null +++ b/public/icons/Common/Pencil.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/PencilSquare.svg b/public/icons/Common/PencilSquare.svg new file mode 100644 index 000000000..dc4d35608 --- /dev/null +++ b/public/icons/Common/PencilSquare.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/Plus.svg b/public/icons/Common/Plus.svg new file mode 100644 index 000000000..a925b905a --- /dev/null +++ b/public/icons/Common/Plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/QuestionMarkCircle.svg b/public/icons/Common/QuestionMarkCircle.svg new file mode 100644 index 000000000..702132598 --- /dev/null +++ b/public/icons/Common/QuestionMarkCircle.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/SquaresPlus.svg b/public/icons/Common/SquaresPlus.svg new file mode 100644 index 000000000..52b0bc090 --- /dev/null +++ b/public/icons/Common/SquaresPlus.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/Trash.svg b/public/icons/Common/Trash.svg new file mode 100644 index 000000000..c3daca02b --- /dev/null +++ b/public/icons/Common/Trash.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Common/XMark.svg b/public/icons/Common/XMark.svg new file mode 100644 index 000000000..457d8e626 --- /dev/null +++ b/public/icons/Common/XMark.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Xelah/ArrowDownOnSquare.svg b/public/icons/Xelah/ArrowDownOnSquare.svg new file mode 100644 index 000000000..3e2cc02c3 --- /dev/null +++ b/public/icons/Xelah/ArrowDownOnSquare.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Xelah/ArrowUturnLeft.svg b/public/icons/Xelah/ArrowUturnLeft.svg new file mode 100644 index 000000000..ea2c6d779 --- /dev/null +++ b/public/icons/Xelah/ArrowUturnLeft.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Xelah/ArrowUturnRight.svg b/public/icons/Xelah/ArrowUturnRight.svg new file mode 100644 index 000000000..24e087f2a --- /dev/null +++ b/public/icons/Xelah/ArrowUturnRight.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Xelah/Bars2.svg b/public/icons/Xelah/Bars2.svg new file mode 100644 index 000000000..0b24f3b92 --- /dev/null +++ b/public/icons/Xelah/Bars2.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Xelah/Bars4.svg b/public/icons/Xelah/Bars4.svg new file mode 100644 index 000000000..5ddbc0bfc --- /dev/null +++ b/public/icons/Xelah/Bars4.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Xelah/Copy.svg b/public/icons/Xelah/Copy.svg new file mode 100644 index 000000000..6c6be15ef --- /dev/null +++ b/public/icons/Xelah/Copy.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Xelah/Paste.svg b/public/icons/Xelah/Paste.svg new file mode 100644 index 000000000..e1da9114b --- /dev/null +++ b/public/icons/Xelah/Paste.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Xelah/Plus.svg b/public/icons/Xelah/Plus.svg new file mode 100644 index 000000000..9486ee03b --- /dev/null +++ b/public/icons/Xelah/Plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/Xelah/RectangleStack.svg b/public/icons/Xelah/RectangleStack.svg new file mode 100644 index 000000000..b8254e223 --- /dev/null +++ b/public/icons/Xelah/RectangleStack.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/logo.svg b/public/icons/logo.svg new file mode 100644 index 000000000..1834738bf --- /dev/null +++ b/public/icons/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/illustrations/greenCheck.png b/public/illustrations/greenCheck.png new file mode 100644 index 000000000..19c7fc84d Binary files /dev/null and b/public/illustrations/greenCheck.png differ diff --git a/public/illustrations/standing.png b/public/illustrations/standing.png new file mode 100644 index 000000000..064d7b785 Binary files /dev/null and b/public/illustrations/standing.png differ diff --git a/renderer/pages/index.js b/renderer/pages/index.js index e460b60aa..daa1e893f 100644 --- a/renderer/pages/index.js +++ b/renderer/pages/index.js @@ -6,6 +6,7 @@ const Home = dynamic( () => import('../src/Home'), { ssr: false }, ); + const index = () => ( <> diff --git a/renderer/src/Home.js b/renderer/src/Home.js index e96a5fc47..b51891b6f 100644 --- a/renderer/src/Home.js +++ b/renderer/src/Home.js @@ -1,5 +1,7 @@ +/* eslint-disable no-nested-ternary */ /* eslint-disable react/jsx-no-useless-fragment */ -import React from 'react'; + +import { useContext, useState, useEffect } from 'react'; import Login from './components/Login/Login'; import AuthenticationContextProvider, { AuthenticationContext } from './components/Login/AuthenticationContextProvider'; import { loadUsers } from './core/Login/handleJson'; @@ -12,11 +14,10 @@ import { getorPutAppLangage } from './core/projects/handleProfile'; import i18n from './translations/i18n'; const Home = () => { - const { states, action } = React.useContext(AuthenticationContext); - const [token, setToken] = React.useState(); - const [user, setUser] = React.useState(); - - React.useEffect(() => { + const { states, action } = useContext(AuthenticationContext); + const [token, setToken] = useState(); + const [user, setUser] = useState(); + useEffect(() => { logger.debug('Home.js', 'Triggers loadUsers for the users list'); loadUsers(); }, []); @@ -41,9 +42,9 @@ const Home = () => { } }; - React.useEffect(() => { + useEffect(() => { validateUser(); - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [states.accessToken]); return ( diff --git a/renderer/src/WebHome.js b/renderer/src/WebHome.js new file mode 100644 index 000000000..3cf91c8f5 --- /dev/null +++ b/renderer/src/WebHome.js @@ -0,0 +1,33 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useRouter } from 'next/navigation'; +import { getorPutAppLangage } from './core/projects/handleProfile'; +import i18n from './translations/i18n'; +import { getSupabaseSession } from '../../supabase'; +import WebLogin from './components/Login/WebLogin'; + +const WebHome = () => { + const [session, setSession] = useState(); + const router = useRouter(); + + useEffect(() => { + const checkSession = async () => { + const data = await getSupabaseSession(); + if (data.session) { + setSession(data.session); + const appLangCode = await getorPutAppLangage('get', data.session.user.email); + if (i18n.language !== appLangCode) { + i18n.changeLanguage(appLangCode); + } + router.push('/projects'); + } + }; + + checkSession(); + }, [router, session]); + + return ( + ); +}; +export default WebHome; diff --git a/renderer/src/components/EditorPage/ObsEditor/ObsEditor.js b/renderer/src/components/EditorPage/ObsEditor/ObsEditor.js index 6fc876fee..444fd7d3e 100644 --- a/renderer/src/components/EditorPage/ObsEditor/ObsEditor.js +++ b/renderer/src/components/EditorPage/ObsEditor/ObsEditor.js @@ -15,6 +15,7 @@ import { splitStringByLastOccurance } from '@/util/splitStringByLastMarker'; import EditorPanel from './EditorPanel'; import * as logger from '../../../logger'; import packageInfo from '../../../../../package.json'; +import { newPath } from '../../../../../supabase'; export const getDetails = () => new Promise((resolve) => { logger.debug('ObsEditor.js', 'In getDetails() for fetching the burrito file of current project'); @@ -31,10 +32,33 @@ export const getDetails = () => new Promise((resolve) => { }); }); }); + +export const getWebDetails = () => new Promise((resolve) => { + localforage.getItem('userProfile').then((value) => { + const username = value?.user?.email; + localforage.getItem('currentProject').then((projectName) => { + const path = require('path'); + const projectsDir = `${newPath}/${username}/projects/${projectName}`; + const metaPath = `${newPath}/${username}/projects/${projectName}/metadata.json`; + resolve({ + projectName, username, projectsDir, metaPath, path, + }); + }); + }); +}); + +function checkDetailsFunc() { + if (isElectron()) { + return getDetails(); + } + return getWebDetails(); +} + const ObsEditor = () => { const [mdData, setMdData] = useState(); const [directoryName, setDirectoryName] = useState(); const { state: { obsNavigation, loadData }, actions: { setLoadData } } = useContext(ReferenceContext); + const updateStory = (story) => { logger.debug('ObsEditor.js', 'In updateStory for upadting the story to the backend md file'); setMdData(story); @@ -52,12 +76,12 @@ const ObsEditor = () => { } }); const storyStr = title + body + end; - getDetails().then((value) => { + checkDetailsFunc().then((value) => { const bookID = obsNavigation.toString().padStart(2, 0); writeToFile({ username: value.username, projectname: value.projectName, - filename: (value.path).join(directoryName, `${bookID}.md`), + filename: isElectron() ? (value.path).join(directoryName, `${bookID}.md`) : `${directoryName}/${bookID}.md`, data: storyStr, }); }); @@ -65,7 +89,7 @@ const ObsEditor = () => { // this function is used to fetch the content from the given story number const readContent = useCallback(() => { setLoadData(false); - getDetails() + checkDetailsFunc() .then(({ projectName, username, projectsDir, metaPath, path, }) => { @@ -78,7 +102,7 @@ const ObsEditor = () => { metaPath, }).then(async (data) => { if (data) { - const _data = JSON.parse(data); + const _data = isElectron() ? JSON.parse(data) : data; Object.entries(_data.ingredients).forEach( ([key]) => { const folderName = key.split(/[(\\)?(/)?]/gm).slice(0); @@ -208,16 +232,16 @@ const ObsEditor = () => { }, [obsNavigation]); useEffect(() => { - if (isElectron()) { - readContent(); - } + readContent(); }, [readContent]); + useEffect(() => { if (loadData === true) { readContent(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [loadData]); + return ( {mdData diff --git a/renderer/src/components/EditorPage/ObsEditor/ObsImage.jsx b/renderer/src/components/EditorPage/ObsEditor/ObsImage.jsx index 33b8e2603..5c682b8f4 100644 --- a/renderer/src/components/EditorPage/ObsEditor/ObsImage.jsx +++ b/renderer/src/components/EditorPage/ObsEditor/ObsImage.jsx @@ -1,20 +1,35 @@ +import { isElectron } from '@/core/handleElectron'; import { environment } from '../../../../environment'; import packageInfo from '../../../../../package.json'; +import { newPath } from '../../../../../supabase'; function ObsImage({ story, online }) { - const path = require('path'); - const newpath = localStorage.getItem('userPath'); - const imageName = story.img.split('/'); - const fileName = imageName[imageName.length - 1]; - const obsImagePath = path.join('file://', newpath, packageInfo.name, 'common', environment.OBS_IMAGE_DIR, fileName); + const path = require('path'); + const newpath = localStorage.getItem('userPath'); + const imageName = story.img.split('/'); + const fileName = imageName[imageName.length - 1]; + const obsImagePath = isElectron() + ? path.join( + 'file://', + newpath, + packageInfo.name, + 'common', + environment.OBS_IMAGE_DIR, + fileName, + ) + : path.join( + newPath, + packageInfo.name, + 'common', + environment.OBS_IMAGE_DIR, + fileName, + ); - return online ? ( - - ) : ( - ( - offline - ) - ); + return online ? ( + + ) : ( + offline + ); } export default ObsImage; diff --git a/renderer/src/components/EditorPage/ObsEditor/ObsWebEditor.js b/renderer/src/components/EditorPage/ObsEditor/ObsWebEditor.js new file mode 100644 index 000000000..c1cac8930 --- /dev/null +++ b/renderer/src/components/EditorPage/ObsEditor/ObsWebEditor.js @@ -0,0 +1,250 @@ +'use client'; + +import { + useState, useEffect, useContext, useCallback, +} from 'react'; +import localforage from 'localforage'; +import { readRefMeta } from '@/core/reference/readRefMeta'; +import { readRefBurrito } from '@/core/reference/readRefBurrito'; +import { readFile } from '@/core/editor/readFile'; +import Editor from '@/modules/editor/Editor'; +import { ReferenceContext } from '@/components/context/ReferenceContext'; +import writeToFile from '@/core/editor/writeToFile'; +import { saveReferenceResource } from '@/core/projects/updateAgSettings'; +import moment from 'moment'; +import { splitStringByLastOccurance } from '@/util/splitStringByLastMarker'; +import EditorPanel from './EditorPanel'; +import * as logger from '../../../logger'; +import packageInfo from '../../../../../package.json'; +import { newPath } from '../../../../../supabase'; + +export const getDetails = () => new Promise((resolve) => { + logger.debug('ObsEditor.js', 'In getDetails() for fetching the burrito file of current project'); + localforage.getItem('userProfile').then((value) => { + const username = value?.username; + localforage.getItem('currentProject').then((projectName) => { + const path = require('path'); + const newpath = localStorage.getItem('userPath'); + const projectsDir = path.join(newpath, packageInfo.name, 'users', username, 'projects', projectName); + const metaPath = path.join(newpath, packageInfo.name, 'users', username, 'projects', projectName, 'metadata.json'); + resolve({ + projectName, username, projectsDir, metaPath, path, + }); + }); + }); +}); + +export const getWebDetails = () => new Promise((resolve) => { + localforage.getItem('userProfile').then((value) => { + const username = value?.user?.email; + localforage.getItem('currentProject').then((projectName) => { + const path = require('path'); + const projectsDir = `${newPath}/${username}/projects/${projectName}`; + const metaPath = `${newPath}/${username}/projects/${projectName}/metadata.json`; + resolve({ + projectName, username, projectsDir, metaPath, path, + }); + }); + }); +}); + +function checkDetailsFunc() { + return getWebDetails(); +} + +const ObsEditor = () => { + const [mdData, setMdData] = useState(); + const [directoryName, setDirectoryName] = useState(); + const { state: { obsNavigation, loadData }, actions: { setLoadData } } = useContext(ReferenceContext); + + const updateStory = (story) => { + logger.debug('ObsEditor.js', 'In updateStory for upadting the story to the backend md file'); + setMdData(story); + let title; let body = ''; let end; + story.forEach((s) => { + if (Object.prototype.hasOwnProperty.call(s, 'title')) { + title = `# ${s.title}\n\n`; + } + if (Object.prototype.hasOwnProperty.call(s, 'end')) { + const foot = ((s.end).trim()); + end = `_${foot}_`; + } + if (Object.prototype.hasOwnProperty.call(s, 'text')) { + body += `![OBS Image](${s.img})\n\n${s.text}\n\n`; + } + }); + const storyStr = title + body + end; + checkDetailsFunc().then((value) => { + const bookID = obsNavigation.toString().padStart(2, 0); + writeToFile({ + username: value.username, + projectname: value.projectName, + filename: `${directoryName}/${bookID}.md`, + data: storyStr, + }); + }); + }; + // this function is used to fetch the content from the given story number + const readContent = useCallback(() => { + setLoadData(false); + checkDetailsFunc() + .then(({ + projectName, username, projectsDir, metaPath, path, + }) => { + readRefMeta({ + projectsDir, + }).then((refs) => { + // setIsLoading(true); + refs.forEach(() => { + readRefBurrito({ + metaPath, + }).then(async (data) => { + if (data) { + const _data = data; + Object.entries(_data.ingredients).forEach( + ([key]) => { + const folderName = key.split(/[(\\)?(/)?]/gm).slice(0); + const dirName = folderName[0]; + setDirectoryName(dirName); + // Fetching data from projectmeta and updating the navigation and lastSeen back + localforage.getItem('currentProject').then(async (projectName) => { + const _projectname = await splitStringByLastOccurance(projectName, '_'); + // const _projectname = projectName?.split('_'); + localforage.getItem('projectmeta').then((value) => { + Object.entries(value).forEach( + ([, _value]) => { + Object.entries(_value).forEach( + ([, resources]) => { + if (resources.identification.name.en === _projectname[0]) { + resources.project[resources.type.flavorType.flavor.name].navigation = obsNavigation; + resources.project[resources.type.flavorType.flavor.name].lastSeen = moment().format(); + } + }, + ); + }, + ); + localforage.setItem('projectmeta', value); + // This func will update the scribe-setting.json file + saveReferenceResource(); + }); + }); + logger.debug('ObsEditor.js', 'Reading the md file for selected OBS story'); + const bookID = obsNavigation?.toString().padStart(2, 0); + if (key === path.join(dirName, `${bookID}.md`)) { + readFile({ + projectname: projectName, + filename: key, + username, + }).then((data) => { + if (data) { + const stories = []; + // eslint-disable-next-line prefer-const + let id = 1; let footer = false; + // eslint-disable-next-line react/prop-types + const allLines = data.split(/\r\n|\n/); + logger.debug('ObsEditor.js', 'Spliting the stories line by line and storing into an array.'); + // Reading line by line + allLines.forEach((line) => { + // To avoid the values after footer, we have added id=0 + if (line && id !== 0) { + if (line.match(/^(\s)*#/gm)) { + // Fetching the header content + const hash = line.match(/# (.*)/); + stories.push({ + id, title: hash[1], + }); + id += 1; + } else if (line.match(/^(\s)*_/gm) || footer === true) { + // Fetching the footer + const objIndex = stories.findIndex(((obj) => obj.id === id)); + if (objIndex !== -1 && Object.prototype.hasOwnProperty.call(stories[objIndex], 'img')) { + stories[objIndex].text = ''; + id += 1; + } + if (line.match(/_(.*)_/g) && footer === false) { + // single line footer + const underscore = line.match(/_(.*)_/); + stories.push({ + id, end: underscore[1], + }); + // Logically footer is the last line of the story + id = 0; + } else { + // To get multi-line footer (footer=true) + footer = true; + if (line.match(/^(\s)*_/gm)) { + // starting of footer + const underscore = line.match(/^(\s)*_(.*)/); + stories.push({ + id, end: underscore[2], + }); + } else if (line.match(/_$/gm)) { + // end of footer + const underscore = line.match(/(.*)_$/); + stories[id - 1].end = `${stories[id - 1].end}\n${underscore[1]}`; + // Logically footer is the last line of the story + id = 0; + } else { + // middle lines of footer if available + stories[id - 1].end = `${stories[id - 1].end}\n${line}`; + } + } + } else if (line.match(/^(\s)*!/gm)) { + // Fetching the IMG url + const objIndex = stories.findIndex(((obj) => obj.id === id)); + if (objIndex !== -1 && Object.prototype.hasOwnProperty.call(stories[objIndex], 'img')) { + stories[objIndex].text = ''; + id += 1; + } + const imgUrl = line.match(/\((.*)\)/); + stories.push({ + id, img: imgUrl[1], + }); + } else { + // Reading the content line by line + const objIndex = stories.findIndex(((obj) => obj.id === id)); + if (objIndex !== -1) { + // Reading first line after img + stories[objIndex].text = line; + id += 1; + } else { + // Reading other lines and appending with previous line data + stories[id - 2].text = `${stories[id - 2].text}\n${line}`; + } + } + } + }); + logger.debug('ObsEditor.js', 'Story for selected navigation is been set to the array for Editor'); + setMdData(stories); + } + }); + } + }, + ); + } + }); + }); + }); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [obsNavigation]); + + useEffect(() => { + readContent(); + }, [readContent]); + + useEffect(() => { + if (loadData === true) { + readContent(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [loadData]); + + return ( + + {mdData + && } + + ); +}; +export default ObsEditor; diff --git a/renderer/src/components/EditorPage/ObsEditor/ReferenceObs.js b/renderer/src/components/EditorPage/ObsEditor/ReferenceObs.js index f43b9cfd1..3db8a8435 100644 --- a/renderer/src/components/EditorPage/ObsEditor/ReferenceObs.js +++ b/renderer/src/components/EditorPage/ObsEditor/ReferenceObs.js @@ -78,9 +78,9 @@ const { t } = useTranslation(); return (
{ isLoading === false ? ( - <> +
{ - stories.map((story, index) => ( + stories?.map((story, index) => (
)) } - +
) : ( )} diff --git a/renderer/src/components/EditorPage/ObsEditor/WebReferenceObs.js b/renderer/src/components/EditorPage/ObsEditor/WebReferenceObs.js new file mode 100644 index 000000000..73e4455cc --- /dev/null +++ b/renderer/src/components/EditorPage/ObsEditor/WebReferenceObs.js @@ -0,0 +1,140 @@ +'use client'; + +import { ReferenceContext } from '@/components/context/ReferenceContext'; +import { + useContext, useEffect, useRef, useState, +} from 'react'; +import PropTypes from 'prop-types'; +import { useTranslation } from 'react-i18next'; +import { checkandDownloadObsImages } from '@/components/Resources/DownloadObsImages/checkandDownloadObsImages'; +// import useNetwork from '@/components/hooks/useNetowrk'; +import LoadingScreen from '../../Loading/LoadingScreen'; +import ObsImage from './ObsImage'; + +const style = { + bold: { + fontWeight: 'bold', + }, + italic: { + fontStyle: 'italic', + }, +}; +const ReferenceObs = ({ stories }) => { + const [isLoading, setIsLoading] = useState(true); + const [networkState, setNetworkState] = useState({ online: true }); + const { + state: { + selectedStory, + selectedFont, + fontSize, + }, + actions: { + setSelectedStory, + }, +} = useContext(ReferenceContext); + +const itemEls = useRef([]); +const { t } = useTranslation(); +// const networkState = useNetwork(); + + useEffect(() => { + if (stories === undefined) { + setIsLoading(true); + } else { + setIsLoading(false); + } + itemEls.current.length = 0; + setSelectedStory(1); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [stories]); + + // scroll based on story part selection + const addtoItemEls = (el, id) => { + if (el && el !== null && !itemEls.current.some((obj) => obj.id === id)) { + itemEls.current.push({ id, el }); + } + }; + + useEffect(() => { + if (stories && selectedStory !== undefined) { + setNetworkState({ online: window?.navigator?.onLine }); + const currentRef = itemEls.current.filter((obj) => obj.id === selectedStory)[0]?.el; + if (currentRef) { + currentRef.scrollIntoView({ block: 'center', inline: 'nearest' }); + } + } + }, [selectedStory, stories]); + + useEffect(() => { + (async () => { + if (window?.navigator?.onLine) { + await checkandDownloadObsImages(window?.navigator?.onLine); + } + // else { + // console.log('No internet connection'); + // } + })(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+ { isLoading === false ? ( +
+ { + stories?.map((story, index) => ( +
addtoItemEls(element, story.id)} + > + { + Object.prototype.hasOwnProperty.call(story, 'title') && ( +

+ {story.title} +

+ ) + } + {Object.prototype.hasOwnProperty.call(story, 'text') && ( + <> + + {/* {index} */} + {index.toString().split('').map((num) => t(`n-${num}`))} + + {/* */} + +

1.3) ? 1.5 : '', + }} + > + {story.text} +

+ + )} + { + Object.prototype.hasOwnProperty.call(story, 'end') && ( +

+ {story.end} +

+ ) + } +
+ )) + } +
+ ) : ( + + )} +
+); +}; + +ReferenceObs.propTypes = { + stories: PropTypes.arrayOf(PropTypes.object), +}; + +export default ReferenceObs; diff --git a/renderer/src/components/EditorPage/ObsEditor/core.js b/renderer/src/components/EditorPage/ObsEditor/core.js index 60e97f068..18ffc8ebf 100644 --- a/renderer/src/components/EditorPage/ObsEditor/core.js +++ b/renderer/src/components/EditorPage/ObsEditor/core.js @@ -1,4 +1,5 @@ import packageInfo from '../../../../../package.json'; +import { newPath, sbStorageDownload } from '../../../../../supabase'; const loadData = (fs, file, projectName, username) => { const newpath = localStorage.getItem('userPath'); @@ -28,6 +29,47 @@ const loadData = (fs, file, projectName, username) => { } return 'No Content'; }; + +export function readBlob(blob) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + + reader.onload = () => { + const content = reader.result; + resolve(content); + }; + + reader.onerror = () => { + reader.abort(); + reject(new Error('Error reading blob.')); + }; + + reader.readAsText(blob, 'utf-8'); + }); +} + +export async function readBlobAsync(blob) { + try { + const content = await readBlob(blob); + return content; + // Do something with the content + } catch (error) { + console.error('Error reading blob:', error); + } +} + +const loadWebData = async (file, projectName, username) => { + const filePath = `${newPath}/${username}/resources/${projectName}/content/${file}.md`; + const { data } = await sbStorageDownload(filePath); + const parsedData = readBlobAsync(data); + console.log({ filePath, data, parsedData }); + + if (parsedData) { + return parsedData; + } + return 'No Content'; +}; + const core = (fs, num, projectName, username) => { const stories = []; // eslint-disable-next-line prefer-const @@ -107,4 +149,89 @@ const core = (fs, num, projectName, username) => { }); return stories; }; -export default core; + +const webCore = async (num, projectName, username) => { + console.log({ num, projectName, username }); + const stories = []; + // eslint-disable-next-line prefer-const + let id = 1; let footer = false; + const data = await loadWebData(num.toString().padStart(2, 0), projectName, username); + console.log({ data }); + const allLines = data.split(/\r\n|\n/); + // Reading line by line + allLines.forEach((line) => { + // To avoid the values after footer, we have added id=0 + if (line && id !== 0) { + if (line.match(/^(\s)*#/gm)) { + // Fetching the header content + const hash = line.match(/# (.*)/); + stories.push({ + id, title: hash[1], + }); + id += 1; + } else if (line.match(/^(\s)*_/gm) || footer === true) { + // Fetching the footer + const objIndex = stories.findIndex(((obj) => obj.id === id)); + if (objIndex !== -1 && Object.prototype.hasOwnProperty.call(stories[objIndex], 'img')) { + stories[objIndex].text = ''; + id += 1; + } + if (line.match(/_(.*)_/g) && footer === false) { + // single line footer + const underscore = line.match(/_(.*)_/); + stories.push({ + id, end: underscore[1], + }); + // Logically footer is the last line of the story + id = 0; + } else { + // To get multi-line footer (footer=true) + footer = true; + if (line.match(/^(\s)*_/gm)) { + // starting of footer + const underscore = line.match(/^(\s)*_(.*)/); + stories.push({ + id, + end: underscore[2], + }); + } else if (line.match(/_$/gm)) { + // end of footer + const underscore = line.match(/(.*)_$/); + stories[id - 1].end = `${stories[id - 1].end}\n${underscore[1]}`; + // Logically footer is the last line of the story + id = 0; + } else { + // middle lines of footer if available + stories[id - 1].end = `${stories[id - 1].end}\n${line}`; + } + } + } else if (line.match(/^(\s)*!/gm)) { + // Fetching the IMG url + const objIndex = stories.findIndex(((obj) => obj.id === id)); + if (objIndex !== -1 && Object.prototype.hasOwnProperty.call(stories[objIndex], 'img')) { + stories[objIndex].text = ''; + id += 1; + } + const imgUrl = line.match(/\((.*)\)/); + stories.push({ + id, img: imgUrl[1], + }); + } else { + // Reading the content line by line + const objIndex = stories.findIndex(((obj) => obj.id === id)); + if (objIndex !== -1) { + // Reading first line after img + stories[objIndex].text = line; + id += 1; + } else { + // Reading other lines and appending with previous line data + stories[id - 2].text = `${stories[id - 2]?.text}\n${line}`; + } + } + } + }); + console.log({ stories }); + return stories; +}; + +export { core, webCore }; diff --git a/renderer/src/components/Login/RightLogin.js b/renderer/src/components/Login/RightLogin.js index 18fe51800..bc11c4dfd 100644 --- a/renderer/src/components/Login/RightLogin.js +++ b/renderer/src/components/Login/RightLogin.js @@ -1,8 +1,8 @@ import LogoIcon from '@/icons/logo.svg'; -import GroupIcon from '@/illustrations/group.svg'; +// import GroupIcon from '@/illustrations/group.svg'; -import VectorOne from '@/illustrations/vector-one.svg'; -import HalfMoon from '@/illustrations/half-moon.svg'; +// import VectorOne from '@/illustrations/vector-one.svg'; +// import HalfMoon from '@/illustrations/half-moon.svg'; // import Quote from '@/illustrations/quote.svg'; export default function RightLogin() { @@ -20,11 +20,11 @@ export default function RightLogin() {
- + /> */}
@@ -49,11 +49,11 @@ export default function RightLogin() {
- + /> */}
@@ -65,7 +65,7 @@ export default function RightLogin() {
- + {/* */}
diff --git a/renderer/src/components/Login/Signin.js b/renderer/src/components/Login/Signin.js new file mode 100644 index 000000000..65be379cc --- /dev/null +++ b/renderer/src/components/Login/Signin.js @@ -0,0 +1,88 @@ +import React, { useState } from 'react'; +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import * as localforage from 'localforage'; +import { supabaseSignIn } from '../../../../supabase'; + +const SignIn = () => { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const [loading, setLoading] = useState(false); + const router = useRouter(); + + const handleSubmit = async (e) => { + e.preventDefault(); + setLoading(true); + const { data, error: signInError } = await supabaseSignIn({ + email, + password, + }); + if (data.session) { + console.log('sign in success', data); + await localforage.setItem('userProfile', data); + router.push('/projects'); + setLoading(false); + } else { + setError(signInError); + setLoading(false); + console.log('sign in error', signInError); + } + }; + + return ( +
+
+

Sign In

+ {error &&

{error.toString()}

} +
+
+ + setEmail(e.target.value)} + /> +
+
+ + setPassword(e.target.value)} + /> +
+ +
+ + Don't have an account? + {' '} + + Sign Up + + + +
+
+ ); +}; + +export default SignIn; diff --git a/renderer/src/components/Login/WebLogin.js b/renderer/src/components/Login/WebLogin.js new file mode 100644 index 000000000..630e7375a --- /dev/null +++ b/renderer/src/components/Login/WebLogin.js @@ -0,0 +1,15 @@ +'use client'; + +import SignIn from './Signin'; +import RightLogin from './RightLogin'; + +export default function WebLogin() { + return ( +
+
+ +
+ +
+ ); +} diff --git a/renderer/src/components/Login/useAuthentication.js b/renderer/src/components/Login/useAuthentication.js index 3e1501c36..63895efce 100644 --- a/renderer/src/components/Login/useAuthentication.js +++ b/renderer/src/components/Login/useAuthentication.js @@ -5,7 +5,7 @@ import { Configuration, PublicApi } from '@ory/kratos-client'; import * as logger from '../../logger'; // import configData from '../../config.json'; // import { isElectron } from '../../core/handleElectron'; -import { useRouter } from 'next/router'; +import { useRouter } from 'next/navigation'; // const kratos = new PublicApi(new Configuration({ basePath: configData.base_url })); const CryptoJS = require("crypto-js"); diff --git a/renderer/src/components/Profile/UserProfile.js b/renderer/src/components/Profile/UserProfile.js index fe89890bd..f89b1c2a2 100644 --- a/renderer/src/components/Profile/UserProfile.js +++ b/renderer/src/components/Profile/UserProfile.js @@ -1,4 +1,4 @@ -import React, { Fragment } from 'react'; +import { useContext, Fragment } from 'react'; import Link from 'next/link'; import { useTranslation } from 'react-i18next'; import { @@ -10,16 +10,38 @@ import { import { classNames } from '@/util/classNames'; import { AuthenticationContext } from '@/components/Login/AuthenticationContextProvider'; import { useGetUserName } from '@/components/hooks/useGetUserName'; +import { useRouter } from 'next/navigation'; +import { isElectron } from '@/core/handleElectron'; +import * as localforage from 'localforage'; +import { supabaseSignout } from '../../../../supabase'; -const UserProfile = (_username) => { - const { action: { logout } } = React.useContext(AuthenticationContext); +const UserProfile = () => { + const { action: { logout } } = useContext(AuthenticationContext); const { t } = useTranslation(); const profile = [t('label-your-profile')]; const userPic = true; + const router = useRouter(); - // get username from custom hook - const { username } = useGetUserName(_username); + const signOut = async () => { + // if(!process.env.NEXT_PUBLIC_IS_ELECTRON){ + const { error } = await supabaseSignout(); + localforage.removeItem('userProfile'); + error ? console.log({ error }) : router.push('/login'); + }; +// } + // get username from custom hook + const { username } = useGetUserName(); + function truncateUsername(username) { + if (username) { + const atIndex = username.indexOf('@'); + if (atIndex !== -1) { + const userName = username.slice(0, atIndex); + return userName; + } + return null; + } + } return (
@@ -66,7 +88,7 @@ const UserProfile = (_username) => { - {username} + {isElectron() ? username : truncateUsername(username)}
{profile.map((item) => ( @@ -76,15 +98,15 @@ const UserProfile = (_username) => { href="/profile" id="profile" className={classNames( - active ? 'bg-gray-100' : '', - 'block px-4 py-2 text-sm text-gray-700', - )} + active ? 'bg-gray-100' : '', + 'block px-4 py-2 text-sm text-gray-700', + )} > {item} -) + ) )} ))} @@ -94,19 +116,19 @@ const UserProfile = (_username) => { logout()} + onClick={() => (isElectron() ? logout() : signOut())} role="button" tabIndex={0} className={classNames( - active ? 'bg-gray-100' : '', - 'block px-4 py-2 text-sm text-gray-700', - )} + active ? 'bg-gray-100' : '', + 'block px-4 py-2 text-sm text-gray-700', + )} > {t('btn-signout')} -) + ) )} diff --git a/renderer/src/components/ProjectsPage/CreateProject/AdvancedSettingsDropdown.js b/renderer/src/components/ProjectsPage/CreateProject/AdvancedSettingsDropdown.js index ce2616547..60b343be4 100644 --- a/renderer/src/components/ProjectsPage/CreateProject/AdvancedSettingsDropdown.js +++ b/renderer/src/components/ProjectsPage/CreateProject/AdvancedSettingsDropdown.js @@ -5,12 +5,14 @@ import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/solid'; import localforage from 'localforage'; import { useTranslation } from 'react-i18next'; import CustomList from '@/modules/projects/CustomList'; +import { isElectron } from '@/core/handleElectron'; import { OT, NT } from '../../../lib/CanonSpecification'; import { ProjectContext } from '../../context/ProjectContext'; import CustomCanonSpecification from './CustomCanonSpecification'; import LicencePopover from './LicencePopover'; import * as logger from '../../../logger'; import packageInfo from '../../../../../package.json'; +import { newPath, sbStorageDownload } from '../../../../../supabase'; function BookNumberTag(props) { const { children } = props; @@ -88,49 +90,92 @@ export default function AdvancedSettingsDropdown({ call, project, projectType }) // console.log('canon spec : ', { canonSpecification }); // selectNew variable is used to track whether its a new selection or loading from the list const setALicense = (licenceTitle, selectNew) => { - let title = licenceTitle; - let myLicence = { }; - const fs = window.require('fs'); - if ((title === 'Custom' || !title) && !selectNew) { - myLicence.title = 'Custom'; - myLicence.locked = false; - myLicence.id = 'Other'; - // To support the Projects of 0.3.0 version of burrito - if (project.copyright?.fullStatementPlain) { - myLicence.licence = project.copyright?.fullStatementPlain?.en; - } else if (project.copyright?.shortStatements) { - myLicence.licence = project.copyright?.shortStatements[0]?.statement; + if (isElectron()) { + let title = licenceTitle; + let myLicence = {}; + const fs = window.require('fs'); + if ((title === 'Custom' || !title) && !selectNew) { + myLicence.title = 'Custom'; + myLicence.locked = false; + myLicence.id = 'Other'; + // To support the Projects of 0.3.0 version of burrito + if (project.copyright?.fullStatementPlain) { + myLicence.licence = project.copyright?.fullStatementPlain?.en; + } else if (project.copyright?.shortStatements) { + myLicence.licence = project.copyright?.shortStatements[0]?.statement; + } else { + const path = require('path'); + const newpath = localStorage.getItem('userPath'); + const id = Object.keys(project.identification.primary.scribe); + localforage.getItem('userProfile').then((value) => { + logger.debug('AdvancedSettingsDropdown.js', 'Fetching the current username'); + const folder = path.join(newpath, packageInfo.name, 'users', value?.username, 'projects', `${project.identification.name.en}_${id[0]}`, 'ingredients', 'license.md'); + if (fs.existsSync(folder)) { + fs.readFile(folder, 'utf8', (err, data) => { + myLicence.licence = data; + }); + } else { + const licensefile = require('../../../lib/license/Custom.md'); + // console.log(myLicence, licensefile.default); + myLicence.licence = licensefile.default; + } + }); + } } else { - const path = require('path'); - const newpath = localStorage.getItem('userPath'); - const id = Object.keys(project.identification.primary.scribe); - localforage.getItem('userProfile').then((value) => { - logger.debug('AdvancedSettingsDropdown.js', 'Fetching the current username'); - const folder = path.join(newpath, packageInfo.name, 'users', value?.username, 'projects', `${project.identification.name.en}_${id[0]}`, 'ingredients', 'license.md'); - if (fs.existsSync(folder)) { - fs.readFile(folder, 'utf8', (err, data) => { - myLicence.licence = data; - }); - } else { - const licensefile = require('../../../lib/license/Custom.md'); - // console.log(myLicence, licensefile.default); - myLicence.licence = licensefile.default; - } - }); + // license names are being updated by a prefix 'CC' so to avoid error with previous versions + // checking whether the prefix is available or not + if (!title.match(/CC/g) && title !== 'Custom') { + const str = `CC ${title}`; + title = str.replace(/_/gm, '-'); + } + myLicence = licenceList.find((item) => item.title === title); + // eslint-disable-next-line import/no-dynamic-require + const licensefile = require(`../../../lib/license/${title}.md`); + myLicence.licence = licensefile.default; } + setCopyRight(myLicence); } else { - // license names are being updated by a prefix 'CC' so to avoid error with previous versions - // checking whether the prefix is available or not - if (!title.match(/CC/g) && title !== 'Custom') { - const str = `CC ${title}`; - title = str.replace(/_/gm, '-'); + let title = licenceTitle; + let myLicence = {}; + if ((title === 'Custom' || !title) && !selectNew) { + myLicence.title = 'Custom'; + myLicence.locked = false; + myLicence.id = 'Other'; + // To support the Projects of 0.3.0 version of burrito + if (project.copyright?.fullStatementPlain) { + myLicence.licence = project.copyright?.fullStatementPlain?.en; + } else if (project.copyright?.shortStatements) { + myLicence.licence = project.copyright?.shortStatements[0]?.statement; + } else { + const path = require('path'); + const id = Object.keys(project.identification.primary.scribe); + localforage.getItem('userProfile').then(async (value) => { + logger.debug('AdvancedSettingsDropdown.js', 'Fetching the current username'); + const folder = path.join(newPath, value?.user?.email, 'projects', `${project.identification.name.en}_${id[0]}`, 'ingredients', 'license.md'); + const { data } = await sbStorageDownload(folder); + if (data) { + myLicence.licence = data; + } else { + const licensefile = require('../../../lib/license/Custom.md'); + // console.log(myLicence, licensefile.default); + myLicence.licence = licensefile.default; + } + }); + } + } else { + // license names are being updated by a prefix 'CC' so to avoid error with previous versions + // checking whether the prefix is available or not + if (!title.match(/CC/g) && title !== 'Custom') { + const str = `CC ${title}`; + title = str.replace(/_/gm, '-'); + } + myLicence = licenceList.find((item) => item.title === title); + // eslint-disable-next-line import/no-dynamic-require + const licensefile = require(`../../../lib/license/${title}.md`); + myLicence.licence = licensefile.default; } - myLicence = licenceList.find((item) => item.title === title); - // eslint-disable-next-line import/no-dynamic-require - const licensefile = require(`../../../lib/license/${title}.md`); - myLicence.licence = licensefile.default; + setCopyRight(myLicence); } - setCopyRight(myLicence); }; const loadLicence = () => { logger.debug('AdvancedSettingsDropdown.js', 'In loadLicence for loading the selected licence'); @@ -153,11 +198,11 @@ export default function AdvancedSettingsDropdown({ call, project, projectType }) if (canonSpecification.title === 'Other') { value.currentScope = canonSpecification.currentScope; } else { - value.currentScope = currentScope.currentScope; + value.currentScope = currentScope.currentScope; } } else if (canonSpecification.title === 'Other' && value.title === 'Other') { - value.currentScope = canonSpecification.currentScope; - } + value.currentScope = canonSpecification.currentScope; + } setcanonSpecification(value); openBibleNav('edit'); }; @@ -177,30 +222,30 @@ export default function AdvancedSettingsDropdown({ call, project, projectType }) return ( <>
- { projectType !== 'OBS' + {projectType !== 'OBS' && ( - - )} + + )} {!isShow && projectType !== 'OBS' && (
diff --git a/renderer/src/components/Protected.js b/renderer/src/components/Protected.js new file mode 100644 index 000000000..70189c6c4 --- /dev/null +++ b/renderer/src/components/Protected.js @@ -0,0 +1,20 @@ +'use client'; + +import { useEffect } from 'react'; +import { useRouter } from 'next/navigation'; +import { getSupabaseUser } from '../../../supabase'; + +const ProtectedRoute = ({ children }) => { + const router = useRouter(); + useEffect(() => { + const checkUser = async () => { + const user = await getSupabaseUser(); + if (!user) { + router.push('/login'); // Redirect to the sign in page if user is not signed in + } + }; + checkUser(); + }, [router]); + return
{children}
; +}; +export default ProtectedRoute; diff --git a/renderer/src/components/Resources/DownloadObsImages/checkandDownloadObsImages.js b/renderer/src/components/Resources/DownloadObsImages/checkandDownloadObsImages.js index 97ea5d048..311a5cb7e 100644 --- a/renderer/src/components/Resources/DownloadObsImages/checkandDownloadObsImages.js +++ b/renderer/src/components/Resources/DownloadObsImages/checkandDownloadObsImages.js @@ -1,37 +1,89 @@ +import { isElectron } from '@/core/handleElectron'; import { environment } from '../../../../environment'; import packageInfo from '../../../../../package.json'; import OBSData from '../../../lib/OBSData.json'; +import { + createDirectory, sbStorageList, sbStorageUpload, sbStorageDownload, +} from '../../../../../supabase'; const downloadImageAndSave = async (url, savePath, fs) => { const response = await fetch(url, { mode: 'no-cors' }); const blob = await response.blob(); const arrayBuffer = await blob.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); + console.log({ blob, arrayBuffer, buffer }); if (blob.type.includes('image')) { await fs.createWriteStream(savePath).write(buffer); } }; +async function downloadImageAndSaveSupabase(url) { + try { + const response = await fetch(url, { + mode: 'no-cors', + headers: { + 'Access-Control-Allow-Origin': 'http://localhost:3000', + }, + }); + if (!response.ok) { + throw new Error('Network response was not ok'); + } + const blob = await response.blob(); + const imageUrl = URL.createObjectURL(blob); + const img = document.createElement('img'); + img.src = imageUrl; + document.body.appendChild(img); + const { data, error } = await sbStorageUpload(`${packageInfo.name}/common/${environment.OBS_IMAGE_DIR}`, blob); + if (error) { + console.log('error uploading images to supabase', { error }); + throw error; + } + console.log(data); + } catch (error) { + console.error('Error fetching image:', error); + } +} + export const checkandDownloadObsImages = async () => { // check for the folder and images // const imageCdnBaseUrl = 'https://cdn.door43.org/obs/jpg/360px/'; - const fs = window.require('fs'); - const path = require('path'); - const newpath = localStorage.getItem('userPath'); - const obsImagePath = path.join(newpath, packageInfo.name, 'common', environment.OBS_IMAGE_DIR); - const exist = await fs.existsSync(obsImagePath); - if (!exist) { - await fs.mkdirSync(obsImagePath, { recursive: true }); - } - const filesInDir = await fs.readdirSync(obsImagePath); - await Object.values(OBSData).forEach(async (storyObj) => { - await Object.values(storyObj.story).forEach(async (story) => { - let fileName = story.url.split('/'); - fileName = fileName[fileName.length - 1]; - if (!filesInDir.includes(fileName)) { - const filePath = path.join(obsImagePath, fileName); - await downloadImageAndSave(story.url, filePath, fs); - } + if (isElectron()) { + const fs = window.require('fs'); + const path = require('path'); + const newpath = localStorage.getItem('userPath'); + const obsImagePath = path.join(newpath, packageInfo.name, 'common', environment.OBS_IMAGE_DIR); + const exist = await fs.existsSync(obsImagePath); + if (!exist) { + await fs.mkdirSync(obsImagePath, { recursive: true }); + } + const filesInDir = await fs.readdirSync(obsImagePath); + Object.values(OBSData).forEach(async (storyObj) => { + Object.values(storyObj.story).forEach(async (story) => { + let fileName = story.url.split('/'); + fileName = fileName[fileName.length - 1]; + if (!filesInDir.includes(fileName)) { + const filePath = path.join(obsImagePath, fileName); + await downloadImageAndSave(story.url, filePath, fs); + } + }); + }); + } else { + const obsImagePath = `${packageInfo.name}/common/${environment.OBS_IMAGE_DIR}`; + const { data: exist } = await sbStorageList(obsImagePath); + if (!exist) { + await createDirectory({path:obsImagePath}); + } + const { data: filesInDir } = await sbStorageDownload(obsImagePath); + console.log({ filesInDir }); + Object.values(OBSData).forEach(async (storyObj) => { + Object.values(storyObj.story).forEach(async (story) => { + let fileName = story.url.split('/'); + fileName = fileName[fileName.length - 1]; + if (filesInDir === null) { + const filePath = `${obsImagePath}/${fileName}`; + await downloadImageAndSaveSupabase(story.url, filePath); + } + }); }); - }); + } }; diff --git a/renderer/src/components/Resources/ResourceUtils/CustomMultiComboBox.js b/renderer/src/components/Resources/ResourceUtils/CustomMultiComboBox.js index 63acd21ac..adf7854a3 100644 --- a/renderer/src/components/Resources/ResourceUtils/CustomMultiComboBox.js +++ b/renderer/src/components/Resources/ResourceUtils/CustomMultiComboBox.js @@ -2,12 +2,10 @@ import React, { useState } from 'react'; import { Combobox } from '@headlessui/react'; import PropTypes from 'prop-types'; import { ChevronUpDownIcon } from '@heroicons/react/24/solid'; -import { useTranslation } from 'react-i18next'; function CustomMultiComboBox({ - selectedList, setSelectedList, customData, placeholder = 'Select Language', filterParams = 'name', multiSelect = false, dropArrow = false, showLangCode = { show: false, langkey: 'lc' }, + selectedList, setSelectedList, customData, filterParams = 'name', multiSelect = false, dropArrow = false, showLangCode = { show: false, langkey: 'lc' }, }) { - const { t } = useTranslation(); let filteredData = []; const [query, setQuery] = useState(''); // eslint-disable-next-line no-unused-vars @@ -34,7 +32,7 @@ function CustomMultiComboBox({ className="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0" // displayValue={(language) => language?.ang} displayValue={(selectedList) => `${selectedList.length > 0 ? `${selectedList[0][filterParams]}${multiSelect ? '... click for more' : '' }` : ''}`} - placeholder={placeholder === 'Select Language' ? t('label-lang') : ''} + placeholder="Select Language" onFocus={() => !open && setIsActive(true)} onBlur={() => setIsActive(false)} onChange={(event) => setQuery(event.target.value)} diff --git a/renderer/src/components/Resources/ResourceUtils/DownloadCreateSBforHelps.js b/renderer/src/components/Resources/ResourceUtils/DownloadCreateSBforHelps.js index 235f0b335..98354f06d 100644 --- a/renderer/src/components/Resources/ResourceUtils/DownloadCreateSBforHelps.js +++ b/renderer/src/components/Resources/ResourceUtils/DownloadCreateSBforHelps.js @@ -1,50 +1,57 @@ +/* eslint-disable no-await-in-loop */ +/* eslint-disable no-restricted-syntax */ import localForage from 'localforage'; import moment from 'moment'; +import { isElectron } from '@/core/handleElectron'; import * as logger from '../../../logger'; import packageInfo from '../../../../../package.json'; +import { + createDirectory, newPath, sbStorageRemove, sbStorageList, sbStorageDownload, +} from '../../../../../supabase'; const JSZip = require('jszip'); const DownloadCreateSBforHelps = async (projectResource, setLoading, update = false, offlineResource = false) => { - try { - // console.log('download/update started --------', { projectResource, update, offlineResource }); - logger.debug('DownloadCreateSBforHelps.js', 'Download Started'); - setLoading(true); - await localForage.getItem('userProfile').then(async (user) => { - logger.debug('DownloadCreateSBforHelps.js', 'In helps-resource download user fetch - ', user?.username); - const fs = window.require('fs'); - const path = require('path'); - const newpath = localStorage.getItem('userPath'); - const folder = path.join(newpath, packageInfo.name, 'users', `${user?.username}`, 'resources'); - // const currentUser = user?.username; - // const key = currentUser + projectResource.name + projectResource.owner + moment().format(); - // const id = uuidv5(key, environment.uuidToken); - // check for existing resources - const existingResource = fs.readdirSync(folder, { withFileTypes: true }); - const downloadProjectName = `${projectResource?.name}_${projectResource?.owner}_${projectResource?.release?.tag_name}`; - existingResource?.forEach((element) => { - if (downloadProjectName === element.name) { - throw new Error('Resource Already Exist'); - } - }); - - // check if resource already exist in offline - if (!update && offlineResource) { - // eslint-disable-next-line array-callback-return - const resourceExist = offlineResource.filter((offline) => { - if (offline?.projectDir === `${projectResource?.name}_${projectResource?.owner}_${projectResource?.release?.tag_name}`) { - return offline; + if (isElectron()) { + try { + // console.log('download/update started --------', { projectResource, update, offlineResource }); + logger.debug('DownloadCreateSBforHelps.js', 'Download Started'); + setLoading(true); + await localForage.getItem('userProfile').then(async (user) => { + logger.debug('DownloadCreateSBforHelps.js', 'In helps-resource download user fetch - ', user?.username); + const fs = window.require('fs'); + const path = require('path'); + const newpath = localStorage.getItem('userPath'); + const folder = path.join(newpath, packageInfo.name, 'users', `${user?.username}`, 'resources'); + // const currentUser = user?.username; + // const key = currentUser + projectResource.name + projectResource.owner + moment().format(); + // const id = uuidv5(key, environment.uuidToken); + // check for existing resources + const existingResource = fs.readdirSync(folder, { withFileTypes: true }); + const downloadProjectName = `${projectResource?.name}_${projectResource?.owner}_${projectResource?.release?.tag_name}`; + existingResource?.forEach((element) => { + if (downloadProjectName === element.name) { + throw new Error('Resource Already Exist'); } }); - if (resourceExist.length > 0) { - throw new Error('Resource Already Exist'); - // eslint-disable-next-line no-throw-literal - // throw 'Resource Already Exist'; + + // check if resource already exist in offline + if (!update && offlineResource) { + // eslint-disable-next-line array-callback-return + const resourceExist = offlineResource.filter((offline) => { + if (offline?.projectDir === `${projectResource?.name}_${projectResource?.owner}_${projectResource?.release?.tag_name}`) { + return offline; + } + }); + if (resourceExist.length > 0) { + throw new Error('Resource Already Exist'); + // eslint-disable-next-line no-throw-literal + // throw 'Resource Already Exist'; + } } - } - // eslint-disable-next-line no-async-promise-executor - // return new Promise(async (resolve) => { + // eslint-disable-next-line no-async-promise-executor + // return new Promise(async (resolve) => { // const json = {}; // download and unzip the content await fetch(projectResource?.zipball_url) @@ -97,22 +104,22 @@ const DownloadCreateSBforHelps = async (projectResource, setLoading, update = fa fs.renameSync(path.join(folder, projectResource?.name), path.join(folder, `${projectResource?.name}_${projectResource?.owner}_${projectResource?.release?.tag_name}`)); fs.unlinkSync(path.join(folder, `${projectResource?.name}.zip`), (err) => { if (err) { - logger.debug('DownloadCreateSBforHelps.js', 'error in deleting zip'); - // console.log(`Removing Resource Zip Failed : ${projectResource?.name}.zip`); - throw new Error(`Removing Resource Zip Failed : ${projectResource?.name}.zip`); + logger.debug('DownloadCreateSBforHelps.js', 'error in deleting zip'); + // console.log(`Removing Resource Zip Failed : ${projectResource?.name}.zip`); + throw new Error(`Removing Resource Zip Failed : ${projectResource?.name}.zip`); } }); if (update && update?.status) { // if updation delete old resource // console.log({ projectOld: `${projectResource?.name}_${projectResource?.owner}_${update?.prevVersion}` }); - try { + try { fs.rmSync(path.join(folder, `${projectResource?.name}_${projectResource?.owner}_${update?.prevVersion}`), { recursive: true }); update && update?.setIsOpen(false); - } catch (err) { + } catch (err) { logger.debug('DownloadCreateSBforHelps.js', 'error in deleting prev resource'); - setLoading(false); - throw new Error(`Removing Previous Resource Failed : ${projectResource?.name}_${projectResource?.owner}_${update?.prevVersion}`); - } + setLoading(false); + throw new Error(`Removing Previous Resource Failed : ${projectResource?.name}_${projectResource?.owner}_${update?.prevVersion}`); + } } } }); @@ -121,10 +128,87 @@ const DownloadCreateSBforHelps = async (projectResource, setLoading, update = fa setLoading(false); // resolve(json); }); - // }); - } catch (err) { - setLoading(false); - throw err; + // }); + } catch (err) { + setLoading(false); + throw err; + } + } else { + const userProfile = await localForage.getItem('userProfile'); + const username = userProfile?.user?.email; + try { + console.log('DownloadCreateSBforHelps.js', 'In helps-resource download'); + setLoading(true); + const folder = `${newPath}/${username}/resources`; + const { data: existingResource } = await sbStorageDownload(folder); + console.log(existingResource); + const downloadProjectName = `${projectResource?.name}_${projectResource?.owner}_${projectResource?.release?.tag_name}`; + existingResource?.forEach((element) => { + if (downloadProjectName === element.name) { + throw new Error('Resource Already Exist'); + } + }); + + await fetch(projectResource?.zipball_url).then((res) => res.arrayBuffer()).then(async (blob) => { + const { data: folderExist } = await sbStorageList(folder); + console.log({ folderExist }); + if (!folderExist) { + await createDirectory({path:folder}); + } + // writing zip to local + await createDirectory({path:`${folder}/${projectResource?.name}.zip}`,payload: Buffer.from(blob)}); + + // extract zip + const { data: filecontent, error: fileContentError } = await sbStorageDownload(`${folder}/${projectResource?.name}.zip}`); + if (fileContentError) { + console.log('Failed to download zip file', fileContentError); + } + + const result = await JSZip.loadAsync(filecontent); + const keys = Object.keys(result.files); + + for (const key of keys) { + const item = result.files[key]; + if (item.dir) { + await createDirectory({path:`${folder}/${item.name}`}); + } else { + const bufferContent = Buffer.from(await item.async('arraybuffer')); + await createDirectory({path:`${folder}/${item.name}`,payload: bufferContent}); + } + } + + await fetch(projectResource.metadata_json_url).then((res) => res.json()).then(async (data) => { + data.agOffline = true; + data.meta = projectResource; + data.lastUpdatedAg = moment().format(); + await createDirectory({path:`${folder}/${projectResource?.name}/metadata.json`, payload :JSON.stringify(data)}); + }).catch((err) => { + console.log('DownloadCreateSBforHelps.js', 'failed to save yml metadata.json : ', err); + }); + + // finally remove zip and rename base folder to projectname_id + console.log('DownloadCreateSBforHelps.js', 'deleting zip file - rename project with project + id in ag format'); + if (folderExist) { + console.log('DownloadCreateSBforHelps.js', 'deleting zip file - rename project with project + id in ag format'); + + if (update && update?.status) { + // if updation delete old resource + console.log({ projectOld: `${projectResource?.name}_${projectResource?.owner}_${update?.prevVersion}` }); + try { + sbStorageRemove(`folder/${projectResource?.name}_${projectResource?.owner}_${update?.prevVersion}`); + update && update?.setIsOpen(false); + } catch (err) { + logger.debug('DownloadCreateSBforHelps.js', 'error in deleting prev resource'); + setLoading(false); + throw new Error(`Removing Previous Resource Failed : ${projectResource?.name}_${projectResource?.owner}_${update?.prevVersion}`); + } + } + } + }); + } catch (error) { + setLoading(false); + console.log('DownloadCreateSBforHelps.js', error); + } } }; diff --git a/renderer/src/components/Resources/ResourceUtils/DownloadResourcePopUp.js b/renderer/src/components/Resources/ResourceUtils/DownloadResourcePopUp.js index 3a9cd745b..4da18943b 100644 --- a/renderer/src/components/Resources/ResourceUtils/DownloadResourcePopUp.js +++ b/renderer/src/components/Resources/ResourceUtils/DownloadResourcePopUp.js @@ -14,9 +14,10 @@ import LoadingScreen from '@/components/Loading/LoadingScreen'; import { XMarkIcon, ArrowDownTrayIcon } from '@heroicons/react/24/solid'; import { AutographaContext } from '@/components/context/AutographaContext'; import { InformationCircleIcon, TrashIcon } from '@heroicons/react/24/outline'; +import { isElectron } from '@/core/handleElectron'; import CustomMultiComboBox from './CustomMultiComboBox'; import langJson from '../../../lib/lang/langNames.json'; -import { handleDownloadResources } from './createDownloadedResourceSB'; +import { handleDownloadResources, handleDownloadWebResources } from './createDownloadedResourceSB'; import * as logger from '../../../logger'; import { environment } from '../../../../environment'; @@ -139,16 +140,16 @@ function DownloadResourcePopUp({ selectResource, isOpenDonwloadPopUp, setIsOpenD }); const fetchedDataJson = await fetchedData.json(); logger.debug('DownloadResourcePopUp.js', 'generating language based resources after fetch'); - fetchedDataJson.data.forEach((element) => { - element.isChecked = false; - if (element.language in temp_resource) { - temp_resource[element.language].push(element); - } else { - temp_resource[element.language] = [element]; - } - }); - setresourceData(temp_resource); - setLoading(false); + fetchedDataJson.data.forEach((element) => { + element.isChecked = false; + if (element.language in temp_resource) { + temp_resource[element.language].push(element); + } else { + temp_resource[element.language] = [element]; + } + }); + setresourceData(temp_resource); + setLoading(false); } catch (err) { logger.debug('DownloadResourcePopUp.js', 'Error on fetch content : ', err); setLoading(false); @@ -232,7 +233,7 @@ function DownloadResourcePopUp({ selectResource, isOpenDonwloadPopUp, setIsOpenD // console.log('resource download started ---', selectedResourceCount); setDownloadStarted(true); const action = { setDownloadCount }; - await handleDownloadResources(resourceData, selectResource, action) + isElectron() ? await handleDownloadResources(resourceData, selectResource, action) : await handleDownloadWebResources(resourceData, selectResource, action) .then(async (resolveResp) => { if (selectedResourceCount === resolveResp?.existing) { setOpenSnackBar(true); diff --git a/renderer/src/components/Resources/ResourceUtils/createDownloadedResourceSB.js b/renderer/src/components/Resources/ResourceUtils/createDownloadedResourceSB.js index 221983d2d..2fb590864 100644 --- a/renderer/src/components/Resources/ResourceUtils/createDownloadedResourceSB.js +++ b/renderer/src/components/Resources/ResourceUtils/createDownloadedResourceSB.js @@ -1,6 +1,12 @@ +/* eslint-disable no-loop-func */ +/* eslint-disable no-async-promise-executor */ +/* eslint-disable no-await-in-loop */ +/* eslint-disable guard-for-in */ +/* eslint-disable no-console */ +/* eslint-disable no-restricted-syntax */ import moment from 'moment'; import { v5 as uuidv5 } from 'uuid'; -import localForage from 'localforage'; +import * as localForage from 'localforage'; import Textburrito from '../../../lib/BurritoTemplete.json'; import OBSburrito from '../../../lib/OBSTemplete.json'; import languageCode from '../../../lib/LanguageCode.json'; @@ -10,6 +16,9 @@ import packageInfo from '../../../../../package.json'; import customLicense from '../../../lib/license/Custom.md'; import OBSLicense from '../../../lib/OBSLicense.md'; import OBSData from '../../../lib/OBSData.json'; +import { + newPath, sbStorageDownload, sbStorageList, sbStorageUpload, sbStorageUpdate, sbStorageRemove, +} from '../../../../../supabase'; const md5 = require('md5'); @@ -17,19 +26,19 @@ const path = require('path'); const JSZip = require('jszip'); const findCode = (list, id) => { - logger.debug('createDownloadedResourceSB.js', 'In findCode for getting the language code'); - let code = ''; - list.forEach((obj) => { - if ((obj.name).toLowerCase() === id.toLowerCase()) { - code = obj.lang_code; - } - }); - return code; - }; + logger.debug('createDownloadedResourceSB.js', 'In findCode for getting the language code'); + let code = ''; + list.forEach((obj) => { + if ((obj.name).toLowerCase() === id.toLowerCase()) { + code = obj.lang_code; + } + }); + return code; +}; export const createDownloadedResourceSB = async (username, resourceMeta, projectResource, selectResource) => { - logger.debug('createDownloadedResourceSB.js', 'Create Metadata for downloaded bible resource'); - // generate unique key - try { + logger.debug('createDownloadedResourceSB.js', 'Create Metadata for downloaded bible resource'); + // generate unique key + try { const key = username + projectResource.name + projectResource.owner + moment().format(); const id = uuidv5(key, environment.uuidToken); const localizedNames = {}; @@ -46,73 +55,73 @@ export const createDownloadedResourceSB = async (username, resourceMeta, project break; default: throw new Error(' can not process :Inavalid Type od Resource requested'); - // break; + // break; } return new Promise((resolve) => { - json.meta.generator.userName = username; - json.meta.generator.softwareName = 'Scribe'; - json.meta.generator.softwareVersion = packageInfo.version; - json.meta.dateCreated = moment().format(); - json.idAuthorities = { - dcs: { - id: new URL(projectResource.url).hostname, - name: { - en: projectResource.owner, - }, + json.meta.generator.userName = username; + json.meta.generator.softwareName = 'Scribe'; + json.meta.generator.softwareVersion = packageInfo.version; + json.meta.dateCreated = moment().format(); + json.idAuthorities = { + dcs: { + id: new URL(projectResource.url).hostname, + name: { + en: projectResource.owner, }, - }; - json.identification.primary = { - scribe: { - [id]: { - revision: '1', - timestamp: moment().format(), - }, - }, - }; - json.identification.upstream = { - dcs: [{ - [`${projectResource.owner}:${projectResource.name}`]: { - revision: projectResource.release.tag_name, - timestamp: projectResource.released, - }, - }, - ], - }; - json.identification.name.en = projectResource.name; - json.identification.abbreviation.en = ''; - - if (resourceMeta.dublin_core.language.identifier) { - json.languages[0].tag = resourceMeta.dublin_core.language.identifier; - } else if (resourceMeta.dublin_core.language.title) { - const code = findCode(languageCode, resourceMeta.dublin_core.language.title); - if (code) { - json.languages[0].tag = code; - } else { - json.languages[0].tag = resourceMeta.dublin_core.language.title.substring(0, 3); - } - } - json.languages[0].name.en = projectResource.language_title; - - json.copyright.shortStatements = [ - { - statement: resourceMeta?.dublin_core?.rights, + }, + }; + json.identification.primary = { + scribe: { + [id]: { + revision: '1', + timestamp: moment().format(), }, - ]; - json.copyright.licenses[0].ingredient = 'LICENSE.md'; - if (selectResource === 'bible') { - resourceMeta.projects.forEach(({ identifier: scope }) => { - json.type.flavorType.currentScope[scope.toUpperCase()] = []; - localizedNames[scope.toUpperCase()] = json.localizedNames[scope.toUpperCase()]; - }); - json.localizedNames = localizedNames; + }, + }; + json.identification.upstream = { + dcs: [{ + [`${projectResource.owner}:${projectResource.name}`]: { + revision: projectResource.release.tag_name, + timestamp: projectResource.released, + }, + }, + ], + }; + json.identification.name.en = projectResource.name; + json.identification.abbreviation.en = ''; + + if (resourceMeta.dublin_core.language.identifier) { + json.languages[0].tag = resourceMeta.dublin_core.language.identifier; + } else if (resourceMeta.dublin_core.language.title) { + const code = findCode(languageCode, resourceMeta.dublin_core.language.title); + if (code) { + json.languages[0].tag = code; + } else { + json.languages[0].tag = resourceMeta.dublin_core.language.title.substring(0, 3); } + } + json.languages[0].name.en = projectResource.language_title; - logger.debug('createDownloadedResourceSB.js', 'Created the createBibleResource SB'); - resolve(json); - }); - } catch (err) { - throw new Error(`Generate Burrito Failed : ${err}`); - } + json.copyright.shortStatements = [ + { + statement: resourceMeta?.dublin_core?.rights, + }, + ]; + json.copyright.licenses[0].ingredient = 'LICENSE.md'; + if (selectResource === 'bible') { + resourceMeta.projects.forEach(({ identifier: scope }) => { + json.type.flavorType.currentScope[scope.toUpperCase()] = []; + localizedNames[scope.toUpperCase()] = json.localizedNames[scope.toUpperCase()]; + }); + json.localizedNames = localizedNames; + } + + logger.debug('createDownloadedResourceSB.js', 'Created the createBibleResource SB'); + resolve(json); + }); + } catch (err) { + throw new Error(`Generate Burrito Failed : ${err}`); + } }; // export default createDownloadedResourceSB; @@ -207,28 +216,73 @@ export const generateResourceIngredientsOBS = async (currentResourceMeta, path, return resourceBurritoFile; }; +export const generateWebResourceIngredientsOBS = async (currentResourceMeta, currentResourceProject, resourceBurritoFile, files) => { + console.log('DownloadResourcePopUp.js', 'In adding ingredients to burrito of OBS'); + + for (const file of files) { + const endPart = file.split('/').pop(); + const regX = /^\d{2}.md$/; + + if (regX.test(endPart) || ['intro.md', 'title.md'].includes(endPart)) { + const userProfile = await localForage.getItem('userProfile'); + const email = userProfile?.user?.email; + const filePath = `${newPath}/${email}/resources/${file}`; + const { data: fileResponse, error: fileError } = await sbStorageDownload(filePath); + + if (fileError) { + logger.debug('DownloadResourcePopUp.js', 'error file not found in resource download'); + console.log('DownloadResourcePopUp.js', 'error file not found in resource download'); + throw new Error(`File not Exist in project Directory: ${file}`); + } + + const filecontent = await fileResponse.text(); + const checksum = md5(filecontent); + const stats = { size: fileResponse.size }; + + resourceBurritoFile.ingredients[file.replace(`${currentResourceProject.name}/`, '')] = { + checksum: { md5: checksum }, + mimeType: currentResourceMeta.dublin_core.format, + size: stats.size, + }; + + if (endPart.toLowerCase() === 'front.md') { + resourceBurritoFile.ingredients[file.replace(`${currentResourceProject.name}/`, '')].role = 'pubdata'; + } else if (regX.test(endPart)) { + const matchingStory = OBSData.find((story) => `${story.storyId.toString().padStart(2, 0)}.md` === endPart.toLowerCase()); + if (matchingStory) { + resourceBurritoFile.ingredients[file.replace(`${currentResourceProject.name}/`, '')].scope = matchingStory.scope; + } + } else { + resourceBurritoFile.ingredients[file.replace(`${currentResourceProject.name}/`, '')].role = 'title'; + } + } + } + + return resourceBurritoFile; +}; + export const handleDownloadResources = async (resourceData, selectResource, action, update = false) => { logger.debug('DownloadResourcePopUp.js', 'In resource download - started : '); const newpath = localStorage.getItem('userPath'); -// console.log({ -// resourceData, selectResource, action, update, -// }); + // console.log({ + // resourceData, selectResource, action, update, + // }); return new Promise((resolve, reject) => { - localForage.getItem('userProfile').then(async (user) => { - logger.debug('DownloadResourcePopUp.js', 'In resource download user fetch - ', user?.username); - const folder = path.join(newpath, packageInfo.name, 'users', `${user?.username}`, 'resources'); - const fs = window.require('fs'); - let resourceBurritoFile = {}; - let currentResourceMeta = ''; - let currentResourceProject = ''; - let licenseFileFound = false; - let currentProjectName = ''; - let customLicenseContent = 'empty'; - let resourceExist = false; - let resourceExistCount = 0; + localForage.getItem('userProfile').then(async (user) => { + logger.debug('DownloadResourcePopUp.js', 'In resource download user fetch - ', user?.username); + const folder = path.join(newpath, packageInfo.name, 'users', `${user?.username}`, 'resources'); + const fs = window.require('fs'); + let resourceBurritoFile = {}; + let currentResourceMeta = ''; + let currentResourceProject = ''; + let licenseFileFound = false; + let currentProjectName = ''; + let customLicenseContent = 'empty'; + let resourceExist = false; + let resourceExistCount = 0; try { // eslint-disable-next-line no-restricted-syntax, guard-for-in - for (const key in resourceData) { + for (const key in resourceData) { // eslint-disable-next-line no-await-in-loop, no-restricted-syntax, guard-for-in for (const row in resourceData[key]) { const resource = resourceData[key][row]; @@ -239,160 +293,160 @@ export const handleDownloadResources = async (resourceData, selectResource, acti const existingResource = fs.readdirSync(folder, { withFileTypes: true }).filter((dir) => dir.isDirectory()); // eslint-disable-next-line no-loop-func existingResource.forEach((element) => { - if (fs.existsSync(path.join(folder, element.name, 'metadata.json'))) { - let filecontentMeta = fs.readFileSync(path.join(folder, element.name, 'metadata.json'), 'utf8'); - filecontentMeta = JSON.parse(filecontentMeta); - if (filecontentMeta?.resourceMeta) { - const storedresourceMeta = filecontentMeta?.resourceMeta; - if (storedresourceMeta?.name === resource?.name && storedresourceMeta?.owner === resource?.owner - && storedresourceMeta?.release?.tag_name === resource?.release?.tag_name) { - logger.debug('DownloadResourcePopUp.js', `In resource download existing resource ${resource?.name}_${resource?.release?.tag_name}`); - resourceExist = true; - resourceExistCount += 1; - } - } + if (fs.existsSync(path.join(folder, element.name, 'metadata.json'))) { + let filecontentMeta = fs.readFileSync(path.join(folder, element.name, 'metadata.json'), 'utf8'); + filecontentMeta = JSON.parse(filecontentMeta); + if (filecontentMeta?.resourceMeta) { + const storedresourceMeta = filecontentMeta?.resourceMeta; + if (storedresourceMeta?.name === resource?.name && storedresourceMeta?.owner === resource?.owner + && storedresourceMeta?.release?.tag_name === resource?.release?.tag_name) { + logger.debug('DownloadResourcePopUp.js', `In resource download existing resource ${resource?.name}_${resource?.release?.tag_name}`); + resourceExist = true; + resourceExistCount += 1; } + } + } }); } if (!resourceExist) { // eslint-disable-next-line no-await-in-loop - await fetch(resource.metadata_json_url) - .then((res) => res.json()) - // eslint-disable-next-line no-loop-func - .then(async (response) => { - logger.debug('passed fetch meta ---------->', { response }); - logger.debug('DownloadResourcePopUp.js', 'In resource download - fetch resourceMeta yml'); - currentResourceMeta = response; - currentResourceProject = resource; - // creating burrito template - resourceBurritoFile = await createDownloadedResourceSB(user?.username, currentResourceMeta, currentResourceProject, selectResource); - // adding online fetch response meta as resourceMeta - resourceBurritoFile.resourceMeta = currentResourceProject; - resourceBurritoFile.resourceMeta.lastUpdatedAg = moment().format(); - logger.debug('passed create burrito ---------->'); - - logger.debug('DownloadResourcePopUp.js', 'In resource download - basic burrito generated for resource ', `${resource.name}-${resource.owner}`); - - currentProjectName = `${resource.name}_${Object.keys(resourceBurritoFile.identification.primary.scribe)[0]}`; - await fetch(resource.zipball_url) - .then((res) => res.arrayBuffer()) - .then(async (blob) => { - logger.debug('DownloadResourcePopUp.js', 'In resource download - downloading zip content '); - if (!fs.existsSync(folder)) { - fs.mkdirSync(folder, { recursive: true }); - } - // wririntg zip to local - await fs.writeFileSync(path.join(folder, `${currentProjectName}.zip`), Buffer.from(blob)); - logger.debug('DownloadResourcePopUp.js', 'In resource download - downloading zip content completed '); - - logger.debug('DownloadResourcePopUp.js', 'In resource download - Unzip downloaded resource'); - // extract zip - const filecontent = await fs.readFileSync(path.join(folder, `${currentProjectName}.zip`)); - const result = await JSZip.loadAsync(filecontent); - const keys = Object.keys(result.files); - - // eslint-disable-next-line no-restricted-syntax - for (const key of keys) { - const item = result.files[key]; - if (item.dir) { - fs.mkdirSync(path.join(folder, item.name), { recursive: true }); - } else { - // eslint-disable-next-line no-await-in-loop - const bufferContent = Buffer.from(await item.async('arraybuffer')); - fs.writeFileSync(path.join(folder, item.name), bufferContent); + await fetch(resource.metadata_json_url) + .then((res) => res.json()) + // eslint-disable-next-line no-loop-func + .then(async (response) => { + logger.debug('passed fetch meta ---------->', { response }); + logger.debug('DownloadResourcePopUp.js', 'In resource download - fetch resourceMeta yml'); + currentResourceMeta = response; + currentResourceProject = resource; + // creating burrito template + resourceBurritoFile = await createDownloadedResourceSB(user?.username, currentResourceMeta, currentResourceProject, selectResource); + // adding online fetch response meta as resourceMeta + resourceBurritoFile.resourceMeta = currentResourceProject; + resourceBurritoFile.resourceMeta.lastUpdatedAg = moment().format(); + logger.debug('passed create burrito ---------->'); + + logger.debug('DownloadResourcePopUp.js', 'In resource download - basic burrito generated for resource ', `${resource.name}-${resource.owner}`); + + currentProjectName = `${resource.name}_${Object.keys(resourceBurritoFile.identification.primary.scribe)[0]}`; + await fetch(resource.zipball_url) + .then((res) => res.arrayBuffer()) + .then(async (blob) => { + logger.debug('DownloadResourcePopUp.js', 'In resource download - downloading zip content '); + if (!fs.existsSync(folder)) { + fs.mkdirSync(folder, { recursive: true }); } - if (key.toLowerCase().includes('license')) { - logger.debug('DownloadResourcePopUp.js', 'In resource download - check license file found'); - licenseFileFound = true; - // console.log('license exist'); - if (fs.existsSync(path.join(folder, key))) { - const licenseContent = fs.readFileSync(path.join(folder, key), 'utf8'); - const checksum = md5(licenseContent); - const stats = fs.statSync(path.join(folder, key)); - resourceBurritoFile.ingredients[key.replace(currentResourceProject.name, '.')] = { - checksum: { md5: checksum }, + // wririntg zip to local + await fs.writeFileSync(path.join(folder, `${currentProjectName}.zip`), Buffer.from(blob)); + logger.debug('DownloadResourcePopUp.js', 'In resource download - downloading zip content completed '); + + logger.debug('DownloadResourcePopUp.js', 'In resource download - Unzip downloaded resource'); + // extract zip + const filecontent = await fs.readFileSync(path.join(folder, `${currentProjectName}.zip`)); + const result = await JSZip.loadAsync(filecontent); + const keys = Object.keys(result.files); + + // eslint-disable-next-line no-restricted-syntax + for (const key of keys) { + const item = result.files[key]; + if (item.dir) { + fs.mkdirSync(path.join(folder, item.name), { recursive: true }); + } else { + // eslint-disable-next-line no-await-in-loop + const bufferContent = Buffer.from(await item.async('arraybuffer')); + fs.writeFileSync(path.join(folder, item.name), bufferContent); + } + if (key.toLowerCase().includes('license')) { + logger.debug('DownloadResourcePopUp.js', 'In resource download - check license file found'); + licenseFileFound = true; + // console.log('license exist'); + if (fs.existsSync(path.join(folder, key))) { + const licenseContent = fs.readFileSync(path.join(folder, key), 'utf8'); + const checksum = md5(licenseContent); + const stats = fs.statSync(path.join(folder, key)); + resourceBurritoFile.ingredients[key.replace(currentResourceProject.name, '.')] = { + checksum: { md5: checksum }, + mimeType: 'text/md', + size: stats.size, + role: 'x-licence', + }; + } + } + } + logger.debug('passed zip extract ---------->'); + + // ingredients add to burrito + switch (selectResource) { + case 'bible': + resourceBurritoFile = await generateResourceIngredientsTextTransaltion(currentResourceMeta, path, folder, currentResourceProject, resourceBurritoFile); + customLicenseContent = customLicense; + break; + case 'obs': + resourceBurritoFile = await generateResourceIngredientsOBS(currentResourceMeta, path, folder, currentResourceProject, resourceBurritoFile, keys); + customLicenseContent = OBSLicense; + break; + default: + throw new Error(' can not process :Inavalid Type od Resource requested'); + } + logger.debug('passed ingredients creations ---------->'); + + // custom license adding + if (!licenseFileFound) { + logger.debug('DownloadResourcePopUp.js', 'In resource custom license add - no license found'); + // console.log('no license file found -', md5(customLicenseContent)); + if (fs.existsSync(path.join(folder, currentResourceProject.name))) { + fs.writeFileSync(path.join(folder, currentResourceProject.name, 'LICENSE.md'), customLicenseContent); + const stats = fs.statSync(path.join(folder, currentResourceProject.name, 'LICENSE.md')); + resourceBurritoFile.ingredients['./LICENSE.md'] = { + checksum: { md5: md5(customLicenseContent) }, mimeType: 'text/md', size: stats.size, role: 'x-licence', }; } } - } - logger.debug('passed zip extract ---------->'); - - // ingredients add to burrito - switch (selectResource) { - case 'bible': - resourceBurritoFile = await generateResourceIngredientsTextTransaltion(currentResourceMeta, path, folder, currentResourceProject, resourceBurritoFile); - customLicenseContent = customLicense; - break; - case 'obs': - resourceBurritoFile = await generateResourceIngredientsOBS(currentResourceMeta, path, folder, currentResourceProject, resourceBurritoFile, keys); - customLicenseContent = OBSLicense; - break; - default: - throw new Error(' can not process :Inavalid Type od Resource requested'); - } - logger.debug('passed ingredients creations ---------->'); - - // custom license adding - if (!licenseFileFound) { - logger.debug('DownloadResourcePopUp.js', 'In resource custom license add - no license found'); - // console.log('no license file found -', md5(customLicenseContent)); - if (fs.existsSync(path.join(folder, currentResourceProject.name))) { - fs.writeFileSync(path.join(folder, currentResourceProject.name, 'LICENSE.md'), customLicenseContent); - const stats = fs.statSync(path.join(folder, currentResourceProject.name, 'LICENSE.md')); - resourceBurritoFile.ingredients['./LICENSE.md'] = { - checksum: { md5: md5(customLicenseContent) }, - mimeType: 'text/md', - size: stats.size, - role: 'x-licence', - }; - } - } - // scribe settings file generation - logger.debug('DownloadResourcePopUp.js', 'generating scribe-settings'); - const settings = await generateAgSettings(resourceBurritoFile, currentResourceMeta, selectResource); - await fs.writeFileSync(path.join(folder, currentResourceProject.name, environment.PROJECT_SETTING_FILE), JSON.stringify(settings)); - const settingsContent = fs.readFileSync(path.join(folder, currentResourceProject.name, environment.PROJECT_SETTING_FILE), 'utf8'); - const checksum = md5(settingsContent); - const stats = fs.statSync(path.join(folder, currentResourceProject.name, environment.PROJECT_SETTING_FILE)); - resourceBurritoFile.ingredients['./scribe-settings.json'] = { - checksum: { md5: checksum }, - mimeType: 'application/json', - size: stats.size, - role: 'x-scribe', - }; - // added new section to avoid ingredients issue in meta some times (new user) - const ymlPath = currentResourceMeta?.projects[0]?.path.replace('./', ''); - const renames = Object.keys(resourceBurritoFile.ingredients); - const regex = new RegExp(`(\\.\\/)|(${ymlPath}[\\/\\\\])`, 'g'); - await renames?.forEach((rename) => { - if (!rename.match(regex)) { - delete resourceBurritoFile.ingredients[rename]; + // scribe settings file generation + logger.debug('DownloadResourcePopUp.js', 'generating scribe-settings'); + const settings = await generateAgSettings(resourceBurritoFile, currentResourceMeta, selectResource); + await fs.writeFileSync(path.join(folder, currentResourceProject.name, environment.PROJECT_SETTING_FILE), JSON.stringify(settings)); + const settingsContent = fs.readFileSync(path.join(folder, currentResourceProject.name, environment.PROJECT_SETTING_FILE), 'utf8'); + const checksum = md5(settingsContent); + const stats = fs.statSync(path.join(folder, currentResourceProject.name, environment.PROJECT_SETTING_FILE)); + resourceBurritoFile.ingredients['./scribe-settings.json'] = { + checksum: { md5: checksum }, + mimeType: 'application/json', + size: stats.size, + role: 'x-scribe', + }; + // added new section to avoid ingredients issue in meta some times (new user) + const ymlPath = currentResourceMeta?.projects[0]?.path.replace('./', ''); + const renames = Object.keys(resourceBurritoFile.ingredients); + const regex = new RegExp(`(\\.\\/)|(${ymlPath}[\\/\\\\])`, 'g'); + await renames?.forEach((rename) => { + if (!rename.match(regex)) { + delete resourceBurritoFile.ingredients[rename]; + } + }); + // write metaData.json + await fs.writeFileSync(path.join(folder, currentResourceProject.name, 'metadata.json'), JSON.stringify(resourceBurritoFile)); + + logger.debug('passed scribe settings creations ---------->'); + + // finally remove zip and rename base folder to projectname_id + logger.debug('DownloadResourcePopUp.js', 'deleting zip file - rename project with project + id in scribe format'); + if (fs.existsSync(folder)) { + fs.renameSync(path.join(folder, currentResourceProject.name), path.join(folder, currentProjectName)); + fs.unlinkSync(path.join(folder, `${currentProjectName}.zip`), () => { + logger.debug('DownloadResourcePopUp.js', 'error in deleting zip'); + throw new Error(`Removing Resource Zip Failed : ${currentResourceProject.name}`); + }); } + }).catch((err) => { + throw new Error(`Download Resource file Failed : ${err}`); }); - // write metaData.json - await fs.writeFileSync(path.join(folder, currentResourceProject.name, 'metadata.json'), JSON.stringify(resourceBurritoFile)); - - logger.debug('passed scribe settings creations ---------->'); - - // finally remove zip and rename base folder to projectname_id - logger.debug('DownloadResourcePopUp.js', 'deleting zip file - rename project with project + id in scribe format'); - if (fs.existsSync(folder)) { - fs.renameSync(path.join(folder, currentResourceProject.name), path.join(folder, currentProjectName)); - fs.unlinkSync(path.join(folder, `${currentProjectName}.zip`), () => { - logger.debug('DownloadResourcePopUp.js', 'error in deleting zip'); - throw new Error(`Removing Resource Zip Failed : ${currentResourceProject.name}`); - }); - } - }).catch((err) => { - throw new Error(`Download Resource file Failed : ${err}`); - }); - }).catch((err) => { - throw new Error(`Fetch Resource Failed : ${err}`); - }); + }).catch((err) => { + throw new Error(`Fetch Resource Failed : ${err}`); + }); } resourceExist = false; logger.debug('DownloadResourcePopUp.js', 'Finished single resource: '); @@ -418,6 +472,217 @@ export const handleDownloadResources = async (resourceData, selectResource, acti resourceExistCount = 0; reject(err); } + }); + }); +}; + +export const handleDownloadWebResources = async (resourceData, selectResource, action, update = false) => { + console.log({ + resourceData, + selectResource, + action, + update, + }); + + return new Promise(async (resolve, reject) => { + const userProfile = await localForage.getItem('userProfile'); + const user = userProfile?.user?.email; + const folder = `${newPath}/${user}/resources`; + let resourceBurritoFile = {}; + let currentResourceMeta = ''; + let currentResourceProject = ''; + let licenseFileFound = false; + let currentProjectName = ''; + let customLicenseContent = 'empty'; + let resourceExist = false; + let resourceExistCount = 0; + + try { + for (const key in resourceData) { + for (const row in resourceData[key]) { + const resource = resourceData[key][row]; + if (resource.isChecked) { + console.log('passed is checked ---------->'); + if (!update) { + const { data: existingResources } = await sbStorageList(folder); + console.log({ existingResources }); + for (const element of existingResources) { + if (element.name !== '.keep') { + console.log(element.name); + const { data: filecontentMeta } = await sbStorageDownload(`${folder}/${element.name}/metadata.json`); + console.log({ filecontentMeta }); + const storedResourceMeta = filecontentMeta; + if (storedResourceMeta?.resourceMeta) { + const storedResourceName = storedResourceMeta.resourceMeta.name; + const storedResourceOwner = storedResourceMeta.resourceMeta.owner; + const storedResourceTag = storedResourceMeta.resourceMeta.release?.tag_name; + if ( + storedResourceName === resource.name + && storedResourceOwner === resource.owner + && storedResourceTag === resource.release?.tag_name + ) { + console.log( + 'DownloadResourcePopUp.js', + `In resource download existing resource ${resource.name}_${resource.release?.tag_name}`, + ); + resourceExist = true; + resourceExistCount += 1; + } + } + } + } + } + + if (!resourceExist) { + const response = await fetch(resource.metadata_json_url); + const metadataJson = await response.json(); + console.log('passed fetch meta ---------->', { metadataJson }); + console.log('DownloadResourcePopUp.js', 'In resource download - fetch resourceMeta yml'); + currentResourceMeta = metadataJson; + currentResourceProject = resource; + + resourceBurritoFile = await createDownloadedResourceSB(user, currentResourceMeta, currentResourceProject, selectResource); + resourceBurritoFile.resourceMeta = currentResourceProject; + resourceBurritoFile.resourceMeta.lastUpdatedAg = moment().format(); + + console.log('DownloadResourcePopUp.js', 'In resource download - basic burrito generated for resource ', `${resource.name}-${resource.owner}`); + + currentProjectName = `${resource.name}_${Object.keys(resourceBurritoFile.identification.primary.scribe)[0]}`; + + const zipResponse = await fetch(resource.zipball_url); + const zipBuffer = await zipResponse.arrayBuffer(); + console.log('DownloadResourcePopUp.js', 'In resource download - downloading zip content completed '); + + console.log('DownloadResourcePopUp.js', 'In resource download - Unzip downloaded resource'); + const jsZip = new JSZip(); + const zip = await jsZip.loadAsync(zipBuffer); + + for (const [zipPath, zipObject] of Object.entries(zip.files)) { + if (zipObject.dir) { + await sbStorageUpload(`${folder}/${zipObject.name}`, '', { upsert: false }); + } else { + const fileContent = await zipObject.async('uint8array'); + await sbStorageUpload(`${folder}/${zipObject.name}`, fileContent, { upsert: false }); + } + if (zipPath.toLowerCase().includes('license')) { + console.log('DownloadResourcePopUp.js', 'In resource download - check license file found'); + licenseFileFound = true; + const { data: customLicense } = await sbStorageDownload(`${folder}/${zipPath}`); + if (customLicense) { + const { data: licenseContent } = await sbStorageDownload(`${folder}/${zipPath}`); + const checksum = md5(licenseContent); + const { data: size } = await sbStorageDownload(`${folder}/${zipPath}`); + resourceBurritoFile.ingredients[zipPath.replace(currentResourceProject.name, '.')] = { + checksum: { md5: checksum }, + mimeType: 'text/md', + size, + role: 'x-licence', + }; + } + } + } + + console.log('passed zip extract ---------->'); + + // ingredients add to burrito + switch (selectResource) { + case 'bible': + resourceBurritoFile = await generateResourceIngredientsTextTransaltion( + currentResourceMeta, + // supabaseStorage(), + folder, + currentResourceProject, + resourceBurritoFile, + ); + customLicenseContent = customLicense; + break; + case 'obs': + resourceBurritoFile = await generateWebResourceIngredientsOBS( + currentResourceMeta, + currentResourceProject, + resourceBurritoFile, + Object.keys(zip.files), + ); + customLicenseContent = OBSLicense; + break; + default: + throw new Error('Cannot process: Invalid type of resource requested'); + } + + console.log('passed ingredients creations ---------->'); + + if (!licenseFileFound) { + console.log('DownloadResourcePopUp.js', 'In resource custom license add - no license found'); + + const { data } = await sbStorageList(`${folder}/${currentResourceProject.name}`); + console.log('what is the length', { data }); + if (data.length > 0) { + await sbStorageUpload(`${folder}/${currentResourceProject.name}/LICENSE.md`, customLicenseContent, { upsert: false }); + const { data: size } = await sbStorageDownload(`${folder}/${currentResourceProject.name}/LICENSE.md`); + if (size) { + console.log('In resource custom license add - custom license added', { size }); + resourceBurritoFile.ingredients['./LICENSE.md'] = { + checksum: { md5: md5(customLicenseContent) }, + mimeType: 'text/md', + // size: , + role: 'x-licence', + }; + } + } + } + + // scribe settings file generation + const settings = await generateAgSettings(resourceBurritoFile, currentResourceMeta, selectResource); + await sbStorageUpload(`${folder}/${currentResourceProject.name}/${environment.PROJECT_SETTING_FILE}`, JSON.stringify(settings), { upsert: false }); + const { data: settingsContent } = await sbStorageDownload(`${folder}/${currentResourceProject.name}/${environment.PROJECT_SETTING_FILE}`); + const checksum = md5(settingsContent); + const { data: size } = await sbStorageDownload(`${folder}/${currentResourceProject.name}/${environment.PROJECT_SETTING_FILE}`); + resourceBurritoFile.ingredients['./scribe-settings.json'] = { + checksum: { md5: checksum }, + mimeType: 'application/json', + size, + role: 'x-scribe', + }; + // added new section to avoid ingredients issue in meta some times (new user) + const ymlPath = currentResourceMeta?.projects[0]?.path.replace('./', ''); + const renames = Object.keys(resourceBurritoFile.ingredients); + const regex = new RegExp(`(\\.\\/)|(${ymlPath}[\\/\\\\])`, 'g'); + renames?.forEach((rename) => { + if (!rename.match(regex)) { + delete resourceBurritoFile.ingredients[rename]; + } + }); + + await sbStorageUpload(`${folder}/${currentResourceProject.name}/metadata.json`, JSON.stringify(resourceBurritoFile), { upsert: false }); + console.log('passed ag settings creations ---------->'); + + // finally remove zip and rename base folder to projectname_id + const { data, error } = await sbStorageList(`${folder}/${currentResourceProject.name}`); + if (data) { + await sbStorageUpdate({ path: `${folder}/${currentResourceProject.name}`, payload: `${folder}/${currentProjectName}`, options: { cacheControl: '3600', upsert: true } }); + await sbStorageRemove(`${folder}/${currentProjectName}.zip`); + } else { + console.log('error in storage.list ---------->', error); + } + } + + resourceExist = false; + console.log('DownloadResourcePopUp.js', 'Finished single resource: '); + console.log('completed single resource ---------->', resource.name); + action && action?.setDownloadCount((prev) => prev + 1); + } + } + + if (update && update.status) { + await sbStorageRemove(`${folder}/${update?.oldResource?.projectDir}`, { recursive: true }); + } + + resolve({ status: 'success', existing: resourceExistCount }); + resourceExistCount = 0; + } + } catch (err) { + console.log('Catching error in download resource', err); + reject(err); + } }); -}); }; diff --git a/renderer/src/components/Resources/ResourceUtils/readResourceMetadata.js b/renderer/src/components/Resources/ResourceUtils/readResourceMetadata.js index 6ae5be617..da948d111 100644 --- a/renderer/src/components/Resources/ResourceUtils/readResourceMetadata.js +++ b/renderer/src/components/Resources/ResourceUtils/readResourceMetadata.js @@ -1,22 +1,39 @@ import { readRefMeta } from '@/core/reference/readRefMeta'; import { readRefBurrito } from '@/core/reference/readRefBurrito'; import * as localforage from 'localforage'; +import { isElectron } from '@/core/handleElectron'; const path = require('path'); export async function readResourceMetadata(projectsDir, resourcePath, setSubMenuItems, parseData, userOrCommon) { - const refs = await readRefMeta({ projectsDir }); - refs.forEach(async (ref) => { - const metaPath = path.join(`${resourcePath}`, ref, 'metadata.json'); - const data = await readRefBurrito({ metaPath }); - if (data) { - const burrito = {}; - burrito.projectDir = ref; - burrito.value = JSON.parse(data); + if (isElectron()) { + const refs = await readRefMeta({ projectsDir }); + refs.forEach(async (ref) => { + const metaPath = path.join(`${resourcePath}`, ref, 'metadata.json'); + const data = await readRefBurrito({ metaPath }); + if (data) { + const burrito = {}; + burrito.projectDir = ref; + burrito.value = JSON.parse(data); burrito.type = userOrCommon; - parseData.push(burrito); - await localforage.setItem('resources', parseData); - setSubMenuItems(parseData); - } - }); + parseData.push(burrito); + await localforage.setItem('resources', parseData); + setSubMenuItems(parseData); + } + }); + } else { + const refs = await readRefMeta({ projectsDir }); + refs.forEach(async (ref) => { + const metaPath = `${projectsDir}/${ref}/metadata.json`; + const data = await readRefBurrito({ metaPath }); + if (data) { + const burrito = {}; + burrito.projectDir = ref; + burrito.value = data; + parseData.push(burrito); + await localforage.setItem('resources', parseData); + setSubMenuItems(parseData); + } + }); + } } diff --git a/renderer/src/components/Resources/ResourcesSideBar.js b/renderer/src/components/Resources/ResourcesSideBar.js index e0ad518aa..c84c18c1e 100644 --- a/renderer/src/components/Resources/ResourcesSideBar.js +++ b/renderer/src/components/Resources/ResourcesSideBar.js @@ -22,10 +22,10 @@ export default function ResourcesSidebar({ selectedProjectMeta, }) { const { t } = useTranslation(); - const handleClick = (id) => { setSelectResource(id); }; + useEffect(() => { if (!selectResource) { switch (selectedProjectMeta.type.flavorType.flavor.name) { @@ -39,8 +39,8 @@ export default function ResourcesSidebar({ setSelectResource('audio'); break; default: - setSelectResource('bible'); - break; + setSelectResource('bible'); + break; } setTitle('Bible'); } @@ -137,22 +137,22 @@ export default function ResourcesSidebar({ />
{resource.subCategory.length !== 0 - && resource.subCategory.map( - (subCategory, categoryIdx) => { - const { Icon, id, title } = subCategory; - return ( - - ); - }, - )} + && resource.subCategory.map( + (subCategory, categoryIdx) => { + const { Icon, id, title } = subCategory; + return ( + + ); + }, + )}
); diff --git a/renderer/src/components/Resources/useReadLocalResources.js b/renderer/src/components/Resources/useReadLocalResources.js index 0205aee8f..8e802adc2 100644 --- a/renderer/src/components/Resources/useReadLocalResources.js +++ b/renderer/src/components/Resources/useReadLocalResources.js @@ -1,22 +1,39 @@ import { readResourceMetadata } from '@/components/Resources/ResourceUtils/readResourceMetadata'; +import { isElectron } from '@/core/handleElectron'; +import * as localforage from 'localforage'; import packageInfo from '../../../../package.json'; +import { createDirectory, newPath } from '../../../../supabase'; +// if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { +// const newPath = require('../../../../supabase').newPath +// const createDirectory = require('../../../../supabase').createDirectory +// } -export default function readLocalResources(username, setSubMenuItems) { - const parseData = []; - const fs = window.require('fs'); - const path = require('path'); - const newpath = localStorage.getItem('userPath'); - const projectsDir = path.join(newpath, packageInfo.name, 'users', username, 'resources');// Read user resources - const userResourceMetaPath = path.join(newpath, packageInfo.name, 'users', username, 'resources'); - fs.mkdirSync(path.join(newpath, packageInfo.name, 'users', username, 'resources'), { - recursive: true, - }); - readResourceMetadata(projectsDir, userResourceMetaPath, setSubMenuItems, parseData, 'user'); +export default async function readLocalResources(username, setSubMenuItems) { + if (isElectron()) { + const parseData = []; + const fs = window.require('fs'); + const path = require('path'); + const newpath = localStorage.getItem('userPath'); + const projectsDir = path.join(newpath, packageInfo.name, 'users', username, 'resources');// Read user resources + const userResourceMetaPath = path.join(newpath, packageInfo.name, 'users', username, 'resources'); + fs.mkdirSync(path.join(newpath, packageInfo.name, 'users', username, 'resources'), { + recursive: true, + }); + readResourceMetadata(projectsDir, userResourceMetaPath, setSubMenuItems, parseData, 'user'); - const commonResourceDir = path.join(newpath, packageInfo.name, 'common', 'resources');// Read common resources - const commonResourceMetaPath = path.join(newpath, packageInfo.name, 'common', 'resources'); - fs.mkdirSync(path.join(newpath, packageInfo.name, 'common', 'resources'), { - recursive: true, - }); - readResourceMetadata(commonResourceDir, commonResourceMetaPath, setSubMenuItems, parseData, 'common'); + const commonResourceDir = path.join(newpath, packageInfo.name, 'common', 'resources');// Read common resources + const commonResourceMetaPath = path.join(newpath, packageInfo.name, 'common', 'resources'); + fs.mkdirSync(path.join(newpath, packageInfo.name, 'common', 'resources'), { + recursive: true, + }); + readResourceMetadata(commonResourceDir, commonResourceMetaPath, setSubMenuItems, parseData, 'common'); + } else { + const userProfile = await localforage.getItem('userProfile'); + const email = userProfile.user.email; + const parseData = []; + const projectsDir = `${newPath}/${email}/resources`; + const userResourceMetaPath = `${newPath}/${email}/resources`; + createDirectory({path:userResourceMetaPath}); + readResourceMetadata(projectsDir, userResourceMetaPath, setSubMenuItems, parseData); + } } diff --git a/renderer/src/components/Signup/Signup.js b/renderer/src/components/Signup/Signup.js index 3df0b1a74..299bfa3c5 100644 --- a/renderer/src/components/Signup/Signup.js +++ b/renderer/src/components/Signup/Signup.js @@ -1,6 +1,6 @@ /* eslint-disable */ import React from 'react'; -import { useRouter } from 'next/router'; +import { useRouter } from 'next/navigation'; import * as logger from '../../logger'; import useApi from './useApi'; import CustomLogin from '../Login/CustomLogin' diff --git a/renderer/src/components/Signup/WebSignup.js b/renderer/src/components/Signup/WebSignup.js new file mode 100644 index 000000000..fa399945f --- /dev/null +++ b/renderer/src/components/Signup/WebSignup.js @@ -0,0 +1,81 @@ +'use client'; + +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { createSupabaseSettingJson, createWebUser } from '@/core/Login/handleLogin'; +import { environment } from '../../../environment'; +import { supabaseSignup, newPath } from '../../../../supabase'; +// if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { +// const newPath = require('../../../../supabase').newPath +// const supabase = require('../../../../../supabase').supabase +// } + +function SignupPage() { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [status, setStatus] = useState(''); + const router = useRouter(); + + async function handleSubmit(e) { + e.preventDefault(); + console.log('Email:', email, 'Password:', password); + + const { data, error } = await supabaseSignup({ + email, + password, + }); + if (data) { + createSupabaseSettingJson(`${newPath}/${data.user.email}/${environment.USER_SETTING_FILE}`); + console.log('signup successful', data); + setStatus('Check your email for the confirmation link.'); + createWebUser(data.user); + router.push('/login'); + } else { + console.error('error occured', error); + setStatus(error); + } + } + + return ( +
+
+

Sign Up

+
+ + setEmail(e.target.value)} + /> +
+
+ + setPassword(e.target.value)} + /> +
+ {status &&

{status}

} + +
+
+ ); +} + +export default SignupPage; diff --git a/renderer/src/components/context/ProjectContext.js b/renderer/src/components/context/ProjectContext.js index 95bd48c43..47479a08a 100644 --- a/renderer/src/components/context/ProjectContext.js +++ b/renderer/src/components/context/ProjectContext.js @@ -5,11 +5,15 @@ import * as localforage from 'localforage'; import { splitStringByLastOccurance } from '@/util/splitStringByLastMarker'; import { isElectron } from '../../core/handleElectron'; import * as logger from '../../logger'; -import saveProjectsMeta from '../../core/projects/saveProjetcsMeta'; +import { saveProjectsMeta, saveSupabaseProjectsMeta } from '../../core/projects/saveProjetcsMeta'; import { environment } from '../../../environment'; import staicLangJson from '../../lib/lang/langNames.json'; import packageInfo from '../../../../package.json'; +import { + newPath, sbStorageList, sbStorageUpload, sbStorageDownload, +} from '../../../../supabase'; + const path = require('path'); const advanceSettings = require('../../lib/AdvanceSettings.json'); @@ -94,7 +98,53 @@ const ProjectContextProvider = ({ children }) => { } fs.writeFileSync(file, JSON.stringify(json)); }; - + const createWebSettingJson = (file) => { + logger.debug('ProjectContext.js', 'Loading data from AdvanceSetting.json file'); + setCanonList(advanceSettings.canonSpecification); + setLicenseList((advanceSettings.copyright).push({ + id: 'Other', title: 'Custom', licence: '', locked: false, + })); + // setLanguages([advanceSettings.languages]); + const json = { + version: environment.AG_USER_SETTING_VERSION, + history: { + copyright: [{ + id: 'Other', title: 'Custom', licence: '', locked: false, + }], + languages: [], + textTranslation: { + canonSpecification: [{ + id: 4, title: 'Other', currentScope: [], locked: false, + }], + }, + }, + appLanguage: 'en', + theme: 'light', + userWorkspaceLocation: '', + commonWorkspaceLocation: '', + resources: { + door43: { + translationNotes: [], + translationQuestions: [], + translationWords: [], + obsTranslationNotes: [], + }, + }, + sync: { services: { door43: [] } }, + }; + console.debug('ProjectContext.js', `Creating a ${environment.USER_SETTING_FILE} file`); + const data = sbStorageList(file); + console.log('ProjectContext.js', { data }); + if (data.length === 0) { + const { data: envSettings } = sbStorageUpload(file, JSON.stringify(json), { + cacheControl: '3600', + upsert: false, + }); + if (envSettings) { + console.log('ProjectContext.js', { envSettings }); + } + } + }; const concatLanguages = async (json, staicLangJson) => { logger.debug('ProjectContext.js', 'In concat languages'); const userlanguages = []; @@ -168,6 +218,51 @@ const ProjectContextProvider = ({ children }) => { createSettingJson(fs, file); } }; + + const loadWebSettings = async () => { + let currentUser; + await localforage.getItem('userProfile').then((value) => { + currentUser = value.user.email; + setUsername(value.user.email); + }); + if (!currentUser) { + console.error('ProjectContext.js', 'Unable to find current user'); + } + + const file = `${newPath}/${currentUser}/${environment.USER_SETTING_FILE}`; + const { data: agUserSettings, error } = await sbStorageDownload(file); + if (error) { + console.error('ProjectContext.js', 'Failed to read the data from file'); + } + const json = JSON.parse(await agUserSettings.text()); + if (json.version === environment.AG_USER_SETTING_VERSION) { + if (json.history?.copyright) { + if (json.history?.copyright?.licence) { + setLicenseList((advanceSettings.copyright) + .concat(json.history?.copyright)); + } else { + const newObj = (advanceSettings.copyright).filter((item) => item.Id !== 'Other'); + newObj.push({ + id: 'Other', title: 'Custom', licence: '', locked: false, + }); + setLicenseList(newObj); + } + } else { + setLicenseList(advanceSettings.copyright); + } + setCanonList(json.history?.textTranslation.canonSpecification + ? (advanceSettings.canonSpecification) + .concat(json.history?.textTranslation.canonSpecification) + : advanceSettings.canonSpecification); + // concat static and custom languages + const langFilter = await concatLanguages(json, staicLangJson); + const filteredLang = langFilter.concatedLang.filter((lang) => lang?.ang.trim() !== ''); + setLanguages([...filteredLang]); + setCustomLanguages(langFilter.userlanguages); + } else { + createWebSettingJson(file); + } + }; // Json for storing advance settings const updateJson = async (currentSettings) => { logger.debug('ProjectContext.js', 'In updateJson'); @@ -221,6 +316,54 @@ const ProjectContextProvider = ({ children }) => { } } }; + + const updateWebJson = async (currentSettings) => { + let currentUser; + await localforage.getItem('userProfile').then((value) => { + currentUser = value.user.email; + setUsername(value.user.email); + }); + const file = `${newPath}/${currentUser}/${environment.USER_SETTING_FILE}`; + const { data } = await sbStorageDownload(file); + if (data) { + const json = JSON.parse(await data.text()); + // eslint-disable-next-line no-nested-ternary + const currentSetting = (currentSettings === 'copyright' ? copyright + : (currentSettings === 'languages' ? { + title: language.ang, + id: language.id, + scriptDirection: language.ld, + langCode: language.lc, + custom: true, + } + : canonSpecification)); + if (currentSettings === 'canonSpecification') { + (json.history?.textTranslation[currentSettings])?.push(currentSetting); + } else if (json.history[currentSettings] + && uniqueId(json.history[currentSettings], currentSetting.id)) { + (json.history[currentSettings]).forEach((setting) => { + if (setting.id === currentSetting.id) { + const keys = Object.keys(setting); + keys.forEach((key) => { + setting[key] = currentSetting[key]; + }); + } + }); + } else { + // updating the canon or pushing new language + (json.history[currentSettings]).push(currentSetting); + } + json.version = environment.AG_USER_SETTING_VERSION; + json.sync.services.door43 = json?.sync?.services?.door43 ? json?.sync?.services?.door43 : []; + console.debug('ProjectContext.js', 'Upadting the settings in existing file'); + await sbStorageUpload(file, JSON.stringify(json)); + console.debug('ProjectContext.js', 'Loading new settings from file'); + await loadWebSettings(); + } else { + console.error('ProjectContext.js', 'Failed to read the data from file'); + } + }; + // common functions for create projects const createProjectCommonUtils = async () => { logger.debug('ProjectContext.js', 'In createProject common utils'); @@ -231,18 +374,18 @@ const ProjectContextProvider = ({ children }) => { if (lang.id === language.id) { if (lang.ang !== language.ang || lang.ld !== language.ld || lang.lc !== language.lc) { - await updateJson('languages'); + isElectron() ? await updateJson('languages') : await updateWebJson('languages'); } } }); } else { // add language to custom - await updateJson('languages'); + isElectron() ? await updateJson('languages') : await updateWebJson('languages'); } } // Update Custom licence into current list. if (copyright.title === 'Custom') { - updateJson('copyright'); + isElectron() ? await updateJson('copyright') : await updateWebJson('copyright'); } else { const myLicence = Array.isArray(licenceList) ? licenceList.find((item) => item.title === copyright.title) : []; // eslint-disable-next-line import/no-dynamic-require @@ -257,7 +400,7 @@ const ProjectContextProvider = ({ children }) => { logger.debug('ProjectContext.js', 'In createProject Translation utils'); // Update Custom canon into current list. if (canonSpecification.title === 'Other') { - await updateJson('canonSpecification'); + isElectron() ? await updateJson('canonSpecification') : await updateWebJson('canonSpecification'); } }; @@ -288,6 +431,31 @@ const ProjectContextProvider = ({ children }) => { return status; }; + const createSupabaseProject = async (call, project, update, projectType) => { + createProjectCommonUtils(); + // common props pass for all project type + const projectMetaObj = { + newProjectFields, + language, + copyright, + importedFiles, + call, + project, + update, + projectType, + }; + if (projectType !== 'OBS') { + createProjectTranslationUtils(); + const temp_obj = { + versificationScheme: versificationScheme.title, + canonSpecification, + }; + Object.assign(projectMetaObj, temp_obj); + } + logger.debug('ProjectContext.js', 'Calling saveSupabaseProjectsMeta with required props'); + const status = saveSupabaseProjectsMeta(projectMetaObj); + return status; + }; const resetProjectStates = () => { const initialState = { language: '', @@ -324,7 +492,31 @@ const ProjectContextProvider = ({ children }) => { }); }); }); + } else if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { + loadWebSettings(); + localforage.getItem('userProfile').then((value) => { + setUsername(value?.user?.email); + }); + localforage.getItem('currentProject').then((projectName) => { + setSelectedProject(projectName); + // setProjectMeta in a var + localforage.getItem('projectmeta').then((projectMeta) => { + setSelectedProject(projectName); + // setProjectMeta in a var + projectMeta?.projects.forEach((meta) => { + const currentprojectId = Object.keys(meta.identification.primary[packageInfo.name])[0]; + const currentprojectName = meta.identification.name.en; + splitStringByLastOccurance(projectName, '_').then((arr) => { + if (arr.length > 0 && arr[0].toLowerCase() === currentprojectName.toLowerCase() + && arr[1].toLowerCase() === currentprojectId.toLocaleLowerCase()) { + setSelectedProjectMeta(meta); + } + }); + }); + }); + }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -359,6 +551,7 @@ const ProjectContextProvider = ({ children }) => { setSideTabTitle, setSelectedProject, createProject, + createSupabaseProject, setLanguage, setScrollLock, setUsername, diff --git a/renderer/src/components/hooks/projects/useProjectsSort.js b/renderer/src/components/hooks/projects/useProjectsSort.js index 221402c84..d1f5bcb64 100644 --- a/renderer/src/components/hooks/projects/useProjectsSort.js +++ b/renderer/src/components/hooks/projects/useProjectsSort.js @@ -1,12 +1,14 @@ import React, { useEffect } from 'react'; import * as localForage from 'localforage'; import moment from 'moment'; -import { updateAgSettings } from '../../../core/projects/updateAgSettings'; +import { + updateAgSettings, + updateWebAgSettings, +} from '../../../core/projects/updateAgSettings'; import parseProjectMetaUpdate from '../../../core/projects/parseProjectMetaUpdate'; // import metaFileReplace from '../../../core/projects/metaFileReplace'; import { isElectron } from '../../../core/handleElectron'; import fetchProjectsMeta from '../../../core/projects/fetchProjectsMeta'; -import parseFetchProjects from '../../../core/projects/parseFetchProjects'; // import parseFileUpdate from '../../../core/projects/parseFileUpdate'; import * as logger from '../../../logger'; @@ -21,6 +23,7 @@ function useProjectsSort() { const [unstarredProjects, setUnStarredProjets] = React.useState(); const [selectedProject, setSelectedProject] = React.useState(''); const [notifications, setNotifications] = React.useState([]); + const [loading, setLoading] = React.useState(false); const [activeNotificationCount, setActiveNotificationCount] = React.useState(0); const starrtedData = []; @@ -39,7 +42,10 @@ function useProjectsSort() { }, [notifications]); const handleClickStarred = async (event, name, property) => { - logger.debug('useProjectsSort.js', 'converting starred to be unstarred and viceversa'); + logger.debug( + 'useProjectsSort.js', + 'converting starred to be unstarred and viceversa', + ); property === 'starred' ? setactive('starred') : setactive('unstarred'); const selectedIndex = property === 'starred' ? starredrow.findIndex((x) => x.name === name) @@ -47,54 +53,67 @@ function useProjectsSort() { const copy = property === 'starred' ? starredrow.splice(selectedIndex, 1) : unstarredrow.splice(selectedIndex, 1); - const projectArrayTemp = []; + const projectArrayTemp = []; if (isElectron()) { let currentUser; await localForage.getItem('userProfile').then((value) => { currentUser = value?.username; }); const projects = localForage.getItem('projectmeta'); - projects.then((value) => { - if (value) { - projectArrayTemp.push(value); - } - }).then(() => { - projectArrayTemp[0].projects.forEach((_project) => { - if (_project.identification.name.en === name) { - let dirName; - switch (_project.type.flavorType.flavor.name) { - case 'textTranslation': - dirName = 'textTranslation'; - break; - case 'textStories': - dirName = 'textStories'; - break; - case 'audioTranslation': - dirName = 'audioTranslation'; - break; - default: - break; - } - const status = _project.project[dirName].starred; - const selectedProject = _project; - selectedProject.project[dirName].starred = !status; - selectedProject.project[dirName].lastSeen = moment().format(); + projects + .then((value) => { + if (value) { + projectArrayTemp.push(value); } - }); - }).finally(() => { - localForage.setItem('projectmeta', projectArrayTemp[0]) + }) .then(() => { projectArrayTemp[0].projects.forEach((_project) => { if (_project.identification.name.en === name) { - const id = Object.keys(_project.identification.primary.scribe); - const projectName = `${name}_${id}`; - logger.debug('useProjectsSort.js', `Updating star/unstar in Scribe settings for ${name}`); - updateAgSettings(currentUser, projectName, _project); + let dirName; + switch (_project.type.flavorType.flavor.name) { + case 'textTranslation': + dirName = 'textTranslation'; + break; + case 'textStories': + dirName = 'textStories'; + break; + case 'audioTranslation': + dirName = 'audioTranslation'; + break; + default: + break; + } + const status = _project.project[dirName].starred; + const selectedProject = _project; + selectedProject.project[dirName].starred = !status; + selectedProject.project[dirName].lastSeen = moment().format(); } }); - // metaFileReplace({ userData: projectArrayTemp[0] }); + }) + .finally(() => { + localForage + .setItem('projectmeta', projectArrayTemp[0]) + .then(() => { + projectArrayTemp[0].projects.forEach((_project) => { + if (_project.identification.name.en === name) { + const id = Object.keys( + _project.identification.primary.scribe, + ); + const projectName = `${name}_${id}`; + logger.debug( + 'useProjectsSort.js', + `Updating star/unstar in Scribe settings for ${name}`, + ); + updateAgSettings( + currentUser, + projectName, + _project, + ); + } + }); + // metaFileReplace({ userData: projectArrayTemp[0] }); + }); }); - }); } else { parseProjectMetaUpdate({ username, @@ -103,6 +122,80 @@ function useProjectsSort() { } settemparray(copy[0]); }; + const handleWebClickStarred = async (event, name, property) => { + logger.debug( + 'useProjectsSort.js', + 'converting starred to be unstarred and viceversa', + ); + property === 'starred' ? setactive('starred') : setactive('unstarred'); + const selectedIndex = property === 'starred' + ? starredrow.findIndex((x) => x.name === name) + : unstarredrow.findIndex((x) => x.name === name); + const copy = property === 'starred' + ? starredrow.splice(selectedIndex, 1) + : unstarredrow.splice(selectedIndex, 1); + const projectArrayTemp = []; + let currentUser; + await localForage.getItem('userProfile').then((value) => { + currentUser = value?.user?.email; + }); + const projects = localForage.getItem('projectmeta'); + projects + .then((value) => { + if (value) { + projectArrayTemp.push(value); + } + }) + .then(() => { + projectArrayTemp[0].projects.forEach((_project) => { + if (_project.identification.name.en === name) { + let dirName; + switch (_project.type.flavorType.flavor.name) { + case 'textTranslation': + dirName = 'textTranslation'; + break; + case 'textStories': + dirName = 'textStories'; + break; + case 'audioTranslation': + dirName = 'audioTranslation'; + break; + default: + break; + } + const status = _project.project[dirName].starred; + const selectedProject = _project; + selectedProject.project[dirName].starred = !status; + selectedProject.project[dirName].lastSeen = moment().format(); + } + }); + }) + .finally(() => { + localForage + .setItem('projectmeta', projectArrayTemp[0]) + .then(() => { + projectArrayTemp[0].projects.forEach((_project) => { + if (_project.identification.name.en === name) { + const id = Object.keys( + _project.identification.primary.scribe, + ); + const projectName = `${name}_${id}`; + logger.debug( + 'useProjectsSort.js', + `Updating star/unstar in Scribe settings for ${name}`, + ); + updateWebAgSettings( + currentUser, + projectName, + _project, + ); + } + }); + // metaFileReplace({ userData: projectArrayTemp[0] }); + }); + }); + settemparray(copy[0]); + }; const handleRequestSortUnstarred = (event, property) => { logger.debug( @@ -119,16 +212,19 @@ function useProjectsSort() { const selectedIndex = property === 'starred' ? starredrow.findIndex((x) => x.name === name) : unstarredrow.findIndex((x) => x.name === name); - logger.debug('useProjectsSort.js', `removing the element with name=${name}`); + logger.debug( + 'useProjectsSort.js', + `removing the element with name=${name}`, + ); /* eslint no-unused-expressions: ["error", { "allowTernary": true }] */ property === 'starred' - ? (starredrow.splice(selectedIndex, 1)) - : (unstarredrow.splice(selectedIndex, 1)); + ? starredrow.splice(selectedIndex, 1) + : unstarredrow.splice(selectedIndex, 1); handleRequestSortUnstarred('asc', 'view'); }; // eslint-disable-next-line - useEffect(() => { + useEffect(() => { if (temparray) { active === 'starred' ? unstarredrow.push(temparray) @@ -139,14 +235,40 @@ function useProjectsSort() { } handleRequestSortUnstarred('asc', 'view'); // eslint-disable-next-line - }, [temparray, active]); + }, [temparray, active]); - const createData = (name, language, date, view, description, id, type, isArchived) => ({ - name, language, date, view, description, id, type, isArchived, - }); + const createData = ( + name, + language, + date, + view, + description, + id, + type, + isArchived, + ) => ({ + name, + language, + date, + view, + description, + id, + type, + isArchived, + }); - const FetchStarred = (ProjectName, Language, createdAt, LastView, ProjectDescription, id, type, isArchived) => { - starrtedData.push(createData( + const FetchStarred = ( + ProjectName, + Language, + createdAt, + LastView, + ProjectDescription, + id, + type, + isArchived, + ) => { + starrtedData.push( + createData( ProjectName, Language, createdAt, @@ -155,11 +277,22 @@ function useProjectsSort() { id, type, isArchived, - )); - }; + ), + ); + }; - const FetchUnstarred = (ProjectName, Language, createdAt, LastView, ProjectDescription, id, type, isArchived) => { - unstarrtedData.push(createData( + const FetchUnstarred = ( + ProjectName, + Language, + createdAt, + LastView, + ProjectDescription, + id, + type, + isArchived, + ) => { + unstarrtedData.push( + createData( ProjectName, Language, createdAt, @@ -168,210 +301,276 @@ function useProjectsSort() { id, type, isArchived, - )); - }; + ), + ); + }; - const FetchProjects = async () => { - if (isElectron()) { - localForage.getItem('userProfile').then((user) => { - if (user) { - logger.debug('useProjectsSort.js', 'Fetching the projects'); - const projectsData = fetchProjectsMeta({ currentUser: user?.username }); - projectsData.then((value) => { - if (value) { - localForage.setItem('projectmeta', value) - .then(() => { - localForage.getItem('projectmeta') - .then((value) => { - if (value) { - value.projects.forEach((_project) => { - const created = Object.keys(_project.identification.primary.scribe); - let lastSeen; - let description; - let flavorType; - let isArchived; - switch (_project.type.flavorType.flavor.name) { - case 'textTranslation': - lastSeen = _project.project?.textTranslation?.lastSeen; - description = _project.project?.textTranslation?.description; - isArchived = _project.project?.textTranslation?.isArchived; - flavorType = 'Text Translation'; - break; - case 'textStories': - lastSeen = _project.project?.textStories?.lastSeen; - description = _project.project?.textStories?.description; - isArchived = _project.project?.textStories?.isArchived; - flavorType = 'OBS'; - break; - case 'audioTranslation': - lastSeen = _project.project?.audioTranslation?.lastSeen; - description = _project.project?.audioTranslation?.description; - isArchived = _project.project?.audioTranslation?.isArchived; - flavorType = 'Audio'; - break; - default: - break; - } - if (_project.project?.textTranslation?.starred === true || _project.project?.textStories?.starred === true || _project.project?.audioTranslation?.starred === true) { - // FetchStarred(projectName,language, createdAt, updatedAt); - FetchStarred( - _project.identification.name.en, - _project.languages[0].name.en, - // _project.identification.primary.scribe[created].timestamp, - _project.meta.dateCreated, - lastSeen, - description, - created, - flavorType, - isArchived, - ); - } else { - FetchUnstarred( - _project.identification.name.en, - _project.languages[0].name.en, - // _project.identification.primary.scribe[created].timestamp, - _project.meta.dateCreated, - lastSeen, - description, - created, - flavorType, - isArchived, - ); - } - }); - } - }).then(() => { - setStarredRow(starrtedData); - setStarredProjets(starrtedData); - setUnStarredRow(unstarrtedData); - setUnStarredProjets(unstarrtedData); - }); + const FetchProjects = async () => { + const userProfile = await localForage.getItem('userProfile'); + const user = isElectron() ? userProfile.username : userProfile.user.email; + console.log('from project sort', { user }); + if (user) { + logger.debug('useProjectsSort.js', 'Fetching the projects'); + const projectsData = fetchProjectsMeta({ currentUser: user }); + projectsData.then((value) => { + console.log('fetchPrjectsmeta', { value }); + if (value) { + localForage + .setItem('projectmeta', value) + .then(() => { + localForage + .getItem('projectmeta') + .then((value) => { + if (value) { + value.projects.forEach((_project) => { + const created = Object.keys( + _project.identification.primary + .scribe, + ); + let lastSeen; + let description; + let flavorType; + let isArchived; + switch ( + _project.type.flavorType.flavor + .name + ) { + case 'textTranslation': + lastSeen = _project.project + ?.textTranslation + ?.lastSeen; + description = _project.project + ?.textTranslation + ?.description; + isArchived = _project.project + ?.textTranslation + ?.isArchived; + flavorType = 'Text Translation'; + break; + case 'textStories': + lastSeen = _project.project + ?.textStories + ?.lastSeen; + description = _project.project + ?.textStories + ?.description; + isArchived = _project.project + ?.textStories + ?.isArchived; + flavorType = 'OBS'; + break; + case 'audioTranslation': + lastSeen = _project.project + ?.audioTranslation + ?.lastSeen; + description = _project.project + ?.audioTranslation + ?.description; + isArchived = _project.project + ?.audioTranslation + ?.isArchived; + flavorType = 'Audio'; + break; + default: + break; + } + if ( + _project.project + ?.textTranslation + ?.starred === true + || _project.project?.textStories + ?.starred === true + || _project.project + ?.audioTranslation + ?.starred === true + ) { + // FetchStarred(projectName,language, createdAt, updatedAt); + FetchStarred( + _project.identification.name + .en, + _project.languages[0].name + .en, + // _project.identification.primary.scribe[created].timestamp, + _project.meta.dateCreated, + lastSeen, + description, + created, + flavorType, + isArchived, + ); + } else { + FetchUnstarred( + _project.identification.name + .en, + _project.languages[0].name + .en, + // _project.identification.primary.scribe[created].timestamp, + _project.meta.dateCreated, + lastSeen, + description, + created, + flavorType, + isArchived, + ); + } + }); + } }) - .catch((err) => { - logger.error('useProjectsSort.js', 'Failed to fetch project list'); - // we got an error - throw err; + .then(() => { + setStarredRow(starrtedData); + setStarredProjets(starrtedData); + setUnStarredRow(unstarrtedData); + setUnStarredProjets(unstarrtedData); }); - } + }) + .catch((err) => { + logger.error( + 'useProjectsSort.js', + 'Failed to fetch project list', + ); + // we got an error + throw err; }); } - }); - } else { - // const projectName = 'Newcanon based Pro'; - parseFetchProjects(username).then((res) => { - res.forEach((projects) => { - if (projects.get('starred') === true) { - FetchStarred( - projects.get('projectName'), - projects.get('language'), - projects.get('date'), - projects.get('lastview'), - projects.get('isArchived'), - ); - } else { - FetchUnstarred( - projects.get('projectName'), - projects.get('language'), - projects.get('date'), - projects.get('lastview'), - projects.get('isArchived'), - ); - } - }); - }).finally(() => { - setStarredRow(starrtedData); - setStarredProjets(starrtedData); - setUnStarredRow(unstarrtedData); - setUnStarredProjets(unstarrtedData); - }); - } - }; + }); + } + }; - /** - * Updates the project's archive status in the localForage database. - * @param name - the name of the project - */ - const archiveProject = async (project, name) => { - const userProfile = await localForage.getItem('userProfile'); - const currentUser = userProfile?.username; + /** + * Updates the project's archive status in the localForage database. + * @param name - the name of the project + */ + const archiveProject = async (project, name) => { + const userProfile = await localForage.getItem('userProfile'); + const currentUser = userProfile?.username; - const projects = await localForage.getItem('projectmeta'); + const projects = await localForage.getItem('projectmeta'); - const projectArrayTemp = JSON.parse(JSON.stringify(projects)); + const projectArrayTemp = JSON.parse(JSON.stringify(projects)); - projectArrayTemp.projects.forEach((_project) => { - if (_project.identification.name.en === name) { - let dirName; - switch (_project.type.flavorType.flavor.name) { - case 'textTranslation': { - dirName = 'textTranslation'; - break; - } - case 'textStories': { - dirName = 'textStories'; - break; - } - case 'audioTranslation': { - dirName = 'audioTranslation'; - break; - } - default: - break; + projectArrayTemp.projects.forEach((_project) => { + if (_project.identification.name.en === name) { + let dirName; + switch (_project.type.flavorType.flavor.name) { + case 'textTranslation': { + dirName = 'textTranslation'; + break; + } + case 'textStories': { + dirName = 'textStories'; + break; } - const status = _project.project[dirName].isArchived; - const selectedProject = _project; - selectedProject.project[dirName].isArchived = !status; - selectedProject.project[dirName].lastSeen = moment().format(); + case 'audioTranslation': { + dirName = 'audioTranslation'; + break; + } + default: + break; } - }); + const status = _project.project[dirName].isArchived; + const selectedProject = _project; + selectedProject.project[dirName].isArchived = !status; + selectedProject.project[dirName].lastSeen = moment().format(); + } + }); + + await localForage.setItem('projectmeta', projectArrayTemp); - await localForage.setItem('projectmeta', projectArrayTemp); + projectArrayTemp.projects.forEach((_project) => { + if (_project.identification.name.en === name) { + const id = Object.keys(_project.identification.primary.scribe); + const projectName = `${name}_${id}`; + logger.debug('useProjectsSort.js', `Updating archive/restore in scribe settings for ${name}`); + updateAgSettings(currentUser, projectName, _project); + } + }); + await FetchProjects(); + }; + const archiveWebProject = async (project, name) => { + const userProfile = await localForage.getItem('userProfile'); + const currentUser = userProfile?.user?.email; - projectArrayTemp.projects.forEach((_project) => { - if (_project.identification.name.en === name) { - const id = Object.keys(_project.identification.primary.scribe); - const projectName = `${name}_${id}`; - logger.debug('useProjectsSort.js', `Updating archive/restore in scribe settings for ${name}`); - updateAgSettings(currentUser, projectName, _project); + const projects = await localForage.getItem('projectmeta'); + + const projectArrayTemp = JSON.parse(JSON.stringify(projects)); + + projectArrayTemp.projects.forEach((_project) => { + if (_project.identification.name.en === name) { + let dirName; + switch (_project.type.flavorType.flavor.name) { + case 'textTranslation': { + dirName = 'textTranslation'; + break; + } + case 'textStories': { + dirName = 'textStories'; + break; + } + case 'audioTranslation': { + dirName = 'audioTranslation'; + break; + } + default: + break; } - }); - await FetchProjects(); - }; + const status = _project.project[dirName].isArchived; + const selectedProject = _project; + selectedProject.project[dirName].isArchived = !status; + selectedProject.project[dirName].lastSeen = moment().format(); + } + }); + + await localForage.setItem('projectmeta', projectArrayTemp); + + projectArrayTemp.projects.forEach((_project) => { + if (_project.identification.name.en === name) { + const id = Object.keys(_project.identification.primary.scribe); + const projectName = `${name}_${id}`; + logger.debug('useProjectsSort.js', `Updating archive/restore in scribe settings for ${name}`); + updateWebAgSettings(currentUser, projectName, _project); + } + }); + await FetchProjects(); + }; - React.useEffect(() => { - FetchProjects(); - // eslint-disable-next-line - }, []); + React.useEffect(() => { + FetchProjects(); + // eslint-disable-next-line + }, []); - const response = { - state: { - starredrow, - unstarredrow, - orderUnstarred, - orderByUnstarred, - starredProjects, - unstarredProjects, - selectedProject, - notifications, - activeNotificationCount, - }, - actions: { - handleClickStarred, - handleDelete, - handleRequestSortUnstarred, - archiveProject, - setStarredRow, - setUnStarredRow, - settemparray, - setactive, - setOrderUnstarred, - setOrderByUnstarred, - FetchProjects, - setSelectedProject, - setNotifications, - setActiveNotificationCount, - }, - }; + const response = { + state: { + starredrow, + unstarredrow, + orderUnstarred, + orderByUnstarred, + starredProjects, + loading, + unstarredProjects, + selectedProject, + notifications, + activeNotificationCount, + }, + actions: { + handleClickStarred, + handleWebClickStarred, + handleDelete, + handleRequestSortUnstarred, + archiveProject, + archiveWebProject, + setStarredRow, + setLoading, + setUnStarredRow, + settemparray, + setactive, + setOrderUnstarred, + setOrderByUnstarred, + FetchProjects, + setSelectedProject, + setNotifications, + setActiveNotificationCount, + }, + }; return response; } export default useProjectsSort; diff --git a/renderer/src/components/hooks/scribex/saveToFile.js b/renderer/src/components/hooks/scribex/saveToFile.js index 039022d91..cb8a2cd7d 100644 --- a/renderer/src/components/hooks/scribex/saveToFile.js +++ b/renderer/src/components/hooks/scribex/saveToFile.js @@ -1,18 +1,23 @@ import localforage from 'localforage'; // import { readRefMeta } from '../../../core/reference/readRefMeta'; +import { isElectron } from '@/core/handleElectron'; import { readRefBurrito } from '../../../core/reference/readRefBurrito'; import writeToFile from '../../../core/editor/writeToFile'; import packageInfo from '../../../../../package.json'; +import { newPath } from '../../../../../supabase'; +// if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { +// const newPath = require('../../../../../supabase').newPath +// } // function to save to file. export const saveToFile = async (usfmText, bookCode) => { try { const userProfile = await localforage.getItem('userProfile'); - const userName = userProfile?.username; + const userName = isElectron() ? userProfile?.username : userProfile?.user?.email; const projectName = await localforage.getItem('currentProject'); const path = require('path'); const newpath = localStorage.getItem('userPath'); // const projectsDir = path.join(newpath, packageInfo.name, 'users', userName, 'projects', projectName); - const metaPath = path.join(newpath, packageInfo.name, 'users', userName, 'projects', projectName, 'metadata.json'); + const metaPath = isElectron() ? path.join(newpath, packageInfo.name, 'users', userName, 'projects', projectName, 'metadata.json') : `${newPath}/${userName}/projects/${projectName}/metadata.json`; // const refs = await readRefMeta({ projectsDir }) const metaData = JSON.parse(await readRefBurrito({ metaPath })); Object.entries(metaData.ingredients).forEach(async ([key, _ingredients]) => { diff --git a/renderer/src/components/hooks/useGetUserName.js b/renderer/src/components/hooks/useGetUserName.js index bec1e1523..e29368600 100644 --- a/renderer/src/components/hooks/useGetUserName.js +++ b/renderer/src/components/hooks/useGetUserName.js @@ -1,23 +1,33 @@ import { useEffect, useState } from 'react'; import localforage from 'localforage'; -import { isElectron } from '@/core/handleElectron'; import * as logger from '../../logger'; +import { supabase } from '../../../../supabase'; + +const IsElectron = process.env.NEXT_PUBLIC_IS_ELECTRON; // custom hook to fetch username from localforage -export const useGetUserName = (_username) => { +export const useGetUserName = () => { const [username, setUsername] = useState(''); useEffect(() => { const fetchUserName = async () => { try { - if (!username && isElectron()) { + if (IsElectron) { const value = await localforage.getItem('userProfile'); setUsername(value?.username); + } else if (!IsElectron) { + const { data: { session }, error } = await supabase.auth.getSession(); + if (error) { + console.error(error); + } + if (session) { + setUsername(session?.user?.email); + } } } catch (error) { logger.error('useGetUserName.js', error); } }; fetchUserName(); - }, [username, _username]); + }, [username]); return { username }; }; diff --git a/renderer/src/core/Login/handleJson.js b/renderer/src/core/Login/handleJson.js index 914959ed3..5d9723f3e 100644 --- a/renderer/src/core/Login/handleJson.js +++ b/renderer/src/core/Login/handleJson.js @@ -1,6 +1,9 @@ import * as localForage from 'localforage'; import * as logger from '../../logger'; import packageInfo from '../../../../package.json'; +import { + createDirectory, sbStorageDownload, sbStorageUpload, supabaseStorage, +} from '../../../../supabase'; const path = require('path'); @@ -109,3 +112,79 @@ export const handleJson = async (values, fs) => { return error; } }; + +export const handleJsonWeb = async (values) => { + // const supabaseStorage = require('../../../../supabase').supabaseStorage + // const createDirectory = require('../../../../supabase').createDirectory + const newpath = `${packageInfo.name}/users`; + error = { userExist: false, fetchFile: false }; + + if (await supabaseStorage().list().then((result) => result.error)) { + console.error('handleJson.js', 'Failed to access the storage'); + error.fetchFile = true; + return error; + } + + if (await sbStorageDownload(`${newpath}/users.json`).then((result) => result.error)) { + const array = []; + array.push(values); + try { + await sbStorageUpload(`${newpath}/users.json`, JSON.stringify(array), { + cacheControl: '3600', + upsert: true, + }); + + console.log('handleJson.js', 'Successfully created and written to the file'); + // Add new user to localForage: + localForage.setItem('users', array, (err) => { + if (err) { + console.error('handleJson.js', 'Failed to Create a file and add user to LocalForage'); + } + console.log('handleJson.js', 'Created a file and added user to LocalForage'); + }); + console.log('handleJson.js', 'Exiting from handleJson'); + return error; + } catch (err) { + console.error('handleJson.js', 'Failed to create and write to the file'); + error.fetchFile = true; + return error; + } + } else { + const { data, error } = await sbStorageDownload(`${newpath}/users.json`); + if (error) { + console.log('handleJson.js', 'Failed to read the data from file'); + error.fetchFile = true; + return error; + } + + console.log('handleJson.js', 'Successfully read the data from file', data); + const json = JSON.parse(await data.text()); + if (uniqueUser(json, values.email)) { + error.userExist = true; + return error; + } + json.push(values); + try { + const { data: newUser } = await sbStorageUpload(`${newpath}/users.json`, JSON.stringify(json), { + cacheControl: '3600', + upsert: true, + }); + + console.log('handleJson.js', 'Successfully added new user to the existing list in file', { newUser }); + await createDirectory({path:`${newpath}/${values.email}/projects`}); + + console.log('handleJson.js', 'Successfully created directories for new user'); + // Add new user to localForage: + localForage.setItem('users', json, (errLoc) => { + if (errLoc) { + console.error('handleJson.js', 'Failed to add new user to existing list'); + } + console.log('handleJson.js', 'Added new user to existing list'); + }); + return error; + } catch (errCatch) { + console.error('handleJson.js', 'Failed to add new user to the file'); + return error; + } + } +}; diff --git a/renderer/src/core/Login/handleLogin.js b/renderer/src/core/Login/handleLogin.js index 29c35e694..f518f43f9 100644 --- a/renderer/src/core/Login/handleLogin.js +++ b/renderer/src/core/Login/handleLogin.js @@ -1,7 +1,12 @@ import * as localforage from 'localforage'; -import { handleJson } from './handleJson'; +import { environment } from '../../../environment'; +import { handleJson, handleJsonWeb } from './handleJson'; import * as logger from '../../logger'; import packageInfo from '../../../../package.json'; +import { supabaseStorage } from '../../../../supabase'; +// if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { +// const supabaseStorage = require('../../../../supabase').supabaseStorage +// } export const createUser = (values, fs) => { logger.debug('handleLogin.js', 'In createUser to create a new user'); @@ -18,6 +23,20 @@ export const createUser = (values, fs) => { return handleJson(obj, fs).then(() => obj); }; +export const createWebUser = async (values) => { + logger.debug('handleLogin.js', 'In createWebUser to create a new user'); + const obj = { + email: values?.email, + firstname: '', + lastname: '', + organization: '', + selectedregion: '', + lastSeen: new Date(), + isArchived: false, + }; + console.log('passed obj', { obj }); + return handleJsonWeb(obj).then(() => obj); +}; /** * It writes the users to a file. * @param users - [{ @@ -56,3 +75,42 @@ export const handleLogin = async (users, values) => { } return null; }; + +export const createSupabaseSettingJson = async (path) => { + const json = { + version: environment.AG_USER_SETTING_VERSION, + history: { + copyright: [{ + id: 'Other', title: 'Custom', licence: '', locked: false, + }], + languages: [], + textTranslation: { + canonSpecification: [{ + id: 4, title: 'Other', currentScope: [], locked: false, + }], + }, + }, + appLanguage: 'en', + theme: 'light', + userWorkspaceLocation: '', + commonWorkspaceLocation: '', + resources: { + door43: { + translationNotes: [], + translationQuestions: [], + translationWords: [], + obsTranslationNotes: [], + }, + }, + sync: { services: { door43: [] } }, + }; + const { data, error } = await supabaseStorage + .upload(path, JSON.stringify(json), { + cacheControl: '3600', + upsert: true, + }); + if (data) { + console.log('success, ag-user.json', data); + } + console.log({ error }); +}; diff --git a/renderer/src/core/editor/readFile.js b/renderer/src/core/editor/readFile.js index 259098185..9d5a7f9c8 100644 --- a/renderer/src/core/editor/readFile.js +++ b/renderer/src/core/editor/readFile.js @@ -1,21 +1,39 @@ +/* eslint-disable no-async-promise-executor */ +import { readBlobAsync } from '@/components/EditorPage/ObsEditor/core'; import packageInfo from '../../../../package.json'; - +import { newPath, sbStorageDownload } from '../../../../supabase'; +import { isElectron } from '../handleElectron'; +// if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { +// const supabaseStorage = require('../../../../supabase').supabaseStorage +// const newPath = require('../../../../supabase').newPath +// } export const readFile = async ({ username, projectname, filename, }) => { - const fs = window.require('fs'); - const path = require('path'); - const newpath = localStorage.getItem('userPath'); - const projectsPath = path.join(newpath, packageInfo.name, 'users', username, 'projects', projectname, filename); - return new Promise((resolve) => { - if (fs.existsSync(projectsPath)) { - const fileContent = fs.readFileSync( - path.join(projectsPath), - 'utf8', - ); - resolve(fileContent); + if (isElectron()) { + const fs = window.require('fs'); + const path = require('path'); + const newpath = localStorage.getItem('userPath'); + const projectsPath = path.join(newpath, packageInfo.name, 'users', username, 'projects', projectname, filename); + return new Promise((resolve) => { + if (fs.existsSync(projectsPath)) { + const fileContent = fs.readFileSync( + path.join(projectsPath), + 'utf8', + ); + resolve(fileContent); + } + }); + } + const projectsPath = `${newPath}/${username}/projects/${projectname}/${filename}`; + return new Promise(async (resolve) => { + const { data: fileContent, error } = await sbStorageDownload(projectsPath); + if (error) { + console.error('readWebFile function error', error); } + const parsedData = readBlobAsync(fileContent); + resolve(parsedData); }); }; diff --git a/renderer/src/core/editor/writeToFile.js b/renderer/src/core/editor/writeToFile.js index 617ab4b8b..3dbb9d64b 100644 --- a/renderer/src/core/editor/writeToFile.js +++ b/renderer/src/core/editor/writeToFile.js @@ -1,5 +1,9 @@ +import { isElectron } from '@/core/handleElectron'; import * as logger from '../../logger'; import packageInfo from '../../../../package.json'; +import { + IsElectron, newPath, sbStorageDownload, sbStorageUpdate, sbStorageUpload, +} from '../../../../supabase'; const writeToFile = async ({ username, @@ -7,19 +11,40 @@ const writeToFile = async ({ filename, data, }) => { - const fs = window.require('fs'); - const path = require('path'); - const newpath = localStorage.getItem('userPath'); - const projectsPath = path.join(newpath, packageInfo.name, 'users', username, 'projects', projectname, filename); - if (fs.existsSync(projectsPath)) { - // appending to an existing file - logger.debug('writeToFile.js', 'Appending to the existing file'); - fs.writeFileSync(projectsPath, data); - } else { - // Creating new file if nothing present - logger.debug('writeToFile.js', 'Creating new file to write'); - fs.writeFileSync(projectsPath, data); + if (isElectron()) { + const fs = window.require('fs'); + const path = require('path'); + const newpath = localStorage.getItem('userPath'); + const projectsPath = path.join(newpath, packageInfo.name, 'users', username, 'projects', projectname, filename); + if (fs.existsSync(projectsPath)) { + // appending to an existing file + logger.debug('writeToFile.js', 'Appending to the existing file'); + fs.writeFileSync(projectsPath, data); + } else { + // Creating new file if nothing present + logger.debug('writeToFile.js', 'Creating new file to write'); + fs.writeFileSync(projectsPath, data); + } } + console.log({ + username, projectname, filename, data, + }); + if (!IsElectron) { + const filePath = `${newPath}/${username}/projects/${projectname}/${filename}`; + const { data: projectsPath, error } = await sbStorageDownload(filePath); + if (projectsPath) { + // appending to an existing file + console.log('writeToFile.js', 'Appending to the existing file'); + sbStorageUpdate({ path: filePath, payload: data }); + } else { + // Creating new file if nothing present + console.log('writeToFile.js', 'Creating new file to write'); + sbStorageUpload(filePath, data); + } + if (error) { + console.log(error); + } + } }; export default writeToFile; diff --git a/renderer/src/core/projects/existProjectInBackEnd.js b/renderer/src/core/projects/existProjectInBackEnd.js index f6fec28f6..0a3534f74 100644 --- a/renderer/src/core/projects/existProjectInBackEnd.js +++ b/renderer/src/core/projects/existProjectInBackEnd.js @@ -1,24 +1,40 @@ import localforage from 'localforage'; +import { isElectron } from '@/core/handleElectron'; import packageInfo from '../../../../package.json'; +import { newPath, sbStorageDownload } from '../../../../supabase'; +// if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { +// const supabaseStorage = require('../../../../supabase').supabaseStorage +// const newPath = require('../../../../supabase').newPath +// } function isBackendProjectExist(ProjectDir) { - const newpath = localStorage.getItem('userPath'); - const fs = window.require('fs'); const path = require('path'); // Step1 : check the project Dir and Meta exist // step2 : exist return True return new Promise((resolve) => { // let checkStatus = false; - localforage.getItem('userProfile').then((value) => { - if (value?.username) { - const resourcePath = path.join(newpath, packageInfo.name, 'users', value.username, 'resources', ProjectDir); - // check for path exist or not and resolve true or false will work for pane 1 now add for other panes - if (fs.existsSync(resourcePath) && fs.existsSync(path.join(resourcePath, 'metadata.json'))) { - resolve(true); + localforage.getItem('userProfile').then(async (value) => { + if (isElectron()) { + const newpath = localStorage.getItem('userPath'); + const fs = window.require('fs'); + if (value?.username) { + const resourcePath = path.join(newpath, packageInfo.name, 'users', value.username, 'resources', ProjectDir); + // check for path exist or not and resolve true or false will work for pane 1 now add for other panes + if (fs.existsSync(resourcePath) && fs.existsSync(path.join(resourcePath, 'metadata.json'))) { + resolve(true); + } else { + resolve(false); + } } else { resolve(false); } } else { + const resourcePath = path.join(newPath, packageInfo.name, 'users', value.user.email, 'resources', ProjectDir); + // check for path exist or not and resolve true or false will work for pane 1 now add for other panes + const { data } = await sbStorageDownload(resourcePath); + if (data) { + resolve(true); + } resolve(false); } }); diff --git a/renderer/src/core/projects/fetchProjectsMeta.js b/renderer/src/core/projects/fetchProjectsMeta.js index 1cf2911ca..78d8313ef 100644 --- a/renderer/src/core/projects/fetchProjectsMeta.js +++ b/renderer/src/core/projects/fetchProjectsMeta.js @@ -1,45 +1,107 @@ +import * as localforage from 'localforage'; +import { isElectron } from '@/core/handleElectron'; import * as logger from '../../logger'; import packageInfo from '../../../../package.json'; import { environment } from '../../../environment'; +import { + newPath, sbStorageList, IsElectron, sbStorageDownload, +} from '../../../../supabase'; +// if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { +// const supabaseStorage = require('../../../../supabase').supabaseStorage +// const newPath = require('../../../../supabase').newPath +// } const fetchProjectsMeta = async ({ currentUser }) => { - logger.debug('fetchProjectsMeta.js', 'In fetchProjectsMeta'); - const newpath = localStorage.getItem('userPath'); - const fs = window.require('fs'); - const path = require('path'); - const projectsMetaPath = path.join(newpath, packageInfo.name, 'users', currentUser, 'projects'); - fs.mkdirSync(projectsMetaPath, { recursive: true }); - const arrayItems = fs.readdirSync(projectsMetaPath); - const burritos = []; - return new Promise((resolve) => { - arrayItems.forEach((dir) => { - const stat = fs.lstatSync(path.join(projectsMetaPath, dir)); - if (stat.isDirectory() && fs.existsSync(path.join(projectsMetaPath, dir, 'metadata.json'))) { - logger.debug('fetchProjectsMeta.js', 'Found burrito for the project'); - const data = fs.readFileSync(path.join(projectsMetaPath, dir, 'metadata.json'), 'utf8'); - const parseData = JSON.parse(data); - let setting; - const result = Object.keys(parseData.ingredients).filter((key) => key.includes(environment.PROJECT_SETTING_FILE)); - if (result[0]) { - setting = fs.readFileSync(path.join(projectsMetaPath, dir, result[0]), 'utf8'); - } else { - logger.error('fetchProjectsMeta.js', 'Unable to find scribe-settings for the project'); + console.log('fetchProjectsMeta', { currentUser }); + if (isElectron()) { + console.log('fetchProjectsMeta', 'isElectron'); + logger.debug('fetchProjectsMeta.js', 'In fetchProjectsMeta'); + const newpath = localStorage.getItem('userPath'); + const fs = window.require('fs'); + const path = require('path'); + const projectsMetaPath = path.join(newpath, packageInfo.name, 'users', currentUser, 'projects'); + fs.mkdirSync(projectsMetaPath, { recursive: true }); + const arrayItems = fs.readdirSync(projectsMetaPath); + const burritos = []; + return new Promise((resolve) => { + arrayItems.forEach((dir) => { + const stat = fs.lstatSync(path.join(projectsMetaPath, dir)); + if (stat.isDirectory() && fs.existsSync(path.join(projectsMetaPath, dir, 'metadata.json'))) { + logger.debug('fetchProjectsMeta.js', 'Found burrito for the project'); + const data = fs.readFileSync(path.join(projectsMetaPath, dir, 'metadata.json'), 'utf8'); + const parseData = JSON.parse(data); + let setting; + const result = Object.keys(parseData.ingredients).filter((key) => key.includes(environment.PROJECT_SETTING_FILE)); + if (result[0]) { + setting = fs.readFileSync(path.join(projectsMetaPath, dir, result[0]), 'utf8'); + } else { + logger.error('fetchProjectsMeta.js', 'Unable to find scribe-settings for the project'); + } + if (setting) { + logger.debug('fetchProjectsMeta.js', 'Found scribe-settings for the project, merging scribe-settings and burrito'); + burritos.push({ ...JSON.parse(setting), ...JSON.parse(data) }); + } else { + logger.debug('fetchProjectsMeta.js', 'Unable to find scribe-settings for the project so pushing only burrito'); + burritos.push(JSON.parse(data)); + } + // resolve({ projects: burritos }); } - if (setting) { - logger.debug('fetchProjectsMeta.js', 'Found scribe-settings for the project, merging scribe-settings and burrito'); - burritos.push({ ...JSON.parse(setting), ...JSON.parse(data) }); - } else { - logger.debug('fetchProjectsMeta.js', 'Unable to find scribe-settings for the project so pushing only burrito'); - burritos.push(JSON.parse(data)); - } - // resolve({ projects: burritos }); + fs.stat(path.join(projectsMetaPath, dir), (err) => { + if (err) { throw err; } + }); + }); + logger.debug('fetchProjectsMeta.js', 'Returning project list'); + resolve({ projects: burritos }); + }); + } +console.log('before fetchProjectsMeta', { currentUser, IsElectron }); + if (!IsElectron) { +console.log('fetchProjectsMeta.js', 'In fetchProjectsMeta'); + const path = `${newPath}/${currentUser}/projects`; + const { data: allProjects } = await sbStorageList(path); +console.log({ allProjects }); + const projectPromises = allProjects?.map(async (proj) => { + const projectName = proj.name; + const { data, error } = await sbStorageDownload(`${path}/${projectName}/metadata.json`); + + if (error) { + console.error('fetchProjectsMeta.js', error); + return null; + } + + const projectJson = JSON.parse(await data.text()); + + let setting; + const result = Object.keys(projectJson.ingredients).filter((key) => key.includes(environment.PROJECT_SETTING_FILE)); + if (result[0]) { + const { data: settingData } = await sbStorageDownload(`${path}/${projectName}/${result[0]}`); + if (settingData) { + setting = JSON.parse(await settingData.text()); + } else { + console.error('ProjectList.js', 'Unable to find scribe-settings for the project'); } - fs.stat(path.join(projectsMetaPath, dir), (err) => { - if (err) { throw err; } + } + + if (setting) { + return { ...setting, ...projectJson }; + } + console.log('ProjectList.js', 'Unable to find scribe-settings for the project so pushing only burrito'); + console.log({ projectJson }); + return projectJson; + }); + + // Wrap the entire code in a Promise and return it. + const projectMetaPromise = new Promise((resolve) => { + Promise.all(projectPromises).then((projectsArray) => { + const filteredProjects = projectsArray.filter((p) => p !== null); + localforage.setItem('projectmeta', { projects: filteredProjects }).then(() => { + resolve({ projects: filteredProjects }); }); }); - logger.debug('fetchProjectsMeta.js', 'Returning project list'); - resolve({ projects: burritos }); }); + + // Return the Promise that resolves with the projects' metadata. + return projectMetaPromise; +} }; export default fetchProjectsMeta; diff --git a/renderer/src/core/projects/handleProfile.js b/renderer/src/core/projects/handleProfile.js index 7ee1824e3..d5dd711ab 100644 --- a/renderer/src/core/projects/handleProfile.js +++ b/renderer/src/core/projects/handleProfile.js @@ -1,36 +1,69 @@ import * as localForage from 'localforage'; +import { isElectron } from '@/core/handleElectron'; import { environment } from '../../../environment'; import { loadUsers } from '../Login/handleJson'; import * as logger from '../../logger'; import packageInfo from '../../../../package.json'; +import { + newPath, sbStorageList, sbStorageDownload, sbStorageUpload, +} from '../../../../supabase'; +// if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { +// const supabaseStorage = require('../../../../supabase').supabaseStorage +// const newPath = require('../../../../supabase').newPath +// } export const getorPutAppLangage = async (method, currentUser, appLang) => { - logger.debug('handleProfile.js', 'In updateAppLang, for updating the App language Selection'); - const newpath = localStorage.getItem('userPath'); - const fs = window.require('fs'); - const path = require('path'); - let file; - if (currentUser) { - file = path.join(newpath, packageInfo.name, 'users', currentUser, environment.USER_SETTING_FILE); - } else { - throw new Error('Not getting current logged user'); + if (isElectron()) { + logger.error('handleProfile.js', 'In updateAppLang, for updating the App language Selection'); + const newpath = localStorage.getItem('userPath'); + const fs = window.require('fs'); + const path = require('path'); + let file; + if (currentUser) { + file = path.join(newpath, packageInfo.name, 'users', currentUser, environment.USER_SETTING_FILE); + } else { + throw new Error('Not getting current logged user'); + } + try { + if (fs.existsSync(file)) { + const data = await fs.readFileSync(file); + const json = JSON.parse(data); + if (method.toLowerCase() === 'get') { + return json.appLanguage; + } if (method.toLowerCase() === 'put') { + // save lang code + json.appLanguage = appLang.code; + logger.debug('handleProfile.js', 'Updating the app lang details in existing file'); + await fs.writeFileSync(file, JSON.stringify(json)); + } + } + } catch (err) { + logger.error('handleProfile.js', 'Failed to read the data from file'); + throw new Error(err?.message || err); + } } - try { - if (fs.existsSync(file)) { - const data = await fs.readFileSync(file); - const json = JSON.parse(data); - if (method.toLowerCase() === 'get') { - return json.appLanguage; - } if (method.toLowerCase() === 'put') { - // save lang code - json.appLanguage = appLang.code; - logger.debug('handleProfile.js', 'Updating the app lang details in existing file'); - await fs.writeFileSync(file, JSON.stringify(json)); + let file; + if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { + if (currentUser) { + file = `${newPath}/${currentUser}/${environment.USER_SETTING_FILE}`; + const { data: settingsFile, error } = await sbStorageList(file); + console.log('settingsFile', settingsFile); + if (settingsFile) { + const { data } = await sbStorageDownload(file); + const settings = JSON.parse(await data.text()); + if (method.toLowerCase() === 'get') { + return settings.appLanguage; + } if (method.toLowerCase() === 'put') { + // save lang code + settings.appLanguage = appLang.code; + console.log('handleProfile.js', 'Updating the app lang details in existing file'); + await sbStorageUpload(file, JSON.stringify(settings)); + } + } + if (error) { + throw new Error(error?.message || error); } } - } catch (err) { - logger.error('handleProfile.js', 'Failed to read the data from file'); - throw new Error(err?.message || err); } }; @@ -67,32 +100,32 @@ const updateOffline = async (data, appLang) => { logger.debug('handleProfile.js', 'In updateOffline'); const status = []; await localForage.getItem('userProfile') - .then(async (userdata) => { - const keys = Object.keys(data); - keys.forEach((key) => { - userdata[key] = data[key]; - }); - logger.debug('handleProfile.js', 'Updating profile data in localForage'); - localForage.setItem('userProfile', userdata); - // call app lang update in user json - await getorPutAppLangage('put', userdata?.username, appLang); - // update user details in users list - const value = updateJson(userdata); - value.then((val) => { - status.push(val); - }); - }); + .then(async (userdata) => { + const keys = Object.keys(data); + keys.forEach((key) => { + userdata[key] = data[key]; + }); + logger.debug('handleProfile.js', 'Updating profile data in localForage'); + localForage.setItem('userProfile', userdata); + // call app lang update in user json + await getorPutAppLangage('put', userdata?.username, appLang); + // update user details in users list + const value = updateJson(userdata); + value.then((val) => { + status.push(val); + }); + }); return status; }; export const saveProfile = async (values, appLang) => { logger.debug('handleProfile.js', 'In saveProfile'); const status = []; await localForage.getItem('appMode') - .then(async (mode) => { - if (mode === 'offline') { - const value = await updateOffline(values, appLang); - status.push(value[0]); - } - }); + .then(async (mode) => { + if (mode === 'offline') { + const value = await updateOffline(values, appLang); + status.push(value[0]); + } + }); return status; }; diff --git a/renderer/src/core/projects/saveProjetcsMeta.js b/renderer/src/core/projects/saveProjetcsMeta.js index 8b351b21e..d61e7fb8c 100644 --- a/renderer/src/core/projects/saveProjetcsMeta.js +++ b/renderer/src/core/projects/saveProjetcsMeta.js @@ -3,14 +3,17 @@ import * as localforage from 'localforage'; import { v5 as uuidv5 } from 'uuid'; import { createAudioVersification } from '@/util/createAudioVersification'; import { checkInitialize, commitChanges, initProject } from '@/components/Sync/Isomorphic/utils'; -import { createVersificationUSFM } from '../../util/createVersificationUSFM'; -import { createObsContent } from '../../util/createObsContent'; +import { createVersificationUSFM, createWebVersificationUSFM } from '../../util/createVersificationUSFM'; +import { createObsContent, createWebObsContent } from '../../util/createObsContent'; import createTranslationSB from '../burrito/createTranslationSB'; import createObsSB from '../burrito/createObsSB'; import * as logger from '../../logger'; import { environment } from '../../../environment'; import createAudioSB from '../burrito/createAudioSB'; import packageInfo from '../../../../package.json'; +import { + createDirectory, newPath, sbStorageList, sbStorageUpload, +} from '../../../../supabase'; const bookAvailable = (list, id) => list.some((obj) => obj === id); const checker = (arr, target) => target.every((v) => arr.includes(v)); @@ -37,8 +40,9 @@ export const checkGitandCommitFiles = async (fs, projectPath, author, currentUse } }; -const saveProjectsMeta = async (projectMetaObj) => { +export const saveProjectsMeta = async (projectMetaObj) => { logger.debug('saveProjectsMeta.js', 'In saveProjectsMeta'); + console.log('saveProjectsMeta.js', { projectMetaObj }); const newpath = localStorage.getItem('userPath'); const status = []; const fs = window.require('fs'); @@ -97,7 +101,7 @@ const saveProjectsMeta = async (projectMetaObj) => { logger.debug('saveProjectsMeta.js', 'Fetching the key from the existing Project'); // from existing metadata scope = (projectMetaObj.canonSpecification.currentScope) - .filter((x) => !(Object.keys(projectMetaObj.project.type.flavorType.currentScope)).includes(x)); + .filter((x) => !(Object.keys(projectMetaObj.project.type.flavorType.currentScope)).includes(x)); id = Object.keys(projectMetaObj.project?.identification?.primary?.scribe); projectMetaObj.importedFiles.forEach((file) => { scope.push(file.id); @@ -161,8 +165,8 @@ const saveProjectsMeta = async (projectMetaObj) => { let id; if (projectMetaObj.call === 'new') { logger.debug('saveProjectsMeta.js', 'Creating a key for the Project'); - const key = currentUser + projectMetaObj.newProjectFields.projectName + moment().format(); - id = uuidv5(key, environment.uuidToken); + const key = currentUser + projectMetaObj.newProjectFields.projectName + moment().format(); + id = uuidv5(key, environment.uuidToken); } else { logger.debug('saveProjectsMeta.js', 'Fetching the key from the existing Project'); // from existing metadata @@ -198,7 +202,7 @@ const saveProjectsMeta = async (projectMetaObj) => { burritoFile.ingredients = { ...projectMetaObj.project.ingredients, ...ingredient }; burritoFile?.sync && delete burritoFile.sync; } else { - burritoFile.ingredients = ingredient; + burritoFile.ingredients = ingredient; } logger.debug('saveProjectsMeta.js', 'Creating a burrito file.'); await fs.writeFileSync(path.join( @@ -238,14 +242,14 @@ const saveProjectsMeta = async (projectMetaObj) => { let scope; if (projectMetaObj.call === 'new') { logger.debug('saveProjectsMeta.js', 'Creating a key for the Project'); - const key = currentUser + projectMetaObj.newProjectFields.projectName + moment().format(); - id = uuidv5(key, environment.uuidToken); - scope = projectMetaObj.canonSpecification.currentScope; + const key = currentUser + projectMetaObj.newProjectFields.projectName + moment().format(); + id = uuidv5(key, environment.uuidToken); + scope = projectMetaObj.canonSpecification.currentScope; } else { logger.debug('saveProjectsMeta.js', 'Fetching the key from the existing Project'); // from existing metadata scope = (projectMetaObj.canonSpecification.currentScope) - .filter((x) => !(Object.keys(projectMetaObj.project.type.flavorType.currentScope)).includes(x)); + .filter((x) => !(Object.keys(projectMetaObj.project.type.flavorType.currentScope)).includes(x)); id = Object.keys(projectMetaObj.project?.identification?.primary?.scribe); projectMetaObj.importedFiles.forEach((file) => { scope.push(file.id); @@ -292,64 +296,371 @@ const saveProjectsMeta = async (projectMetaObj) => { 'metadata.json', ), JSON.stringify(burritoFile)); }) - .then(async () => { - // Adding text USFM to audio project - if ((projectMetaObj.importedFiles).length !== 0) { - const newScope = []; - projectMetaObj.importedFiles.forEach((file) => { - newScope.push(file.id); - }); - // ingredient has the list of created files in the form of SB Ingredients - logger.debug('saveProjectsMeta.js', 'Calling creatVersification for generating USFM files.'); - await createVersificationUSFM( - currentUser, - projectMetaObj.newProjectFields, - projectMetaObj.versificationScheme, - newScope, - projectMetaObj.language.ld, - id, - projectMetaObj.importedFiles, - projectMetaObj.copyright, - projectMetaObj.project, - projectMetaObj.call, - 'Audio', - ).then(async (ingredient) => { - logger.debug('saveProjectsMeta.js', 'Calling createTranslationSB for creating burrito.'); - const burritoFile = await createTranslationSB( + .then(async () => { + // Adding text USFM to audio project + if ((projectMetaObj.importedFiles).length !== 0) { + const newScope = []; + projectMetaObj.importedFiles.forEach((file) => { + newScope.push(file.id); + }); + // ingredient has the list of created files in the form of SB Ingredients + logger.debug('saveProjectsMeta.js', 'Calling creatVersification for generating USFM files.'); + await createVersificationUSFM( currentUser, projectMetaObj.newProjectFields, - scope, - projectMetaObj.language.ang, - projectMetaObj.language.lc, + projectMetaObj.versificationScheme, + newScope, projectMetaObj.language.ld, - projectMetaObj.copyright, id, + projectMetaObj.importedFiles, + projectMetaObj.copyright, projectMetaObj.project, projectMetaObj.call, - projectMetaObj.update, - ); - if (projectMetaObj.call === 'edit') { - burritoFile.ingredients = { ...projectMetaObj.project.ingredients, ...ingredient }; - } else { - burritoFile.ingredients = ingredient; - } - logger.debug('saveProjectsMeta.js', 'Creating a burrito file.'); - await fs.writeFileSync(path.join( - projectDir, - `${projectMetaObj.newProjectFields.projectName}_${id}`, - 'text-1', - 'metadata.json', - ), JSON.stringify(burritoFile)); - }); + 'Audio', + ).then(async (ingredient) => { + logger.debug('saveProjectsMeta.js', 'Calling createTranslationSB for creating burrito.'); + const burritoFile = await createTranslationSB( + currentUser, + projectMetaObj.newProjectFields, + scope, + projectMetaObj.language.ang, + projectMetaObj.language.lc, + projectMetaObj.language.ld, + projectMetaObj.copyright, + id, + projectMetaObj.project, + projectMetaObj.call, + projectMetaObj.update, + ); + if (projectMetaObj.call === 'edit') { + burritoFile.ingredients = { ...projectMetaObj.project.ingredients, ...ingredient }; + } else { + burritoFile.ingredients = ingredient; + } + logger.debug('saveProjectsMeta.js', 'Creating a burrito file.'); + await fs.writeFileSync(path.join( + projectDir, + `${projectMetaObj.newProjectFields.projectName}_${id}`, + 'text-1', + 'metadata.json', + ), JSON.stringify(burritoFile)); + }); + } + }) + .then(async () => { + // init git for the Project + const projectGitPath = path.join(projectDir, `${projectMetaObj.newProjectFields.projectName}_${id}`); + await checkGitandCommitFiles(fs, projectGitPath, null, currentUser); + }) + .finally(() => { + logger.debug('saveProjectsMeta.js', projectMetaObj.call === 'new' ? 'New project created successfully.' : 'Updated the Changes.'); + status.push({ type: 'success', value: (projectMetaObj.call === 'new' ? 'New project created' : 'Updated the changes') }); + }); + } + }; + // Switch Project Creation + if (projectNameExists === false || projectMetaObj.call === 'edit') { + switch (projectMetaObj.projectType) { + case 'Translation': + await translationBurritoChecksAndCreation(); + break; + + case 'OBS': + await obsBurritoChecksAndCreation(); + break; + + case 'Audio': + await audioBurritoChecksAndCreation(); + break; + + default: + break; + } + } else { + logger.warn('saveProjectsMeta.js', 'Project already exists'); + status.push({ type: 'error', value: 'Project already exists' }); + } + return status; +}; + +export const saveSupabaseProjectsMeta = async (projectMetaObj) => { + console.log('saveProjectsMeta.js', { projectMetaObj }); + const userProfile = await localforage.getItem('userProfile'); + const currentUser = userProfile.user.email; + const status = []; + await createDirectory({path:`${newPath}/${currentUser}/projects`}); + const projectDir = `${newPath}/${currentUser}/projects}`; + let projectNameExists = false; + let checkCanon = false; + const { data: folderList } = await sbStorageList(projectDir); + folderList.forEach((folder) => { + const name = folder.name.split('_'); + if (name[0] === projectMetaObj.newProjectFields.projectName && projectMetaObj.call === 'new') { + projectNameExists = true; + // checking for duplicates + console.log('saveProjectsMeta.js', 'Project Name already exists'); + status.push({ type: 'warning', value: 'projectname exists, check your archived or projects tab' }); + } + }); + + // Translation burrito creation and checks + const translationBurritoChecksAndCreation = async () => { + console.debug('saveProjectsMeta.js', 'In translation Burrito Checks And Creation'); + + projectMetaObj.importedFiles.forEach((file) => { + if (!bookAvailable(projectMetaObj.canonSpecification.currentScope, file.id)) { + checkCanon = true; + console.warn('saveProjectsMeta.js', `${file.id} is not added in Canon Specification or scope`); + status.push({ type: 'warning', value: `${file.id} is not added in Canon Specification` }); + } + }); + + if (projectMetaObj.call === 'edit' && !checker((projectMetaObj.canonSpecification.currentScope), Object.keys(projectMetaObj.project.type.flavorType.currentScope))) { + checkCanon = true; + console.warn('saveProjectsMeta.js', 'Not allowed to remove previous scope'); + status.push({ type: 'warning', value: 'You are not allowed to remove previous scope.' }); + } + + if (checkCanon === false) { + let id; + let scope; + if (projectMetaObj.call === 'new') { + console.debug('saveProjectsMeta.js', 'Creating a key for the Project'); + const key = currentUser + projectMetaObj.newProjectFields.projectName + moment().format(); + id = uuidv5(key, environment.uuidToken); + scope = projectMetaObj.canonSpecification.currentScope; + } else { + console.debug('saveProjectsMeta.js', 'Fetching the key from the existing Project'); + // from existing metadata + scope = (projectMetaObj.canonSpecification.currentScope) + .filter((x) => !(Object.keys(projectMetaObj.project.type.flavorType.currentScope)).includes(x)); + id = Object.keys(projectMetaObj.project?.identification?.primary?.scribe); + projectMetaObj.importedFiles.forEach((file) => { + scope.push(file.id); + }); + } + + // Create New burrito + // ingredient has the list of created files in the form of SB Ingredients + console.debug('saveProjectsMeta.js', 'Calling creatVersification for generating USFM files.'); + await createWebVersificationUSFM( + currentUser, + projectMetaObj.newProjectFields, + projectMetaObj.versificationScheme, + scope, + projectMetaObj.language.ld, + id, + projectMetaObj.importedFiles, + projectMetaObj.copyright, + projectMetaObj.project, + projectMetaObj.call, + projectMetaObj.projectType, + ).then(async (ingredient) => { + console.debug('saveProjectsMeta.js', 'Calling createTranslationSB for creating burrito.'); + const burritoFile = await createTranslationSB( + currentUser, + projectMetaObj.newProjectFields, + scope, + projectMetaObj.language.ang, + projectMetaObj.language.lc, + projectMetaObj.language.ld, + projectMetaObj.copyright, + id, + projectMetaObj.project, + projectMetaObj.call, + projectMetaObj.update, + ); + + if (projectMetaObj.call === 'edit') { + burritoFile.ingredients = { ...projectMetaObj.project.ingredients, ...ingredient }; + burritoFile?.sync && delete burritoFile.sync; + } else { + burritoFile.ingredients = ingredient; } - }) - .then(async () => { - // init git for the Project - const projectGitPath = path.join(projectDir, `${projectMetaObj.newProjectFields.projectName}_${id}`); - await checkGitandCommitFiles(fs, projectGitPath, null, currentUser); - }) - .finally(() => { - logger.debug('saveProjectsMeta.js', projectMetaObj.call === 'new' ? 'New project created successfully.' : 'Updated the Changes.'); + console.debug('saveProjectsMeta.js', 'Creating a burrito file.'); + await sbStorageUpload(`${newPath}/${currentUser}/projects/${projectMetaObj.newProjectFields.projectName}_${id}/metadata.json`, JSON.stringify(burritoFile)); + }).finally(() => { + console.debug('saveProjectsMeta.js', projectMetaObj.call === 'new' ? 'New project created successfully.' : 'Updated the Changes.'); + status.push({ type: 'success', value: (projectMetaObj.call === 'new' ? 'New project created' : 'Updated the changes') }); + }); + } + }; + + // OBS burrito creation and checks + const obsBurritoChecksAndCreation = async () => { + console.debug('saveProjectsMeta.js', 'In OBS Burrito Checks And Creation'); + let id; + if (projectMetaObj.call === 'new') { + console.debug('saveProjectsMeta.js', 'Creating a key for the Project'); + const key = currentUser + projectMetaObj.newProjectFields.projectName + moment().format(); + id = uuidv5(key, environment.uuidToken); + } else { + console.debug('saveProjectsMeta.js', 'Fetching the key from the existing Project'); + // from existing metadata + id = Object.keys(projectMetaObj.project?.identification?.primary?.scribe); + } + + // Create New burrito + // ingredient has the list of created files in the form of SB Ingredients + console.debug('saveProjectsMeta.js', 'Calling createObsContent for generating md files.'); + await createWebObsContent( + currentUser, + projectMetaObj.newProjectFields, + projectMetaObj.language.ld, + id, + projectMetaObj.project, + projectMetaObj.importedFiles, + projectMetaObj.copyright, + projectMetaObj.call, + ).then(async (ingredient) => { + console.debug('saveProjectsMeta.js', 'Calling createObsSB for creating burrito.'); + const burritoFile = await createObsSB( + currentUser, + projectMetaObj.newProjectFields, + projectMetaObj.language.ang, + projectMetaObj.language.lc, + projectMetaObj.language.ld, + projectMetaObj.copyright, + id, + projectMetaObj.project, + projectMetaObj.call, + projectMetaObj.update, + ); + if (projectMetaObj.call === 'edit') { + burritoFile.ingredients = { ...projectMetaObj.project.ingredients, ...ingredient }; + burritoFile?.sync && delete burritoFile.sync; + } else { + burritoFile.ingredients = ingredient; + } + console.debug('saveProjectsMeta.js', 'Creating a burrito file.'); + await sbStorageUpload(`${newPath}/${currentUser}/projects/${projectMetaObj.newProjectFields.projectName}_${id}/metadata.json`, JSON.stringify(burritoFile)); + }).finally(() => { + console.debug('saveProjectsMeta.js', projectMetaObj.call === 'new' ? 'New project created successfully.' : 'Updated the Changes.'); + status.push({ type: 'success', value: (projectMetaObj.call === 'new' ? 'New project created' : 'Updated the changes') }); + }); + }; + + // Translation burrito creation and checks + const audioBurritoChecksAndCreation = async () => { + console.debug('saveProjectsMeta.js', 'In audio Burrito Checks And Creation'); + + projectMetaObj.importedFiles.forEach((file) => { + if (!bookAvailable(projectMetaObj.canonSpecification.currentScope, file.id)) { + checkCanon = true; + console.warn('saveProjectsMeta.js', `${file.id} is not added in Canon Specification or scope`); + status.push({ type: 'warning', value: `${file.id} is not added in Canon Specification` }); + } + }); + + if (projectMetaObj.call === 'edit' && !checker((projectMetaObj.canonSpecification.currentScope), Object.keys(projectMetaObj.project.type.flavorType.currentScope))) { + checkCanon = true; + console.warn('saveProjectsMeta.js', 'Not allowed to remove previous scope'); + status.push({ type: 'warning', value: 'You are not allowed to remove previous scope.' }); + } + + if (checkCanon === false) { + let id; + let scope; + if (projectMetaObj.call === 'new') { + console.debug('saveProjectsMeta.js', 'Creating a key for the Project'); + const key = currentUser + projectMetaObj.newProjectFields.projectName + moment().format(); + id = uuidv5(key, environment.uuidToken); + scope = projectMetaObj.canonSpecification.currentScope; + } else { + console.debug('saveProjectsMeta.js', 'Fetching the key from the existing Project'); + // from existing metadata + scope = (projectMetaObj.canonSpecification.currentScope) + .filter((x) => !(Object.keys(projectMetaObj.project.type.flavorType.currentScope)).includes(x)); + id = Object.keys(projectMetaObj.project?.identification?.primary?.scribe); + projectMetaObj.importedFiles.forEach((file) => { + scope.push(file.id); + }); + } + + // Create New burrito + // ingredient has the list of created files in the form of SB Ingredients + console.debug('saveProjectsMeta.js', 'Calling createAudioVersification for generating USFM files.'); + await createAudioVersification( + currentUser, + projectMetaObj.newProjectFields, + projectMetaObj.versificationScheme, + // scope, + id, + // projectMetaObj.importedFiles, + projectMetaObj.copyright, + projectMetaObj.project, + projectMetaObj.call, + ).then(async (ingredient) => { + console.debug('saveProjectsMeta.js', 'Calling createAudioSB for creating burrito.'); + const burritoFile = await createAudioSB( + currentUser, + projectMetaObj.newProjectFields, + scope, + projectMetaObj.language.ang, + projectMetaObj.language.lc, + projectMetaObj.language.ld, + projectMetaObj.copyright, + id, + projectMetaObj.project, + projectMetaObj.call, + projectMetaObj.update, + ); + if (projectMetaObj.call === 'edit') { + burritoFile.ingredients = { ...projectMetaObj.project.ingredients, ...ingredient }; + burritoFile?.sync && delete burritoFile.sync; + } else { + burritoFile.ingredients = ingredient; + } + console.debug('saveProjectsMeta.js', 'Creating a burrito file.'); + await sbStorageUpload(`${newPath}/${currentUser}/projects/${projectMetaObj.newProjectFields.projectName}_${id}/metadata.json`, JSON.stringify(burritoFile)).then(async () => { + // Adding text USFM to audio project + if ((projectMetaObj.importedFiles).length !== 0) { + const newScope = []; + projectMetaObj.importedFiles.forEach((file) => { + newScope.push(file.id); + }); + // ingredient has the list of created files in the form of SB Ingredients + console.debug('saveProjectsMeta.js', 'Calling creatVersification for generating USFM files.'); + await createWebVersificationUSFM( + currentUser, + projectMetaObj.newProjectFields, + projectMetaObj.versificationScheme, + newScope, + projectMetaObj.language.ld, + id, + projectMetaObj.importedFiles, + projectMetaObj.copyright, + projectMetaObj.project, + projectMetaObj.call, + 'Audio', + ).then(async (ingredient) => { + logger.debug('saveProjectsMeta.js', 'Calling createTranslationSB for creating burrito.'); + const burritoFile = await createTranslationSB( + currentUser, + projectMetaObj.newProjectFields, + scope, + projectMetaObj.language.ang, + projectMetaObj.language.lc, + projectMetaObj.language.ld, + projectMetaObj.copyright, + id, + projectMetaObj.project, + projectMetaObj.call, + projectMetaObj.update, + ); + if (projectMetaObj.call === 'edit') { + burritoFile.ingredients = { ...projectMetaObj.project.ingredients, ...ingredient }; + } else { + burritoFile.ingredients = ingredient; + } + logger.debug('saveProjectsMeta.js', 'Creating a burrito file.'); + const { data } = sbStorageUpload(`${newPath}/${currentUser}/projects/${projectMetaObj.newProjectFields.projectName}_${id}/metadata.json`, JSON.stringify(burritoFile)); + console.log('saveProjectsMeta.js', { data }); + }); + } + }); + }).finally(() => { + console.debug('saveProjectsMeta.js', projectMetaObj.call === 'new' ? 'New project created successfully.' : 'Updated the Changes.'); status.push({ type: 'success', value: (projectMetaObj.call === 'new' ? 'New project created' : 'Updated the changes') }); }); } @@ -373,10 +684,8 @@ const saveProjectsMeta = async (projectMetaObj) => { break; } } else { - logger.warn('saveProjectsMeta.js', 'Project already exists'); + console.warn('saveProjectsMeta.js', 'Project already exists'); status.push({ type: 'error', value: 'Project already exists' }); } return status; }; - -export default saveProjectsMeta; diff --git a/renderer/src/core/projects/updateAgSettings.js b/renderer/src/core/projects/updateAgSettings.js index 30727d110..9c0f4ee73 100644 --- a/renderer/src/core/projects/updateAgSettings.js +++ b/renderer/src/core/projects/updateAgSettings.js @@ -1,8 +1,16 @@ import localforage from 'localforage'; import { splitStringByLastOccurance } from '@/util/splitStringByLastMarker'; +import { isElectron } from '@/core/handleElectron'; import * as logger from '../../logger'; import { environment } from '../../../environment'; import packageInfo from '../../../../package.json'; +import { + newPath, sbStorageDownload, sbStorageUpload, +} from '../../../../supabase'; +// if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { +// const supabaseStorage = require('../../../../supabase').supabaseStorage +// const newPath = require('../../../../supabase').newPath +// } export const updateAgSettings = async (username, projectName, data) => { logger.debug('updateAgSettings.js', 'In updateAgSettings'); @@ -18,13 +26,35 @@ export const updateAgSettings = async (username, projectName, data) => { if (!setting.sync && !setting.sync?.services) { setting.sync = { services: { door43: [] } }; } else { - setting.sync.services.door43 = setting?.sync?.services?.door43 ? setting?.sync?.services?.door43 : []; + setting.sync.services.door43 = setting?.sync?.services?.door43 ? setting?.sync?.services?.door43 : []; + } } -} setting.project[data.type.flavorType.flavor.name] = data.project[data.type.flavorType.flavor.name]; logger.debug('updateAgSettings.js', `Updating the ${environment.PROJECT_SETTING_FILE}`); await fs.writeFileSync(folder, JSON.stringify(setting)); }; + +export const updateWebAgSettings = async (username, projectName, data) => { + const result = Object.keys(data.ingredients).filter((key) => key.includes(environment.PROJECT_SETTING_FILE)); + const folder = `${newPath}/${username}/projects/${projectName}/${result[0]}`; + const { data: settings } = await sbStorageDownload(folder); + let setting = {}; + setting = JSON.parse(await settings.text()); + if (settings.version !== environment.AG_SETTING_VERSION) { + setting.version = environment.AG_SETTING_VERSION; + if (!setting.sync && !setting.sync?.services) { + setting.sync = { services: { door43: [] } }; + } else { + setting.sync.services.door43 = setting?.sync?.services?.door43 ? setting?.sync?.services?.door43 : []; + } + } + setting.project[data.type.flavorType.flavor.name] = data.project[data.type.flavorType.flavor.name]; + await sbStorageUpload(folder, JSON.stringify(setting), { + // cacheControl: '3600', + upsert: true, + }); +}; + export const saveReferenceResource = () => { logger.debug('updateAgSettings.js', 'In saveReferenceResource for saving the reference data'); localforage.getItem('currentProject').then(async (projectName) => { @@ -38,7 +68,11 @@ export const saveReferenceResource = () => { const id = Object.keys(resources.identification.primary[packageInfo.name]); if (id[0] === _projectname[1]) { localforage.getItem('userProfile').then(async (value) => { - await updateAgSettings(value?.username, projectName, resources); + if (isElectron()) { + await updateAgSettings(value?.username, projectName, resources); + } else { + await updateWebAgSettings(value?.user?.email, projectName, resources); + } }); } }, diff --git a/renderer/src/core/reference/readRefBurrito.js b/renderer/src/core/reference/readRefBurrito.js index 640f21cbe..3fa9c5fd4 100644 --- a/renderer/src/core/reference/readRefBurrito.js +++ b/renderer/src/core/reference/readRefBurrito.js @@ -1,19 +1,36 @@ +/* eslint-disable no-async-promise-executor */ +import { isElectron } from '@/core/handleElectron'; +import { sbStorageDownload } from '../../../../supabase'; import * as logger from '../../logger'; +// if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { +// const supabaseStorage = require('../../../../supabase').supabaseStorage +// } export const readRefBurrito = async ({ metaPath, }) => { - logger.debug('readRefBurrito.js', 'In readRefBurrito'); - const fs = window.require('fs'); - const path = require('path'); - return new Promise((resolve) => { - if (fs.existsSync(metaPath)) { - const fileContent = fs.readFileSync( - path.join(metaPath), - 'utf8', - ); - logger.debug('readIngreadients.js', 'Returning the metadata (burrito)'); - resolve((fileContent)); + if (isElectron()) { + logger.debug('readRefBurrito.js', 'In readRefBurrito'); + const fs = window.require('fs'); + const path = require('path'); + return new Promise((resolve) => { + if (fs.existsSync(metaPath)) { + const fileContent = fs.readFileSync( + path.join(metaPath), + 'utf8', + ); + logger.debug('readIngreadients.js', 'Returning the metadata (burrito)'); + resolve((fileContent)); + } + }); + } + return new Promise(async (resolve) => { + const { data: files, error } = await sbStorageDownload(metaPath); + if (error) { + console.error('Error fetching files:', error); + return; } + const _files = JSON.parse(await files.text()); + resolve(_files); }); }; diff --git a/renderer/src/core/reference/readRefMeta.js b/renderer/src/core/reference/readRefMeta.js index ade68c266..baf43cefc 100644 --- a/renderer/src/core/reference/readRefMeta.js +++ b/renderer/src/core/reference/readRefMeta.js @@ -1,10 +1,17 @@ +import { isElectron } from '@/core/handleElectron'; +import { sbStorageList } from '../../../../supabase'; +// if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { +// const supabaseStorage = require('../../../../../supabase').supabaseStorage +// } + export const readRefMeta = async ({ projectsDir, }) => { + if (isElectron()) { const fs = window.require('fs'); const path = require('path'); return new Promise((resolve) => { - if (fs.existsSync(projectsDir)) { + if (fs.existsSync(projectsDir)) { const files = fs.readdirSync(projectsDir); const _files = []; // read dir to find references bundles @@ -15,6 +22,20 @@ export const readRefMeta = async ({ resolve(_files); } }); - } + } }); + } + try { + const { data: files, error } = await sbStorageList(`${projectsDir}`); + if (error) { + console.error('Error fetching files:', error); + return []; + } + console.log('readRefMeta.js , folders in resources', files); + const directoryNames = files.map((file) => file.name); + return directoryNames; + } catch (error) { + console.error('Error reading reference metadata:', error); + return []; + } }; diff --git a/renderer/src/layouts/editor/EditorSection.js b/renderer/src/layouts/editor/EditorSection.js index 74e8d5bf0..eb0fae4a9 100644 --- a/renderer/src/layouts/editor/EditorSection.js +++ b/renderer/src/layouts/editor/EditorSection.js @@ -58,7 +58,6 @@ export default function EditorSection({ const { states: { scrollLock, selectedProjectMeta }, } = useContext(ProjectContext); - function removeResource() { setOpenModal(true); } diff --git a/renderer/src/layouts/editor/MenuBar.js b/renderer/src/layouts/editor/MenuBar.js index 9bf7658a8..c8d79d05f 100644 --- a/renderer/src/layouts/editor/MenuBar.js +++ b/renderer/src/layouts/editor/MenuBar.js @@ -5,7 +5,7 @@ import { import { ArrowLeftIcon, } from '@heroicons/react/24/outline'; -import router from 'next/router'; +import { useRouter } from 'next/navigation'; import { useTranslation } from 'react-i18next'; import EditorSideBar from '@/modules/editorsidebar/EditorSideBar'; import { ReferenceContext } from '@/components/context/ReferenceContext'; @@ -26,6 +26,7 @@ export default function TopMenuBar() { setOpenSideBar, }, } = useContext(ProjectContext); + const { state: { fontSize, @@ -38,6 +39,7 @@ export default function TopMenuBar() { const [projectname, setprojectname] = useState(undefined); const { t } = useTranslation(); + const router = useRouter(); useEffect(() => { (async () => { @@ -60,9 +62,10 @@ export default function TopMenuBar() { } }; const goToProjectPage = async () => { - await saveReferenceResource(); + saveReferenceResource(); router.push('/projects'); }; + return ( <> diff --git a/renderer/src/layouts/editor/SectionContainer.js b/renderer/src/layouts/editor/SectionContainer.js index e8147f5ad..5b2208cb6 100644 --- a/renderer/src/layouts/editor/SectionContainer.js +++ b/renderer/src/layouts/editor/SectionContainer.js @@ -16,22 +16,21 @@ const MainPlayer = dynamic( ); const SectionContainer = () => { const [editor, setEditor] = useState(); + useEffect(() => { - if (!editor) { - localforage.getItem('userProfile').then((value) => { - const username = value?.username; - localforage.getItem('currentProject').then((projectName) => { - const path = require('path'); - const fs = window.require('fs'); - const newpath = localStorage.getItem('userPath'); - const metaPath = path.join(newpath, packageInfo.name, 'users', username, 'projects', projectName, 'metadata.json'); - const data = fs.readFileSync(metaPath, 'utf-8'); - const metadata = JSON.parse(data); - setEditor(metadata.type.flavorType.flavor.name); - }); + localforage.getItem('userProfile').then((value) => { + const username = value?.username; + localforage.getItem('currentProject').then((projectName) => { + const path = require('path'); + const fs = window.require('fs'); + const newpath = localStorage.getItem('userPath'); + const metaPath = path.join(newpath, packageInfo.name, 'users', username, 'projects', projectName, 'metadata.json'); + const data = fs.readFileSync(metaPath, 'utf-8'); + const metadata = JSON.parse(data); + setEditor(metadata.type.flavorType.flavor.name); }); - } - }); + }); + }, [editor]); const { usfmData, bookAvailable } = useReadUsfmFile(); const { state, actions } = useContext(ScribexContext); diff --git a/renderer/src/layouts/editor/SectionPlaceholder1.js b/renderer/src/layouts/editor/SectionPlaceholder1.js index cb12189b8..975778088 100644 --- a/renderer/src/layouts/editor/SectionPlaceholder1.js +++ b/renderer/src/layouts/editor/SectionPlaceholder1.js @@ -9,8 +9,7 @@ import { ProjectContext } from '@/components/context/ProjectContext'; import CustomNavigation from '@/components/EditorPage/Navigation/CustomNavigation'; import NavigationObs from '@/components/EditorPage/ObsEditor/NavigationObs'; import ReferenceObs from '@/components/EditorPage/ObsEditor/ReferenceObs'; -import { isElectron } from '@/core/handleElectron'; -import core from '@/components/EditorPage/ObsEditor/core'; +import { core } from '@/components/EditorPage/ObsEditor/core'; import ReferenceAudio from '@/components/EditorPage/Reference/Audio/ReferenceAudio'; import { SnackBar } from '@/components/SnackBar'; import useAddNotification from '@/components/hooks/useAddNotification'; @@ -117,9 +116,9 @@ const SectionPlaceholder1 = ({ editor }) => { if (sectionNum === 2) { setHideAddition(false); } else { - setHideAddition(true); + setHideAddition(true); } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [layout, sectionNum]); useEffect(() => { @@ -163,9 +162,9 @@ const SectionPlaceholder1 = ({ editor }) => { setRemovingSection('2'); setLoadResource2(false); } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [resetResourceOnDeleteOffline?.referenceColumnOneData1Reset, resetResourceOnDeleteOffline?.referenceColumnOneData2Reset, - removingSection]); + removingSection]); const getReferenceHistoryOnLoad = async () => new Promise((resolve) => { fetchSettingsResourceHistory( @@ -185,17 +184,17 @@ const SectionPlaceholder1 = ({ editor }) => { setOpenSnackBar, addNotification, sectionPlaceholderNum, - ).then(() => { - resolve(); - }); + ).then(() => { + resolve(); }); + }); // call useEffect on Load resource useEffect(() => { - getReferenceHistoryOnLoad().then(() => { - logger.debug('SectionPlaceholder1.js', 'Getting Resources Reference on Load'); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps + getReferenceHistoryOnLoad().then(() => { + logger.debug('SectionPlaceholder1.js', 'Getting Resources Reference on Load'); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // call useEffect on Save reference (call on new resource / new pane) @@ -218,9 +217,9 @@ const SectionPlaceholder1 = ({ editor }) => { setReferenceColumnOneData2, setOpenResource1, setOpenResource2, - ); + ); })(); - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [openResource1, openResource2, referenceColumnOneData1?.languageId, referenceColumnOneData1.refName, referenceColumnOneData1?.selectedResource, referenceColumnOneData2?.languageId, referenceColumnOneData2?.refName, referenceColumnOneData2?.selectedResource, sectionNum, layout, @@ -228,7 +227,7 @@ const SectionPlaceholder1 = ({ editor }) => { referenceColumnOneData1?.offlineResource, resetResourceOnDeleteOffline?.referenceColumnOneData1Reset, resetResourceOnDeleteOffline?.referenceColumnOneData2Reset, referenceColumnOneData2, referenceColumnOneData1]); - // referenceColumnOneData2 referenceColumnOneData1 + // referenceColumnOneData2 referenceColumnOneData1 const CustomNavigation1 = ( { ); useEffect(() => { // Set OBS stories - if (isElectron()) { - localforage.getItem('userProfile').then((user) => { - const fs = window.require('fs'); + const readObs = async () => { + // if (isElectron()) { + localforage.getItem('userProfile').then(async (user) => { if (_obsNavigation1 && referenceColumnOneData1.refName && referenceColumnOneData1.selectedResource === 'obs') { + const fs = window.require('fs'); setStories1(core(fs, _obsNavigation1, referenceColumnOneData1.refName, user.username)); } if (_obsNavigation2 && referenceColumnOneData2.refName && referenceColumnOneData2.selectedResource === 'obs') { - setStories2(core(fs, _obsNavigation2, referenceColumnOneData2.refName, user.username)); + const fs = window.require('fs'); + setStories2(core(fs, _obsNavigation2, referenceColumnOneData1.refName, user.username)); } }); - } + // } + }; + readObs(); }, [_obsNavigation1, _obsNavigation2, referenceColumnOneData1, referenceColumnOneData2]); + console.log({ + referenceColumnOneData2, referenceColumnOneData1, stories1, stories2, + }); return ( <> {(layout > 0 && layout <= 2) && ( @@ -308,41 +314,41 @@ const SectionPlaceholder1 = ({ editor }) => { setFont={setFont1} > { - (loadResource1 === true) - && ((referenceColumnOneData1.selectedResource === 'bible' && ( - <> - {referenceColumnOneData1?.languageId - && ( - - + {referenceColumnOneData1?.languageId + && ( + + + + )} + + )) || (referenceColumnOneData1.selectedResource === 'obs' && ( + <> + {referenceColumnOneData1?.languageId + && ( + + )} + + )) || (referenceColumnOneData1.selectedResource === 'audio' && ( + - - )} - - )) || (referenceColumnOneData1.selectedResource === 'obs' && ( - <> - {referenceColumnOneData1?.languageId - && ( - - )} - - )) || (referenceColumnOneData1.selectedResource === 'audio' && ( - - )) || ( + )) || ( { story={_obsNavigation1} offlineResource={referenceColumnOneData1.offlineResource} /> + ) ) - ) - } + } - )} + )} {openResource2 === false && ( - - { - (loadResource2 === true) - && ((referenceColumnOneData2.selectedResource === 'bible' && ( - <> - {referenceColumnOneData2?.languageId - && ( - - + { + (loadResource2 === true) + && ((referenceColumnOneData2.selectedResource === 'bible' && ( + <> + {referenceColumnOneData2?.languageId + && ( + + + + )} + + )) || (referenceColumnOneData2.selectedResource === 'obs' && ( + <> + {referenceColumnOneData2?.languageId + && ( + - - )} - - )) || (referenceColumnOneData2.selectedResource === 'obs' && ( - <> - {referenceColumnOneData2?.languageId - && ( - - )} - - )) || (referenceColumnOneData2.selectedResource === 'audio' && ( - + )) || (referenceColumnOneData2.selectedResource === 'audio' && ( + + )) || ( + - )) || ( - + ) ) - ) - } - - )} + } + + )}
)} diff --git a/renderer/src/layouts/editor/SectionPlaceholder2.js b/renderer/src/layouts/editor/SectionPlaceholder2.js index 5af4f3569..699b286fd 100644 --- a/renderer/src/layouts/editor/SectionPlaceholder2.js +++ b/renderer/src/layouts/editor/SectionPlaceholder2.js @@ -9,8 +9,7 @@ import { ProjectContext } from '@/components/context/ProjectContext'; import CustomNavigation from '@/components/EditorPage/Navigation/CustomNavigation'; import NavigationObs from '@/components/EditorPage/ObsEditor/NavigationObs'; import ReferenceObs from '@/components/EditorPage/ObsEditor/ReferenceObs'; -import { isElectron } from '@/core/handleElectron'; -import core from '@/components/EditorPage/ObsEditor/core'; +import { core } from '@/components/EditorPage/ObsEditor/core'; import ReferenceAudio from '@/components/EditorPage/Reference/Audio/ReferenceAudio'; import { SnackBar } from '@/components/SnackBar'; import useAddNotification from '@/components/hooks/useAddNotification'; @@ -110,7 +109,7 @@ const SectionPlaceholder2 = ({ editor }) => { setRow(0); if (sectionNum === 0) { setSectionNum(1); } } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [layout]); useEffect(() => { @@ -155,9 +154,9 @@ const SectionPlaceholder2 = ({ editor }) => { )); setLoadResource4(false); } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [resetResourceOnDeleteOffline?.referenceColumnTwoData1Reset, resetResourceOnDeleteOffline?.referenceColumnTwoData2Reset, - removingSection]); + removingSection]); const getReferenceHistoryOnLoad = async () => new Promise((resolve) => { fetchSettingsResourceHistory( @@ -177,17 +176,17 @@ const SectionPlaceholder2 = ({ editor }) => { setOpenSnackBar, addNotification, sectionPlaceholderNum, - ).then(() => { - resolve(); - }); + ).then(() => { + resolve(); }); + }); // call useEffect on Load resource useEffect(() => { getReferenceHistoryOnLoad().then(() => { logger.debug('SectionPlaceholder2.js', 'Getting Resources Reference on Load'); }); - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { @@ -223,7 +222,7 @@ const SectionPlaceholder2 = ({ editor }) => { setReferenceColumnTwoData2, setOpenResource3, setOpenResource4, - ); + ); })(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [openResource3, openResource4, referenceColumnTwoData1?.languageId, @@ -232,9 +231,9 @@ const SectionPlaceholder2 = ({ editor }) => { referenceColumnTwoData1?.owner, referenceColumnTwoData2?.owner, removingSection, addingSection, referenceColumnTwoData1.offlineResource, referenceColumnTwoData2.offlineResource, resetResourceOnDeleteOffline?.referenceColumnTwoData1Reset, resetResourceOnDeleteOffline?.referenceColumnTwoData2Reset, - ]); + ]); - // referenceColumnTwoData1, referenceColumnTwoData2 openResource1, openResource2, + // referenceColumnTwoData1, referenceColumnTwoData2 openResource1, openResource2, const CustomNavigation1 = ( { /> ); useEffect(() => { - if (isElectron()) { - localforage.getItem('userProfile').then((user) => { + localforage.getItem('userProfile').then((user) => { + if (_obsNavigation1 && referenceColumnTwoData1.refName && referenceColumnTwoData1.selectedResource === 'obs') { const fs = window.require('fs'); - if (_obsNavigation1 && referenceColumnTwoData1.refName && referenceColumnTwoData1.selectedResource === 'obs') { - setStories1(core(fs, _obsNavigation1, referenceColumnTwoData1.refName, user.username)); - } - if (_obsNavigation2 && referenceColumnTwoData2.refName && referenceColumnTwoData2.selectedResource === 'obs') { - setStories2(core(fs, _obsNavigation2, referenceColumnTwoData2.refName, user.username)); - } - }); - } + setStories1(core(fs, _obsNavigation1, referenceColumnTwoData1.refName, user.username)); + } + if (_obsNavigation2 && referenceColumnTwoData2.refName && referenceColumnTwoData2.selectedResource === 'obs') { + const fs = window.require('fs'); + setStories2(core(fs, _obsNavigation2, referenceColumnTwoData2.refName, user.username)); + } + }); }, [_obsNavigation1, _obsNavigation2, referenceColumnTwoData1, referenceColumnTwoData2]); return ( <> {((openResource1 === true && openResource2 === true) - ? (layout >= 1 && layout <= 2) : (layout > 1 && layout <= 2)) && ( - <> - {(openResource3 === false || openResource4 === false) && ( -
- - { - (loadResource3 === true) - && ((referenceColumnTwoData1.selectedResource === 'bible' && ( - <> - {referenceColumnTwoData1?.languageId - && ( - - = 1 && layout <= 2) : (layout > 1 && layout <= 2)) && ( + <> + {(openResource3 === false || openResource4 === false) && ( +
+ + { + (loadResource3 === true) + && ((referenceColumnTwoData1.selectedResource === 'bible' && ( + <> + {referenceColumnTwoData1?.languageId + && ( + + + + )} + + )) || (referenceColumnTwoData1.selectedResource === 'obs' && ( + <> + {referenceColumnTwoData1?.languageId + && ( + + )} + + )) || (referenceColumnTwoData1.selectedResource === 'audio' && ( + - - )} - - )) || (referenceColumnTwoData1.selectedResource === 'obs' && ( - <> - {referenceColumnTwoData1?.languageId - && ( - - )} - - )) || (referenceColumnTwoData1.selectedResource === 'audio' && ( - - )) || ( - - ) - ) - } - - - { - (loadResource4 === true) - && ((referenceColumnTwoData2.selectedResource === 'bible' && ( - <> - {referenceColumnTwoData2?.languageId - && ( - - + ) + ) + } + + + { + (loadResource4 === true) + && ((referenceColumnTwoData2.selectedResource === 'bible' && ( + <> + {referenceColumnTwoData2?.languageId + && ( + + + + )} + + )) || (referenceColumnTwoData2.selectedResource === 'obs' && ( + <> + {referenceColumnTwoData2?.languageId + && ( + + )} + + )) || (referenceColumnTwoData2.selectedResource === 'audio' && ( + + )) || ( + - - )} - - )) || (referenceColumnTwoData2.selectedResource === 'obs' && ( - <> - {referenceColumnTwoData2?.languageId - && ( - - )} - - )) || (referenceColumnTwoData2.selectedResource === 'audio' && ( - - )) || ( - - ) - ) - } - -
- )} - - )} + ) + ) + } +
+
+ )} + + )} { @@ -32,6 +38,9 @@ export default function SubMenuBar() { state: { layout, row, + // openResource1, + // openResource3, + }, actions: { setOpenResource1, @@ -67,7 +76,7 @@ export default function SubMenuBar() { renderElement: , callback: activate, }, - ]; + ]; const handleResource = () => { if (layout === 0) { @@ -91,21 +100,37 @@ export default function SubMenuBar() { // This below code is for identifying the type of resource to remove Bookmarks from OBS const [resourceType, setResourceType] = useState(); + + async function supabaseResourceType() { + const projectName = await localforage.getItem('currentProject'); + const userProfile = await localforage.getItem('userProfile'); + const email = userProfile.user.email; + const { data, error } = await sbStorageDownload(`${newPath}/${email}/projects/${projectName}/metadata.json`); + if (error) { + console.log('SubMenuBar.js', error); + } + const metadata = JSON.parse(await data.text()); + setResourceType(metadata.type.flavorType.flavor.name); + } + useEffect(() => { - localforage.getItem('userProfile').then((value) => { - const username = value?.username; - localforage.getItem('currentProject').then((projectName) => { - const path = require('path'); - const fs = window.require('fs'); - const newpath = localStorage.getItem('userPath'); - const metaPath = path.join(newpath, packageInfo.name, 'users', username, 'projects', projectName, 'metadata.json'); - const data = fs.readFileSync(metaPath, 'utf-8'); - const metadata = JSON.parse(data); - setResourceType(metadata.type.flavorType.flavor.name); + if (isElectron()) { + localforage.getItem('userProfile').then((value) => { + const username = value?.username; + localforage.getItem('currentProject').then((projectName) => { + const path = require('path'); + const fs = window.require('fs'); + const newpath = localStorage.getItem('userPath'); + const metaPath = path.join(newpath, packageInfo.name, 'users', username, 'projects', projectName, 'metadata.json'); + const data = fs.readFileSync(metaPath, 'utf-8'); + const metadata = JSON.parse(data); + setResourceType(metadata.type.flavorType.flavor.name); + }); }); - }); + } else { + supabaseResourceType(); + } }); - return ( <> diff --git a/renderer/src/layouts/editor/WebMenuBar.js b/renderer/src/layouts/editor/WebMenuBar.js new file mode 100644 index 000000000..aba4fa9b3 --- /dev/null +++ b/renderer/src/layouts/editor/WebMenuBar.js @@ -0,0 +1,164 @@ +'use client'; + +import React, { useContext, useEffect, useState } from 'react'; +import { + Disclosure, +} from '@headlessui/react'; +import { + ArrowLeftIcon, +} from '@heroicons/react/24/outline'; +import { useRouter } from 'next/navigation'; +import { useTranslation } from 'react-i18next'; +import EditorSideBar from '@/modules/editorsidebar/EditorSideBar'; +import { ReferenceContext } from '@/components/context/ReferenceContext'; +import { ProjectContext } from '@/components/context/ProjectContext'; +import { saveReferenceResource } from '@/core/projects/updateAgSettings'; +import UserProfile from '@/components/Profile/UserProfile'; +import * as localforage from 'localforage'; +import { splitStringByLastOccurance } from '@/util/splitStringByLastMarker'; +import styles from './MenuBar.module.css'; +import LogoIcon from '@/icons/logo.svg'; + +export default function TopMenuBar() { + const { + states: { + selectedProject, + openSideBar, + }, + actions: { + setOpenSideBar, + }, + } = useContext(ProjectContext); + + const { + state: { + fontSize, + }, + actions: { + setFontsize, + }, + } = useContext(ReferenceContext); + + const [projectname, setprojectname] = useState(undefined); + + const { t } = useTranslation(); + const router = useRouter(); + + useEffect(() => { + (async () => { + const selectedProject = await localforage.getItem('currentProject'); + if (selectedProject && !projectname) { + const splitProjectName = await splitStringByLastOccurance(selectedProject, '_'); + setprojectname(splitProjectName); + } + })(); + }, [selectedProject, projectname]); + + function closeSideBar(open) { + setOpenSideBar(open); + } + const handleFontSize = (status) => { + if (status === 'dec' && fontSize > 0.70) { + setFontsize(fontSize - 0.2); + } + if (status === 'inc' && fontSize < 2) { + setFontsize(fontSize + 0.2); + } + }; + const goToProjectPage = async () => { + saveReferenceResource(); + router.push('/projects'); + }; + return ( + <> + + + {() => ( + <> + +
+ +
+
+ + {projectname?.[0]} + +
+
+
+ {/*
+ + +
+ */} +
+
+
+
+ {/* + + */} + + {/* + */} + + {/* Profile dropdown */} + +
+
+ + + )} +
+ + ); +} diff --git a/renderer/src/layouts/editor/WebSectionContainer.js b/renderer/src/layouts/editor/WebSectionContainer.js new file mode 100644 index 000000000..4e3e4a2af --- /dev/null +++ b/renderer/src/layouts/editor/WebSectionContainer.js @@ -0,0 +1,61 @@ +'use client'; + +import { useState, useEffect, useContext } from 'react'; +import dynamic from 'next/dynamic'; +import localforage from 'localforage'; +import ObsEditor from '@/components/EditorPage/ObsEditor/ObsWebEditor'; +import AudioEditor from '@/components/EditorPage/AudioEditor/AudioEditor'; +import { ScribexContext } from '@/components/context/ScribexContext'; +import { useReadUsfmFile } from '@/components/hooks/scribex/useReadUsfmFile'; +import Scribex from '@/components/EditorPage/Scribex/Scribex'; // eslint-disable-line +import SectionPlaceholder1 from './WebSectionPlaceholder1'; +import SectionPlaceholder2 from './WebSectionPlaceholder2'; +import { newPath, sbStorageDownload } from '../../../../supabase'; +// if (!process.env.NEXT_PUBLIC_IS_ELECTRON) { +// const supabaseStorage = require('../../../../../supabase').supabaseStorage +// const newPath = require('../../../../supabase').newPath +// } + +const MainPlayer = dynamic( + () => import('@/components/EditorPage/AudioEditor/MainPlayer'), + { ssr: false }, +); +const SectionContainer = () => { + const [editor, setEditor] = useState(); + + useEffect(() => { + const setSupabaseEditor = async () => { + const userProfile = await localforage.getItem('userProfile'); + const username = userProfile?.user?.email; + const projectName = await localforage.getItem('currentProject'); + const { data } = await sbStorageDownload(`${newPath}/${username}/projects/${projectName}/metadata.json`); + const metadata = JSON.parse(await data.text()); + setEditor(metadata.type.flavorType.flavor.name); + }; + + setSupabaseEditor(); + }, [editor]); + + const { usfmData, bookAvailable } = useReadUsfmFile(); + const { state, actions } = useContext(ScribexContext); + const props = { + usfmData, + bookAvailable, + state, + actions, + }; + + return ( + <> +
+ + + {(editor === 'textTranslation' && ) + || (editor === 'textStories' && ) + || (editor === 'audioTranslation' && )} +
+ {(editor === 'audioTranslation' && ())} + + ); +}; +export default SectionContainer; diff --git a/renderer/src/layouts/editor/WebSectionPlaceholder1.js b/renderer/src/layouts/editor/WebSectionPlaceholder1.js new file mode 100644 index 000000000..4030e5d40 --- /dev/null +++ b/renderer/src/layouts/editor/WebSectionPlaceholder1.js @@ -0,0 +1,454 @@ +'use client'; + +/* eslint-disable react/jsx-no-useless-fragment */ +import dynamic from 'next/dynamic'; +import { useContext, useEffect, useState } from 'react'; +import localforage from 'localforage'; +import PropTypes from 'prop-types'; +import { ReferenceContext } from '@/components/context/ReferenceContext'; +import EditorSection from '@/layouts/editor/EditorSection'; +import { ProjectContext } from '@/components/context/ProjectContext'; +import CustomNavigation from '@/components/EditorPage/Navigation/CustomNavigation'; +import NavigationObs from '@/components/EditorPage/ObsEditor/NavigationObs'; +import ReferenceObs from '@/components/EditorPage/ObsEditor/ReferenceObs'; +import { webCore } from '@/components/EditorPage/ObsEditor/core'; +import ReferenceAudio from '@/components/EditorPage/Reference/Audio/ReferenceAudio'; +import { SnackBar } from '@/components/SnackBar'; +import useAddNotification from '@/components/hooks/useAddNotification'; +import { fetchSettingsResourceHistory } from '@/core/editor/fetchSettingsResourceHistory'; +import { saveSettingsResourceHistory } from '@/core/editor/saveSettingsResourceHistory'; +import ReferenceBibleX from '@/components/EditorPage/Reference/ReferenceBible/ReferenceBibleX'; + +import ScribexContextProvider from '@/components/context/ScribexContext'; +import * as logger from '../../logger'; + +const TranslationHelps = dynamic( + () => import('@/components/EditorPage/Reference/TranslationHelps'), + { ssr: false }, +); + +const SectionPlaceholder1 = ({ editor }) => { + // const supportedBooks = null; + const sectionPlaceholderNum = '1'; + const [snackBar, setOpenSnackBar] = useState(false); + const [snackText, setSnackText] = useState(''); + const [notify, setNotify] = useState(); + const { addNotification } = useAddNotification(); + + const [referenceColumnOneData1, setReferenceColumnOneData1] = useState({ + languageId: '', + selectedResource: '', + refName: '', + header: '', + owner: '', + offlineResource: { offline: false }, + }); + const [referenceColumnOneData2, setReferenceColumnOneData2] = useState({ + languageId: '', + selectedResource: '', + refName: '', + header: '', + owner: '', + offlineResource: { offline: false }, + }); + const [loadResource1, setLoadResource1] = useState(false); + const [loadResource2, setLoadResource2] = useState(false); + const { + state: { + layout, + openResource1, + openResource2, + bookId, + chapter, + verse, + obsNavigation, + resetResourceOnDeleteOffline, + font1, + font2, + }, + actions: { + setRow, + setLayout, + setOpenResource1, + setOpenResource2, + setFont1, + setFont2, + // applyBooksFilter, + setResetResourceOnDeleteOffline, + }, + } = useContext(ReferenceContext); + + const { + states: { + scrollLock, + }, + } = useContext(ProjectContext); + + const [sectionNum, setSectionNum] = useState(0); + const [hideAddition, setHideAddition] = useState(true); + const [removingSection, setRemovingSection] = useState(); + const [addingSection, setAddingSection] = useState(); + const [naviagation1, setNavigation1] = useState({ + bookId, + chapter, + verse, + }); + + const [naviagation2, setNavigation2] = useState({ + bookId, + chapter, + verse, + }); + + const _bookId1 = scrollLock === false ? bookId : naviagation1.bookId; + const _chapter1 = scrollLock === false ? chapter : naviagation1.chapter; + const _verse1 = scrollLock === false ? verse : naviagation1.verse; + + const _bookId2 = scrollLock === false ? bookId : naviagation2.bookId; + const _chapter2 = scrollLock === false ? chapter : naviagation2.chapter; + const _verse2 = scrollLock === false ? verse : naviagation2.verse; + + useEffect(() => { + if (layout > 0 && layout <= 2) { + setRow(0); + if (sectionNum === 0) { + setSectionNum(1); + } + } + if (sectionNum === 2) { + setHideAddition(false); + } else { + setHideAddition(true); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [layout, sectionNum]); + + useEffect(() => { + if (resetResourceOnDeleteOffline?.referenceColumnOneData1Reset || removingSection === '1') { + logger.debug('SectionPlaceholder1.js', 'delete resource for pane C0R0'); + setReferenceColumnOneData1((prev) => ({ + ...prev, + languageId: '', + selectedResource: '', + refName: '', + header: '', + owner: '', + offlineResource: { offline: false }, + } + )); + setResetResourceOnDeleteOffline((prev) => ({ + ...prev, + referenceColumnOneData1Reset: false, + } + )); + setRemovingSection('1'); + setLoadResource1(false); + } + if (resetResourceOnDeleteOffline?.referenceColumnOneData2Reset || removingSection === '2') { + logger.debug('SectionPlaceholder1.js', 'delete resource for pane C0R1'); + setReferenceColumnOneData2((prev) => ({ + ...prev, + languageId: '', + selectedResource: '', + refName: '', + header: '', + owner: '', + offlineResource: { offline: false }, + } + )); + setResetResourceOnDeleteOffline((prev) => ({ + ...prev, + referenceColumnOneData2Reset: false, + } + )); + setRemovingSection('2'); + setLoadResource2(false); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [resetResourceOnDeleteOffline?.referenceColumnOneData1Reset, resetResourceOnDeleteOffline?.referenceColumnOneData2Reset, + removingSection]); + + const getReferenceHistoryOnLoad = async () => new Promise((resolve) => { + fetchSettingsResourceHistory( + setRemovingSection, + setReferenceColumnOneData1, + setReferenceColumnOneData2, + referenceColumnOneData1, + referenceColumnOneData2, + setLayout, + setLoadResource1, + setLoadResource2, + setOpenResource1, + setOpenResource2, + setSectionNum, + setNotify, + setSnackText, + setOpenSnackBar, + addNotification, + sectionPlaceholderNum, + ).then(() => { + resolve(); + }); + }); + + // call useEffect on Load resource + useEffect(() => { + getReferenceHistoryOnLoad().then(() => { + logger.debug('SectionPlaceholder1.js', 'Getting Resources Reference on Load'); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // call useEffect on Save reference (call on new resource / new pane) + useEffect(() => { + logger.debug('SectionPlaceholder1.js', 'in Save reference C0'); + (async () => { + saveSettingsResourceHistory( + sectionNum, + openResource1, + openResource2, + layout, + referenceColumnOneData1, + referenceColumnOneData2, + addingSection, + removingSection, + setAddingSection, + setRemovingSection, + sectionPlaceholderNum, + setReferenceColumnOneData1, + setReferenceColumnOneData2, + setOpenResource1, + setOpenResource2, + ); + })(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [openResource1, openResource2, referenceColumnOneData1?.languageId, referenceColumnOneData1.refName, + referenceColumnOneData1?.selectedResource, referenceColumnOneData2?.languageId, referenceColumnOneData2?.refName, + referenceColumnOneData2?.selectedResource, sectionNum, layout, + referenceColumnOneData2?.owner, removingSection, addingSection, referenceColumnOneData2?.offlineResource, + referenceColumnOneData1?.offlineResource, resetResourceOnDeleteOffline?.referenceColumnOneData1Reset, + resetResourceOnDeleteOffline?.referenceColumnOneData2Reset, referenceColumnOneData2, referenceColumnOneData1]); + + // referenceColumnOneData2 referenceColumnOneData1 + const CustomNavigation1 = ( + + ); + + const CustomNavigation2 = ( + + ); + const [obsNavigation1, setObsNavigation1] = useState(1); + const [obsNavigation2, setObsNavigation2] = useState(1); + const [stories1, setStories1] = useState(); + const [stories2, setStories2] = useState(); + const _obsNavigation1 = scrollLock === false ? obsNavigation : obsNavigation1; + const _obsNavigation2 = scrollLock === false ? obsNavigation : obsNavigation2; + const ObsNavigation1 = ( + setObsNavigation1(value)} + number={obsNavigation1} + /> + ); + const ObsNavigation2 = ( + setObsNavigation2(value)} + number={obsNavigation2} + /> + ); + useEffect(() => { + // Set OBS stories + const readObs = async () => { + localforage.getItem('userProfile').then(async (user) => { + if (_obsNavigation1 && referenceColumnOneData1.refName && referenceColumnOneData1.selectedResource === 'obs') { + setStories1(await webCore(_obsNavigation1, referenceColumnOneData1.refName, user.user.email)); + } + if (_obsNavigation2 && referenceColumnOneData2.refName && referenceColumnOneData2.selectedResource === 'obs') { + setStories2(await webCore(_obsNavigation2, referenceColumnOneData1.refName, user.user.email)); + } + }); + }; + readObs(); + }, [_obsNavigation1, _obsNavigation2, referenceColumnOneData1, referenceColumnOneData2]); + + console.log({ stories1, stories2 }); + return ( + <> + {(layout > 0 && layout <= 2) && ( + <> + {(openResource1 === false || openResource2 === false) && ( +
+ {openResource1 === false && ( + + { + (loadResource1 === true) + && ((referenceColumnOneData1.selectedResource === 'bible' && ( + <> + {referenceColumnOneData1?.languageId + && ( + + + + )} + + )) || (referenceColumnOneData1.selectedResource === 'obs' && ( + <> + {referenceColumnOneData1?.languageId + && ( + + )} + + )) || (referenceColumnOneData1.selectedResource === 'audio' && ( + + )) || ( + + ) + ) + } + + )} + {openResource2 === false && ( + + { + (loadResource2 === true) + && ((referenceColumnOneData2.selectedResource === 'bible' && ( + <> + {referenceColumnOneData2?.languageId + && ( + + + + )} + + )) || (referenceColumnOneData2.selectedResource === 'obs' && ( + <> + {referenceColumnOneData2?.languageId + && ( + + )} + + )) || (referenceColumnOneData2.selectedResource === 'audio' && ( + + )) || ( + + ) + ) + } + + )} +
+ )} + + )} + + + ); +}; +export default SectionPlaceholder1; + +SectionPlaceholder1.propTypes = { + editor: PropTypes.string, +}; diff --git a/renderer/src/layouts/editor/WebSectionPlaceholder2.js b/renderer/src/layouts/editor/WebSectionPlaceholder2.js new file mode 100644 index 000000000..daf58ea99 --- /dev/null +++ b/renderer/src/layouts/editor/WebSectionPlaceholder2.js @@ -0,0 +1,453 @@ +'use client'; + +/* eslint-disable react/jsx-no-useless-fragment */ +import dynamic from 'next/dynamic'; +import { useContext, useEffect, useState } from 'react'; +import localforage from 'localforage'; +import PropTypes from 'prop-types'; +import { ReferenceContext } from '@/components/context/ReferenceContext'; +import EditorSection from '@/layouts/editor/EditorSection'; +import { ProjectContext } from '@/components/context/ProjectContext'; +import CustomNavigation from '@/components/EditorPage/Navigation/CustomNavigation'; +import NavigationObs from '@/components/EditorPage/ObsEditor/NavigationObs'; +import ReferenceObs from '@/components/EditorPage/ObsEditor/ReferenceObs'; +import { webCore } from '@/components/EditorPage/ObsEditor/core'; +import ReferenceAudio from '@/components/EditorPage/Reference/Audio/ReferenceAudio'; +import { SnackBar } from '@/components/SnackBar'; +import useAddNotification from '@/components/hooks/useAddNotification'; +import { fetchSettingsResourceHistory } from '@/core/editor/fetchSettingsResourceHistory'; +import { saveSettingsResourceHistory } from '@/core/editor/saveSettingsResourceHistory'; +import ReferenceBibleX from '@/components/EditorPage/Reference/ReferenceBible/ReferenceBibleX'; +import ScribexContextProvider from '@/components/context/ScribexContext'; +import * as logger from '../../logger'; + +const TranslationHelps = dynamic( + () => import('@/components/EditorPage/Reference/TranslationHelps'), + { ssr: false }, +); + +const SectionPlaceholder2 = ({ editor }) => { + const sectionPlaceholderNum = '2'; + const [snackBar, setOpenSnackBar] = useState(false); + const [snackText, setSnackText] = useState(''); + const [notify, setNotify] = useState(); + const { addNotification } = useAddNotification(); + const supportedBooks = null; + const [referenceColumnTwoData1, setReferenceColumnTwoData1] = useState({ + languageId: '', + selectedResource: '', + refName: '', + header: '', + owner: '', + offlineResource: { offline: false }, + }); + const [referenceColumnTwoData2, setReferenceColumnTwoData2] = useState({ + languageId: '', + selectedResource: '', + refName: '', + header: '', + owner: '', + offlineResource: { offline: false }, + }); + const [loadResource3, setLoadResource3] = useState(false); + const [loadResource4, setLoadResource4] = useState(false); + const [removingSection, setRemovingSection] = useState(); + const [addingSection, setAddingSection] = useState(); + const { + state: { + layout, + openResource1, + openResource2, + openResource3, + openResource4, + bookId, + chapter, + verse, + obsNavigation, + resetResourceOnDeleteOffline, + font3, + font4, + }, + actions: { + setRow, + setOpenResource3, + setOpenResource4, + applyBooksFilter, + setLayout, + setResetResourceOnDeleteOffline, + setFont3, + setFont4, + }, + } = useContext(ReferenceContext); + const { + states: { + scrollLock, + }, + } = useContext(ProjectContext); + const [sectionNum, setSectionNum] = useState(0); + const [hideAddition, setHideAddition] = useState(true); + const [naviagation1, setNavigation1] = useState({ + bookId, + chapter, + verse, + }); + + const [naviagation2, setNavigation2] = useState({ + bookId, + chapter, + verse, + }); + + const _bookId1 = scrollLock === false ? bookId : naviagation1.bookId; + const _chapter1 = scrollLock === false ? chapter : naviagation1.chapter; + const _verse1 = scrollLock === false ? verse : naviagation1.verse; + + const _bookId2 = scrollLock === false ? bookId : naviagation2.bookId; + const _chapter2 = scrollLock === false ? chapter : naviagation2.chapter; + const _verse2 = scrollLock === false ? verse : naviagation2.verse; + + useEffect(() => { + if (layout > 0 && layout <= 2) { + setRow(0); + if (sectionNum === 0) { setSectionNum(1); } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [layout]); + + useEffect(() => { + applyBooksFilter(supportedBooks); + }, [applyBooksFilter, supportedBooks]); + + // reset panes on delete offline contents + useEffect(() => { + if (resetResourceOnDeleteOffline?.referenceColumnTwoData1Reset || removingSection === '3') { + setReferenceColumnTwoData1((prev) => ({ + ...prev, + languageId: '', + selectedResource: '', + refName: '', + header: '', + owner: '', + offlineResource: { offline: false }, + } + )); + setResetResourceOnDeleteOffline((prev) => ({ + ...prev, + referenceColumnTwoData1Reset: false, + } + )); + setLoadResource3(false); + } + if (resetResourceOnDeleteOffline?.referenceColumnTwoData2Reset || removingSection === '4') { + setReferenceColumnTwoData2((prev) => ({ + ...prev, + languageId: '', + selectedResource: '', + refName: '', + header: '', + owner: '', + offlineResource: { offline: false }, + } + )); + setResetResourceOnDeleteOffline((prev) => ({ + ...prev, + referenceColumnTwoData2Reset: false, + } + )); + setLoadResource4(false); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [resetResourceOnDeleteOffline?.referenceColumnTwoData1Reset, resetResourceOnDeleteOffline?.referenceColumnTwoData2Reset, + removingSection]); + + const getReferenceHistoryOnLoad = async () => new Promise((resolve) => { + fetchSettingsResourceHistory( + setRemovingSection, + setReferenceColumnTwoData1, + setReferenceColumnTwoData2, + referenceColumnTwoData1, + referenceColumnTwoData2, + setLayout, + setLoadResource3, + setLoadResource4, + setOpenResource3, + setOpenResource4, + setSectionNum, + setNotify, + setSnackText, + setOpenSnackBar, + addNotification, + sectionPlaceholderNum, + ).then(() => { + resolve(); + }); + }); + + // call useEffect on Load resource + useEffect(() => { + getReferenceHistoryOnLoad().then(() => { + logger.debug('SectionPlaceholder2.js', 'Getting Resources Reference on Load'); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + if (sectionNum === 2) { + setHideAddition(false); + } else { + setHideAddition(true); + } + if (openResource1 === true && openResource2 === true + && openResource3 === true && openResource4 === true) { + if (layout === 1) { + setLayout(0); + } + } + }, [layout, openResource1, openResource2, openResource3, openResource4, sectionNum, setLayout]); + + // call useEffect on Save reference (call on new resource / new pane) + useEffect(() => { + (async () => { + saveSettingsResourceHistory( + sectionNum, + openResource3, + openResource4, + layout, + referenceColumnTwoData1, + referenceColumnTwoData2, + addingSection, + removingSection, + setAddingSection, + setRemovingSection, + sectionPlaceholderNum, + setReferenceColumnTwoData1, + setReferenceColumnTwoData2, + setOpenResource3, + setOpenResource4, + ); + })(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [openResource3, openResource4, referenceColumnTwoData1?.languageId, + referenceColumnTwoData1.refName, referenceColumnTwoData1?.selectedResource, referenceColumnTwoData2?.languageId, + referenceColumnTwoData2?.refName, referenceColumnTwoData2?.selectedResource, sectionNum, layout, + referenceColumnTwoData1?.owner, referenceColumnTwoData2?.owner, removingSection, addingSection, + referenceColumnTwoData1.offlineResource, referenceColumnTwoData2.offlineResource, + resetResourceOnDeleteOffline?.referenceColumnTwoData1Reset, resetResourceOnDeleteOffline?.referenceColumnTwoData2Reset, + ]); + + // referenceColumnTwoData1, referenceColumnTwoData2 openResource1, openResource2, + const CustomNavigation1 = ( + + ); + + const CustomNavigation2 = ( + + ); + const [obsNavigation1, setObsNavigation1] = useState(1); + const [obsNavigation2, setObsNavigation2] = useState(1); + const [stories1, setStories1] = useState(); + const [stories2, setStories2] = useState(); + const _obsNavigation1 = scrollLock === false ? obsNavigation : obsNavigation1; + const _obsNavigation2 = scrollLock === false ? obsNavigation : obsNavigation2; + const ObsNavigation1 = ( + setObsNavigation1(value)} + number={obsNavigation1} + /> + ); + const ObsNavigation2 = ( + setObsNavigation2(value)} + number={obsNavigation2} + /> + ); + useEffect(() => { + async function fetchStories() { + const user = await localforage.getItem('userProfile'); + if (_obsNavigation1 && referenceColumnTwoData1.refName && referenceColumnTwoData1.selectedResource === 'obs') { + setStories1(await webCore(_obsNavigation1, referenceColumnTwoData1.refName, user?.user?.email)); + } + if (_obsNavigation2 && referenceColumnTwoData2.refName && referenceColumnTwoData2.selectedResource === 'obs') { + setStories2(await webCore(_obsNavigation2, referenceColumnTwoData2.refName, user?.user?.email)); + } + } + fetchStories(); + }, [_obsNavigation1, _obsNavigation2, referenceColumnTwoData1, referenceColumnTwoData2]); + return ( + <> + {((openResource1 === true && openResource2 === true) + ? (layout >= 1 && layout <= 2) : (layout > 1 && layout <= 2)) && ( + <> + {(openResource3 === false || openResource4 === false) && ( +
+ + { + (loadResource3 === true) + && ((referenceColumnTwoData1.selectedResource === 'bible' && ( + <> + {referenceColumnTwoData1?.languageId + && ( + + + + )} + + )) || (referenceColumnTwoData1.selectedResource === 'obs' && ( + <> + {referenceColumnTwoData1?.languageId + && ( + + )} + + )) || (referenceColumnTwoData1.selectedResource === 'audio' && ( + + )) || ( + + ) + ) + } + + + { + (loadResource4 === true) + && ((referenceColumnTwoData2.selectedResource === 'bible' && ( + <> + {referenceColumnTwoData2?.languageId + && ( + + + + )} + + )) || (referenceColumnTwoData2.selectedResource === 'obs' && ( + <> + {referenceColumnTwoData2?.languageId + && ( + + )} + + )) || (referenceColumnTwoData2.selectedResource === 'audio' && ( + + )) || ( + + ) + ) + } + +
+ )} + + )} + + + ); +}; +export default SectionPlaceholder2; + +SectionPlaceholder2.propTypes = { + editor: PropTypes.string, +}; diff --git a/renderer/src/layouts/projects/ImportProjectPopUp.js b/renderer/src/layouts/projects/ImportProjectPopUp.js index cc5b4d32b..7224a951c 100644 --- a/renderer/src/layouts/projects/ImportProjectPopUp.js +++ b/renderer/src/layouts/projects/ImportProjectPopUp.js @@ -6,7 +6,7 @@ import { Dialog, Transition } from '@headlessui/react'; import { FolderOpenIcon, InformationCircleIcon, CheckIcon, XMarkIcon, } from '@heroicons/react/24/outline'; -import { useRouter } from 'next/router'; +import { useRouter } from 'next/navigation'; import localforage from 'localforage'; import { useTranslation } from 'react-i18next'; import { SnackBar } from '@/components/SnackBar'; diff --git a/renderer/src/layouts/projects/Layout.js b/renderer/src/layouts/projects/Layout.js index dfa70f578..9c4898ab2 100644 --- a/renderer/src/layouts/projects/Layout.js +++ b/renderer/src/layouts/projects/Layout.js @@ -55,82 +55,82 @@ export default function ProjectsLayout(props) { {title && ( -
- {!isTwoCol - ? ( -
-
+
+ {!isTwoCol + ? ( +
-

{showArchived ? t('label-archived-prj') : title}

- {header} +
+

{showArchived ? t('label-archived-prj') : title}

+ {header} +
-
-
- {isImport && !showArchived - && ( - <> +
+ {isImport && !showArchived + && ( + <> + + + + )} + + {conflictPopup.open + && ( +
+ +
+ )} + + {/* Archived projects button */} + {archive === 'enable' && ( +
- - - )} - - {conflictPopup.open - && ( -
- -
- )} - {/* Archived projects button */} - {archive === 'enable' && ( -
- + {showArchived ? ( + <> + + {t('label-active')} + + ) : ( + <> + + {t('label-archived')} + + )} + -
- )} -
-
- ) - : ( -
-
-

{title}

- {colOne} +
+ )} +
-
- {colTwo} + ) + : ( +
+
+

{title}

+ {colOne} +
+
+ {colTwo} +
-
- )} + )} -
+
)} {children} diff --git a/renderer/src/modules/projects/NewProject.js b/renderer/src/modules/projects/NewProject.js index 5de4212ae..8a068605c 100644 --- a/renderer/src/modules/projects/NewProject.js +++ b/renderer/src/modules/projects/NewProject.js @@ -293,7 +293,6 @@ export default function NewProject({ call, project, closeEdit }) { } // eslint-disable-next-line react-hooks/exhaustive-deps }, [call]); - return ( {children}
+ ); +} + +function BibleHeaderTagDropDown(headerDropDown, handleDropDown, call) { + return ( + call === 'new' + ? ( + + + + ) + : ( + + ) + + ); +} +export default function NewWebProject({ call, project, closeEdit }) { + const { + states: { + newProjectFields, + languages, + language, + }, + actions: { + setLanguage, + createSupabaseProject, + setNewProjectFields, + }, + } = useContext(ProjectContext); + + const { + states: { + loading, + }, + action: { + setLoading, + }, + } = useContext(AutographaContext); + const { t } = useTranslation(); + const { action: { validateField, isLengthValidated, isTextValidated } } = useValidator(); + const router = useRouter(); + const [snackBar, setOpenSnackBar] = useState(false); + const [snackText, setSnackText] = useState(''); + const [notify, setNotify] = useState(); + const [metadata, setMetadata] = useState(); + const [openModal, setOpenModal] = useState(false); + const [projectLangData, setProjectLangData] = useState({}); + const [error, setError] = useState({ + projectName: {}, + abbr: {}, + description: {}, + }); + + const [headerDropDown, setHeaderDropDown] = useState(solutions[0].name); + const handleDropDown = (currentSelection) => { + setHeaderDropDown(currentSelection); + }; + + function getAbbreviation(text) { + if (typeof text !== 'string' || !text) { + return ''; + } + const abbr = []; + const splitText = text.trim().split(' '); + splitText.forEach((t) => { + abbr.push(t.charAt(0)); + }); + return abbr.join('').toUpperCase(); + } + + const handleProjectName = (e) => { + const abbreviation = getAbbreviation(e.target.value); + setNewProjectFields({ ...newProjectFields, projectName: e.target.value, abbreviation }); + }; + + const setEditLanguage = async (value) => { + // check the language already in the list or set a new one which create on save project + if (value.ang) { + const editLang = languages.filter((l) => l.ang?.toLowerCase() === value.ang?.toLowerCase()); + if (editLang.length > 0) { + setLanguage(editLang[0]); + } else { + const key = value.ang + +moment().format(); + const id = uuidv5(key, environment.uuidToken); + setLanguage( + { + id, + ang: value.ang, + ld: value?.ld || 'LTR', + lc: value?.lc, + custom: true, + }, + ); + } + } + }; + + const createWebProject = async (update) => { + setLoading(true); + if (newProjectFields.projectName && newProjectFields.abbreviation) { + const value = await createSupabaseProject(call, metadata, update, headerDropDown); + console.log({ value }); + + const status = value[0]; + logger.debug('NewProject.js', status.value); + setLoading(false); + setNotify(status.type); + setSnackText(status.value); + setOpenSnackBar(true); + if (status.type === 'success') { + if (call === 'edit') { + closeEdit(); + } else { + router.push('/projects'); + } + } + } + }; + const validate = async () => { + setLoading(true); + let create = true; + if (newProjectFields.projectName && newProjectFields.abbreviation) { + logger.debug('NewProject.js', 'Validating all the fields.'); + const checkName = await validateField([isLengthValidated(newProjectFields.projectName, { minLen: 5, maxLen: 40 }), isTextValidated(newProjectFields.projectName, 'nonSpecChar')]); + if (checkName[0].isValid === false || checkName[1].isValid === false) { + logger.warn('NewProject.js', 'Validation failed for Project Name.'); + create = false; + } + const checkAbbr = await validateField([isLengthValidated(newProjectFields.abbreviation, { minLen: 1, maxLen: 10 }), isTextValidated(newProjectFields.abbreviation, 'nonSpecChar')]); + if (checkAbbr[0].isValid === false || checkAbbr[1].isValid === false) { + logger.warn('NewProject.js', 'Validation failed for Abbreviation.'); + create = false; + } + // eslint-disable-next-line max-len + const checkDesc = await validateField([isLengthValidated(newProjectFields.description, { minLen: 0, maxLen: 400 })]); + if (checkDesc[0].isValid === false) { + logger.warn('NewProject.js', 'Validation failed for Description.'); + create = false; + } + setError({ + ...error, projectName: checkName, abbr: checkAbbr, description: checkDesc, + }); + } else { + create = false; + logger.warn('NewProject.js', 'Validation Failed - Fill all the required fields.'); + setNotify('warning'); + setSnackText(t('dynamic-msg-fill-all-fields')); + setOpenSnackBar(true); + } + if (create === true) { + // Checking whether the burrito is of latest version + console.warn('NewProject.js', 'Checking whether the burrito is of latest version.'); + if (call === 'edit' && burrito?.meta?.version !== metadata?.meta?.version) { + setOpenModal(true); + setLoading(false); + } else { + logger.warn('NewProject.js', 'Calling createTheProject function'); + createWebProject(false); + } + } else { + setLoading(false); + } + }; + + const updateBurritoVersion = () => { + setOpenModal(false); + logger.warn('NewProject.js', 'Calling createTheProject function with burrito update'); + createWebProject(true); + }; + const [openPopUp, setOpenPopUp] = useState(false); + const [replaceWarning, setReplaceWarning] = useState(false); + + function openImportPopUp() { + setOpenPopUp(true); + } + + function closeImportPopUp() { + setOpenPopUp(false); + if (call === 'edit') { + setReplaceWarning(true); + } + } + const loadData = async (project) => { + logger.debug('NewProject.js', 'In loadData for loading current project details in Edit page'); + setNewProjectFields({ + projectName: project.identification.name.en, + abbreviation: project.identification.abbreviation.en, + description: project.project[project.type.flavorType.flavor.name].description, + }); + setProjectLangData({ + ang: project.languages[0].name.en, + ld: project.project[project.type.flavorType.flavor.name].scriptDirection, + lc: project.languages[0]?.tag ? project.languages[0].tag : project.languages[0].name.en.substring(0, 3), + }); + setMetadata(project); + // set dropdown to the project type + switch (project.type.flavorType.flavor.name) { + case 'textTranslation': + setHeaderDropDown('Translation'); + break; + + case 'textStories': + setHeaderDropDown('OBS'); + break; + + case 'audioTranslation': + setHeaderDropDown('Audio'); + break; + + default: + break; + } + }; + + useEffect(() => { + setEditLanguage(projectLangData); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [languages.length, projectLangData]); + + useEffect(() => { + if (call === 'edit') { + loadData(project); + } else if (call === 'new') { + // set englsh as default lang + const defaulLang = languages.filter((lang) => lang.lc === 'en'); + setLanguage(defaulLang[0]); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [call]); + + return ( + + {loading === true + ? ( +
+ + + + +
+ ) + : ( +
+
+ +
+

+ {t('label-project-name')} + * +

+ { + handleProjectName(e); + }} + disabled={call !== 'new'} + className={classNames(call !== 'new' ? 'bg-gray-200' : '', 'w-52 lg:w-80 block rounded shadow-sm sm:text-sm focus:border-primary border-gray-300')} + /> + {error.projectName[0]?.message || error.projectName[1]?.message} +

{t('label-description')}

+