♻️ 姑且能跑 dev,尚需调试功能

This commit is contained in:
目棃
2024-07-02 23:05:33 +08:00
parent 1214501691
commit 367307029b
66 changed files with 12626 additions and 2887 deletions

View File

@@ -1,13 +1,13 @@
/**
* @file utils/TGClient.ts
* @desc 负责米游社客户端的 callback 处理
* @since Beta v0.4.4
* @since Beta v0.5.0
*/
import { event, invoke } from "@tauri-apps/api";
import { event, core } from "@tauri-apps/api";
import type { Event } from "@tauri-apps/api/event";
import type { UnlistenFn } from "@tauri-apps/api/helpers/event";
import { appWindow, WebviewWindow } from "@tauri-apps/api/window";
import type { UnlistenFn } from "@tauri-apps/api/event";
import { Window } from "@tauri-apps/api/window";
import showSnackbar from "../components/func/snackbar.js";
import TGSqlite from "../plugins/Sqlite/index.js";
@@ -44,11 +44,11 @@ class TGClient {
/**
* @private 窗口实例
* @since Beta v0.3.4
* @type {WebviewWindow}
* @since Beta v0.5.0
* @type {Window}
* @memberof TGClient
*/
private window: WebviewWindow | null;
private window: Window | null;
/**
* @private 模拟路由
@@ -66,7 +66,7 @@ class TGClient {
*/
constructor() {
try {
this.window = WebviewWindow.getByLabel("mhy_client");
this.window = Window.getByLabel("mhy_client");
} catch (error) {
this.window = null;
}
@@ -94,7 +94,7 @@ class TGClient {
/**
* @func callback
* @since Beta v0.4.2
* @since Beta v0.5.0
* @desc 回调函数
* @param {string} callback - 回调函数名
* @param {object} data - 回调数据
@@ -108,7 +108,7 @@ class TGClient {
};
const js = `javascript:mhyWebBridge("${callback}", ${JSON.stringify(response)});`;
console.info(`[callback] ${js}`);
await invoke("execute_js", { label: "mhy_client", js });
await core.invoke("execute_js", { label: "mhy_client", js });
}
/**
@@ -270,13 +270,13 @@ class TGClient {
await this.nullCallback(<TGApp.Plugins.JSBridge.NullArg>argParse);
break;
default:
console.warn(`[${arg.windowLabel}] ${JSON.stringify(argParse)}`);
console.warn(`[${arg.event}] ${JSON.stringify(argParse)}`);
}
}
/**
* @func handleCustomCallback
* @since Beta v0.4.3
* @since Beta v0.5.0
* @desc 处理自定义的 callback
* @param {TGApp.Plugins.JSBridge.Arg<any>} arg - 事件参数
* @returns {Promise<void>} - 返回值
@@ -294,7 +294,7 @@ class TGClient {
const executeJS = `javascript:(function(){
window.location.reload();
})();`;
await invoke("execute_js", { label: "mhy_client", js: executeJS });
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
await this.loadJSBridge();
break;
}
@@ -380,7 +380,7 @@ class TGClient {
};
document.addEventListener("mousedown", mouseDownListener);
})()`;
await invoke("execute_js", { label: "mhy_client", js: executeJS });
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
break;
}
default:
@@ -390,7 +390,7 @@ class TGClient {
/**
* @func hideOverlay
* @since Beta v0.3.7
* @since Beta v0.5.0
* @desc 隐藏遮罩
* @returns {Promise<void>}
*/
@@ -401,12 +401,12 @@ class TGClient {
box.remove();
}
})();`;
await invoke("execute_js", { label: "mhy_client", js: executeJS });
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
}
/**
* @func hideSideBar
* @since Beta v0.3.5
* @since Beta v0.5.0
* @desc 隐藏侧边栏
* @returns {void} - 无返回值
*/
@@ -421,12 +421,12 @@ class TGClient {
document.querySelector('body').appendChild(style);
}
})();`;
await invoke("execute_js", { label: "mhy_client", js: executeJS });
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
}
/**
* @func loadJSBridge
* @since Beta v0.3.8
* @since Beta v0.5.0
* @desc 加载 JSBridge
* @returns {void} - 无返回值
*/
@@ -438,12 +438,12 @@ class TGClient {
closePage: function() { this.postMessage('{"method":"closePage"}') },
};
})();`;
await invoke("execute_js", { label: "mhy_client", js: executeJS });
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
}
/**
* @func loadSignIn
* @since Beta v0.4.4
* @since Beta v0.5.0
* @desc 自动检测登录ck
* @returns {Promise<void>}
*/
@@ -465,7 +465,7 @@ class TGClient {
}
}
})();`;
await invoke("execute_js", { label: "mhy_client", js: executeJS });
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
}
/**
@@ -481,7 +481,7 @@ class TGClient {
/**
* @func open
* @since Beta v0.4.4
* @since Beta v0.5.0
* @desc 打开米游社客户端
* @param {string} func - 方法名
* @param {string} url - url
@@ -491,8 +491,8 @@ class TGClient {
if (url === undefined) url = this.getUrl(func);
this.route = [url];
await TGLogger.Info(`[TGClient][open][${func}] ${url}`);
await invoke<InvokeArg>("create_mhy_client", { func, url });
this.window = WebviewWindow.getByLabel("mhy_client");
await core.invoke<InvokeArg>("create_mhy_client", { func, url });
this.window = Window.getByLabel("mhy_client");
await this.window?.show();
await this.window?.setFocus();
await this.loadJSBridge();
@@ -504,7 +504,7 @@ class TGClient {
/* JSBridge 回调处理 */
/**
* @func closePage
* @since Beta v0.3.9
* @since Beta v0.5.0
* @desc 关闭米游社客户端的页面
* @param {TGApp.Plugins.JSBridge.NullArg} arg - 请求参数
* @returns {void} - 无返回值
@@ -520,7 +520,7 @@ class TGClient {
const executeJS = `javascript:(function(){
window.location.href = '${url}';
})();`;
await invoke("execute_js", { label: "mhy_client", js: executeJS });
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
await this.loadJSBridge();
}
@@ -618,7 +618,7 @@ class TGClient {
/**
* @func getCookieToken
* @since Beta v0.4.0
* @since Beta v0.5.0
* @desc 获取米游社客户端的 cookie_token
* @param {TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.GetCookieTokenPayload>} arg - 请求参数
* @returns {void} - 无返回值
@@ -648,7 +648,7 @@ class TGClient {
document.cookie = "ltmid_v2=${user.cookie.mid};domain=.mihoyo.com;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;";
})();`;
console.info(`[getCookieToken] ${executeJS}`);
await invoke("execute_js", { label: "mhy_client", js: executeJS });
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
const data = {
cookie_token: user.cookie.cookie_token,
};
@@ -744,7 +744,7 @@ class TGClient {
/**
* @func onClickImg
* @since Beta v0.3.9
* @since Beta v0.5.0
* @desc 点击图片,下载到本地
* @param {TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.OnClickImgPayload>} arg - 方法参数
* @returns {void} - 无返回值
@@ -755,12 +755,12 @@ class TGClient {
const { image_list } = arg.payload;
const image = image_list[0];
const executeJS = this.getSaveImgJS(image.url, image.format);
await invoke("execute_js", { label: "mhy_client", js: executeJS });
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
}
/**
* @func openApplication
* @since Beta v0.3.9
* @since Beta v0.5.0
* @desc 打开应用
* @param {TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.OpenApplicationPayload>} arg - 方法参数
* @returns {void} - 无返回值
@@ -769,7 +769,8 @@ class TGClient {
arg: TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.OpenApplicationPayload>,
): Promise<void> {
console.log(`[openApplication] ${JSON.stringify(arg.payload)}`);
await appWindow.setFocus();
const appWindow = Window.getByLabel("TeyvatGuide");
await appWindow?.setFocus();
showSnackbar({
text: `不支持的操作OpenApplication(${JSON.stringify(arg.payload)})`,
color: "error",
@@ -784,7 +785,7 @@ class TGClient {
/**
* @func pushPage
* @since Beta v0.3.9
* @since Beta v0.5.0
* @desc 打开米游社客户端的页面
* @param {TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.PushPagePayload>} arg - 方法参数
* @returns {Promise<void>} - 无返回值
@@ -794,7 +795,8 @@ class TGClient {
): Promise<void> {
const res = await parseLink(arg.payload.page, true);
if (!res) {
await appWindow.setFocus();
const appWindow = Window.getByLabel("TeyvatGuide");
await appWindow?.setFocus();
showSnackbar({
text: `未知链接:${arg.payload.page}`,
color: "error",
@@ -814,7 +816,7 @@ class TGClient {
const executeJS = `javascript:(function(){
window.location.href = '${res}';
})();`;
await invoke("execute_js", { label: "mhy_client", js: executeJS });
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
await this.loadJSBridge();
await this.hideSideBar();
await this.hideOverlay();
@@ -838,7 +840,7 @@ class TGClient {
/**
* @func share
* @since Beta v0.4.4
* @since Beta v0.5.0
* @desc 分享
* @param {TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.SharePayload>} arg - 方法参数
* @returns {Promise<void>} - 无返回值
@@ -849,7 +851,7 @@ class TGClient {
const image = arg.payload.content.image_url;
const format = image.split(".").pop();
const executeJS = this.getSaveImgJS(image, format ?? "png");
await invoke("execute_js", { label: "mhy_client", js: executeJS });
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
await this.callback(arg.callback, {});
return;
}
@@ -924,7 +926,7 @@ class TGClient {
}
mhyWebBridge("${arg.callback}", {});
})();`;
await invoke("execute_js", { label: "mhy_client", js: executeJS });
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
return;
}
if (arg.payload.type === "image") {
@@ -952,7 +954,7 @@ class TGClient {
}
mhyWebBridge('${arg.callback}', {});
})();`;
await invoke("execute_js", { label: "mhy_client", js: executeJS });
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
return;
}
}

62
src/utils/TGHttp.ts Normal file
View File

@@ -0,0 +1,62 @@
/**
* @file utils/TGHttp.ts
* @description 封装HTTP请求
* @since Beta v0.5.0
*/
import { fetch } from "@tauri-apps/plugin-http";
/**
* @description 请求参数
* @since Beta v0.5.0
* @property {"GET"|"POST"} method 请求方法
* @property {Record<string,string>} headers 请求头
* @property {Record<string,string>} query 请求参数
* @property {string} body 请求体
* @return TGHttpParams
*/
type TGHttpParams = {
method: "GET" | "POST";
headers?: Record<string, string>;
query?: Record<string, any>;
body?: string;
};
/**
* @description 发送请求
* @since Beta v0.5.0
* @template T
* @param {string} url 请求地址
* @param {TGHttpParams} options 请求参数
* @returns {Promise<T>}
*/
async function TGHttp<T>(url: string, options: TGHttpParams): Promise<T> {
let httpHeaders = new Headers();
if (options.headers) {
httpHeaders = new Headers(options.headers);
}
const fetchOptions: RequestInit = {
method: options.method,
headers: httpHeaders,
};
if (options.query) {
const query = new URLSearchParams(options.query).toString();
url += `?${query}`;
}
if (options.body) {
fetchOptions.body = options.body;
}
return await fetch(url, fetchOptions)
.then((res) => {
if (res.ok) {
return res.json();
}
throw new Error(`HTTP error! status: ${res.status}`);
})
.catch((err) => {
console.error("HTTP error: ", err);
return err;
});
}
export default TGHttp;

