feat: refetch token before open splatnet

main
imspace 2023-03-09 22:08:42 +08:00
parent 4c458861c6
commit 3fdc66c384
7 changed files with 93 additions and 33 deletions

View File

@ -57,7 +57,7 @@ document.addEventListener("DOMContentLoaded", () => {{
const style = document.createElement('style');
style.innerHTML = `
[class^="App_App_"] , [class^="InAppContent_children_"] , [class^="SwipableView_swipableViewItem_"] ,
[class^="MainWrapper_wrapper_"] {{
[class^="MainWrapper_wrapper_"] , [class^="FriendList_wrapper_"] {{
overflow: auto;
}}
`;

View File

@ -0,0 +1,59 @@
import { invoke } from '@tauri-apps/api';
import classNames from 'classnames';
import { usePromise } from 'hooks/usePromise';
import React, { useState } from 'react'
import { getConfig, getProfile, setProfile } from 'services/config';
import { ensureTokenValid } from 'services/s3si';
import { composeLoadable } from 'utils/composeLoadable';
import { ErrorContent } from './ErrorContent';
type OpenSplatnetProps = {
children?: React.ReactNode
}
export const OpenSplatnet: React.FC<OpenSplatnetProps> = ({ children }) => {
let { loading, error, retry, result } = composeLoadable({
config: usePromise(getConfig),
profile: usePromise(() => getProfile(0)),
});
const [doing, setDoing] = useState(false);
const [err, setError] = useState<any>();
const onClick = async () => {
setDoing(true);
try {
if (!result) {
return;
}
const state = result.profile.state;
const newState = await ensureTokenValid(state);
await setProfile(0, {
...result.profile,
state: newState,
});
retry?.();
const gtoken = newState.loginState?.gToken;
await invoke('open_splatnet', {
gtoken,
});
} catch (e) {
setError(e);
} finally {
setDoing(false);
}
};
if (error || err) {
return <>
<ErrorContent error={error || err} retry={retry} />
</>
}
return <>
<button className={classNames('btn', {
'btn-disabled': !result?.profile.state.loginState?.sessionToken,
'loading': loading || doing,
})} onClick={onClick}>{children}</button>
</>
}

View File

@ -1,41 +1,13 @@
import { invoke } from '@tauri-apps/api';
import { ErrorContent } from 'components/ErrorContent';
import { Loading } from 'components/Loading';
import { OpenSplatnet } from 'components/OpenSplatnet';
import { LogPanel, RunPanel } from 'components/RunPanel';
import { STAT_INK } from 'constant';
import { usePromise } from 'hooks/usePromise';
import React from 'react'
import { useTranslation } from 'react-i18next';
import { Link } from "react-router-dom";
import { getConfig, getProfile } from 'services/config';
import { composeLoadable } from 'utils/composeLoadable';
export const Home: React.FC = () => {
let { loading, error, retry, result } = composeLoadable({
config: usePromise(getConfig),
profile: usePromise(() => getProfile(0)),
});
const { t } = useTranslation();
if (loading) {
return <>
<div className='h-full flex items-center justify-center'><Loading /></div>
</>
}
if (error) {
return <>
<ErrorContent error={error} retry={retry} />
</>
}
const gtoken = result?.profile.state.loginState?.gToken
const onOpenSplatnet3 = async () => {
await invoke('open_splatnet', {
gtoken,
})
};
return <div className='flex p-2 w-full h-full gap-2'>
<div className='max-w-full h-full md:max-w-sm flex-auto'>
<div className='flex flex-col gap-2 h-full'>
@ -43,7 +15,7 @@ export const Home: React.FC = () => {
<RunPanel />
<Link to='/settings' className='btn'>{t('设置')}</Link>
<div className='flex gap-2 flex-auto-all'>
<button className='btn' onClick={onOpenSplatnet3}>{t('打开鱿鱼圈3')}</button>
<OpenSplatnet>{t('打开鱿鱼圈3')}</OpenSplatnet>
<a className='btn' href={STAT_INK} target='_blank' rel='noreferrer'>{t('前往 stat.ink')}</a>
</div>
</div>

View File

@ -140,3 +140,11 @@ export async function run(state: State, opts: ExportOpts) {
}
return r.result;
}
export async function ensureTokenValid(state: State) {
const r = await client.ensureTokenValid(state);
if (r.error) {
throw new Error(r.error.message);
}
return r.result;
}

View File

@ -10,8 +10,9 @@ import { DEFAULT_ENV, Env } from "./env.ts";
import { Queue } from "./jsonrpc/channel.ts";
import { ExportOpts, Log } from "./jsonrpc/types.ts";
import { App } from "./app.ts";
import { InMemoryStateBackend, State } from "./state.ts";
import { InMemoryStateBackend, Profile, State } from "./state.ts";
import { MemoryCache } from "./cache.ts";
import { Splatnet3 } from "./splatnet3.ts";
class S3SIServiceImplement implements S3SIService, Service {
loginMap: Map<string, {
@ -71,6 +72,25 @@ class S3SIServiceImplement implements S3SIService, Service {
result: await loginSteps(this.env, step2),
};
}
async ensureTokenValid(state: State): Promise<
RPCResult<State>
> {
const stateBackend = new InMemoryStateBackend(state);
const profile = new Profile({ stateBackend, env: this.env });
await profile.readState();
const splatnet3 = new Splatnet3({ profile, env: this.env });
if (!await splatnet3.checkToken()) {
return {
error: {
code: 101,
message: "SessionToken is invalid",
},
};
}
return {
result: stateBackend.state,
};
}
async getLogs(): Promise<RPCResult<Log[]>> {
const log = await this.loggerQueue.pop();
return {

View File

@ -132,6 +132,7 @@ export interface S3SIService {
}
>
>;
ensureTokenValid(state: State): Promise<RPCResult<State>>;
getLogs(): Promise<RPCResult<Log[]>>;
run(state: State, opts: ExportOpts): Promise<RPCResult<State>>;
// deno-lint-ignore no-explicit-any

View File

@ -155,7 +155,7 @@ export class Splatnet3 {
}
try {
await this.request(Queries.HomeQuery);
await this.request(Queries.ConfigureAnalyticsQuery);
return true;
} catch (_e) {
return false;