feat: 接入Bmob储存自定义配置

This commit is contained in:
streakingman 2022-09-25 02:56:32 +08:00
parent e35ddfa44e
commit f6681cabb0
8 changed files with 2064 additions and 75 deletions

2
.env Normal file
View File

@ -0,0 +1,2 @@
VITE_BMOB_SECRETKEY=yourBmobAppSecretKey
VITE_BMOB_SECCODE=youBmobAppSecurityCode

View File

@ -22,6 +22,10 @@
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<script>
// vite没有global手动声明
var global = global || window;
</script>
<script type="module" src="/src/main.tsx"></script> <script type="module" src="/src/main.tsx"></script>
<script <script
async async
@ -30,19 +34,26 @@
<script async> <script async>
// 如果您基于此项目二创,可以删除以下代码 // 如果您基于此项目二创,可以删除以下代码
// 否则请标明原仓库地址 // 否则请标明原仓库地址
setTimeout(()=>{ setTimeout(() => {
const {hostname} = location const { hostname } = location;
if(hostname!=='localhost'&&!hostname.endsWith('streakingman.com')){ if (
const a = document.createElement('a') hostname !== 'localhost' &&
a.setAttribute('href','https://github.com/StreakingMan/solvable-sheep-game') !hostname.endsWith('streakingman.com')
a.setAttribute('target','_blank') ) {
a.innerText='本项目仅供交流禁止商业用途点击查看原github仓库' const a = document.createElement('a');
const p = document.createElement('p') a.setAttribute(
p.style.textAlign = 'center' 'href',
p.append(a) 'https://github.com/StreakingMan/solvable-sheep-game'
document.body.prepend(p) );
a.setAttribute('target', '_blank');
a.innerText =
'本项目仅供交流禁止商业用途点击查看原github仓库';
const p = document.createElement('p');
p.style.textAlign = 'center';
p.append(a);
document.body.prepend(p);
} }
},5000) }, 5000);
</script> </script>
</body> </body>
</html> </html>

View File

