diff --git a/README.md b/README.md
index 57424a0..2f82131 100644
--- a/README.md
+++ b/README.md
@@ -46,4 +46,5 @@ React 版 P大树洞,[pkuhelper.pku.edu.cn/hole](http://pkuhelper.pku.edu.cn/h
- 检测瀑布流中被删除的树洞和树洞被删除的评论(`//setflag DELETION_DETECT=on`)
- 自定义背景图片(`//setflag REPLACE_ERIRI_WITH_URL=http://...`)
-- 禁用 3D Touch 功能(`//setflag DISABLE_PRESSURE=on`)
\ No newline at end of file
+- 禁用 3D Touch 功能(`//setflag DISABLE_PRESSURE=on`)
+- 禁用自动显示引用树洞功能(`//setflag DISABLE_QUOTE=on`)
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index a77cd9f..98e7161 100644
--- a/public/index.html
+++ b/public/index.html
@@ -6,7 +6,7 @@
-
+
diff --git a/public/static/fonts_2/icomoon.ttf b/public/static/fonts_2/icomoon.ttf
deleted file mode 100644
index 132accf..0000000
Binary files a/public/static/fonts_2/icomoon.ttf and /dev/null differ
diff --git a/public/static/fonts_2/icomoon.woff b/public/static/fonts_2/icomoon.woff
deleted file mode 100644
index 25b7f5b..0000000
Binary files a/public/static/fonts_2/icomoon.woff and /dev/null differ
diff --git a/public/static/fonts_2/icomoon.css b/public/static/fonts_5/icomoon.css
similarity index 71%
rename from public/static/fonts_2/icomoon.css
rename to public/static/fonts_5/icomoon.css
index ac5caf3..73f16c3 100644
--- a/public/static/fonts_2/icomoon.css
+++ b/public/static/fonts_5/icomoon.css
@@ -1,9 +1,9 @@
@font-face {
font-family: 'icomoon';
src:
- url('icomoon.ttf?y01gys') format('truetype'),
- url('icomoon.woff?y01gys') format('woff'),
- url('icomoon.svg?y01gys#icomoon') format('svg');
+ url('icomoon.ttf?gqzyp6') format('truetype'),
+ url('icomoon.woff?gqzyp6') format('woff'),
+ url('icomoon.svg?gqzyp6#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
@@ -27,15 +27,28 @@
.icon-send:before {
content: "\e900";
}
+.icon-history:before {
+ content: "\e94d";
+}
.icon-reply:before {
content: "\e96b";
}
+.icon-quote:before {
+ content: "\e977";
+}
.icon-loading:before {
content: "\e979";
}
.icon-login:before {
content: "\e98d";
}
+.icon-stats:before {
+ content: "\e99b";
+}
+.icon-upload:before {
+ content: "\e9c3";
+ font-size: 1.2em;
+}
.icon-attention:before {
content: "\e9d3";
}
@@ -54,6 +67,9 @@
.icon-refresh:before {
content: "\ea2e";
}
+.icon-back:before {
+ content: "\ea44";
+}
.icon-github:before {
content: "\eab0";
}
diff --git a/public/static/fonts_2/icomoon.svg b/public/static/fonts_5/icomoon.svg
similarity index 67%
rename from public/static/fonts_2/icomoon.svg
rename to public/static/fonts_5/icomoon.svg
index c64966a..7e6eec1 100644
--- a/public/static/fonts_2/icomoon.svg
+++ b/public/static/fonts_5/icomoon.svg
@@ -8,14 +8,19 @@
- {parts.map((part,idx)=>{ + {this.props.parts.map((part,idx)=>{ let [rule,p]=part; return ( { rule==='url' ? {p} : rule==='pid' ? {e.preventDefault(); this.props.show_pid(p);}}>{p} : rule==='nickname' ? {p} : + rule==='search' ? {p} : p } ); diff --git a/src/Flows.css b/src/Flows.css index b758871..9986afe 100644 --- a/src/Flows.css +++ b/src/Flows.css @@ -169,4 +169,26 @@ color: white; background-color: rgba(0,0,0,.6); pointer-events: none; +} + +.flow-item-row-quote { + opacity: .8; + filter: brightness(90%); +} + +.flow-item-quote>.box { + margin-left: 2.5em; + max-height: 15em; + overflow-y: hidden; +} + +.quote-tip { + margin-top: .5em; + margin-bottom: -10em; /* so that it will not block reply bar */ + float: left; + display: flex; + flex-direction: column; + width: 2.5em; + text-align: center; + color: white; } \ No newline at end of file diff --git a/src/Flows.js b/src/Flows.js index b83df7a..d29fa6a 100644 --- a/src/Flows.js +++ b/src/Flows.js @@ -1,7 +1,8 @@ import React, {Component, PureComponent} from 'react'; import copy from 'copy-to-clipboard'; import {ColorPicker} from './color_picker'; -import {format_time, Time, TitleLine, HighlightedText, ClickHandler} from './Common'; +import {split_text,NICKNAME_RE,PID_RE,URL_RE} from './text_splitter'; +import {format_time, build_highlight_re, Time, TitleLine, HighlightedText, ClickHandler} from './Common'; import './Flows.css'; import LazyLoad from 'react-lazyload'; import {AudioWidget} from './AudioWidget'; @@ -62,6 +63,11 @@ class Reply extends PureComponent { } render() { + let parts=split_text(this.props.info.text,[ + ['url',URL_RE], + ['pid',PID_RE], + ['nickname',NICKNAME_RE], + ]); return (-); @@ -95,41 +101,54 @@ class FlowItem extends PureComponent { render() { let props=this.props; + let parts=props.parts||split_text(props.info.text,[ + ['url',URL_RE], + ['pid',PID_RE], + ['nickname',NICKNAME_RE], + ]); 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}
- - --))} @@ -562,8 +672,9 @@ export class Flow extends PureComponent { return (- {props.info.type==='image' && - - {props.img_clickable ? -
+: -
- } -
+ {!!props.is_quote && +); } @@ -331,10 +350,9 @@ class FlowItemRow extends PureComponent { this.state={ replies: [], reply_status: 'done', - info: props.info, + info: Object.assign({},props.info,{variant: {}}), attention: false, }; - this.state.info.variant={}; this.color_picker=new ColorPicker(); this.show_pid=load_single_meta(this.props.show_sidebar,this.props.token); } @@ -385,12 +403,32 @@ class FlowItemRow extends PureComponent { } render() { - return ( -++ } ++提到++ {parseInt(props.info.pid,10)>window.LATEST_POST_ID && } +- {!!(props.attention && props.info.variant.latest_reply) && -+ {!!parseInt(props.info.likenum,10) && + + {props.info.likenum} + + + } + {!!parseInt(props.info.reply,10) && + + {props.info.reply} + + + } ++#{props.info.pid}
+ + +++ {!!(props.attention && props.info.variant.latest_reply) && ++ {props.info.type==='image' && + + {props.img_clickable ? +
+ } + {props.info.type==='audio' &&: +
+ } +
} + 最新回复
} - {props.info.type==='audio' &&} 最新回复
- }{ + let hl_rules=[ + ['url',URL_RE], + ['pid',PID_RE], + ['nickname',NICKNAME_RE], + ]; + if(this.props.search_param) + hl_rules.push(['search',build_highlight_re(this.props.search_param,' ')]); + let parts=split_text(this.state.info.text,hl_rules); + + let quote_id=null; + if(!this.props.is_quote && localStorage['DISABLE_QUOTE']!=='on') + for(let [mode,content] of parts) + if(mode==='pid') + if(quote_id===null) + quote_id=parseInt(content); + else { + quote_id=null; + break; + } + + let res=( +}{ if(!CLICKABLE_TAGS[event.target.tagName.toLowerCase()]) this.show_sidebar(); }}> -); + + return quote_id ? ( + {this.state.reply_status==='loading' &&加载中} @@ -406,6 +444,78 @@ class FlowItemRow extends PureComponent {+ {res} ++ ) : res; + } +} + +class FlowItemQuote extends PureComponent { + constructor(props) { + super(props); + this.state={ + loading_status: 'empty', + error_msg: null, + info: null, + }; + } + + componentDidMount() { + this.load(); + } + + load() { + this.setState({ + loading_status: 'loading', + },()=>{ + API.get_single(this.props.pid,this.props.token) + .then((json)=>{ + this.setState({ + loading_status: 'done', + info: json.data, + }); + }) + .catch((err)=>{ + if((''+err).indexOf('没有这条树洞')!==-1) + this.setState({ + loading_status: 'empty', + }); + else + this.setState({ + loading_status: 'error', + error_msg: ''+err, + }); + }); + }); + } + + render() { + if(this.state.loading_status==='empty') + return null; + else if(this.state.loading_status==='loading') + return ( ++ + + 提到了 #{this.props.pid} ++ ); + else if(this.state.loading_status==='error') + return ( ++ ++ ); + else // 'done' + return ( +{this.state.error_msg}
++ ); } } @@ -425,7 +535,7 @@ function FlowChunk(props) { + deletion_detect={props.deletion_detect} search_param={props.search_param} /> {this.state.loading_status==='failed' &&