From 3df43b04df3c4f5ecd3c30f93006d0da3f9d1038 Mon Sep 17 00:00:00 2001 From: bLue Date: Sun, 17 Dec 2023 12:08:35 +0800 Subject: [PATCH] feat: support switching competition login form --- src/pages/competitions/$id/index.tsx | 205 ++++++++++++++---- src/pages/competitions/models/competitions.ts | 8 + 2 files changed, 165 insertions(+), 48 deletions(-) diff --git a/src/pages/competitions/$id/index.tsx b/src/pages/competitions/$id/index.tsx index 5654d1a..3e74b3b 100644 --- a/src/pages/competitions/$id/index.tsx +++ b/src/pages/competitions/$id/index.tsx @@ -13,6 +13,8 @@ import router from 'umi/router'; import { urlf } from '@/utils/format'; import tracker from '@/utils/tracker'; import { getCompetitionUserAvailablePages } from '@/utils/competition'; +import { ICompetition, ICompetitionSettings } from '@/common/interfaces/competition'; +import PageLoading from '@/components/PageLoading'; interface ICompetitionSessionState extends ICompetitionSessionStatus { _code: number; @@ -22,16 +24,97 @@ export interface Props extends ReduxProps, RouteProps, FormProps { id: number; globalSession: ISessionStatus; session: ICompetitionSessionState; + detail: ICompetition; + settings: ICompetitionSettings; } -interface State {} +type TabType = '' | 'loginGlobal' | 'loginCompetition'; + +interface State { + tab: TabType; +} class CompetitionBase extends React.Component { static defaultProps: Partial = {}; - constructor(props) { + private tabs = { + loginGlobal: { + title: 'Login OJ Account', + body: () => { + const { settings } = this.props; + const { getFieldDecorator } = this.props.form; + return ( +
+ + {getFieldDecorator('loginName', { + rules: [{ + required: true, message: 'Please input email or username', + }], + })()} + + + + {getFieldDecorator('password', { + rules: [{ required: true, message: 'Please input password' }], + })()} + + + {settings.allowedAuthMethods.includes('password') && + + Switch to this.switchTab(e, 'loginCompetition')}>UID/Password Method + } + + + + +
+ ); + }, + }, + + loginCompetition: { + title: 'Login Competition', + body: () => { + const { globalSession, settings } = this.props; + const { getFieldDecorator } = this.props.form; + return ( +
+ + {getFieldDecorator('userId', { + rules: [ + { + required: true, + message: 'Please input UID', + }, + ], + })()} + + + + {getFieldDecorator('password', { + rules: [{ required: true, message: 'Please input password' }], + })()} + + + {!globalSession.loggedIn && settings.allowedAuthMethods.includes('session') && + + Have an OJ account signed up this competition? this.switchTab(e, 'loginGlobal')}>Login + } + + + + +
+ ); + }, + }, + } + + constructor(props: Props) { super(props); - this.state = {}; + this.state = { + tab: props.settings?.allowedAuthMethods.includes('session') ? 'loginGlobal' : 'loginCompetition', + }; } componentDidMount(): void { @@ -46,6 +129,15 @@ class CompetitionBase extends React.Component { } } + switchTab = (e, tab) => { + this.setState({ tab }); + tracker.event({ + category: 'competitions', + action: 'switchLoginTab', + label: tab, + }); + }; + redirect(props: Readonly) { let redirectUri: string = props.location.query.redirect ? `/competitions/${props.id}${props.location.query.redirect}` @@ -69,68 +161,83 @@ class CompetitionBase extends React.Component { this.props.form.validateFields((err, values) => { if (!err) { const { id, dispatch } = this.props; - dispatch({ - type: 'competitions/login', - payload: { - id, - data: { - userId: +values.userId, - password: values.password, - }, - }, - }).then((ret) => { - msg.auto(ret); - if (ret.success) { - msg.success(`Login Success`); - tracker.event({ - category: 'competitions', - action: 'loginContest', + switch (this.state.tab) { + case 'loginGlobal': { + dispatch({ + type: 'session/login', + payload: values, + }).then((ret) => { + msg.auto(ret); + if (ret.success) { + msg.success(`Welcome back, ${ret.data.nickname}`); + tracker.event({ + category: 'competitions', + action: 'loginCompetition', + label: this.state.tab, + }); + dispatch({ + type: 'session/setSession', + payload: { user: ret.data }, + }); + dispatch({ + type: 'competitions/getSession', + payload: { + id, + force: true, + }, + }); + } }); + break; + } + case 'loginCompetition': { dispatch({ - type: 'competitions/getSession', + type: 'competitions/login', payload: { id, - force: true, + data: { + userId: +values.userId, + password: values.password, + }, }, + }).then((ret) => { + msg.auto(ret); + if (ret.success) { + msg.success(`Login Success`); + tracker.event({ + category: 'competitions', + action: 'loginCompetition', + label: this.state.tab, + }); + dispatch({ + type: 'competitions/getSession', + payload: { + id, + force: true, + }, + }); + } }); + break; } - }); + } } }); }; render() { - const { getFieldDecorator } = this.props.form; + const { tab } = this.state; + if (!tab) { + return ; + } + return (
-

Login Competition

+

{this.tabs[tab].title}

-
- - {getFieldDecorator('userId', { - rules: [ - { - required: true, - message: 'Please input UID', - }, - ], - })()} - - - - {getFieldDecorator('password', { - rules: [{ required: true, message: 'Please input password' }], - })()} - - - - - -
+ {this.tabs[tab].body()}
); @@ -143,6 +250,8 @@ function mapStateToProps(state) { id, globalSession: state.session, session: state.competitions.session[id], + detail: state.competitions.detail[id], + settings: state.competitions.settings[id], }; } diff --git a/src/pages/competitions/models/competitions.ts b/src/pages/competitions/models/competitions.ts index c6b1876..9eb075f 100644 --- a/src/pages/competitions/models/competitions.ts +++ b/src/pages/competitions/models/competitions.ts @@ -177,6 +177,14 @@ export default { } return ret; }, + *getListData({ payload: query }, { call }) { + const formattedQuery: IListQuery = { + order: [['competitionId', 'DESC']], + ...formatListQuery(query), + }; + const ret: IApiResponse> = yield call(service.getList, formattedQuery); + return ret; + }, *getSession({ payload: { id, force = false } }, { call, put, select }) { if (!force) { const savedState = yield select((state) => state.competitions.session[id]);