import React, { Component } from 'react';
import {
get_api_base,
get_api_base_2,
STORAGE_BASE,
SafeTextarea,
PromotionBar,
HighlightedMarkdown,
check_service_work_update,
} from './Common';
import { MessageViewer } from './Message';
import { LoginPopup } from './infrastructure/widgets';
import { ColorPicker } from './color_picker';
import { ConfigUI } from './Config';
import copy from 'copy-to-clipboard';
import { get_json } from './flows_api';
import { save_attentions } from './Attention';
import './UserAction.css';
const REPOSITORY = 'https://git.thu.monster/newthuhole/';
const EMAIL = 'hole_thu@riseup.net';
export const TokenCtx = React.createContext({
value: null,
set_value: () => {},
});
export function InfoSidebar(props) {
return (
意见反馈请加tag #意见反馈 或到github后端的issue区。
新T树洞强烈期待有其他更多树洞的出现,一起分布式互联,构建清华树洞族。详情见
关于 中的描述。
联系我们:{EMAIL} 。
);
}
export class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
custom_title: window.TITLE || '',
auto_block_rank: window.AUTO_BLCOK || 2,
is_admin: window.IS_ADMIN,
};
}
update_title(title, token) {
if (title === window.TITLE) {
alert('无变化');
return;
}
let data = new FormData();
data.append('title', title);
data.append('secret', window.TITLE_SECRET);
fetch(get_api_base_2() + '/set-title', {
method: 'POST',
headers: { 'User-Token': token },
body: data,
})
.then(get_json)
.then((j) => {
if (j.code !== 0) {
throw new Error(j.msg);
}
window.TITLE = title;
window.TITLE_SECRET = j.data;
localStorage['TITLE_SECRET'] = j.data;
alert('专属头衔设置成功');
window.IS_ADMIN = false;
window.IS_CANDIDATE = false;
this.setState({ is_admin: false });
})
.catch((err) => alert('设置头衔出错了:\n' + err));
}
update_auto_block(rank, token) {
let data = new FormData();
data.append('rank', rank);
fetch(get_api_base() + '/auto_block', {
method: 'POST',
headers: { 'User-Token': token },
body: data,
})
.then(get_json)
.then((j) => {
if (j.code !== 0) {
throw new Error(j.msg);
}
window.AUTO_BLCOK = rank;
alert('设置自动拉黑阈值成功');
})
.catch((err) => alert('设置自动拉黑出错了:\n' + err));
}
copy_token(token) {
if (copy(token)) alert('复制成功!\n请一定不要泄露哦');
}
render() {
return (
{(token) => (
{/*{!!token.value &&*/}
{/*
*/}
{/*}*/}
{token.value ? (
) : (
{(do_popup) => (
新T树洞
面向T大学生,通过已验证身份的第三方服务授权登陆。
)}
)}
)}
);
}
}
export class ReplyForm extends Component {
constructor(props) {
super(props);
this.state = {
text: '',
loading_status: 'done',
preview: false,
use_title: false,
};
this.on_change_bound = this.on_change.bind(this);
this.on_use_title_change_bound = this.on_use_title_change.bind(this);
this.area_ref = this.props.area_ref || React.createRef();
this.global_keypress_handler_bound =
this.global_keypress_handler.bind(this);
this.color_picker = new ColorPicker();
this.forced_use_title = window.IS_ADMIN || window.IS_CANDIDATE;
}
global_keypress_handler(e) {
if (
e.code === 'Enter' &&
!e.ctrlKey &&
!e.altKey &&
['input', 'textarea'].indexOf(e.target.tagName.toLowerCase()) === -1
) {
if (this.area_ref.current) {
e.preventDefault();
this.area_ref.current.focus();
}
}
}
componentDidMount() {
document.addEventListener('keypress', this.global_keypress_handler_bound);
}
componentWillUnmount() {
document.removeEventListener(
'keypress',
this.global_keypress_handler_bound,
);
}
on_change(value) {
this.setState({
text: value,
});
}
on_use_title_change(event) {
this.setState({
use_title: event.target.checked,
});
}
on_submit(event) {
if (event) event.preventDefault();
if (this.state.loading_status === 'loading') return;
if (!this.state.text) return;
this.setState({
loading_status: 'loading',
});
const { pid } = this.props;
const { text, use_title } = this.state;
let data = new URLSearchParams({
text: text,
use_title: use_title ? '1' : '',
});
fetch(`${get_api_base_2()}/post/${pid}/comment`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Token': this.props.token,
},
body: data,
})
.then(get_json)
.then((json) => {
if (json.code !== 0) {
throw new Error(json.msg);
}
let saved_attentions = window.saved_attentions;
if (!saved_attentions.includes(pid)) {
saved_attentions.unshift(pid);
window.saved_attentions = saved_attentions;
save_attentions();
}
this.setState(
{
loading_status: 'done',
text: '',
preview: false,
},
() => {
this.area_ref.current.clear_and_then(this.props.on_complete);
},
);
})
.catch((e) => {
console.error(e);
alert('回复失败\n' + e);
this.setState({
loading_status: 'done',
});
});
}
toggle_preview() {
this.setState({
preview: !this.state.preview,
});
}
render() {
return (
);
}
}
export class PostForm extends Component {
constructor(props) {
super(props);
this.state = {
text: '',
upload_progress_text: '',
is_loading: false,
file_name: '',
file_type: '',
cw: window.CW_BACKUP || '',
allow_search: window.AS_BACKUP || false,
loading_status: 'done',
preview: false,
has_poll: !!window.POLL_BACKUP,
poll_options: JSON.parse(window.POLL_BACKUP || '[""]'),
use_title: false,
};
this.area_ref = React.createRef();
this.on_change_bound = this.on_change.bind(this);
this.on_allow_search_change_bound = this.on_allow_search_change.bind(this);
this.on_use_title_change_bound = this.on_use_title_change.bind(this);
this.on_cw_change_bound = this.on_cw_change.bind(this);
this.on_poll_option_change_bound = this.on_poll_option_change.bind(this);
this.color_picker = new ColorPicker();
this.forced_use_title = window.IS_ADMIN || window.IS_CANDIDATE;
}
componentDidMount() {
if (this.area_ref.current) this.area_ref.current.focus();
}
componentWillUnmount() {
const { cw, allow_search, has_poll, poll_options } = this.state;
window.CW_BACKUP = cw;
window.AS_BACKUP = allow_search;
localStorage['DEFAULT_ALLOW_SEARCH'] = allow_search ? '1' : '';
window.POLL_BACKUP = has_poll ? JSON.stringify(poll_options) : null;
}
on_allow_search_change(event) {
this.setState({
allow_search: event.target.checked,
});
}
on_use_title_change(event) {
this.setState({
use_title: event.target.checked,
});
}
on_cw_change(event) {
this.setState({
cw: event.target.value,
});
}
on_change(value) {
this.setState({
text: value,
});
}
do_post() {
const { cw, text, allow_search, use_title, has_poll, poll_options } =
this.state;
let data = new URLSearchParams({
cw: cw,
text: text,
allow_search: allow_search ? '1' : '',
use_title: use_title ? '1' : '',
room_id: window.config.room,
});
if (has_poll) {
poll_options.forEach((opt) => {
if (opt) data.append('poll_options', opt);
});
}
fetch(get_api_base() + '/dopost', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Token': this.props.token,
},
body: data,
})
.then(get_json)
.then((json) => {
if (json.code !== 0) {
throw new Error(json.msg);
}
this.setState(
{
loading_status: 'done',
text: '',
preview: false,
cw: '',
has_poll: false,
},
() => {
this.area_ref.current.clear_and_then(this.props.on_complete);
},
);
})
.catch((e) => {
console.error(e);
alert('发表失败\n' + e);
this.setState({
loading_status: 'done',
});
});
}
on_submit(event) {
if (event) event.preventDefault();
if (this.state.loading_status === 'loading') return;
if (!this.state.text) return;
this.setState({
loading_status: 'loading',
});
this.do_post();
}
toggle_preview() {
this.setState({
preview: !this.state.preview,
});
}
on_poll_option_change(event, idx) {
let poll_options = this.state.poll_options;
let text = event.target.value;
poll_options[idx] = text;
if (!text && poll_options.length > 1) {
poll_options.splice(idx, 1);
}
if (poll_options[poll_options.length - 1] && poll_options.length < 8) {
poll_options.push('');
}
this.setState({ poll_options: poll_options });
}
on_file_change(event) {
console.log(event);
let f = event.target.files[0];
if (f) {
console.log(f);
this.setState({ is_loading: true, file_name: f.name, file_type: f.type });
// let data = new FormData();
// data.append('file', f);
var xh = new XMLHttpRequest();
xh.upload.addEventListener(
'progress',
this.upload_progress.bind(this),
false,
);
xh.addEventListener('load', this.upload_complete.bind(this), false);
xh.addEventListener('error', this.upload_error.bind(this), false);
xh.addEventListener('abort', this.upload_abort.bind(this), false);
xh.open('POST', get_api_base_2() + '/upload');
xh.setRequestHeader('User-Token', this.props.token);
xh.send(f);
}
}
update_text_after_upload(data) {
const { file_name, file_type } = this.state;
let url = `${STORAGE_BASE}${data.path}?filename=${encodeURIComponent(
file_name,
)}&filetype=${encodeURIComponent(file_type)}`;
let new_text =
this.state.text +
'\n' +
(file_type.startsWith('image/') ? `` : url);
this.setState({ text: new_text });
this.area_ref.current.set(new_text);
}
upload_progress(event) {
console.log(event.loaded, event.total);
this.setState({
upload_progress_text: `${((event.loaded * 100) / event.total).toFixed(
2,
)}%`,
});
}
upload_complete(event) {
try {
let j = JSON.parse(event.target.responseText);
if (j.code !== 0) {
alert(j.msg);
throw new Error();
}
this.update_text_after_upload(j.data);
this.setState({ is_loading: false });
} catch (e) {
console.log(e);
this.upload_error(event);
}
}
upload_error(event) {
alert(
'上传失败\n' +
(event.target.responseText.length < 100
? event.target.responseText
: event.target.status),
);
this.setState({ is_loading: false });
}
upload_abort(event) {
alert('上传已中断');
this.setState({ is_loading: false });
}
render() {
const {
has_poll,
poll_options,
preview,
loading_status,
upload_progress_text,
is_loading,
file_name,
} = this.state;
return (
);
}
}