@ -13,6 +13,7 @@
}, },
"dependencies": { "dependencies": {
"classnames": "^2.3.2", "classnames": "^2.3.2",
"hydrogen-js-sdk": "^2.3.10",
"qrcode.react": "^3.1.0", "qrcode.react": "^3.1.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0" "react-dom": "^18.2.0"

View File

@ -9,7 +9,7 @@ import React, {
import './App.scss'; import './App.scss';
import { GithubIcon } from './components/GithubIcon'; import { GithubIcon } from './components/GithubIcon';
import { import {
parsePathCustomTheme, parsePathCustomThemeId,
parsePathThemeName, parsePathThemeName,
randomString, randomString,
waitTimeout, waitTimeout,
@ -24,6 +24,7 @@ import { BeiAn } from './components/BeiAn';
import { Info } from './components/Info'; import { Info } from './components/Info';
import { owTheme } from './themes/ow'; import { owTheme } from './themes/ow';
import { ConfigDialog } from './components/ConfigDialog'; import { ConfigDialog } from './components/ConfigDialog';
import Bmob from 'hydrogen-js-sdk';
// 内置主题 // 内置主题
const builtInThemes: Theme<any>[] = [ const builtInThemes: Theme<any>[] = [
@ -159,7 +160,7 @@ const Symbol: FC<SymbolProps> = ({ x, y, icon, isCover, status, onClick }) => {
> >
<div <div
className="symbol-inner" className="symbol-inner"
style={{ opacity: isCover ? 0.5 : 1 }} style={{ opacity: isCover ? 0.4 : 1 }}
> >
{typeof icon.content === 'string' ? ( {typeof icon.content === 'string' ? (
icon.content.startsWith('http') ? ( icon.content.startsWith('http') ? (
@ -180,7 +181,7 @@ const Symbol: FC<SymbolProps> = ({ x, y, icon, isCover, status, onClick }) => {
// 从url初始化主题 // 从url初始化主题
const themeFromPath: string = parsePathThemeName(location.href); const themeFromPath: string = parsePathThemeName(location.href);
const customThemeFromPath = parsePathCustomTheme(location.href); const customThemeIdFromPath = parsePathCustomThemeId(location.href);
const App: FC = () => { const App: FC = () => {
const [curTheme, setCurTheme] = useState<Theme<any>>(defaultTheme); const [curTheme, setCurTheme] = useState<Theme<any>>(defaultTheme);
@ -216,10 +217,25 @@ const App: FC = () => {
// 初始化主题 // 初始化主题
useEffect(() => { useEffect(() => {
if (customThemeFromPath) { if (customThemeIdFromPath) {
// 自定义主题 // 自定义主题
setThemes([...themes, customThemeFromPath]); Bmob.Query('config')
setCurTheme(customThemeFromPath); .get(customThemeIdFromPath)
.then((res) => {
// @ts-ignore
const { content } = res;
try {
const customTheme = JSON.parse(content);
setThemes([...themes, customTheme]);
setCurTheme(customTheme);
} catch (e) {
console.log(e);
}
})
.catch((e) => {
console.log(e);
});
} else if (themeFromPath) { } else if (themeFromPath) {
// 内置主题 // 内置主题
setCurTheme( setCurTheme(
@ -240,7 +256,7 @@ const App: FC = () => {
} }
restart(); restart();
// 更改路径query // 更改路径query
if (customThemeFromPath) return; if (customThemeIdFromPath) return;
history.pushState( history.pushState(
{}, {},
curTheme.title, curTheme.title,

View File

@ -4,6 +4,7 @@ import classNames from 'classnames';
import { Icon, Sound, Theme } from '../themes/interface'; import { Icon, Sound, Theme } from '../themes/interface';
import { defaultSounds } from '../themes/default'; import { defaultSounds } from '../themes/default';
import { QRCodeSVG } from 'qrcode.react'; import { QRCodeSVG } from 'qrcode.react';
import Bmob from 'hydrogen-js-sdk';
const STORAGEKEY = 'customTheme'; const STORAGEKEY = 'customTheme';
let storageTheme: Theme<any>; let storageTheme: Theme<any>;
@ -78,7 +79,7 @@ export const ConfigDialog: FC<{
// 音效保存 // 音效保存
const saveSound = (sound: Sound, idx?: number) => { const saveSound = (sound: Sound, idx?: number) => {
if (!sound.src.startsWith('http')) return '请输入http/https链接'; if (!sound.src.startsWith('https')) return '请输入https链接';
const newSounds = sounds.slice(); const newSounds = sounds.slice();
const newIcons = icons.slice(); const newIcons = icons.slice();
if (idx != null) { if (idx != null) {
@ -123,9 +124,9 @@ export const ConfigDialog: FC<{
const saveIcon = (icon: Icon, idx?: number) => { const saveIcon = (icon: Icon, idx?: number) => {
if ( if (
typeof icon.content !== 'string' || typeof icon.content !== 'string' ||
!icon.content?.startsWith('http') !icon.content?.startsWith('https')
) )
return '请输入http/https链接'; return '请输入https链接';
const newIcons = icons.slice(); const newIcons = icons.slice();
if (idx != null) { if (idx != null) {
// 编辑 // 编辑
@ -202,8 +203,8 @@ export const ConfigDialog: FC<{
// 生成主题 // 生成主题
const generateTheme: () => Promise<Theme<any>> = async () => { const generateTheme: () => Promise<Theme<any>> = async () => {
const { title, desc, bgm } = customThemeInfo; const { title, desc, bgm } = customThemeInfo;
if (bgm && bgm.startsWith('http')) if (bgm && bgm.startsWith('https'))
return Promise.reject('bgm请输入http/https链接'); return Promise.reject('bgm请输入https链接');
if (!title) return Promise.reject('请填写标题'); if (!title) return Promise.reject('请填写标题');
if (icons.length !== 10) return Promise.reject('图片素材需要提供10张'); if (icons.length !== 10) return Promise.reject('图片素材需要提供10张');
@ -258,10 +259,18 @@ export const ConfigDialog: FC<{
.then((theme) => { .then((theme) => {
const stringify = JSON.stringify(theme); const stringify = JSON.stringify(theme);
localStorage.setItem(STORAGEKEY, stringify); localStorage.setItem(STORAGEKEY, stringify);
const link = `${ const query = Bmob.Query('config');
location.origin query.set('content', stringify);
}?customTheme=${encodeURIComponent(stringify)}`; query
setGenLink(link); .save()
.then((res) => {
//@ts-ignore
const link = `${location.origin}?customTheme=${res.objectId}`;
setGenLink(link);
})
.catch((e) => {
console.log(e);
});
}) })
.catch((e) => { .catch((e) => {
setConfigError(e); setConfigError(e);
@ -302,8 +311,8 @@ export const ConfigDialog: FC<{
)} )}
> >
<p> <p>
https链/
<strong></strong> mp3外链
</p> </p>
{/*基本配置*/} {/*基本配置*/}
@ -339,7 +348,7 @@ export const ConfigDialog: FC<{
<input <input
value={customThemeInfo.bgm} value={customThemeInfo.bgm}
placeholder="可选 http(s)://example.com/src.audioOrImage" placeholder="可选 https://example.com/src.audioOrImage"
className="flex-grow" className="flex-grow"
onChange={(e) => onChange={(e) =>
setCustomThemeInfo({ setCustomThemeInfo({
@ -373,7 +382,7 @@ export const ConfigDialog: FC<{
/> />
)} )}
</div> </div>
<h4> {icons.length}/10</h4> <h4> {icons.length}/10 </h4>
<div className="flex-container"> <div className="flex-container">
{icons.map((icon, idx) => ( {icons.map((icon, idx) => (
<div className="flex-container flex-column" key={icon.name}> <div className="flex-container flex-column" key={icon.name}>
@ -440,7 +449,7 @@ export const ConfigDialog: FC<{
<input <input
ref={(ref) => ref && (inputRefMap.current.link = ref)} ref={(ref) => ref && (inputRefMap.current.link = ref)}
className="flex-grow" className="flex-grow"
placeholder="http(s)://example.com/src.audioOrImage" placeholder="https://example.com/src.audioOrImage"
onChange={(e) => onChange={(e) =>
setAddDialog({ setAddDialog({
...addDialog, ...addDialog,

View File

@ -3,6 +3,14 @@ import ReactDOM from 'react-dom/client';
import App from './App'; import App from './App';
import './styles/global.scss'; import './styles/global.scss';
import './styles/utils.scss'; import './styles/utils.scss';
import Bmob from 'hydrogen-js-sdk';
// Bmob初始化
// @ts-ignore
Bmob.initialize(
import.meta.env.VITE_BMOB_SECRETKEY,
import.meta.env.VITE_BMOB_SECCODE
);
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode> <React.StrictMode>

View File

@ -26,22 +26,8 @@ export const parsePathThemeName: (url: string) => string = (url) => {
}; };
// 从url解析自定义主题JSON // 从url解析自定义主题JSON
export const parsePathCustomTheme: (url: string) => Theme<string> | null = ( export const parsePathCustomThemeId: (url: string) => string = (url) => {
url
) => {
const urlObj = new URL(url); const urlObj = new URL(url);
const params = urlObj.searchParams; const params = urlObj.searchParams;
const customThemeJsonString = params.get('customTheme'); return params.get('customTheme') || '';
if (!customThemeJsonString) return null;
try {
const parseTheme = JSON.parse(
decodeURIComponent(customThemeJsonString)
);
// TODO 解析内容校验
console.log(parseTheme);
return parseTheme;
} catch (e) {
console.log(e);
return null;
}
}; };

2008
yarn.lock

File diff suppressed because it is too large Load Diff