123 lines
2.8 KiB
TypeScript
123 lines
2.8 KiB
TypeScript
|
|
import { invoke } from "@tauri-apps/api";
|
||
|
|
import { JSONRPCClient, S3SIService, StdioTransport } from "jsonrpc";
|
||
|
|
import { ExportOpts, Log, State } from "jsonrpc/types";
|
||
|
|
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
|
||
|
|
|
||
|
|
const client = new JSONRPCClient<S3SIService>({
|
||
|
|
transport: new StdioTransport()
|
||
|
|
}).getProxy();
|
||
|
|
const LOG_SUB = new Set<(logs: Log[]) => void>();
|
||
|
|
|
||
|
|
async function getLogs() {
|
||
|
|
while (true) {
|
||
|
|
const r = await client.getLogs()
|
||
|
|
|
||
|
|
if (r.error) {
|
||
|
|
throw new Error(r.error.message);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const { level, msg } of r.result) {
|
||
|
|
switch (level) {
|
||
|
|
case 'debug':
|
||
|
|
console.debug(...msg);
|
||
|
|
break;
|
||
|
|
case 'log':
|
||
|
|
console.log(...msg);
|
||
|
|
break;
|
||
|
|
case 'warn':
|
||
|
|
console.warn(...msg);
|
||
|
|
break;
|
||
|
|
case 'error':
|
||
|
|
console.error(...msg);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for (const cb of LOG_SUB) {
|
||
|
|
cb(r.result);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
getLogs()
|
||
|
|
|
||
|
|
const LOG_CONTEXT = createContext<{
|
||
|
|
logs: Log[],
|
||
|
|
renderedLogs: React.ReactNode[]
|
||
|
|
}>({
|
||
|
|
logs: [],
|
||
|
|
renderedLogs: [],
|
||
|
|
});
|
||
|
|
|
||
|
|
export const useLog = () => {
|
||
|
|
return useContext(LOG_CONTEXT);
|
||
|
|
}
|
||
|
|
|
||
|
|
function renderLevel(log: Log) {
|
||
|
|
return `[${log.level.toUpperCase()}]`.padEnd(7)
|
||
|
|
}
|
||
|
|
|
||
|
|
function renderLog(log: Log) {
|
||
|
|
return `${renderLevel(log)} ${log.msg.map(String).join(' ')}`
|
||
|
|
}
|
||
|
|
|
||
|
|
export const LogProvider: React.FC<{ limit?: number, children?: React.ReactNode }> = ({ children, limit = 10 }) => {
|
||
|
|
const [logs, setLogs] = useState<Log[]>([]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const cb = (logs: Log[]) => {
|
||
|
|
setLogs(old => [...old, ...logs].slice(-limit));
|
||
|
|
}
|
||
|
|
LOG_SUB.add(cb);
|
||
|
|
return () => {
|
||
|
|
LOG_SUB.delete(cb);
|
||
|
|
}
|
||
|
|
}, [limit])
|
||
|
|
|
||
|
|
|
||
|
|
const renderedLogs = useMemo(() => logs.map(renderLog), [logs])
|
||
|
|
|
||
|
|
return <LOG_CONTEXT.Provider value={{
|
||
|
|
logs,
|
||
|
|
renderedLogs,
|
||
|
|
}}>
|
||
|
|
{children}
|
||
|
|
</LOG_CONTEXT.Provider>
|
||
|
|
}
|
||
|
|
|
||
|
|
export const useLogin = () => {
|
||
|
|
const login = useCallback(async () => {
|
||
|
|
const result = await client.loginSteps();
|
||
|
|
if (result.error) {
|
||
|
|
throw new Error(result.error.message);
|
||
|
|
}
|
||
|
|
|
||
|
|
const login: string | null = await invoke('open_login_window', {
|
||
|
|
url: result.result.url
|
||
|
|
})
|
||
|
|
if (login === null || login === '') {
|
||
|
|
console.log('user cancel login');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const loginResult: { url: string } = JSON.parse(login);
|
||
|
|
const sessionToken = await client.loginSteps({
|
||
|
|
authCodeVerifier: result.result.authCodeVerifier,
|
||
|
|
login: loginResult.url,
|
||
|
|
})
|
||
|
|
if (sessionToken.error) {
|
||
|
|
throw new Error(sessionToken.error.message);
|
||
|
|
}
|
||
|
|
return sessionToken.result;
|
||
|
|
}, [])
|
||
|
|
|
||
|
|
return {
|
||
|
|
login
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function run(state: State, opts: ExportOpts) {
|
||
|
|
const r = await client.run(state, opts);
|
||
|
|
if (r.error) {
|
||
|
|
throw new Error(r.error.message);
|
||
|
|
}
|
||
|
|
return r.result;
|
||
|
|
}
|