forked from newthuhole/hole_thu_frontend
保存关注到本地,时间线分子模式
This commit is contained in:
@@ -5,6 +5,7 @@ import { Sidebar } from './Sidebar';
|
|||||||
import { PressureHelper } from './PressureHelper';
|
import { PressureHelper } from './PressureHelper';
|
||||||
import { TokenCtx } from './UserAction';
|
import { TokenCtx } from './UserAction';
|
||||||
import { load_config, bgimg_style } from './Config';
|
import { load_config, bgimg_style } from './Config';
|
||||||
|
import { load_attentions } from './Attention.js';
|
||||||
import { listen_darkmode } from './infrastructure/functions';
|
import { listen_darkmode } from './infrastructure/functions';
|
||||||
import { LoginPopup, TitleLine } from './infrastructure/widgets';
|
import { LoginPopup, TitleLine } from './infrastructure/widgets';
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ class App extends Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
load_config();
|
load_config();
|
||||||
|
load_attentions();
|
||||||
listen_darkmode(
|
listen_darkmode(
|
||||||
{ default: undefined, light: false, dark: true }[
|
{ default: undefined, light: false, dark: true }[
|
||||||
window.config.color_scheme
|
window.config.color_scheme
|
||||||
|
|||||||
7
src/Attention.js
Normal file
7
src/Attention.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export function load_attentions() {
|
||||||
|
window.saved_attentions = JSON.parse(localStorage['saved_attentions'] || '[]');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function save_attentions() {
|
||||||
|
localStorage['saved_attentions'] = JSON.stringify(window.saved_attentions);
|
||||||
|
}
|
||||||
@@ -319,3 +319,23 @@
|
|||||||
height: 50vh;
|
height: 50vh;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.export-btn {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-submode-choice {
|
||||||
|
display: inline-block;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-submode-choice a {
|
||||||
|
margin: 10px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-submode-choice a.choiced {
|
||||||
|
margin: 10px;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|||||||
155
src/Flows.js
155
src/Flows.js
@@ -21,10 +21,9 @@ import {
|
|||||||
import './Flows.css';
|
import './Flows.css';
|
||||||
import LazyLoad, { forceCheck } from './react-lazyload/src';
|
import LazyLoad, { forceCheck } from './react-lazyload/src';
|
||||||
import { TokenCtx, ReplyForm } from './UserAction';
|
import { TokenCtx, ReplyForm } from './UserAction';
|
||||||
|
|
||||||
import { API } from './flows_api';
|
import { API } from './flows_api';
|
||||||
|
|
||||||
import { cache } from './cache';
|
import { cache } from './cache';
|
||||||
|
import { save_attentions } from './Attention'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
const IMAGE_BASE = 'https://thimg.yecdn.com/';
|
const IMAGE_BASE = 'https://thimg.yecdn.com/';
|
||||||
@@ -429,7 +428,8 @@ class FlowSidebar extends PureComponent {
|
|||||||
loading_status: 'loading',
|
loading_status: 'loading',
|
||||||
});
|
});
|
||||||
const prev_info = this.state.info;
|
const prev_info = this.state.info;
|
||||||
API.set_attention(this.state.info.pid, !this.state.attention, this.props.token)
|
const pid = prev_info.pid;
|
||||||
|
API.set_attention(pid, !this.state.attention, this.props.token)
|
||||||
.then((json) => {
|
.then((json) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading_status: 'done',
|
loading_status: 'done',
|
||||||
@@ -439,6 +439,17 @@ class FlowSidebar extends PureComponent {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
console.log(json);
|
console.log(json);
|
||||||
|
|
||||||
|
let saved_attentions = window.saved_attentions;
|
||||||
|
if (json.attention && !saved_attentions.includes(pid)) {
|
||||||
|
saved_attentions.unshift(pid)
|
||||||
|
} else if (!json.attention && saved_attentions.includes(pid)) {
|
||||||
|
const idx = saved_attentions.indexOf(pid);
|
||||||
|
saved_attentions.splice(idx, 1)
|
||||||
|
}
|
||||||
|
window.saved_attentions = saved_attentions;
|
||||||
|
save_attentions();
|
||||||
|
|
||||||
this.syncState({
|
this.syncState({
|
||||||
attention: json.attention,
|
attention: json.attention,
|
||||||
info: Object.assign({}, prev_info, {
|
info: Object.assign({}, prev_info, {
|
||||||
@@ -1102,6 +1113,67 @@ function FlowChunk(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Flow extends PureComponent {
|
export class Flow extends PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
submode: this.props.mode == 'list' ? (window.config.by_c ? 1 : 0) : 0,
|
||||||
|
subflow_render_key: +new Date(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_submode_names(mode) {
|
||||||
|
switch(mode) {
|
||||||
|
case('list'):
|
||||||
|
return ['最新', '最近回复', '近期热门'];
|
||||||
|
case('attention'):
|
||||||
|
return ['线上', '本地']
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
set_submode(submode) {
|
||||||
|
if (this.props.mode === 'list' && submode === 2) {
|
||||||
|
alert('将在下个版本提供');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
submode: submode,
|
||||||
|
subflow_render_key: +new Date(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { submode } = this.state;
|
||||||
|
const submode_names = this.get_submode_names(this.props.mode)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="aux-margin flow-submode-choice">
|
||||||
|
{submode_names.map((name, idx) => (
|
||||||
|
<a
|
||||||
|
key={idx}
|
||||||
|
className={submode === idx ? 'choiced' : ''}
|
||||||
|
onClick={this.set_submode.bind(this, idx)}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SubFlow
|
||||||
|
key={this.state.subflow_render_key}
|
||||||
|
show_sidebar={this.props.show_sidebar}
|
||||||
|
mode={this.props.mode}
|
||||||
|
submode={this.state.submode}
|
||||||
|
search_text={this.props.search_text}
|
||||||
|
token={this.props.token}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SubFlow extends PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -1112,7 +1184,6 @@ export class Flow extends PureComponent {
|
|||||||
title: '',
|
title: '',
|
||||||
data: [],
|
data: [],
|
||||||
},
|
},
|
||||||
can_export: false,
|
|
||||||
export_text: '',
|
export_text: '',
|
||||||
loading_status: 'done',
|
loading_status: 'done',
|
||||||
error_msg: null,
|
error_msg: null,
|
||||||
@@ -1137,7 +1208,7 @@ export class Flow extends PureComponent {
|
|||||||
console.log('fetching page', page);
|
console.log('fetching page', page);
|
||||||
cache();
|
cache();
|
||||||
if (this.state.mode === 'list') {
|
if (this.state.mode === 'list') {
|
||||||
API.get_list(page, this.props.token)
|
API.get_list(page, this.props.token, this.props.submode)
|
||||||
.then((json) => {
|
.then((json) => {
|
||||||
if (page === 1 && json.data.length) {
|
if (page === 1 && json.data.length) {
|
||||||
// update latest_post_id
|
// update latest_post_id
|
||||||
@@ -1235,33 +1306,52 @@ export class Flow extends PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(use_search, use_regex);
|
console.log(use_search, use_regex);
|
||||||
API.get_attention(this.props.token)
|
|
||||||
.then((json) => {
|
if (this.props.submode === 0) {
|
||||||
this.setState({
|
API.get_attention(this.props.token)
|
||||||
chunks: {
|
.then((json) => {
|
||||||
title: `${
|
this.setState({
|
||||||
use_search
|
chunks: {
|
||||||
? use_regex
|
title: `${
|
||||||
? `Result for RegEx ${regex_search.toString()} in `
|
use_search
|
||||||
: `Result for "${this.state.search_param}" in `
|
? use_regex
|
||||||
: ''
|
? `Result for RegEx ${regex_search.toString()} in `
|
||||||
}Attention List`,
|
: `Result for "${this.state.search_param}" in `
|
||||||
data: !use_search
|
: ''
|
||||||
? json.data
|
}Attention List`,
|
||||||
: !use_regex
|
data: !use_search
|
||||||
? json.data.filter((post) => {
|
? json.data
|
||||||
return this.state.search_param
|
: !use_regex
|
||||||
|
? json.data.filter((post) => {
|
||||||
|
return this.state.search_param
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.every((keyword) => post.text.includes(keyword));
|
.every((keyword) => post.text.includes(keyword));
|
||||||
}) // Not using regex
|
}) // Not using regex
|
||||||
: json.data.filter((post) => !!post.text.match(regex_search)), // Using regex
|
: json.data.filter((post) => !!post.text.match(regex_search)), // Using regex
|
||||||
},
|
},
|
||||||
mode: 'attention_finished',
|
mode: 'attention_finished',
|
||||||
loading_status: 'done',
|
loading_status: 'done',
|
||||||
can_export: !use_search,
|
});
|
||||||
});
|
if (!use_search) {
|
||||||
})
|
window.saved_attentions = Array.from(
|
||||||
.catch(failed);
|
new Set([
|
||||||
|
...window.saved_attentions,
|
||||||
|
...json.data.map(post => post.pid)
|
||||||
|
])
|
||||||
|
).sort().reverse();
|
||||||
|
save_attentions();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(failed);
|
||||||
|
} else if (this.props.submode === 1) {
|
||||||
|
this.setState({
|
||||||
|
title: 'Attention List: Local',
|
||||||
|
data: [],
|
||||||
|
export_text: `以下是浏览器本地保存的关注列表,将在下个版本提供直接展示\n\n#${
|
||||||
|
window.saved_attentions.join('\n#')
|
||||||
|
}`
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('nothing to load');
|
console.log('nothing to load');
|
||||||
return;
|
return;
|
||||||
@@ -1314,8 +1404,9 @@ export class Flow extends PureComponent {
|
|||||||
const should_deletion_detect = localStorage['DELETION_DETECT'] === 'on';
|
const should_deletion_detect = localStorage['DELETION_DETECT'] === 'on';
|
||||||
return (
|
return (
|
||||||
<div className="flow-container">
|
<div className="flow-container">
|
||||||
{this.state.can_export && (
|
|
||||||
<button type="button" onClick={this.gen_export.bind(this)}>导出</button>
|
{this.state.mode === 'attention_finished' && this.props.submode == 0 && (
|
||||||
|
<button className="export-btn" type="button" onClick={this.gen_export.bind(this)}>导出</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{this.state.export_text && (
|
{this.state.export_text && (
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
API,
|
API,
|
||||||
get_json,
|
get_json,
|
||||||
} from './flows_api';
|
} from './flows_api';
|
||||||
|
import { save_attentions } from './Attention'
|
||||||
|
|
||||||
import './UserAction.css';
|
import './UserAction.css';
|
||||||
|
|
||||||
@@ -266,7 +267,8 @@ export class ReplyForm extends Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let data = new URLSearchParams();
|
let data = new URLSearchParams();
|
||||||
data.append('pid', this.props.pid);
|
let pid = this.props.pid;
|
||||||
|
data.append('pid', pid);
|
||||||
data.append('text', this.state.text);
|
data.append('text', this.state.text);
|
||||||
fetch(
|
fetch(
|
||||||
API_BASE + '/docomment',
|
API_BASE + '/docomment',
|
||||||
@@ -286,6 +288,13 @@ export class ReplyForm extends Component {
|
|||||||
throw new Error(JSON.stringify(json));
|
throw new Error(JSON.stringify(json));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let saved_attentions = window.saved_attentions;
|
||||||
|
if (!saved_attentions.includes(pid)) {
|
||||||
|
saved_attentions.unshift(pid)
|
||||||
|
window.saved_attentions = saved_attentions;
|
||||||
|
save_attentions();
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
loading_status: 'done',
|
loading_status: 'done',
|
||||||
text: '',
|
text: '',
|
||||||
|
|||||||
@@ -136,11 +136,11 @@ export const API = {
|
|||||||
return handle_response(response, true);
|
return handle_response(response, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
get_list: async (page, token) => {
|
get_list: async (page, token, submode) => {
|
||||||
let response = await fetch(
|
let response = await fetch(
|
||||||
API_BASE + '/getlist?p=' + page
|
API_BASE + '/getlist?p=' + page
|
||||||
+ (window.config.no_c_post ? '&no_cw' : '')
|
+ (window.config.no_c_post ? '&no_cw' : '')
|
||||||
+ (window.config.by_c ? '&by_c' : ''),
|
+ (submode ? '&by_c' : ''),
|
||||||
{
|
{
|
||||||
headers: {'User-Token': token},
|
headers: {'User-Token': token},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user