mirror of
https://github.com/Qortal/q-tube.git
synced 2025-12-12 15:52:59 +00:00
refactored app so that it works with qapp-core version 1.0.35
This commit is contained in:
@@ -1,18 +0,0 @@
|
||||
module.exports = {
|
||||
env: { browser: true, amd: true, node: true, es2020: true },
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: { ecmaVersion: "latest", sourceType: "module" },
|
||||
plugins: ["react-refresh"],
|
||||
rules: {
|
||||
"react-refresh/only-export-components": "warn",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unused-vars": "warn",
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
},
|
||||
};
|
||||
37
eslint.config.js
Normal file
37
eslint.config.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import js from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import reactHooks from 'eslint-plugin-react-hooks';
|
||||
import reactRefresh from 'eslint-plugin-react-refresh';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import prettierPlugin from 'eslint-plugin-prettier';
|
||||
import prettierConfig from 'eslint-config-prettier';
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
plugins: {
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
prettier: prettierPlugin,
|
||||
},
|
||||
rules: {
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
'prettier/prettier': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
// This disables ESLint rules that would conflict with Prettier
|
||||
name: 'prettier-config',
|
||||
rules: prettierConfig.rules,
|
||||
}
|
||||
);
|
||||
23
package-lock.json
generated
23
package-lock.json
generated
@@ -47,6 +47,7 @@
|
||||
"eslint": "^9.17.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.16",
|
||||
"globals": "^15.15.0",
|
||||
"prettier": "^3.4.2",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^6.3.5",
|
||||
@@ -332,6 +333,15 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse/node_modules/globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
|
||||
@@ -3344,11 +3354,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||
"version": "15.15.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
|
||||
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/goober": {
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"prettier": "^3.4.2",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^6.3.5",
|
||||
"globals": "^15.15.0",
|
||||
"vite-plugin-static-copy": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
31
src/App.tsx
31
src/App.tsx
@@ -2,7 +2,6 @@ import { CssBaseline } from "@mui/material";
|
||||
import { ThemeProvider } from "@mui/material/styles";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Provider, useSelector } from "react-redux";
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import { PersistGate } from "redux-persist/integration/react";
|
||||
import { subscriptionListFilter } from "./App-Functions.ts";
|
||||
import Notification from "./components/common/Notification/Notification";
|
||||
@@ -18,13 +17,13 @@ import DownloadWrapper from "./wrappers/DownloadWrapper";
|
||||
import GlobalWrapper from "./wrappers/GlobalWrapper";
|
||||
import { ScrollWrapper } from "./wrappers/ScrollWrapper.tsx";
|
||||
import { QappCoreWrapper } from "./QappCoreWrapper.tsx";
|
||||
import { Routes } from "./Routes.tsx";
|
||||
|
||||
function App() {
|
||||
// const themeColor = window._qdnTheme
|
||||
|
||||
const [theme, setTheme] = useState("dark");
|
||||
|
||||
useIframe();
|
||||
// useIframe();
|
||||
|
||||
useEffect(() => {
|
||||
subscriptionListFilter(false).then(filteredList => {
|
||||
@@ -35,29 +34,15 @@ function App() {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<ThemeProvider theme={theme === "light" ? lightTheme : darkTheme}>
|
||||
<QappCoreWrapper>
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<CssBaseline />
|
||||
<Notification />
|
||||
<DownloadWrapper>
|
||||
<GlobalWrapper setTheme={(val: string) => setTheme(val)}>
|
||||
<ScrollWrapper>
|
||||
<CssBaseline />
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/video/:name/:id" element={<VideoContent />} />
|
||||
<Route
|
||||
path="/playlist/:name/:id"
|
||||
element={<PlaylistContent />}
|
||||
/>
|
||||
<Route
|
||||
path="/channel/:name"
|
||||
element={<IndividualProfile />}
|
||||
/>
|
||||
</Routes>
|
||||
</ScrollWrapper>
|
||||
</GlobalWrapper>
|
||||
|
||||
|
||||
<Routes />
|
||||
|
||||
</DownloadWrapper>
|
||||
</QappCoreWrapper>
|
||||
</ThemeProvider>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
|
||||
35
src/AppWrapper.tsx
Normal file
35
src/AppWrapper.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { GlobalProvider } from 'qapp-core'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import Layout from './Layout';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { RootState } from './state/store';
|
||||
import { ScrollWrapper } from './wrappers/ScrollWrapper';
|
||||
import GlobalWrapper from './wrappers/GlobalWrapper';
|
||||
|
||||
export const AppWrapper = () => {
|
||||
const { user } = useSelector((state: RootState) => state.auth);
|
||||
|
||||
return (
|
||||
<GlobalProvider
|
||||
config={{
|
||||
auth: {
|
||||
authenticateOnMount: false,
|
||||
userAccountInfo: {
|
||||
address: user?.address,
|
||||
publicKey: user?.publicKey
|
||||
},
|
||||
|
||||
},
|
||||
publicSalt: "usVbeM9YpjGCbLrTcc78YJS0ap1AxDkHAOMZrp3+wDY=",
|
||||
appName: "Q-Tube",
|
||||
enableGlobalVideoFeature: true
|
||||
}}
|
||||
>
|
||||
<GlobalWrapper>
|
||||
<ScrollWrapper>
|
||||
<Layout />
|
||||
</ScrollWrapper>
|
||||
</GlobalWrapper>
|
||||
</GlobalProvider>
|
||||
)
|
||||
}
|
||||
20
src/Layout.tsx
Normal file
20
src/Layout.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { useIframe } from './hooks/useIframe';
|
||||
import { EditVideo } from './components/Publish/EditVideo/EditVideo';
|
||||
import { EditPlaylist } from './components/Publish/EditPlaylist/EditPlaylist';
|
||||
|
||||
const Layout = () => {
|
||||
useIframe();
|
||||
return (
|
||||
<>
|
||||
{/* Add Header here */}
|
||||
<main>
|
||||
|
||||
<Outlet /> {/* This is where page content will be rendered */}
|
||||
</main>
|
||||
{/* Add Footer here */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
48
src/Routes.tsx
Normal file
48
src/Routes.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
||||
import App from './App';
|
||||
import { AppWrapper } from './AppWrapper';
|
||||
import { Home } from './pages/Home/Home';
|
||||
import { VideoContent } from './pages/ContentPages/VideoContent/VideoContent';
|
||||
import { PlaylistContent } from './pages/ContentPages/PlaylistContent/PlaylistContent';
|
||||
import { IndividualProfile } from './pages/ContentPages/IndividualProfile/IndividualProfile';
|
||||
|
||||
|
||||
interface CustomWindow extends Window {
|
||||
_qdnBase: string;
|
||||
}
|
||||
const customWindow = window as unknown as CustomWindow;
|
||||
const baseUrl = customWindow?._qdnBase || '';
|
||||
|
||||
export function Routes() {
|
||||
const router = createBrowserRouter(
|
||||
[
|
||||
{
|
||||
path: '/',
|
||||
element: <AppWrapper />, // GlobalProvider wrapper
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Home />,
|
||||
},
|
||||
{
|
||||
path: 'video/:name/:id',
|
||||
element: <VideoContent />,
|
||||
},
|
||||
{
|
||||
path: 'playlist/:name/:id',
|
||||
element: <PlaylistContent />,
|
||||
},
|
||||
{
|
||||
path: 'channel/:name',
|
||||
element: <IndividualProfile />,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
{
|
||||
basename: baseUrl,
|
||||
}
|
||||
);
|
||||
|
||||
return <RouterProvider router={router} />;
|
||||
}
|
||||
@@ -428,7 +428,7 @@ export const PublishVideo = ({
|
||||
if (!selectExistingPlaylist) {
|
||||
throw new Error("select a playlist");
|
||||
}
|
||||
|
||||
|
||||
// get raw data for playlist
|
||||
const responseData = await qortalRequest({
|
||||
action: "FETCH_QDN_RESOURCE",
|
||||
|
||||
@@ -41,6 +41,7 @@ export const DownloadTaskManager: React.FC = () => {
|
||||
const handleCloseDownload = () => {
|
||||
setAnchorEl(null);
|
||||
setOpenDownload(false);
|
||||
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -95,9 +95,10 @@ export const VideoPlayer = forwardRef<videoRefType, VideoPlayerProps>(
|
||||
// <VideoContext.Provider value={contextData}>
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
height: '70vh'
|
||||
height: '70vh',
|
||||
background: 'black'
|
||||
}}>
|
||||
<QappVideoPlayer timelineActions={timelineActions} poster={props.poster} videoRef={videoRef} qortalVideoResource={{name: props.name, service: props.service as Service, identifier: props.identifier}} />
|
||||
<QappVideoPlayer timelineActions={timelineActions} poster={props.poster} videoRef={videoRef} qortalVideoResource={{name: props.name, service: props.service as Service, identifier: props.identifier}} />
|
||||
</Box>
|
||||
// <VideoContainer
|
||||
// tabIndex={0}
|
||||
|
||||
68
src/global.d.ts
vendored
68
src/global.d.ts
vendored
@@ -1,68 +0,0 @@
|
||||
// src/global.d.ts
|
||||
interface Location {
|
||||
service: string;
|
||||
name: string;
|
||||
identifier?: string;
|
||||
}
|
||||
interface QortalRequestOptions {
|
||||
action: string;
|
||||
name?: string;
|
||||
service?: string;
|
||||
data64?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
category?: string;
|
||||
tags?: string[];
|
||||
identifier?: string;
|
||||
address?: string;
|
||||
metaData?: string;
|
||||
encoding?: string;
|
||||
includeMetadata?: boolean;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
reverse?: boolean;
|
||||
resources?: any[];
|
||||
filename?: string;
|
||||
list_name?: string;
|
||||
item?: string;
|
||||
items?: strings[];
|
||||
tag1?: string;
|
||||
tag2?: string;
|
||||
tag3?: string;
|
||||
tag4?: string;
|
||||
tag5?: string;
|
||||
coin?: string;
|
||||
destinationAddress?: string;
|
||||
amount?: number;
|
||||
blob?: Blob;
|
||||
mimeType?: string;
|
||||
file?: File;
|
||||
encryptedData?: string;
|
||||
mode?: string;
|
||||
query?: string;
|
||||
excludeBlocked?: boolean;
|
||||
exactMatchNames?: boolean;
|
||||
nameListFilter?: string[];
|
||||
location?: Location;
|
||||
}
|
||||
|
||||
declare function qortalRequest(options: QortalRequestOptions): Promise<any>;
|
||||
declare function qortalRequestWithTimeout(
|
||||
options: QortalRequestOptions,
|
||||
time: number
|
||||
): Promise<any>;
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
_qdnBase: any; // Replace 'any' with the appropriate type if you know it
|
||||
_qdnTheme: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
showSaveFilePicker: (
|
||||
options?: SaveFilePickerOptions
|
||||
) => Promise<FileSystemFileHandle>;
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@ const customWindow = window as unknown as CustomWindow;
|
||||
|
||||
const baseUrl = customWindow?._qdnBase || "";
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
<BrowserRouter basename={baseUrl}>
|
||||
<>
|
||||
<App />
|
||||
<div id="modal-root" />
|
||||
</BrowserRouter>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, Typography, useMediaQuery } from "@mui/material";
|
||||
import { Box, Button, Typography, useMediaQuery } from "@mui/material";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import DeletedVideo from "../../../assets/img/DeletedVideo.jpg";
|
||||
@@ -66,6 +66,8 @@ export const VideoContent = () => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const [newVideo, setNewVideo] = useState(null)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
@@ -78,17 +80,27 @@ export const VideoContent = () => {
|
||||
}}
|
||||
onClick={focusVideo}
|
||||
>
|
||||
<Button onClick={()=> {
|
||||
setNewVideo({
|
||||
identifier: 'MYTEST2_vid_hymn-for-peace_gEpkBT',
|
||||
name: 'SafetyMongoose',
|
||||
service: 'VIDEO'
|
||||
})
|
||||
}}>play another</Button>
|
||||
<Button onClick={()=> setNewVideo(null)}>go back</Button>
|
||||
{videoReference ? (
|
||||
<VideoPlayerContainer
|
||||
sx={{
|
||||
// width: `${videoWidth}%`,
|
||||
// marginLeft: "0%",
|
||||
height: '70vh',
|
||||
backgroundColor: 'black'
|
||||
}}
|
||||
>
|
||||
<VideoPlayer
|
||||
name={videoReference?.name}
|
||||
service={videoReference?.service}
|
||||
identifier={videoReference?.identifier}
|
||||
name={newVideo?.name || videoReference?.name}
|
||||
service={newVideo?.service || videoReference?.service}
|
||||
identifier={newVideo?.identifier || videoReference?.identifier}
|
||||
user={channelName}
|
||||
jsonId={id}
|
||||
poster={videoCover || ""}
|
||||
@@ -98,17 +110,11 @@ export const VideoContent = () => {
|
||||
video: { aspectRatio: "16 / 9" },
|
||||
}}
|
||||
duration={videoData?.duration}
|
||||
|
||||
/>
|
||||
</VideoPlayerContainer>
|
||||
) : isVideoLoaded ? (
|
||||
<img
|
||||
src={DeletedVideo}
|
||||
width={"70%"}
|
||||
height={"37%"}
|
||||
style={{ marginLeft: "5%" }}
|
||||
/>
|
||||
) : (
|
||||
<Box sx={{ width: "55vw", aspectRatio: "16/9" }}></Box>
|
||||
<Box sx={{ width: "100%", height: '70vh', background: 'black'}}></Box>
|
||||
)}
|
||||
<VideoContentContainer
|
||||
sx={{ paddingLeft: isScreenSmall ? "5px" : "0px" }}
|
||||
|
||||
@@ -36,7 +36,6 @@ import { getPrimaryAccountName } from "../utils/qortalRequestFunctions.ts";
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
setTheme: (val: string) => void;
|
||||
}
|
||||
|
||||
let timer: number | null = null;
|
||||
@@ -44,7 +43,8 @@ let timer: number | null = null;
|
||||
export const queue = new RequestQueue();
|
||||
export const queueSuperlikes = new RequestQueue();
|
||||
|
||||
const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
||||
const GlobalWrapper: React.FC<Props> = ({ children }) => {
|
||||
const [theme, setTheme] = useState('dark')
|
||||
const dispatch = useDispatch();
|
||||
const isDragging = useRef(false);
|
||||
const [userAvatar, setUserAvatar] = useState<string>("");
|
||||
@@ -210,7 +210,8 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
||||
<>
|
||||
{isLoadingGlobal && <PageLoader />}
|
||||
<ConsentModal />
|
||||
|
||||
<EditVideo />
|
||||
<EditPlaylist />
|
||||
<NavBar
|
||||
setTheme={(val: string) => setTheme(val)}
|
||||
isAuthenticated={!!user?.name}
|
||||
@@ -219,8 +220,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
||||
userAvatar={userAvatar}
|
||||
authenticate={askForAccountInformation}
|
||||
/>
|
||||
<EditVideo />
|
||||
<EditPlaylist />
|
||||
|
||||
{/*<Rnd*/}
|
||||
{/* onDragStart={onDragStart}*/}
|
||||
{/* onDragStop={onDragStop}*/}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"noUnusedParameters": false,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"strictNullChecks": false,
|
||||
"types": ["node"]
|
||||
"types": ["node", "qapp-core/global"]
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
|
||||
Reference in New Issue
Block a user