adds infinite scroll setting, prevents post rerendering on list updates, adds debounce to scroll event listener

This commit is contained in:
Travis Kohlbeck 2020-02-16 16:43:49 -05:00
parent cd57edbb32
commit 2620bb8282
3 changed files with 70 additions and 8 deletions

View File

@ -101,6 +101,11 @@ export class Post extends React.Component<any, IPostState> {
});
}
shouldComponentUpdate(nextProps: any, nextState: any) {
if (nextState == this.state) return false
return true
}
togglePostMenu() {
this.setState({ menuIsOpen: !this.state.menuIsOpen });
}

View File

@ -65,6 +65,7 @@ import DomainDisabledIcon from "@material-ui/icons/DomainDisabled";
import AccountSettingsIcon from "mdi-material-ui/AccountSettings";
import AlphabeticalVariantOffIcon from "mdi-material-ui/AlphabeticalVariantOff";
import DashboardIcon from "@material-ui/icons/Dashboard";
import InfiniteIcon from "@material-ui/icons/AllInclusive";
import { Config } from "../types/Config";
import { Account } from "../types/Account";
@ -88,6 +89,7 @@ interface ISettingsState {
currentUser?: Account;
imposeCharacterLimit: boolean;
masonryLayout?: boolean;
infiniteScroll?: boolean;
}
class SettingsPage extends Component<any, ISettingsState> {
@ -120,7 +122,8 @@ class SettingsPage extends Component<any, ISettingsState> {
brandName: "Hyperspace",
federated: true,
imposeCharacterLimit: getUserDefaultBool("imposeCharacterLimit"),
masonryLayout: getUserDefaultBool("isMasonryLayout")
masonryLayout: getUserDefaultBool("isMasonryLayout"),
infiniteScroll: getUserDefaultBool("isInfiniteScroll")
};
this.toggleDarkMode = this.toggleDarkMode.bind(this);
@ -130,6 +133,7 @@ class SettingsPage extends Component<any, ISettingsState> {
this.toggleThemeDialog = this.toggleThemeDialog.bind(this);
this.toggleVisibilityDialog = this.toggleVisibilityDialog.bind(this);
this.toggleMasonryLayout = this.toggleMasonryLayout.bind(this);
this.toggleInfiniteScroll = this.toggleInfiniteScroll.bind(this);
this.changeThemeName = this.changeThemeName.bind(this);
this.changeTheme = this.changeTheme.bind(this);
this.setVisibility = this.setVisibility.bind(this);
@ -170,7 +174,6 @@ class SettingsPage extends Component<any, ISettingsState> {
getConfig().then((result: any) => {
if (result !== undefined) {
let config: Config = result;
console.log(!config.federation.allowPublicPosts);
this.setState({
federated: config.federation.allowPublicPosts
});
@ -250,6 +253,11 @@ class SettingsPage extends Component<any, ISettingsState> {
setUserDefaultBool("isMasonryLayout", !this.state.masonryLayout);
}
toggleInfiniteScroll() {
this.setState({ infiniteScroll: !this.state.infiniteScroll });
setUserDefaultBool("isInfiniteScroll", !this.state.infiniteScroll);
}
changeTheme() {
setUserDefaultTheme(this.state.selectThemeName);
window.location.reload();
@ -675,6 +683,22 @@ class SettingsPage extends Component<any, ISettingsState> {
/>
</ListItemSecondaryAction>
</ListItem>
<ListItem>
<ListItemAvatar>
<InfiniteIcon color="action" />
</ListItemAvatar>
<ListItemText
primary="Enable infinite scroll"
secondary="Automatically load more posts when scrolling"
/>
<ListItemSecondaryAction>
<Switch
checked={this.state.infiniteScroll}
onChange={this.toggleInfiniteScroll}
/>
</ListItemSecondaryAction>
</ListItem>
</List>
</Paper>
<br />

View File

@ -77,6 +77,11 @@ interface ITimelinePageState {
* the user settings.
*/
isMasonryLayout?: boolean;
/**
* Whether posts should automatically load when scrolling.
*/
isInfiniteScroll?: boolean;
}
/**
@ -109,7 +114,8 @@ class TimelinePage extends Component<ITimelinePageProps, ITimelinePageState> {
this.state = {
viewIsLoading: true,
backlogPosts: null,
isMasonryLayout: getUserDefaultBool("isMasonryLayout")
isMasonryLayout: getUserDefaultBool("isMasonryLayout"),
isInfiniteScroll: getUserDefaultBool("isInfiniteScroll"),
};
// Generate the client.
@ -132,8 +138,7 @@ class TimelinePage extends Component<ITimelinePageProps, ITimelinePageState> {
this.streamListener.on("connect", () => {
// Get the latest posts from this timeline.
this.client
.get(this.props.timeline, { limit: 10 })
.get(this.props.timeline, { limit: 50 })
// If we succeeded, update the state and turn off loading.
.then((resp: any) => {
let statuses: [Status] = resp.data;
@ -200,11 +205,34 @@ class TimelinePage extends Component<ITimelinePageProps, ITimelinePageState> {
this.streamListener.on("heartbeat", () => {});
}
/**
* Insert a delay between repeated function calls
* codeburst.io/throttling-and-debouncing-in-javascript-646d076d0a44
* @param delay How long to wait before calling function (ms)
* @param fn The function to call
*/
debounced(delay: number, fn: Function) {
let lastCall = 0
return function(...args: any) {
const now = (new Date).getTime();
if (now - lastCall < delay) {
return
}
lastCall = now;
return fn(...args)
}
}
/**
* Listen for when scroll position changes
*/
componentDidMount() {
window.addEventListener("scroll", this.shouldLoadMorePosts);
if (this.state.isInfiniteScroll) {
window.addEventListener(
"scroll",
this.debounced(200, this.shouldLoadMorePosts),
);
}
}
/**
@ -212,7 +240,12 @@ class TimelinePage extends Component<ITimelinePageProps, ITimelinePageState> {
*/
componentWillUnmount() {
this.streamListener.stop();
window.removeEventListener("scroll", this.shouldLoadMorePosts);
if (this.state.isInfiniteScroll) {
window.removeEventListener(
"scroll",
this.shouldLoadMorePosts,
);
}
}
/**
@ -241,7 +274,7 @@ class TimelinePage extends Component<ITimelinePageProps, ITimelinePageState> {
this.client
.get(this.props.timeline, {
max_id: this.state.posts[this.state.posts.length - 1].id,
limit: 20
limit: 50
})
// If we succeeded, append them to the end of the list of posts.