mirror of
https://ghproxy.com/https://github.com/StreakingMan/solvable-sheep-game
synced 2025-05-23 05:40:20 +08:00
feat: 接入Bmob储存自定义配置
This commit is contained in:
parent
e35ddfa44e
commit
f6681cabb0
2
.env
Normal file
2
.env
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
VITE_BMOB_SECRETKEY=yourBmobAppSecretKey
|
||||||
|
VITE_BMOB_SECCODE=youBmobAppSecurityCode
|
35
index.html
35
index.html
|
@ -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>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
30
src/App.tsx
30
src/App.tsx
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>
|
||||||
|
|
18
src/utils.ts
18
src/utils.ts
|
@ -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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user