Create Recommendations page
This commit is contained in:
parent
e0a7034bfd
commit
b97a61986d
|
@ -17,6 +17,7 @@ import SearchPage from './pages/Search';
|
|||
import Composer from './pages/Compose';
|
||||
import WelcomePage from './pages/Welcome';
|
||||
import MessagesPage from './pages/Messages';
|
||||
import RecommendationsPage from './pages/Recommendations';
|
||||
import {withSnackbar} from 'notistack';
|
||||
import {PrivateRoute} from './interfaces/overrides';
|
||||
import { userLoggedIn } from './utilities/accounts';
|
||||
|
@ -60,6 +61,7 @@ class App extends Component<any, any> {
|
|||
<PrivateRoute path="/settings" component={Settings}/>
|
||||
<PrivateRoute path="/about" component={AboutPage}/>
|
||||
<PrivateRoute path="/compose" component={Composer}/>
|
||||
<PrivateRoute path="/recommended" component={RecommendationsPage}/>
|
||||
</div>
|
||||
|
||||
</MuiThemeProvider>
|
||||
|
|
|
@ -238,9 +238,9 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
|||
}
|
||||
<Divider/>
|
||||
<ListSubheader>More</ListSubheader>
|
||||
<LinkableListItem button key="recommended" to="/recommended" disabled>
|
||||
<LinkableListItem button key="recommended" to="/recommended">
|
||||
<ListItemIcon><GroupIcon/></ListItemIcon>
|
||||
<ListItemText primary="Who to follow" secondary="Coming soon!"/>
|
||||
<ListItemText primary="Who to follow"/>
|
||||
</LinkableListItem>
|
||||
<LinkableListItem button key="settings" to="/settings">
|
||||
<ListItemIcon><SettingsIcon/></ListItemIcon>
|
||||
|
|
|
@ -0,0 +1,228 @@
|
|||
import React, {Component} from 'react';
|
||||
import {withStyles, Typography, List, ListItem, Paper, ListItemText, Avatar, ListItemSecondaryAction, ListItemAvatar, ListSubheader, CircularProgress, IconButton, Divider} from '@material-ui/core';
|
||||
import {styles} from './PageLayout.styles';
|
||||
import Mastodon from 'megalodon';
|
||||
import {Account} from '../types/Account';
|
||||
import { LinkableIconButton } from '../interfaces/overrides';
|
||||
import AccountCircleIcon from '@material-ui/icons/AccountCircle';
|
||||
import PersonAddIcon from '@material-ui/icons/PersonAdd';
|
||||
import CheckIcon from '@material-ui/icons/Check';
|
||||
import CloseIcon from '@material-ui/icons/Close';
|
||||
import {withSnackbar, withSnackbarProps} from 'notistack';
|
||||
|
||||
interface IRecommendationsPageProps extends withSnackbarProps {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
interface IRecommendationsPageState {
|
||||
viewIsLoading: boolean;
|
||||
viewDidLoad?: boolean;
|
||||
viewDidError?: Boolean;
|
||||
viewDidErrorCode?: string;
|
||||
requestedFollows?: [Account];
|
||||
followSuggestions?: [Account];
|
||||
}
|
||||
|
||||
class RecommendationsPage extends Component<IRecommendationsPageProps, IRecommendationsPageState> {
|
||||
|
||||
client: Mastodon;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.client = new Mastodon(localStorage.getItem('access_token') as string, localStorage.getItem('baseurl') + "/api/v1");
|
||||
this.state = {
|
||||
viewIsLoading: true
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.client.get('/follow_requests').then((resp: any) => {
|
||||
let requestedFollows: [Account] = resp.data;
|
||||
this.setState({ requestedFollows })
|
||||
}).catch((err: Error) => {
|
||||
this.setState({
|
||||
viewIsLoading: false,
|
||||
viewDidError: true,
|
||||
viewDidErrorCode: err.name
|
||||
});
|
||||
console.error(err.message);
|
||||
})
|
||||
|
||||
this.client.get('/suggestions').then((resp: any) => {
|
||||
let followSuggestions: [Account] = resp.data;
|
||||
this.setState({
|
||||
viewIsLoading: false,
|
||||
viewDidLoad: true,
|
||||
followSuggestions
|
||||
})
|
||||
}).catch((err: Error) => {
|
||||
this.setState({
|
||||
viewIsLoading: false,
|
||||
viewDidError: true,
|
||||
viewDidErrorCode: err.name
|
||||
});
|
||||
console.error(err.message);
|
||||
})
|
||||
}
|
||||
|
||||
followMember(acct: Account) {
|
||||
this.client.post(`/accounts/${acct.id}/follow`).then((resp: any) => {
|
||||
this.props.enqueueSnackbar('You are now following this account.');
|
||||
this.client.del(`/suggestions/${acct.id}`).then((resp: any) => {
|
||||
let followSuggestions = this.state.followSuggestions;
|
||||
if (followSuggestions) {
|
||||
followSuggestions.forEach((suggestion: Account, index: number) => {
|
||||
if (followSuggestions && suggestion.id === acct.id) {
|
||||
followSuggestions.splice(index, 1);
|
||||
}
|
||||
});
|
||||
this.setState({ followSuggestions });
|
||||
}
|
||||
})
|
||||
}).catch((err: Error) => {
|
||||
this.props.enqueueSnackbar("Couldn't follow account: " + err.name, { variant: 'error' });
|
||||
console.error(err.message);
|
||||
})
|
||||
}
|
||||
|
||||
handleFollowRequest(acct: Account, type: "authorize" | "reject") {
|
||||
this.client.post(`/follow_requests/${acct.id}/${type}`).then((resp: any) => {
|
||||
|
||||
let requestedFollows = this.state.requestedFollows;
|
||||
if (requestedFollows) {
|
||||
requestedFollows.forEach((request: Account, index: number) => {
|
||||
if (requestedFollows && request.id === acct.id) {
|
||||
requestedFollows.splice(index, 1);
|
||||
};
|
||||
});
|
||||
};
|
||||
this.setState({requestedFollows});
|
||||
|
||||
let verb: string = type;
|
||||
verb === "authorize"? verb = "authorized": verb = "rejected";
|
||||
this.props.enqueueSnackbar(`You have ${verb} this request.`);
|
||||
}).catch((err: Error) => {
|
||||
this.props.enqueueSnackbar(`Couldn't ${type} this request: ${err.name}`, { variant: 'error' });
|
||||
console.error(err.message);
|
||||
})
|
||||
}
|
||||
|
||||
showFollowRequests() {
|
||||
const {classes} = this.props;
|
||||
return (
|
||||
<div>
|
||||
<ListSubheader>Follow requests</ListSubheader>
|
||||
<Paper className={classes.pageListConstraints}>
|
||||
<List>
|
||||
{
|
||||
this.state.requestedFollows?
|
||||
this.state.requestedFollows.map((request: Account) => {
|
||||
return (
|
||||
<ListItem key={request.id}>
|
||||
<ListItemAvatar>
|
||||
<Avatar alt={request.username} src={request.avatar_static}/>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={request.display_name || request.acct} secondary={request.acct}/>
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton onClick={() => this.handleFollowRequest(request, "authorize")}>
|
||||
<CheckIcon/>
|
||||
</IconButton>
|
||||
<IconButton onClick={() => this.handleFollowRequest(request, "reject")}>
|
||||
<CloseIcon/>
|
||||
</IconButton>
|
||||
<LinkableIconButton to={`/profile/${request.id}`}>
|
||||
<AccountCircleIcon/>
|
||||
</LinkableIconButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
);
|
||||
}): null
|
||||
}
|
||||
</List>
|
||||
</Paper>
|
||||
<br/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
showFollowSuggestions() {
|
||||
const {classes} = this.props;
|
||||
return (
|
||||
<div>
|
||||
<ListSubheader>Suggested accounts</ListSubheader>
|
||||
<Paper className={classes.pageListConstraints}>
|
||||
<List>
|
||||
{
|
||||
this.state.followSuggestions?
|
||||
this.state.followSuggestions.map((suggestion: Account) => {
|
||||
return (
|
||||
<ListItem key={suggestion.id}>
|
||||
<ListItemAvatar>
|
||||
<Avatar alt={suggestion.username} src={suggestion.avatar_static}/>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={suggestion.display_name || suggestion.acct} secondary={suggestion.acct}/>
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton onClick={() => this.followMember(suggestion)}>
|
||||
<PersonAddIcon/>
|
||||
</IconButton>
|
||||
<LinkableIconButton to={`/profile/${suggestion.id}`}>
|
||||
<AccountCircleIcon/>
|
||||
</LinkableIconButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
);
|
||||
}): null
|
||||
}
|
||||
</List>
|
||||
</Paper>
|
||||
<br/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {classes} = this.props;
|
||||
return (
|
||||
<div className={classes.pageLayoutConstraints}>
|
||||
{
|
||||
this.state.viewDidLoad?
|
||||
<div>
|
||||
{
|
||||
this.state.requestedFollows && this.state.requestedFollows.length > 0?
|
||||
this.showFollowRequests():
|
||||
<div>
|
||||
<Typography variant="h6">You don't have any follow requests.</Typography>
|
||||
<br/>
|
||||
</div>
|
||||
}
|
||||
<Divider/>
|
||||
<br/>
|
||||
{
|
||||
this.state.followSuggestions && this.state.followSuggestions.length > 0? this.showFollowSuggestions():
|
||||
<div>
|
||||
<Typography variant="h5">We don't have any suggestions for you.</Typography>
|
||||
<Typography paragraph>Why not interact with the fediverse a bit by creating a new post?</Typography>
|
||||
</div>
|
||||
}
|
||||
</div>: null
|
||||
}
|
||||
{
|
||||
this.state.viewDidError?
|
||||
<Paper className={classes.errorCard}>
|
||||
<Typography variant="h4">Bummer.</Typography>
|
||||
<Typography variant="h6">Something went wrong when loading this timeline.</Typography>
|
||||
<Typography>{this.state.viewDidErrorCode? this.state.viewDidErrorCode: ""}</Typography>
|
||||
</Paper>:
|
||||
<span/>
|
||||
}
|
||||
{
|
||||
this.state.viewIsLoading?
|
||||
<div style={{ textAlign: 'center' }}><CircularProgress className={classes.progress} color="primary" /></div>:
|
||||
<span/>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(withSnackbar(RecommendationsPage));
|
Loading…
Reference in New Issue