diff --git a/src/BalanceShower.css b/src/BalanceShower.css deleted file mode 100644 index 4b5a918..0000000 --- a/src/BalanceShower.css +++ /dev/null @@ -1,19 +0,0 @@ -.balance-popover { - position: absolute; - top: 2em; - margin: auto; - left: 50%; - transform: translateX(-50%); - z-index: 1; -} - -.balance-value { - opacity: 0; - animation: balance-disappear 2s ease-in; -} - -@keyframes balance-disappear { - from {opacity: 1;} - 75% {opacity: 1;} - to {opacity: 0;} -} \ No newline at end of file diff --git a/src/BalanceShower.js b/src/BalanceShower.js deleted file mode 100644 index a68f037..0000000 --- a/src/BalanceShower.js +++ /dev/null @@ -1,83 +0,0 @@ -import React, {Component, PureComponent} from 'react'; -import {PKUHELPER_ROOT} from './infrastructure/const'; -import {API_VERSION_PARAM, get_json} from './flows_api'; -import {TokenCtx} from './UserAction'; - -import './BalanceShower.css'; - -export class BalanceShower extends PureComponent { - constructor(props) { - super(props); - this.state={ - loading_status: 'idle', - error: null, - balance: null, - }; - } - - do_load(e,token) { - if(this.state.loading_status==='loading') - return; - if(e.target.closest('a')) // clicking at a link - return; - if(!token || !window.config.easter_egg) { - this.setState({ - loading_status: 'idle', - }); - return; - } - - this.setState({ - loading_status: 'loading', - },()=>{ - fetch( - PKUHELPER_ROOT+'api_xmcp/isop/card_balance' - +'?user_token='+encodeURIComponent(token) - +API_VERSION_PARAM() - ) - .then(get_json) - .then((json)=>{ - console.log(json); - if(!json.success) - throw new Error(JSON.stringify(json)); - - this.setState({ - loading_status: 'done', - error: null, - balance: json.balance, - }); - }) - .catch((e)=>{ - console.error(e); - this.setState({ - loading_status: 'error', - error: ''+e, - }); - }); - }) - } - - render_popover() { - if(this.state.loading_status==='idle') // no token or disabled - return null; - else if(this.state.loading_status==='loading') - return (
……
); - else if(this.state.loading_status==='error') - return (
{alert(this.state.error)}}>无法查询余额
); - else if(this.state.loading_status==='done') - return (
校园卡 ¥{this.state.balance.toFixed(2)}
); - else - return null; - } - - render() { - return ( - {(token)=>( -
this.do_load(e,token.value)}> -
{this.render_popover()}
- {this.props.children} -
- )}
- ) - } -} \ No newline at end of file diff --git a/src/Title.js b/src/Title.js index 916e34a..f9af391 100644 --- a/src/Title.js +++ b/src/Title.js @@ -1,65 +1,12 @@ import React, {Component, PureComponent} from 'react'; import {AppSwitcher} from './infrastructure/widgets'; -import {LoginForm, PostForm} from './UserAction'; +import {InfoSidebar, PostForm} from './UserAction'; import {TokenCtx} from './UserAction'; -import {PromotionBar} from './Common'; -import {ConfigUI} from './Config'; import './Title.css'; -import {BalanceShower} from './BalanceShower'; -import {cache} from './cache'; const flag_re=/^\/\/setflag ([a-zA-Z0-9_]+)=(.*)$/; -const HELP_TEXT=( -
-

- PKUHelper 网页版树洞 by @xmcp, - 基于  - GPLv3 -  协议在 GitHub 开源 -

-

- PKUHelper 网页版的诞生离不开  - React - 、 - IcoMoon -  等开源项目 -

-

- { - if('serviceWorker' in navigator) { - navigator.serviceWorker.getRegistrations() - .then((registrations)=>{ - for(let registration of registrations) { - console.log('unregister',registration); - registration.unregister(); - } - }); - } - cache().clear(); - setTimeout(()=>{ - window.location.reload(true); - },200); - }}>强制检查更新 - ({process.env.REACT_APP_BUILD_INFO||'---'} {process.env.NODE_ENV} 会自动在后台检查更新并在下次访问时更新) -

-

- This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. -

-

- This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  - GNU General Public License -  for more details. -

