diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 63ef268..c02066d 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -10,7 +10,9 @@ module.exports = { parser: '@typescript-eslint/parser', plugins: ['react-refresh'], rules: { + "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-expressions": "off", 'react-refresh/only-export-components': [ 'warn', { allowConstantExport: true }, diff --git a/renderer/components/ActionBar.tsx b/renderer/components/ActionBar.tsx index a87c740..55e1094 100644 --- a/renderer/components/ActionBar.tsx +++ b/renderer/components/ActionBar.tsx @@ -1,5 +1,5 @@ -import { useState, useEffect } from "react"; -import { useTranslation } from 'react-i18next'; +import { useEffect } from "react"; +import { useTranslation } from "react-i18next"; import { Button, Dropdown, @@ -9,10 +9,10 @@ import { } from "@nextui-org/react"; function ActionBar(props) { - let lastMovement = 0; const { t } = useTranslation(); useEffect(() => { + let lastMovement = 0; const mouseEvent = () => { lastMovement = Date.now(); }; @@ -51,38 +51,44 @@ function ActionBar(props) { const handleDisplay = () => { props.onDisplay && props.onDisplay(); - } + }; const handlePressNexus = () => { props.onPressNexus && props.onPressNexus(); - } + }; const handleLongPressNexus = () => { props.onLongPressNexus && props.onLongPressNexus(); - } + }; return (
- {t('Toggle Performance')} + {t("Toggle Performance")} + + + {t("Display settings")} + + + {t("Press Nexus")} + + + {t("Long press Nexus")} - {t('Display settings')} - {t('Press Nexus')} - {t('Long press Nexus')} - {t('Disconnect')} + {t("Disconnect")} diff --git a/renderer/components/Alert.tsx b/renderer/components/Alert.tsx index 7b02f3c..65dd8e5 100644 --- a/renderer/components/Alert.tsx +++ b/renderer/components/Alert.tsx @@ -1,30 +1,34 @@ -import {Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button} from "@nextui-org/react"; +import { + Modal, + ModalContent, + ModalHeader, + ModalBody, + ModalFooter, + Button, +} from "@nextui-org/react"; function Alert(props) { - const handleConfirm = () => { - props.onClose && props.onClose() - } + props.onClose && props.onClose(); + }; return ( - {(onClose) => ( - <> - { props.title || 'Warning' } - - { props.content } - - - - - - )} + <> + + {props.title || "Warning"} + + {props.content} + + + + ); } -export default Alert +export default Alert; diff --git a/renderer/components/AuthModal.tsx b/renderer/components/AuthModal.tsx index 256b2be..5bab795 100644 --- a/renderer/components/AuthModal.tsx +++ b/renderer/components/AuthModal.tsx @@ -1,13 +1,20 @@ -import React from 'react' -import {Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button} from "@nextui-org/react" +import React from "react"; +import { + Modal, + ModalContent, + ModalHeader, + ModalBody, + ModalFooter, + Button, +} from "@nextui-org/react"; // import { useTranslation } from 'react-i18next'; const AuthModal = ({ show, onConfirm }) => { // const { t } = useTranslation() const handleConfirm = () => { - onConfirm && onConfirm() - } + onConfirm && onConfirm(); + }; return ( @@ -25,7 +32,7 @@ const AuthModal = ({ show, onConfirm }) => { - ) -} + ); +}; -export default AuthModal +export default AuthModal; diff --git a/renderer/components/Display.tsx b/renderer/components/Display.tsx index 548b57d..c1115e2 100644 --- a/renderer/components/Display.tsx +++ b/renderer/components/Display.tsx @@ -1,7 +1,15 @@ import { useState, useEffect } from "react"; -import {Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button, Slider} from "@nextui-org/react"; -import { useTranslation } from 'react-i18next'; -import { DISPLAY_KEY } from '../common/constans'; +import { + Modal, + ModalContent, + ModalHeader, + ModalBody, + ModalFooter, + Button, + Slider, +} from "@nextui-org/react"; +import { useTranslation } from "react-i18next"; +import { DISPLAY_KEY } from "../common/constans"; const DEFAULT_OPTIONS = { sharpness: 5, @@ -13,127 +21,125 @@ const DEFAULT_OPTIONS = { function Display(props) { const { t } = useTranslation(); - const [options, setOptions] = useState(DEFAULT_OPTIONS) + const [options, setOptions] = useState(DEFAULT_OPTIONS); useEffect(() => { const _localOPtions = window.localStorage.getItem(DISPLAY_KEY); - let localOPtions: any = DEFAULT_OPTIONS + let localOPtions: any = DEFAULT_OPTIONS; if (_localOPtions) { try { - localOPtions = JSON.parse(_localOPtions) - } catch(e) { - localOPtions = DEFAULT_OPTIONS + localOPtions = JSON.parse(_localOPtions); + } catch (e) { + localOPtions = DEFAULT_OPTIONS; } } - setOptions(prevOptions => ({ + setOptions((prevOptions) => ({ ...prevOptions, - ...localOPtions + ...localOPtions, })); }, []); const handleClose = () => { - props.onClose && props.onClose() - } + props.onClose && props.onClose(); + }; const handleReset = () => { - setOptions(prevOptions => ({ + setOptions((prevOptions) => ({ ...prevOptions, - ...DEFAULT_OPTIONS + ...DEFAULT_OPTIONS, })); - handleValueChange() - } + handleValueChange(); + }; const handleConfirm = () => { - window.localStorage.setItem(DISPLAY_KEY, JSON.stringify(options)) - props.onClose && props.onClose() - } + window.localStorage.setItem(DISPLAY_KEY, JSON.stringify(options)); + props.onClose && props.onClose(); + }; const handleValueChange = () => { - props.onValueChange && props.onValueChange(options) - } + props.onValueChange && props.onValueChange(options); + }; return ( - {(onClose) => ( - <> - Display - - { - setOptions(prevOptions => ({ - ...prevOptions, - sharpness: value - })); - handleValueChange() - }} - /> + <> + Display + + { + setOptions((prevOptions) => ({ + ...prevOptions, + sharpness: value, + })); + handleValueChange(); + }} + /> - { - setOptions(prevOptions => ({ - ...prevOptions, - saturation: value - })); - handleValueChange(); - }} - /> + { + setOptions((prevOptions) => ({ + ...prevOptions, + saturation: value, + })); + handleValueChange(); + }} + /> - { - setOptions(prevOptions => ({ - ...prevOptions, - contrast: value - })); - handleValueChange(); - }} - /> + { + setOptions((prevOptions) => ({ + ...prevOptions, + contrast: value, + })); + handleValueChange(); + }} + /> - { - setOptions(prevOptions => ({ - ...prevOptions, - brightness: value - })); - handleValueChange(); - }} - /> - - - - - - - )} + { + setOptions((prevOptions) => ({ + ...prevOptions, + brightness: value, + })); + handleValueChange(); + }} + /> + + + + + + ); diff --git a/renderer/components/FeedbackModal.tsx b/renderer/components/FeedbackModal.tsx index 13f727f..a1ead04 100644 --- a/renderer/components/FeedbackModal.tsx +++ b/renderer/components/FeedbackModal.tsx @@ -8,7 +8,7 @@ import { Button, } from "@nextui-org/react"; import { useTranslation } from "react-i18next"; -import Image from 'next/image'; +import Image from "next/image"; const FeedbackModal = ({ show, onClose }) => { const { t } = useTranslation(); @@ -17,12 +17,15 @@ const FeedbackModal = ({ show, onClose }) => { onClose && onClose(); }; return ( - + <> - - 反馈及支持 - + 反馈及支持

如果你使用过程遇到任何问题或有更好的建议及想法,都可以加入XStreaming使用群进行交流。 @@ -31,15 +34,27 @@ const FeedbackModal = ({ show, onClose }) => { {/*

QQ

*/} -

XStreaming始终坚持开源免费,旨在为Xbox玩家串流玩家提供多一个串流选择,后续还会继续推出其他平台客户端,作者平时也喜欢玩游戏,也是使用业余时间开发软件,如果觉得XStreaming好用,不妨请作者喝杯咖啡,大家的支持就是持续开发维护的动力。

+

+ XStreaming始终坚持开源免费,旨在为Xbox玩家串流玩家提供多一个串流选择,后续还会继续推出其他平台客户端,作者平时也喜欢玩游戏,也是使用业余时间开发软件,如果觉得XStreaming好用,不妨请作者喝杯咖啡,大家的支持就是持续开发维护的动力。 +

微信

- wechat + wechat

支付宝

- alipay + alipay
diff --git a/renderer/components/Loading.tsx b/renderer/components/Loading.tsx index 759e52e..f6fdfbe 100644 --- a/renderer/components/Loading.tsx +++ b/renderer/components/Loading.tsx @@ -1,11 +1,16 @@ -import { Spinner } from "@nextui-org/react" +import { Spinner } from "@nextui-org/react"; const Loading = ({ loadingText }) => { return (
- +
- ) -} + ); +}; -export default Loading \ No newline at end of file +export default Loading; diff --git a/renderer/components/SettingItem.tsx b/renderer/components/SettingItem.tsx index 9728e5c..7dff664 100644 --- a/renderer/components/SettingItem.tsx +++ b/renderer/components/SettingItem.tsx @@ -1,166 +1,162 @@ -import { useEffect, useState, useRef } from "react" -import {Card, CardHeader, CardBody, CardFooter, RadioGroup, Radio, Slider, Autocomplete, AutocompleteItem} from "@nextui-org/react" -import { useSettings } from "../context/userContext" +import { useEffect, useState } from "react"; +import { + Card, + CardBody, + RadioGroup, + Radio, + Slider, + Autocomplete, + AutocompleteItem, +} from "@nextui-org/react"; +import { useSettings } from "../context/userContext"; import Ipc from "../lib/ipc"; const SettingItem = (props) => { - const { settings, setSettings} = useSettings() - console.log('settings:', settings) - const item = props.item || {} + const { settings, setSettings } = useSettings(); + console.log("settings:", settings); + const item = props.item || {}; - const [defaultValue, setDefaultValue] = useState(settings[item.name]) + const [defaultValue, setDefaultValue] = useState(settings[item.name]); useEffect(() => { - if (item.name === 'signaling_cloud' || item.name === 'signaling_home') { - const method = item.name === 'signaling_cloud' ? 'getXcloudToken' : 'getXhomeToken' - Ipc.send('xCloud', method).then(data => { + if (item.name === "signaling_cloud" || item.name === "signaling_home") { + const method = + item.name === "signaling_cloud" ? "getXcloudToken" : "getXhomeToken"; + Ipc.send("xCloud", method).then((data) => { if (data) { - const regions = data.offeringSettings.regions - - item.data = regions.map(region => { + const regions = data.offeringSettings.regions; + + item.data = regions.map((region) => { if (region.isDefault) { - setDefaultValue(region.name) + setDefaultValue(region.name); } return { value: region.name, - label: region.name - } - }) + label: region.name, + }; + }); } else { - item.data = [] + item.data = []; } // console.log('item:', item) - }) + }); } - }, []) + }, []); - const handleChangeSetting = value => { - console.log('handleChangeSetting:', value) - const key = item.name + const handleChangeSetting = (value) => { + console.log("handleChangeSetting:", value); + const key = item.name; if (key) { - if (item.name === 'signaling_cloud' || item.name === 'signaling_home') { - const method = item.name === 'signaling_cloud' ? 'setXcloudTokenDefault' : 'setXhomeTokenDefault' - Ipc.send('xCloud', method, value) + if (item.name === "signaling_cloud" || item.name === "signaling_home") { + const method = + item.name === "signaling_cloud" + ? "setXcloudTokenDefault" + : "setXhomeTokenDefault"; + Ipc.send("xCloud", method, value); } else { setSettings({ ...settings, - [key]: value - }) + [key]: value, + }); } } - console.log('handleChangeSetting:', value) - setDefaultValue(value) - } - + console.log("handleChangeSetting:", value); + setDefaultValue(value); + }; return (
-
{ item.title }
-
{ item.description }
- { - item.type === 'select' && defaultValue !== undefined && ( - { - handleChangeSetting(value) - }} - > - { - item.data.map(i => { - return ( - - {i.label} - - ) - }) - } - - ) - } +
{item.title}
+
{item.description}
+ {item.type === "select" && defaultValue !== undefined && ( + { + handleChangeSetting(value); + }} + > + {item.data.map((i) => { + return ( + {i.label} + ); + })} + + )} + + {item.type === "radio" && ( + { + handleChangeSetting(value); + }} + > + {item.data.map((i) => { + return ( + + {i.label} + + ); + })} + + )} - { - item.type === 'radio' && ( - { - handleChangeSetting(value) + {item.type === "slider" && ( + { + handleChangeSetting(value); + }} + /> + )} + + {item.type === "bitrate" && ( +
+ { + setSettings({ + ...settings, + [item.name + "_mode"]: value, + }); }} > - { - item.data.map(i => { - return ( - {i.label} - ) - }) - } + Auto + Custom - ) - } - - { - item.type === 'slider' && ( - { - handleChangeSetting(value) - }} - /> - ) - } - { - item.type === 'bitrate' && ( -
- { - setSettings({ - ...settings, - [item.name + '_mode']: value - }) + {settings[item.name + "_mode"] === "Custom" && ( + { + handleChangeSetting(value); }} - > - Auto - Custom - - - { - settings[item.name + '_mode'] === 'Custom' && ( - { - handleChangeSetting(value) - }} - /> - ) - } - -
- ) - } + /> + )} +
+ )}
- ) -} + ); +}; -export default SettingItem \ No newline at end of file +export default SettingItem; diff --git a/renderer/components/TitleItem.tsx b/renderer/components/TitleItem.tsx index 01e2f44..a4ac9a1 100644 --- a/renderer/components/TitleItem.tsx +++ b/renderer/components/TitleItem.tsx @@ -1,36 +1,28 @@ -import { useEffect, useState, useRef } from "react"; -import { - Card, - CardBody, - CardFooter, - Image, -} from "@nextui-org/react"; +import { Card, CardBody, CardFooter, Image } from "@nextui-org/react"; function TitleItem(props) { - const titleItem = props.title || {name: '123'} + const titleItem = props.title || { name: "123" }; const handleClick = () => { - props.onClick && props.onClick(titleItem) - } + props.onClick && props.onClick(titleItem); + }; return ( <> - { - titleItem ? ( - - - Card background - - -

{ titleItem.ProductTitle }

-
-
- ) : null - } + {titleItem ? ( + + + Card background + + +

{titleItem.ProductTitle}

+
+
+ ) : null} ); } diff --git a/renderer/components/TitleModal.tsx b/renderer/components/TitleModal.tsx index bd302a2..79a06ea 100644 --- a/renderer/components/TitleModal.tsx +++ b/renderer/components/TitleModal.tsx @@ -1,69 +1,82 @@ -import { useEffect, useState, useRef } from "react"; import { useRouter } from "next/router"; -import {Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button, Image, Chip} from "@nextui-org/react"; +import { + Modal, + ModalContent, + ModalHeader, + ModalBody, + ModalFooter, + Button, + Image, + Chip, +} from "@nextui-org/react"; -const XCLOUD_PREFIX = 'xcloud_' +const XCLOUD_PREFIX = "xcloud_"; function TitleModal(props) { - const router = useRouter() + const router = useRouter(); - const titleItem = props.title || {} + const titleItem = props.title || {}; const handleClose = () => { - console.log('handleClose') - props.onClose && props.onClose() - } + console.log("handleClose"); + props.onClose && props.onClose(); + }; const handleStartGame = () => { - console.log('titleItem:', titleItem) - const titleId = titleItem.titleId || titleItem.XCloudTitleId - router.push('stream/' + XCLOUD_PREFIX + titleId) - } + console.log("titleItem:", titleItem); + const titleId = titleItem.titleId || titleItem.XCloudTitleId; + router.push("stream/" + XCLOUD_PREFIX + titleId); + }; return ( - + - {(onClose) => ( - <> - { titleItem.ProductTitle } - -
-
- -
+ <> + + {titleItem.ProductTitle} + + +
+
+ +
-
-

{ titleItem.PublisherName }

-

{ titleItem.ProductDescription || '' }

-
- { - titleItem.Categories.map((item, idx) => { - return ( - { item } - ) - }) - } -
+
+

{titleItem.PublisherName}

+

{titleItem.ProductDescription || ""}

+
+ {titleItem.Categories.map((item, idx) => { + return ( + + {item} + + ); + })}
- - - - - - - )} +
+ + + + + + ); } -export default TitleModal +export default TitleModal; diff --git a/renderer/i18n.ts b/renderer/i18n.ts index b3fec63..bd3630f 100644 --- a/renderer/i18n.ts +++ b/renderer/i18n.ts @@ -15,8 +15,6 @@ const resources = { jp, }; -let streamSettings: any = {} - const lng = 'en' i18n diff --git a/renderer/pages/gamepad/test.tsx b/renderer/pages/gamepad/test.tsx index 4f0c4d5..1fdc2de 100644 --- a/renderer/pages/gamepad/test.tsx +++ b/renderer/pages/gamepad/test.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useRef } from "react"; import { Button } from "@nextui-org/react"; import { useRouter } from 'next/navigation' -function gamepadTester() { +function GamepadTester() { const router = useRouter() const timer = useRef(null) @@ -120,8 +120,7 @@ function gamepadTester() { const keys = ["index", "id", "connected", "mapping"]; function processController(info) { - const { elem, gamepad, axes, buttons } = info; - const lines = [`gamepad : ${gamepad.index}`]; + const { gamepad, axes, buttons } = info; for (const key of keys) { info[key].textContent = gamepad[key]; } @@ -205,4 +204,4 @@ function gamepadTester() { ); } -export default gamepadTester; +export default GamepadTester; diff --git a/renderer/pages/home.tsx b/renderer/pages/home.tsx index 8dfb55a..eeaa97d 100644 --- a/renderer/pages/home.tsx +++ b/renderer/pages/home.tsx @@ -1,153 +1,166 @@ -import { useEffect, useState, useRef } from "react" +import { useEffect, useState, useRef } from "react"; import { - Button, - Card, - CardBody, - CardFooter, - Divider, - Chip + Button, + Card, + CardBody, + CardFooter, + Divider, + Chip, } from "@nextui-org/react"; -import { useTranslation } from 'react-i18next' -import { useRouter } from 'next/navigation' -import Layout from "../components/Layout" -import AuthModal from "../components/AuthModal" +import { useTranslation } from "react-i18next"; +import { useRouter } from "next/navigation"; +import Layout from "../components/Layout"; +import AuthModal from "../components/AuthModal"; import Ipc from "../lib/ipc"; -import Loading from '../components/Loading' +import Loading from "../components/Loading"; import Nav from "../components/Nav"; -import {useTheme} from "next-themes" - -import Image from 'next/image'; +import Image from "next/image"; function Home() { + const { t } = useTranslation(); - const { t } = useTranslation() - const { theme, setTheme } = useTheme() - - const router = useRouter() - const [loading, setLoading] = useState(false) - const [loadingText, setLoadingText] = useState('') - const [isLogined, setIsLogined] = useState(true) - const [showLoginModal, setShowLoginModal] = useState(false) - const [consoles, setConsoles] = useState([]) + const router = useRouter(); + const [loading, setLoading] = useState(false); + const [loadingText, setLoadingText] = useState(""); + const [isLogined, setIsLogined] = useState(true); + const [showLoginModal, setShowLoginModal] = useState(false); + const [consoles, setConsoles] = useState([]); - const authInterval = useRef(null) + const authInterval = useRef(null); useEffect(() => { - setLoading(true) - setLoadingText('Loading...') - Ipc.send('app', 'checkAuthentication').then(isLogin => { - if (isLogin) { // Silence login, refresh token - console.log('Silence login, refresh token') + setLoading(true); + setLoadingText("Loading..."); + Ipc.send("app", "checkAuthentication").then((isLogin) => { + if (isLogin) { + // Silence login, refresh token + console.log("Silence login, refresh token"); authInterval.current = setInterval(() => { - console.log('Requesting AuthState...') - - Ipc.send('app', 'getAuthState').then(args => { - console.log('Received AuthState:', args) - - if(args.isAuthenticating === true){ - setLoading(true) - } else if(args.isAuthenticated === true && args.user.signedIn === true){ - clearInterval(authInterval.current) - setIsLogined(true) - - // Get Consoles - setLoadingText('Fetching consoles...') - Ipc.send('consoles', 'get').then(res => { - console.log('consoles:', res) - setConsoles(res) - setLoading(false) - }) - } - }) - }, 500) + console.log("Requesting AuthState..."); + + Ipc.send("app", "getAuthState").then((args) => { + console.log("Received AuthState:", args); + + if (args.isAuthenticating === true) { + setLoading(true); + } else if ( + args.isAuthenticated === true && + args.user.signedIn === true + ) { + clearInterval(authInterval.current); + setIsLogined(true); + + // Get Consoles + setLoadingText("Fetching consoles..."); + Ipc.send("consoles", "get").then((res) => { + console.log("consoles:", res); + setConsoles(res); + setLoading(false); + }); + } + }); + }, 500); } else { - console.log('Full auth flow') - setLoading(false) - setShowLoginModal(true) + console.log("Full auth flow"); + setLoading(false); + setShowLoginModal(true); } - }) - + }); + return () => { - if(authInterval.current) clearInterval(authInterval.current) - } - }, []) + if (authInterval.current) clearInterval(authInterval.current); + }; + }, []); const handleLogin = () => { - setLoading(true) - setLoadingText('Loading...') - Ipc.send('app', 'login').then(() => { - setShowLoginModal(false) + setLoading(true); + setLoadingText("Loading..."); + Ipc.send("app", "login").then(() => { + setShowLoginModal(false); // Check login state authInterval.current = setInterval(() => { - console.log('Requesting AuthState...') - Ipc.send('app', 'getAuthState').then(args => { - console.log('Received AuthState:', args) - - if(args.isAuthenticating === true){ - setLoading(true) - } else if(args.isAuthenticated === true && args.user.signedIn === true){ - clearInterval(authInterval.current) - setIsLogined(true) - window.sessionStorage.setItem('isLogined', '1') - setLoading(false) - - // Get Consoles - setLoadingText('Fetching consoles...') - Ipc.send('consoles', 'get').then(res => { - console.log('consoles:', res) - setConsoles(res) - }) - } - }) - }, 500) - }) - } + console.log("Requesting AuthState..."); + Ipc.send("app", "getAuthState").then((args) => { + console.log("Received AuthState:", args); + + if (args.isAuthenticating === true) { + setLoading(true); + } else if ( + args.isAuthenticated === true && + args.user.signedIn === true + ) { + clearInterval(authInterval.current); + setIsLogined(true); + window.sessionStorage.setItem("isLogined", "1"); + setLoading(false); + + // Get Consoles + setLoadingText("Fetching consoles..."); + Ipc.send("consoles", "get").then((res) => { + console.log("consoles:", res); + setConsoles(res); + }); + } + }); + }, 500); + }); + }; const startSession = (sessionId) => { - console.log('sessionId:', sessionId) - router.push('stream/' + sessionId) - } + console.log("sessionId:", sessionId); + router.push("stream/" + sessionId); + }; return ( <> -