View File

@@ -1,22 +1,24 @@
/**
* @file utils/TGShare.ts
* @description 生成分享截图并保存到本地
* @since Beta v0.4.10
* @since Beta v0.5.0
*/
import { dialog, fs, http, path } from "@tauri-apps/api";
import type { Response } from "@tauri-apps/api/http";
import { path } from "@tauri-apps/api";
import { save } from "@tauri-apps/plugin-dialog";
import { writeFile } from "@tauri-apps/plugin-fs";
import html2canvas from "html2canvas";
import showConfirm from "../components/func/confirm.js";
import showSnackbar from "../components/func/snackbar.js";
import TGHttp from "./TGHttp.js";
import TGLogger from "./TGLogger.js";
import { bytesToSize } from "./toolFunc.js";
/**
* @description 保存图片-canvas
* @since Beta v0.4.10
* @since Beta v0.5.0
* @param {Uint8Array} buffer - 图片数据
* @param {string} filename - 文件名
* @param {string} format - 文件格式
@@ -28,7 +30,7 @@ export async function saveCanvasImg(
format?: string,
): Promise<void> {
if (format === undefined) format = "png";
const res = await dialog.save({
const res = await save({
title: "保存图片",
filters: [{ name: "图片", extensions: [format] }],
defaultPath: `${await path.downloadDir()}${path.sep}${filename}.${format}`,
@@ -37,38 +39,32 @@ export async function saveCanvasImg(
await TGLogger.Info(`[saveCanvasImg][${filename}] 未选择保存路径`);
return;
}
await fs.writeBinaryFile({ path: res, contents: buffer });
await writeFile(res, buffer);
await TGLogger.Info(`[saveCanvasImg][${filename}] 已将图像保存到本地`);
}
/**
* @description 将图片保存到本地
* @since Beta v0.4.10
* @since Beta v0.5.0
* @param {string} url - 图片链接
* @returns {Promise<string>} 图片元素
*/
export async function saveImgLocal(url: string): Promise<string> {
const res: Response<Uint8Array> = await http.fetch(url, {
method: "GET",
responseType: http.ResponseType.Binary,
});
const buffer = new Uint8Array(res.data);
const res = await TGHttp<Uint8Array>(url, { method: "GET" });
const buffer = new Uint8Array(res);
const blob = new Blob([buffer], { type: "image/png" });
return URL.createObjectURL(blob);
}
/**
* @description 返回图片 buffer
* @since Beta v0.4.10
* @since Beta v0.5.0
* @param {string} url - 图片链接
* @returns {Promise<Uint8Array>} 图片 buffer
*/
export async function getImageBuffer(url: string): Promise<Uint8Array> {
const res: Response<Uint8Array> = await http.fetch(url, {
method: "GET",
responseType: http.ResponseType.Binary,
});
return new Uint8Array(res.data);
const res = await TGHttp<Uint8Array>(url, { method: "GET" });
return new Uint8Array(res);
}
/**

View File

@@ -1,18 +1,19 @@
/**
* @file utils/TGWindow.ts
* @description 窗口创建相关工具函数
* @since Beta v0.4.2
* @since Beta v0.5.0
*/
import { invoke, window as TauriWindow } from "@tauri-apps/api";
import { core, window as TauriWindow } from "@tauri-apps/api";
import type { WindowOptions } from "@tauri-apps/api/window";
import TGLogger from "./TGLogger.js";
/**
* @description 创建TG窗口
* @since Beta v0.3.4
* @since Beta v0.5.0
* @see https://github.com/tauri-apps/tauri/issues/5380
* @todo 需要根据 2.0 版本的 Tauri API 进行修改
* @param {string} url 窗口地址
* @param {string} label 窗口标签
* @param {string} title 窗口标题
@@ -38,7 +39,7 @@ export function createTGWindow(
height,
width,
resizable,
url,
// url,
title,
visible,
x: left,
@@ -46,7 +47,8 @@ export function createTGWindow(
};
const isGet = TauriWindow.WebviewWindow.getByLabel(label);
if (isGet === null) {
invoke("create_window", { label, option })
core
.invoke("create_window", { label, option })
.then(() => {
createTGWindow(url, label, title, width, height, resizable, visible);
})
@@ -57,7 +59,8 @@ export function createTGWindow(
isGet
.close()
.then(() => {
invoke("create_window", { label, option })
core
.invoke("create_window", { label, option })
.then(() => {
console.log(`[createTGWindow][${label}] ${title} created.`);
})

View File

@@ -1,11 +1,11 @@
/**
* @file utils/dataBS.ts
* @description 用户数据的备份、恢复、迁移
* @since Beta v0.4.10
* @since Beta v0.5.0
*/
import { fs, path } from "@tauri-apps/api";
import { FileEntry } from "@tauri-apps/api/fs";
import { path } from "@tauri-apps/api";
import { exists, mkdir, writeTextFile, readDir, readTextFile } from "@tauri-apps/plugin-fs";
import showSnackbar from "../components/func/snackbar.js";
import TGSqlite from "../plugins/Sqlite/index.js";
@@ -17,23 +17,23 @@ import { exportUigfData, readUigfData, verifyUigfData } from "./UIGF.js";
/**
* @description 备份用户数据
* @since Beta v0.4.7
* @since Beta v0.5.0
* @param {string} dir 备份目录路径
* @returns {Promise<void>}
*/
export async function backUpUserData(dir: string): Promise<void> {
if (!(await fs.exists(dir))) {
if (!(await exists(dir))) {
console.log("备份目录不存在,创建备份目录");
await fs.createDir(dir, { recursive: true });
await mkdir(dir, { recursive: true });
}
const dataAchi = await TSUserAchi.getUIAF();
await fs.writeTextFile(`${dir}${path.sep}UIAF.json`, JSON.stringify(dataAchi));
await writeTextFile(`${dir}${path.sep}UIAF.json`, JSON.stringify(dataAchi));
// 备份 ck
const dataCK = await TGSqlite.getCookie();
await fs.writeTextFile(`${dir}${path.sep}cookie.json`, JSON.stringify(dataCK));
await writeTextFile(`${dir}${path.sep}cookie.json`, JSON.stringify(dataCK));
// 备份深渊数据
const dataAbyss = await TGSqlite.getAbyss();
await fs.writeTextFile(`${dir}${path.sep}abyss.json`, JSON.stringify(dataAbyss));
await writeTextFile(`${dir}${path.sep}abyss.json`, JSON.stringify(dataAbyss));
// 备份祈愿数据
const uidList = await TSUserGacha.getUidList();
for (const uid of uidList) {
@@ -45,28 +45,28 @@ export async function backUpUserData(dir: string): Promise<void> {
/**
* @description 恢复用户数据
* @since Beta v0.4.7
* @since Beta v0.5.0
* @param {string} dir 备份目录路径
* @returns {Promise<void>}
*/
export async function restoreUserData(dir: string): Promise<void> {
let errNum = 0;
if (!(await fs.exists(dir))) {
if (!(await exists(dir))) {
showSnackbar({
text: "备份目录不存在",
color: "error",
});
return;
}
const filesRead = await fs.readDir(dir, { recursive: false });
const files: FileEntry[] = filesRead.filter((item: FileEntry) => item.path.endsWith(".json"));
const filesRead = await readDir(dir);
const files = filesRead.filter((item) => item.isFile && item.name.endsWith(".json"));
await TGLogger.Info(`[DataBS][restoreUserData] files: ${JSON.stringify(files)}`);
// 恢复成就数据
const achiFind = files.find((item) => item.name === "UIAF.json");
if (achiFind) {
try {
const dataAchi: TGApp.Plugins.UIAF.Achievement[] = JSON.parse(
await fs.readTextFile(achiFind.path),
await readTextFile(achiFind.name),
);
await TSUserAchi.mergeUIAF(dataAchi);
await TGLogger.Info(`[DataBS][restoreUserData] 成就数据恢复成功`);
@@ -90,7 +90,7 @@ export async function restoreUserData(dir: string): Promise<void> {
const ckFind = files.find((item) => item.name === "cookie.json");
if (ckFind) {
try {
const dataCK = await fs.readTextFile(ckFind.path);
const dataCK = await readTextFile(ckFind.name);
await TGSqlite.saveAppData("cookie", JSON.stringify(JSON.parse(dataCK)));
await TGLogger.Info(`[DataBS][restoreUserData] Cookie 数据恢复成功`);
} catch (e) {
@@ -114,7 +114,7 @@ export async function restoreUserData(dir: string): Promise<void> {
if (abyssFind) {
try {
const dataAbyss: TGApp.Sqlite.Abyss.SingleTable[] = JSON.parse(
await fs.readTextFile(abyssFind.path),
await readTextFile(abyssFind.name),
);
await TGSqlite.restoreAbyss(dataAbyss);
} catch (e) {
@@ -134,14 +134,14 @@ export async function restoreUserData(dir: string): Promise<void> {
}
// 恢复祈愿数据
const reg = /UIGF_(\d+).json/;
const dataGachaList = files.filter((item) => reg.test(item.path));
const dataGachaList = files.filter((item) => reg.test(item.name));
for (const item of dataGachaList) {
const check = await verifyUigfData(item.path);
const check = await verifyUigfData(item.name);
if (!check) {
errNum++;
continue;
}
const data = await readUigfData(item.path);
const data = await readUigfData(item.name);
const uid = data.info.uid;
try {
await TSUserGacha.mergeUIGF(uid, data.list);

View File

@@ -1,10 +1,11 @@
/**
* @file utils/toolFunc.ts
* @description 一些工具函数
* @since Beta v0.4.1
* @since Beta v0.5.0
*/
import { os, path } from "@tauri-apps/api";
import { path } from "@tauri-apps/api";
import { type } from "@tauri-apps/plugin-os";
import colorConvert from "color-convert";
import type { KEYWORD } from "color-convert/conversions.js";
import { v4 } from "uuid";
@@ -108,17 +109,18 @@ export function bytesToSize(bytes: number): string {
/**
* @description 获取缓存目录
* @since Beta v0.3.4
* @since Beta v0.5.0
* @returns {string|string[]} 缓存目录
*/
export async function getCacheDir(): Promise<string[] | false> {
const cacheDir = await path.appCacheDir();
const osType = await os.type();
if (osType === "Windows_NT") {
const osType = type().toLowerCase();
if (osType === "windows") {
const cache = `${cacheDir}EBWebview${path.sep}Default${path.sep}Cache`;
const codeCache = `${cacheDir}EBWebview${path.sep}Default${path.sep}Code Cache`;
return [cache, codeCache];
} else if (osType === "Darwin") {
}
if (osType === "macos") {
return [`${cacheDir}WebKit`];
}
return false;