-
-); - class ControlBar extends PureComponent { constructor(props) { super(props); @@ -148,27 +95,7 @@ class ControlBar extends PureComponent { { this.props.show_sidebar( 'P大树洞', -
- - -
- {this.props.show_sidebar( - '设置', - - )}}> - - -    - - - -    - - - -
- {HELP_TEXT} -
+ ) }}> @@ -199,13 +126,16 @@ export function Title(props) {
- -
-

+

+

+ props.show_sidebar( + 'P大树洞', + + )}> P大树洞 -

-
- + +

+
diff --git a/src/UserAction.css b/src/UserAction.css index 00b1c63..4098729 100644 --- a/src/UserAction.css +++ b/src/UserAction.css @@ -56,4 +56,25 @@ width: 100%; min-height: 5em; height: 20em; +} + +.life-info-table { + width: 100%; + margin: auto; +} +@media screen and (min-width: 375px) { + .life-info-table { + width: 315px; + } +} +.life-info-table td { + padding: .25em; +} +.life-info-table td:nth-child(1) { + font-weight: bold; + text-align: right; +} + +.life-info-error a { + --var-link-color: hsl(25,100%,45%); } \ No newline at end of file diff --git a/src/UserAction.js b/src/UserAction.js index 776d17f..97b9f72 100644 --- a/src/UserAction.js +++ b/src/UserAction.js @@ -1,11 +1,13 @@ import React, {Component, PureComponent} from 'react'; import copy from 'copy-to-clipboard'; -import {API_BASE,SafeTextarea} from './Common'; +import {API_BASE, SafeTextarea, PromotionBar} from './Common'; import {MessageViewer} from './Message'; import {API_VERSION_PARAM, PKUHELPER_ROOT, API, get_json, token_param} from './flows_api'; import './UserAction.css'; import {LoginPopup} from './infrastructure/widgets'; +import {ConfigUI} from './Config'; +import {cache} from './cache'; const BASE64_RATE=4/3; const MAX_IMG_DIAM=8000; @@ -17,6 +19,252 @@ export const TokenCtx=React.createContext({ set_value: ()=>{}, }); +class LifeInfoBox extends Component { + constructor(props) { + super(props); + if(!window._life_info_cache) + window._life_info_cache={}; + this.CACHE_TIMEOUT_S=15; + this.state={ + today_info: this.cache_get('today_info'), + card_balance: this.cache_get('card_balance'), + net_balance: this.cache_get('net_balance'), + mail_count: this.cache_get('mail_count'), + }; + this.INTERNAL_NETWORK_FAILURE='_network_failure'; + this.API_NAME={ + today_info: 'hole/today_info', + card_balance: 'isop/card_balance', + net_balance: 'isop/net_balance', + mail_count: 'isop/mail_count', + }; + } + + cache_get(key) { + let cache_item=window._life_info_cache[key]; + if(!cache_item || (+new Date())-cache_item[0]>1000*this.CACHE_TIMEOUT_S) + return null; + else + return cache_item[1]; + } + cache_set(key,value) { + if(!window._life_info_cache[key] || window._life_info_cache[key][1]!==value) + window._life_info_cache[key]=[+new Date(),value]; + } + + load(state_key) { + this.setState({ + [state_key]: null, + },()=>{ + fetch( + PKUHELPER_ROOT+'api_xmcp/'+this.API_NAME[state_key] + +'?user_token='+encodeURIComponent(this.props.token) + +API_VERSION_PARAM() + ) + .then(get_json) + .then((json)=>{ + console.log(json); + + this.setState({ + [state_key]: json, + }); + }) + .catch((e)=>{ + this.setState({ + [state_key]: { + errMsg: '网络错误 '+e, + errCode: this.INTERNAL_NETWORK_FAILURE, + success: false, + } + }); + }) + }); + } + + componentDidMount() { + ['today_info','card_balance','net_balance','mail_count'].forEach((k)=>{ + if(!this.state[k]) + this.load(k); + }); + } + + reload_all() { + ['today_info','card_balance','net_balance','mail_count'].forEach((k)=>{ + this.load(k); + }); + } + + render_line(state_key,title,value_fn,action,url_fn,do_login) { + let s=this.state[state_key]; + if(!s) + return ( + + {title} + 加载中…… + + + ); + else if(!s.success) { + let type='加载失败'; + if(s.errCode===this.INTERNAL_NETWORK_FAILURE) + type='网络错误'; + else if(['E01','E02','E03'].indexOf(s.errCode)!==-1) + type='授权失效'; + + let details=JSON.stringify(s); + if(s.errMsg) + details=s.errMsg; + else if(s.error) + details=s.error; + + return ( + + {title} + + alert(details)}>{type} + + + {type==='授权失效' ? + +  重新登录 + : + this.load(state_key)}> +  重试 + + } + + + ) + } + else { + this.cache_set(state_key,s); + + return ( + + {title} + {value_fn(s)} + + +  {action} + + + + ); + } + } + + render() { + return ( + { + this.props.set_token(t); + this.reload_all(); + }}>{(do_login)=>( +
+ + + {this.render_line( + 'today_info', + '今日',(s)=>s.info, + '校历',(s)=>s.schedule_url, + do_login, + )} + {this.render_line( + 'card_balance', + '校园卡',(s)=>`余额¥${s.balance.toFixed(2)}`, + '充值',()=>'https://virtualprod.alipay.com/educate/educatePcRecharge.htm?schoolCode=PKU&schoolName=', + do_login, + )} + {this.render_line( + 'net_balance', + '网费',(s)=>`余额¥${s.balance.toFixed(2)}`, + '充值',()=>'https://its.pku.edu.cn/epay.jsp', + do_login, + )} + {this.render_line( + 'mail_count', + '邮件',(s)=>`未读 ${s.count} 封`, + '查看',()=>'https://mail.pku.edu.cn/', + do_login, + )} + +
+
+ )}
+ ) + } +} + +export function InfoSidebar(props) { + return ( +
+ + + +
+

+ PKUHelper 网页版树洞 by @xmcp, + 基于  + GPLv3 +  协议在 GitHub 开源 +

+

+ PKUHelper 网页版的诞生离不开  + React + 、 + IcoMoon +  等开源项目 +

+

+ { + if('serviceWorker' in navigator) { + navigator.serviceWorker.getRegistrations() + .then((registrations)=>{ + for(let registration of registrations) { + console.log('unregister',registration); + registration.unregister(); + } + }); + } + cache().clear(); + setTimeout(()=>{ + window.location.reload(true); + },200); + }}>强制检查更新 + ({process.env.REACT_APP_BUILD_INFO||'---'} {process.env.NODE_ENV} 会自动在后台检查更新并在下次访问时更新) +

+

+ This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +

+

+ This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  + GNU General Public License +  for more details. +

+
+
+ ); +} + class ResetUsertokenWidget extends Component { constructor(props) { super(props); @@ -80,45 +328,50 @@ export class LoginForm extends Component { render() { return ( {(token)=> -
- {token.value ? -
-

- 您已登录。 - -
-

-

- 根据计算中心要求,访问授权三个月内有效。
若提示“授权过期”,请注销后重新登录。 -

-

- {this.props.show_sidebar( - '系统消息', - - )}}>查看系统消息
- 当您发送的内容违规时,我们将用系统消息提示您 -

-

- 复制 User Token
- User Token 用于迁移登录状态,切勿告知他人,若怀疑被盗号请尽快 -

-
: - {(do_popup)=>( +
+ {!!token.value && + + } +
+ {token.value ?

- +

-

- PKU Helper 面向北京大学学生,通过 ISOP(北京大学数据共享开放服务平台)验证您的身份并提供服务。 -

-
- )} - } +

+ 根据计算中心要求,访问授权三个月内有效,过期需重新登录。 +

+

+ {this.props.show_sidebar( + '系统消息', + + )}}>查看系统消息
+ 当您发送的内容违规时,我们将用系统消息提示您 +

+

+ 复制 User Token
+ User Token 用于迁移登录状态,切勿告知他人,若怀疑被盗号请尽快 +

+
: + {(do_popup)=>( +
+

+ +

+

+ PKU Helper 面向北京大学学生,通过 ISOP(北京大学数据共享开放服务平台)验证您的身份并提供服务。 +

+
+ )}
+ } +
}
) diff --git a/src/infrastructure b/src/infrastructure index 8004bac..110f225 160000 --- a/src/infrastructure +++ b/src/infrastructure @@ -1 +1 @@ -Subproject commit 8004bac71e4a5769ecdd640726b1547dbcdcce1d +Subproject commit 110f225b4ef614a52ce603ae1338b7fbecbda8ac