Improve scroll handling and cache fetch results
This commit is contained in:
parent
2aff4e97b6
commit
8555fbfa0f
|
@ -38,19 +38,17 @@ export function Admin(props) {
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
Automate with wget (keep secret, that's <b>your</b> login token): <br />
|
||||||
Automate with wget (keep secret, that's <b>your</b> login token):
|
{reveal ?
|
||||||
{reveal ?
|
<pre>
|
||||||
<pre>
|
wget \
|
||||||
wget \
|
<br /> --content-disposition \
|
||||||
<br /> --content-disposition \
|
<br /> --header="Authorization: Token {token}" \
|
||||||
<br /> --header="Authorization: Token {token}" \
|
<br /> {apiUrl}/backup/
|
||||||
<br /> {apiUrl}/backup/
|
</pre>
|
||||||
</pre>
|
:
|
||||||
:
|
<Button onClick={() => setReveal(true)}>Show Secret</Button>
|
||||||
<div><Button onClick={() => setReveal(true)}>Show Secret</Button></div>
|
}
|
||||||
}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
<p>Loading...</p>
|
<p>Loading...</p>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import './semantic-ui/semantic.min.css';
|
||||||
import './light.css';
|
import './light.css';
|
||||||
import { Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react';
|
import { Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react';
|
||||||
import { isAdmin, requester } from './utils.js';
|
import { isAdmin, requester } from './utils.js';
|
||||||
|
import { ManageScroll } from './ManageScroll.js';
|
||||||
import { Home } from './Home.js';
|
import { Home } from './Home.js';
|
||||||
import { Account } from './Account.js';
|
import { Account } from './Account.js';
|
||||||
import { Transactions, TransactionDetail } from './Transactions.js';
|
import { Transactions, TransactionDetail } from './Transactions.js';
|
||||||
|
@ -71,6 +72,8 @@ function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<ManageScroll />
|
||||||
|
|
||||||
<div className='content-wrap'>
|
<div className='content-wrap'>
|
||||||
<div className='content-wrap-inside'>
|
<div className='content-wrap-inside'>
|
||||||
|
|
||||||
|
|
|
@ -48,14 +48,17 @@ function ClassTable(props) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let classesCache = false;
|
||||||
|
|
||||||
export function Classes(props) {
|
export function Classes(props) {
|
||||||
const [classes, setClasses] = useState(false);
|
const [classes, setClasses] = useState(classesCache);
|
||||||
const { token } = props;
|
const { token } = props;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
requester('/sessions/', 'GET', token)
|
requester('/sessions/', 'GET', token)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
setClasses(res.results);
|
setClasses(res.results);
|
||||||
|
classesCache = res.results;
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|
|
@ -8,14 +8,17 @@ import { NotFound, PleaseLogin } from './Misc.js';
|
||||||
import { InstructorCourseList, InstructorCourseDetail } from './InstructorCourses.js';
|
import { InstructorCourseList, InstructorCourseDetail } from './InstructorCourses.js';
|
||||||
import { InstructorClassList } from './InstructorClasses.js';
|
import { InstructorClassList } from './InstructorClasses.js';
|
||||||
|
|
||||||
|
let courseCache = false;
|
||||||
|
|
||||||
export function Courses(props) {
|
export function Courses(props) {
|
||||||
const [courses, setCourses] = useState(false);
|
const [courses, setCourses] = useState(courseCache);
|
||||||
const { token, user } = props;
|
const { token, user } = props;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
requester('/courses/', 'GET', token)
|
requester('/courses/', 'GET', token)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
setCourses(res.results);
|
setCourses(res.results);
|
||||||
|
courseCache = res.results;
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|
44
webclient/src/ManageScroll.js
Normal file
44
webclient/src/ManageScroll.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import React, { useState, useEffect, useReducer, useContext } from 'react';
|
||||||
|
import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
|
let scrollPositions = {};
|
||||||
|
let timeout = null;
|
||||||
|
|
||||||
|
export function ManageScroll() {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const scrollListener = () => {
|
||||||
|
if (timeout) {
|
||||||
|
window.cancelAnimationFrame(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout = window.requestAnimationFrame(() => {
|
||||||
|
const key = history.location.key;
|
||||||
|
if (key in scrollPositions) {
|
||||||
|
scrollPositions[key] = window.scrollY;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener('scroll', scrollListener);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('scroll', scrollListener);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const key = history.location.key;
|
||||||
|
|
||||||
|
if (key in scrollPositions) {
|
||||||
|
window.scrollTo(0, scrollPositions[key]);
|
||||||
|
} else {
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
scrollPositions[key] = 0;
|
||||||
|
}
|
||||||
|
}, [history.location]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
null
|
||||||
|
);
|
||||||
|
};
|
|
@ -49,13 +49,16 @@ export function MembersDropdown(props) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let searchCache = '';
|
||||||
|
|
||||||
export function Members(props) {
|
export function Members(props) {
|
||||||
const [response, setResponse] = useState(false);
|
const [response, setResponse] = useState(false);
|
||||||
const searchDefault = {seq: 0, q: ''};
|
const searchDefault = {seq: 0, q: searchCache};
|
||||||
const [search, setSearch] = useState(searchDefault);
|
const [search, setSearch] = useState(searchDefault);
|
||||||
const { token } = props;
|
const { token } = props;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
searchCache = search.q;
|
||||||
requester('/search/', 'POST', token, search)
|
requester('/search/', 'POST', token, search)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (!search.seq || res.seq > response.seq) {
|
if (!search.seq || res.seq > response.seq) {
|
||||||
|
@ -82,7 +85,7 @@ export function Members(props) {
|
||||||
{search.q.length ?
|
{search.q.length ?
|
||||||
<Button
|
<Button
|
||||||
content='Clear'
|
content='Clear'
|
||||||
onClick={() => setSearch(searchDefault)}
|
onClick={() => setSearch({seq: 0, q: ''})}
|
||||||
/> : ''
|
/> : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,17 +121,20 @@ export function Members(props) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let resultCache = {};
|
||||||
|
|
||||||
export function MemberDetail(props) {
|
export function MemberDetail(props) {
|
||||||
const [result, setResult] = useState(false);
|
const { id } = useParams();
|
||||||
|
const [result, setResult] = useState(resultCache[id] || false);
|
||||||
const [refreshCount, refreshResult] = useReducer(x => x + 1, 0);
|
const [refreshCount, refreshResult] = useReducer(x => x + 1, 0);
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
const { token, user } = props;
|
const { token, user } = props;
|
||||||
const { id } = useParams();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
requester('/search/'+id+'/', 'GET', token)
|
requester('/search/'+id+'/', 'GET', token)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
setResult(res);
|
setResult(res);
|
||||||
|
resultCache[id] = res;
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user