import React, {Component, PureComponent} from 'react';
import {ColorPicker} from './color_picker';
import {Time, TitleLine, HighlightedText} from './Common.js';
import './Flows.css';
import LazyLoad from 'react-lazyload';
import {AudioWidget} from './AudioWidget.js';
import {TokenCtx} from './UserAction';
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};
const PREVIEW_REPLY_COUNT=10;
window.LATEST_POST_ID=parseInt(localStorage['_LATEST_POST_ID'],10)||0;
function Reply(props) {
return (
);
}
function FlowItem(props) {
return (
{parseInt(props.info.pid,10)>window.LATEST_POST_ID &&
}
{!!parseInt(props.info.likenum,10) &&
{props.info.likenum}
}
{!!parseInt(props.info.reply,10) &&
{props.info.reply}
}
#{props.info.pid}
{props.info.type==='image' ?

: null}
{props.info.type==='audio' ?
: null}
);
}
class FlowItemRow extends PureComponent {
constructor(props) {
super(props);
this.state={
replies: [],
reply_status: 'done',
info: props.info,
attention: false,
};
this.color_picker=new ColorPicker();
}
componentDidMount() {
if(parseInt(this.state.info.reply,10)) {
this.load_replies();
}
}
load_replies(callback) {
console.log('fetching reply',this.state.info.pid);
this.setState({
reply_status: 'loading',
});
const token_param=this.props.token ? '&token='+this.props.token : '';
fetch(
API_BASE+'/api.php?action=getcomment'+
'&pid='+this.state.info.pid+
token_param
)
.then((res)=>res.json())
.then((json)=>{
if(json.code!==0)
throw new Error(json);
const replies=json.data
.sort((a,b)=>{
return parseInt(a.timestamp,10)-parseInt(b.timestamp,10);
})
.map((info)=>{
info._display_color=this.color_picker.get(info.name);
return info;
});
this.setState((prev,props)=>({
replies: replies,
info: Object.assign({}, prev.info, {
reply: ''+replies.length,
}),
attention: !!json.attention,
reply_status: 'done',
}),callback);
})
.catch((e)=>{
console.trace(e);
this.setState({
replies: [],
reply_status: 'failed',
},callback);
});
}
show_sidebar() {
this.props.show_sidebar(
'帖子详情',
{this.state.replies.map((reply)=>(
))}
);
}
render() {
// props.do_show_details
return (
{
if(!CLICKABLE_TAGS[event.target.tagName.toLowerCase()])
this.show_sidebar();
}}>
{this.state.reply_status==='loading' &&
加载中
}
{this.state.reply_status==='failed' &&
}
{this.state.replies.slice(0,PREVIEW_REPLY_COUNT).map((reply)=>(
))}
{this.state.replies.length>PREVIEW_REPLY_COUNT &&
还有 {this.state.replies.length-PREVIEW_REPLY_COUNT} 条
}
);
}
}
function FlowChunk(props) {
return (
{({value: token})=>(
{props.list.map((info)=>(
))}
)}
);
}
export class Flow extends PureComponent {
constructor(props) {
super(props);
this.state={
mode: props.mode,
search_param: props.search_text,
loaded_pages: 0,
chunks: [],
loading_status: 'done',
};
this.on_scroll_bound=this.on_scroll.bind(this);
window.LATEST_POST_ID=parseInt(localStorage['_LATEST_POST_ID'],10)||0;
}
load_page(page) {
const failed=(err)=>{
console.trace(err);
this.setState((prev,props)=>({
loaded_pages: prev.loaded_pages-1,
loading_status: 'failed',
}));
};
const token_param=this.props.token ? '&token='+this.props.token : '';
if(page>this.state.loaded_pages+1)
throw new Error('bad page');
if(page===this.state.loaded_pages+1) {
console.log('fetching page',page);
if(this.state.mode==='list') {
fetch(
API_BASE+'/api.php?action=getlist'+
'&p='+page+
token_param
)
.then((res)=>res.json())
.then((json)=>{
if(json.code!==0)
throw new Error(json);
json.data.forEach((x)=>{
if(parseInt(x.pid,10)>(parseInt(localStorage['_LATEST_POST_ID'],10)||0))
localStorage['_LATEST_POST_ID']=x.pid;
});
this.setState((prev,props)=>({
chunks: prev.chunks.concat([{
title: 'Page '+page,
data: json.data.filter((x)=>(
prev.chunks.length===0 ||
!(prev.chunks[prev.chunks.length-1].data.some((p)=>p.pid===x.pid))
)),
}]),
loading_status: 'done',
}));
})
.catch(failed);
} else if(this.state.mode==='search') {
fetch(
API_BASE+'/api.php?action=search'+
'&pagesize='+SEARCH_PAGESIZE*page+
'&keywords='+encodeURIComponent(this.state.search_param)+
token_param
)
.then((res)=>res.json())
.then((json)=>{
if(json.code!==0)
throw new Error(json);
const finished=json.data.lengthres.json())
.then((json)=>{
if(json.code!==0)
throw new Error(json);
this.setState({
chunks: [{
title: 'PID = '+pid,
data: [json.data],
}],
mode: 'single_finished',
loading_status: 'done',
});
})
.catch(failed);
} else if(this.state.mode==='attention') {
fetch(
API_BASE+'/api.php?action=getattention'+
token_param
)
.then((res)=>res.json())
.then((json)=>{
if(json.code!==0)
throw new Error(json);
this.setState({
chunks: [{
title: 'Attention List',
data: json.data,
}],
mode: 'attention_finished',
loading_status: 'done',
});
})
.catch(failed);
} else {
console.log('nothing to load');
return;
}
this.setState((prev,props)=>({
loaded_pages: prev.loaded_pages+1,
loading_status: 'loading',
}));
}
}
on_scroll(event) {
if(event.target===document) {
const avail=document.body.scrollHeight-window.scrollY-window.innerHeight;
if(avail
{this.state.chunks.map((chunk)=>(
))}
{this.state.loading_status==='failed' &&
}
);
}
}