forked from newthuhole/hole_thu_frontend
improvements
- add sidebar-stack - add control-btn-label - better locating dz - enable reply_rev when 1 reply - replace latest_post_id when page=1
This commit is contained in:
48
src/App.js
48
src/App.js
@@ -17,8 +17,7 @@ class App extends Component {
|
||||
load_config();
|
||||
listen_darkmode({default: undefined, light: false, dark: true}[window.config.color_scheme]);
|
||||
this.state={
|
||||
sidebar_title: null,
|
||||
sidebar_content: null, // determine status of sidebar
|
||||
sidebar_stack: [[null,null]], // list of [status, content]
|
||||
mode: 'list', // list, single, search, attention
|
||||
search_text: null,
|
||||
flow_render_key: +new Date(),
|
||||
@@ -41,20 +40,39 @@ class App extends Component {
|
||||
}
|
||||
|
||||
on_pressure() {
|
||||
if(this.state.sidebar_title!==null)
|
||||
this.setState((prevState)=>({
|
||||
sidebar_title: null,
|
||||
sidebar_content: prevState.sidebar_content,
|
||||
}));
|
||||
if(this.state.sidebar_stack.length>1)
|
||||
this.show_sidebar(null,null,'clear');
|
||||
else
|
||||
this.set_mode('list',null);
|
||||
}
|
||||
|
||||
show_sidebar(title,content) {
|
||||
this.setState({
|
||||
sidebar_title: title,
|
||||
sidebar_content: content,
|
||||
});
|
||||
show_sidebar(title,content,mode='push') {
|
||||
if(mode==='push') {
|
||||
this.setState((prevState)=>({
|
||||
sidebar_stack: prevState.sidebar_stack.concat([[title,content]]),
|
||||
}));
|
||||
} else if(mode==='pop') {
|
||||
this.setState((prevState)=>{
|
||||
let ns=prevState.sidebar_stack.slice();
|
||||
ns.pop();
|
||||
return {
|
||||
sidebar_stack: ns,
|
||||
};
|
||||
});
|
||||
} else if(mode==='replace') {
|
||||
this.setState((prevState)=>{
|
||||
let ns=prevState.sidebar_stack.slice();
|
||||
ns.pop();
|
||||
return {
|
||||
sidebar_stack: ns.concat([[title,content]]),
|
||||
};
|
||||
});
|
||||
} else if(mode==='clear') {
|
||||
this.setState({
|
||||
sidebar_stack: [[null,null]],
|
||||
});
|
||||
} else
|
||||
throw new Error('bad show_sidebar mode');
|
||||
}
|
||||
|
||||
set_mode(mode,search_text) {
|
||||
@@ -102,11 +120,7 @@ class App extends Component {
|
||||
<br />
|
||||
</div>
|
||||
)}</TokenCtx.Consumer>
|
||||
<Sidebar do_close={()=>{
|
||||
this.setState({
|
||||
sidebar_title: null,
|
||||
});
|
||||
}} content={this.state.sidebar_content} title={this.state.sidebar_title} />
|
||||
<Sidebar show_sidebar={this.show_sidebar_bound} stack={this.state.sidebar_stack} />
|
||||
</TokenCtx.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
72
src/Flows.js
72
src/Flows.js
@@ -19,10 +19,12 @@ const QUOTE_BLACKLIST=['23333','233333','66666','666666','10086','10000','100000
|
||||
|
||||
window.LATEST_POST_ID=parseInt(localStorage['_LATEST_POST_ID'],10)||0;
|
||||
|
||||
const DZ_NAME='洞主';
|
||||
|
||||
function load_single_meta(show_sidebar,token,parents) {
|
||||
return (pid)=>{
|
||||
let title_elem=<FlowSidebarTitle pid={pid} parents={parents} show_sidebar={show_sidebar} token={token} />;
|
||||
const color_picker=new ColorPicker();
|
||||
let color_picker=new ColorPicker();
|
||||
let title_elem='树洞 #'+pid;
|
||||
show_sidebar(
|
||||
title_elem,
|
||||
<div className="box box-tip">
|
||||
@@ -46,7 +48,8 @@ function load_single_meta(show_sidebar,token,parents) {
|
||||
info={single.data} replies={replies.data} attention={replies.attention}
|
||||
token={token} show_sidebar={show_sidebar} color_picker={color_picker}
|
||||
deletion_detect={localStorage['DELETION_DETECT']==='on'} parents={parents}
|
||||
/>
|
||||
/>,
|
||||
'replace'
|
||||
)
|
||||
})
|
||||
.catch((e)=>{
|
||||
@@ -56,7 +59,8 @@ function load_single_meta(show_sidebar,token,parents) {
|
||||
<div className="box box-tip">
|
||||
<p><a onClick={()=>load_single_meta(show_sidebar,token,parents)(pid)}>重新加载</a></p>
|
||||
<p>{''+e}</p>
|
||||
</div>
|
||||
</div>,
|
||||
'replace'
|
||||
);
|
||||
})
|
||||
};
|
||||
@@ -142,6 +146,11 @@ class FlowItem extends PureComponent {
|
||||
<div className="flow-item-dot" />
|
||||
}
|
||||
<div className="box-header">
|
||||
{!!this.props.do_filter_name &&
|
||||
<span className="reply-header-badge clickable" onClick={()=>{this.props.do_filter_name(DZ_NAME);}}>
|
||||
<span className="icon icon-locate" />
|
||||
</span>
|
||||
}
|
||||
{!!parseInt(props.info.likenum,10) &&
|
||||
<span className="box-header-badge">
|
||||
{props.info.likenum}
|
||||
@@ -334,18 +343,20 @@ class FlowSidebar extends PureComponent {
|
||||
// key for lazyload elem
|
||||
let view_mode_key=(this.state.rev ? 'y-' : 'n-')+(this.state.filter_name||'null');
|
||||
|
||||
let replies_cnt={};
|
||||
let replies_cnt={[DZ_NAME]:1};
|
||||
replies_to_show.forEach((r)=>{
|
||||
if(replies_cnt[r.name]===undefined)
|
||||
replies_cnt[r.name]=0;
|
||||
replies_cnt[r.name]++;
|
||||
});
|
||||
|
||||
let main_thread_elem=(
|
||||
// hide main thread when filtered
|
||||
let main_thread_elem=(this.state.filter_name && this.state.filter_name!==DZ_NAME) ? null : (
|
||||
<ClickHandler callback={(e)=>{this.show_reply_bar('',e);}}>
|
||||
<FlowItem info={this.state.info} attention={this.state.attention} img_clickable={true}
|
||||
color_picker={this.color_picker} show_pid={show_pid} replies={this.state.replies}
|
||||
set_variant={(variant)=>{this.set_variant(null,variant);}}
|
||||
do_filter_name={replies_cnt[DZ_NAME]>1 ? this.set_filter_name.bind(this) : null}
|
||||
/>
|
||||
</ClickHandler>
|
||||
);
|
||||
@@ -364,7 +375,7 @@ class FlowSidebar extends PureComponent {
|
||||
<a onClick={this.load_replies.bind(this)}>
|
||||
<span className="icon icon-refresh" /><label>刷新</label>
|
||||
</a>
|
||||
{(this.state.replies.length>1 || this.state.rev) &&
|
||||
{(this.state.replies.length>=1 || this.state.rev) &&
|
||||
<span>
|
||||
|
||||
<a onClick={this.toggle_rev.bind(this)}>
|
||||
@@ -386,6 +397,15 @@ class FlowSidebar extends PureComponent {
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
{!!this.state.filter_name &&
|
||||
<div className="box box-tip flow-item filter-name-bar">
|
||||
<p>
|
||||
<span style={{float: 'left'}}><a onClick={()=>{this.set_filter_name(null)}}>还原</a></span>
|
||||
<span className="icon icon-locate" /> 当前只看
|
||||
<ColoredSpan colors={this.color_picker.get(this.state.filter_name)}>{this.state.filter_name}</ColoredSpan>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
{!this.state.rev &&
|
||||
main_thread_elem
|
||||
}
|
||||
@@ -400,15 +420,6 @@ class FlowSidebar extends PureComponent {
|
||||
{parseInt(this.state.info.reply)-this.state.replies.length} 条回复被删除
|
||||
</div>
|
||||
}
|
||||
{!!this.state.filter_name &&
|
||||
<div className="box box-tip flow-item filter-name-bar">
|
||||
<p>
|
||||
<span style={{float: 'left'}}><a onClick={()=>{this.set_filter_name(null)}}>还原</a></span>
|
||||
<span className="icon icon-locate" /> 当前只看
|
||||
<ColoredSpan colors={this.color_picker.get(this.state.filter_name)}>{this.state.filter_name}</ColoredSpan>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
{replies_to_show.map((reply)=>(
|
||||
<LazyLoad key={reply.cid+view_mode_key} offset={1500} height="5em" overflow={true} once={true}>
|
||||
<ClickHandler callback={(e)=>{this.show_reply_bar(reply.name,e);}}>
|
||||
@@ -433,24 +444,6 @@ class FlowSidebar extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
function FlowSidebarTitle(props) {
|
||||
let last_pid=props.parents.length ? props.parents[props.parents.length-1] : null;
|
||||
return (
|
||||
<span>
|
||||
树洞
|
||||
{!!last_pid &&
|
||||
<span>
|
||||
<a onClick={()=>load_single_meta(props.show_sidebar,props.token,props.parents.slice(0,-1))(last_pid)}>
|
||||
#{last_pid}
|
||||
</a>
|
||||
→
|
||||
</span>
|
||||
}
|
||||
#{props.pid}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
class FlowItemRow extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -499,7 +492,7 @@ class FlowItemRow extends PureComponent {
|
||||
|
||||
show_sidebar() {
|
||||
this.props.show_sidebar(
|
||||
<FlowSidebarTitle pid={this.state.info.pid} parents={[]} show_sidebar={this.props.show_sidebar} token={this.props.token} />,
|
||||
'树洞 #'+this.state.info.pid,
|
||||
<FlowSidebar key={+new Date()}
|
||||
info={this.state.info} replies={this.state.replies} attention={this.state.attention} sync_state={this.setState.bind(this)}
|
||||
token={this.props.token} show_sidebar={this.props.show_sidebar} color_picker={this.color_picker}
|
||||
@@ -692,11 +685,14 @@ export class Flow extends PureComponent {
|
||||
if(this.state.mode==='list') {
|
||||
API.get_list(page,this.props.token)
|
||||
.then((json)=>{
|
||||
if(page===1)
|
||||
if(page===1 && json.data.length) { // update latest_post_id
|
||||
let max_id=-1;
|
||||
json.data.forEach((x)=>{
|
||||
if(parseInt(x.pid,10)>(parseInt(localStorage['_LATEST_POST_ID'],10)||0))
|
||||
localStorage['_LATEST_POST_ID']=x.pid;
|
||||
if(parseInt(x.pid,10)>max_id)
|
||||
max_id=parseInt(x.pid,10);
|
||||
});
|
||||
localStorage['_LATEST_POST_ID']=''+max_id;
|
||||
}
|
||||
this.setState((prev,props)=>({
|
||||
chunks: {
|
||||
title: 'News Feed',
|
||||
|
||||
@@ -5,26 +5,39 @@ export class Sidebar extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.sidebar_ref=React.createRef();
|
||||
this.do_close_bound=this.do_close.bind(this);
|
||||
this.do_back_bound=this.do_back.bind(this);
|
||||
}
|
||||
|
||||
componentDidUpdate(nextProps) {
|
||||
if(this.props.content!==nextProps.content) {
|
||||
if(this.props.stack!==nextProps.stack) {
|
||||
//console.log('sidebar top');
|
||||
if(this.sidebar_ref.current)
|
||||
this.sidebar_ref.current.scrollTop=0;
|
||||
}
|
||||
}
|
||||
|
||||
do_close() {
|
||||
this.props.show_sidebar(null,null,'clear');
|
||||
}
|
||||
do_back() {
|
||||
this.props.show_sidebar(null,null,'pop');
|
||||
}
|
||||
|
||||
render() {
|
||||
let [cur_title,cur_content]=this.props.stack[this.props.stack.length-1];
|
||||
return (
|
||||
<div className={this.props.title!==null ? 'sidebar-on' : ''}>
|
||||
<div className="sidebar-shadow" onClick={this.props.do_close} onTouchEnd={(e)=>{e.preventDefault();e.target.click();}} />
|
||||
<div className={cur_title!==null ? 'sidebar-on' : ''}>
|
||||
<div className="sidebar-shadow" onClick={this.do_back_bound} onTouchEnd={(e)=>{e.preventDefault();e.target.click();}} />
|
||||
<div ref={this.sidebar_ref} className="sidebar">
|
||||
{this.props.content}
|
||||
{cur_content}
|
||||
</div>
|
||||
<div className="sidebar-title">
|
||||
<a className="no-underline" onClick={this.props.do_close}> <span className="icon icon-back" /> </a>
|
||||
{this.props.title}
|
||||
<a className="no-underline" onClick={this.do_close_bound}> <span className="icon icon-close" /> </a>
|
||||
{this.props.stack.length>2 &&
|
||||
<a className="no-underline" onClick={this.do_back_bound}> <span className="icon icon-back" /> </a>
|
||||
}
|
||||
{cur_title}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
flex: 0 0 2em;
|
||||
flex: 0 0 4.5em;
|
||||
text-align: center;
|
||||
color: black;
|
||||
border-radius: 5px;
|
||||
@@ -32,6 +32,17 @@
|
||||
background-color: #555555;
|
||||
color: white;
|
||||
}
|
||||
.control-btn-label {
|
||||
margin-left: .25em;
|
||||
}
|
||||
@media screen and (max-width: 900px) {
|
||||
.control-btn {
|
||||
flex: 0 0 2em;
|
||||
}
|
||||
.control-btn-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.root-dark-mode .control-btn {
|
||||
color: var(--foreground-dark);
|
||||
@@ -42,11 +53,6 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.control-btn .icon:before {
|
||||
margin: .5em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.control-search {
|
||||
flex: auto;
|
||||
color: black;
|
||||
|
||||
@@ -132,10 +132,12 @@ class ControlBar extends PureComponent {
|
||||
<div className="control-bar">
|
||||
<a className="no-underline control-btn" onClick={this.do_refresh_bound}>
|
||||
<span className="icon icon-refresh" />
|
||||
<span className="control-btn-label">最新</span>
|
||||
</a>
|
||||
{!!token &&
|
||||
<a className="no-underline control-btn" onClick={this.do_attention_bound}>
|
||||
<span className="icon icon-attention" />
|
||||
<span className="control-btn-label">关注</span>
|
||||
</a>
|
||||
}
|
||||
<input className="control-search" value={this.state.search_text} placeholder="搜索 或 #PID"
|
||||
@@ -168,6 +170,7 @@ class ControlBar extends PureComponent {
|
||||
)
|
||||
}}>
|
||||
<span className={'icon icon-'+(token ? 'about' : 'login')} />
|
||||
<span className="control-btn-label">{token ? '账户' : '登录'}</span>
|
||||
</a>
|
||||
{!!token &&
|
||||
<a className="no-underline control-btn" onClick={()=>{
|
||||
@@ -180,6 +183,7 @@ class ControlBar extends PureComponent {
|
||||
)
|
||||
}}>
|
||||
<span className="icon icon-plus" />
|
||||
<span className="control-btn-label">发表</span>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user