add reply form
This commit is contained in:
@@ -9,6 +9,8 @@ import './Common.css';
|
||||
|
||||
const chinese_format=buildFormatter(chineseStrings);
|
||||
|
||||
export const API_BASE=window.location.protocol==='https:' ? '/api_proxy' : 'http://www.pkuhelper.com/services/pkuhole';
|
||||
|
||||
const PID_RE=/(^|[^\d])([1-9]\d{4,5})(?!\d)/g;
|
||||
const NICKNAME_RE=/(^|[^A-Za-z])((?:(?:Angry|Baby|Crazy|Diligent|Excited|Fat|Greedy|Hungry|Interesting|Japanese|Kind|Little|Magic|Naïve|Old|Powerful|Quiet|Rich|Superman|THU|Undefined|Valuable|Wifeless|Xiangbuchulai|Young|Zombie)\s)?(?:Alice|Bob|Carol|Dave|Eve|Francis|Grace|Hans|Isabella|Jason|Kate|Louis|Margaret|Nathan|Olivia|Paul|Queen|Richard|Susan|Thomas|Uma|Vivian|Winnie|Xander|Yasmine|Zach)|You Win|洞主)(?![A-Za-z])/gi;
|
||||
|
||||
@@ -54,3 +56,42 @@ export class HighlightedText extends PureComponent {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
window.TEXTAREA_BACKUP={};
|
||||
|
||||
export class SafeTextarea extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state={
|
||||
text: window.TEXTAREA_BACKUP[props.id]||'',
|
||||
};
|
||||
this.on_change_bound=this.on_change.bind(this);
|
||||
this.clear=this.clear.bind(this);
|
||||
this.area_ref=React.createRef();
|
||||
this.change_callback=props.on_change;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.TEXTAREA_BACKUP[this.props.id]=this.state.text;
|
||||
this.change_callback(this.state.text);
|
||||
}
|
||||
|
||||
on_change(event) {
|
||||
this.setState({
|
||||
text: event.target.value,
|
||||
});
|
||||
this.change_callback(event.target.value);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.setState({
|
||||
text: '',
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<textarea ref={this.area_ref} onChange={this.on_change_bound} value={this.state.text} />
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -97,11 +97,13 @@
|
||||
.flow-item-row p.img {
|
||||
text-align: center;
|
||||
}
|
||||
p.img img {
|
||||
.flow-item-row p.img img {
|
||||
max-width: 100%;
|
||||
max-height: 100vh;
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,.4);
|
||||
}
|
||||
.left-container .flow-item-row p.img img {
|
||||
max-height: 80vh;
|
||||
}
|
||||
|
||||
.box-header-badge {
|
||||
float: right;
|
||||
|
||||
21
src/Flows.js
21
src/Flows.js
@@ -1,14 +1,14 @@
|
||||
import React, {Component, PureComponent} from 'react';
|
||||
import {ColorPicker} from './color_picker';
|
||||
import {Time, TitleLine, HighlightedText} from './Common.js';
|
||||
import {Time, TitleLine, HighlightedText} from './Common';
|
||||
import './Flows.css';
|
||||
import LazyLoad from 'react-lazyload';
|
||||
import {AudioWidget} from './AudioWidget.js';
|
||||
import {TokenCtx} from './UserAction';
|
||||
import {AudioWidget} from './AudioWidget';
|
||||
import {TokenCtx, ReplyForm} from './UserAction';
|
||||
|
||||
import {API_BASE} from './Common';
|
||||
const IMAGE_BASE='http://www.pkuhelper.com/services/pkuhole/images/';
|
||||
const AUDIO_BASE='/audio_proxy/';
|
||||
const API_BASE=window.location.protocol==='https:' ? '/api_proxy' : 'http://www.pkuhelper.com/services/pkuhole';
|
||||
|
||||
const SEARCH_PAGESIZE=50;
|
||||
const CLICKABLE_TAGS={a: true, audio: true};
|
||||
@@ -145,15 +145,17 @@ class FlowItemRow extends PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
reload_sidebar() {
|
||||
this.props.show_sidebar('帖子详情',<p className="box box-tip">加载中……</p>);
|
||||
this.load_replies(this.show_sidebar.bind(this));
|
||||
}
|
||||
|
||||
show_sidebar() {
|
||||
this.props.show_sidebar(
|
||||
'帖子详情',
|
||||
<div className="flow-item-row sidebar-flow-item">
|
||||
<div className="box box-tip">
|
||||
<a onClick={()=>{
|
||||
this.props.show_sidebar('帖子详情',<p className="box box-tip">加载中……</p>);
|
||||
this.load_replies(this.show_sidebar.bind(this));
|
||||
}}>刷新回复</a>
|
||||
<a onClick={this.reload_sidebar.bind(this)}>刷新回复</a>
|
||||
{this.props.token &&
|
||||
<span>
|
||||
/
|
||||
@@ -175,6 +177,9 @@ class FlowItemRow extends PureComponent {
|
||||
<Reply info={reply} color_picker={this.color_picker} />
|
||||
</LazyLoad>
|
||||
))}
|
||||
{this.props.token &&
|
||||
<ReplyForm pid={this.state.info.pid} token={this.props.token} on_complete={this.reload_sidebar.bind(this)} />
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const HELP_TEXT=(
|
||||
<li>在列表中点击帖子可以展开全部回复</li>
|
||||
<li>在搜索框输入 #472865 等可以查看指定 ID 的树洞</li>
|
||||
<li>新的帖子会在左上角显示一个圆点</li>
|
||||
<li>登录后可以关注帖子</li>
|
||||
<li>本网站支持 3D Touch,重压屏幕可以快速返回 / 刷新树洞</li>
|
||||
<li>请注意:使用 HTTPS 访问本站可能会<b>大幅减慢</b>加载速度</li>
|
||||
<li>自定义背景图片请修改 <code>localStorage['REPLACE_ERIRI_WITH_URL']</code></li>
|
||||
</ul>
|
||||
|
||||
@@ -5,4 +5,17 @@
|
||||
|
||||
.login-form button {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.reply-form {
|
||||
display: flex;
|
||||
}
|
||||
.reply-form textarea {
|
||||
resize: vertical;
|
||||
flex: 1;
|
||||
min-height: 3em;
|
||||
height: 5em;
|
||||
}
|
||||
.reply-form button {
|
||||
flex: 0 0 50px;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import React, {Component, PureComponent} from 'react';
|
||||
import {SafeTextarea} from './Common';
|
||||
|
||||
import './UserAction.css';
|
||||
|
||||
import {API_BASE} from './Common';
|
||||
const LOGIN_BASE=window.location.protocol==='https:' ? '/login_proxy' : 'http://www.pkuhelper.com/services/login';
|
||||
|
||||
export const TokenCtx=React.createContext({
|
||||
@@ -63,7 +65,10 @@ export class LoginForm extends Component {
|
||||
<TokenCtx.Consumer>{(token)=>
|
||||
<div className="login-form">
|
||||
<form onSubmit={(e)=>this.do_login(e,token.set_value)} className="box">
|
||||
<p>Token: <code>{token.value||'(null)'}</code></p>
|
||||
<p>{token.value ?
|
||||
<span><b>您已登录。</b>Token: <code>{token.value||'(null)'}</code></span> :
|
||||
'登录后可以使用关注、回复等功能'
|
||||
}</p>
|
||||
<p>
|
||||
<label>
|
||||
学号:
|
||||
@@ -95,4 +100,79 @@ export class LoginForm extends Component {
|
||||
}</TokenCtx.Consumer>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class ReplyForm extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state={
|
||||
text: '',
|
||||
loading_status: 'done',
|
||||
};
|
||||
this.on_change_bound=this.on_change.bind(this);
|
||||
this.area_ref=React.createRef();
|
||||
}
|
||||
|
||||
on_change(value) {
|
||||
this.setState({
|
||||
text: value,
|
||||
});
|
||||
}
|
||||
|
||||
on_submit(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if(this.state.loading_status==='loading')
|
||||
return;
|
||||
this.setState({
|
||||
loading_status: 'loading',
|
||||
});
|
||||
|
||||
let data=new URLSearchParams();
|
||||
data.append('action','docomment');
|
||||
data.append('pid',this.props.pid);
|
||||
data.append('text',this.state.text);
|
||||
data.append('token',this.props.token);
|
||||
fetch(API_BASE+'/api.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: data,
|
||||
})
|
||||
.then((res)=>res.json())
|
||||
.then((json)=>{
|
||||
if(json.code!==0)
|
||||
throw new Error(json);
|
||||
|
||||
this.setState({
|
||||
loading_status: 'done',
|
||||
text: '',
|
||||
});
|
||||
this.area_ref.current.clear();
|
||||
this.props.on_complete();
|
||||
})
|
||||
.catch((e)=>{
|
||||
console.trace(e);
|
||||
alert('回复失败\n(树洞服务器经常抽风,其实有可能已经回复上了,不妨点“刷新回复”看一看)');
|
||||
this.setState({
|
||||
loading_status: 'done',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="box">
|
||||
<form onSubmit={this.on_submit.bind(this)} className="reply-form">
|
||||
<SafeTextarea ref={this.area_ref} id={this.props.pid} on_change={this.on_change_bound} />
|
||||
{this.state.loading_status==='loading' ?
|
||||
<button disabled="disabled">正在回复……</button> :
|
||||
<button type="submit">回复</button>
|
||||
}
|
||||
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: '微软雅黑', 'Microsoft YaHei', sans-serif;
|
||||
background-size: cover;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
@@ -10,6 +9,10 @@ body::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body, textarea, pre {
|
||||
font-family: '微软雅黑', 'Microsoft YaHei', sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
word-wrap: break-word;
|
||||
@@ -26,11 +29,13 @@ a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 0 1em;
|
||||
input, textarea {
|
||||
border-radius: 5px;
|
||||
border: 1px solid black;
|
||||
outline: none;
|
||||
}
|
||||
input {
|
||||
padding: 0 1em;
|
||||
line-height: 2em;
|
||||
}
|
||||
|
||||
@@ -40,7 +45,6 @@ audio {
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
font-family: '微软雅黑', 'Microsoft YaHei', sans-serif;
|
||||
}
|
||||
|
||||
button, .button {
|
||||
|
||||
Reference in New Issue
Block a user