2019-04-21 19:45:47 +02:00
|
|
|
import React, {Component} from 'react';
|
2019-04-21 20:18:25 +02:00
|
|
|
import {withStyles, Typography, List, ListItem, Paper, ListItemText, Avatar, ListItemSecondaryAction, ListItemAvatar, ListSubheader, CircularProgress, IconButton, Divider, Tooltip} from '@material-ui/core';
|
2019-04-21 19:45:47 +02:00
|
|
|
import {styles} from './PageLayout.styles';
|
|
|
|
import Mastodon from 'megalodon';
|
|
|
|
import {Account} from '../types/Account';
|
2019-04-24 23:12:08 +02:00
|
|
|
import { LinkableIconButton, LinkableAvatar } from '../interfaces/overrides';
|
2019-04-21 19:45:47 +02:00
|
|
|
import AccountCircleIcon from '@material-ui/icons/AccountCircle';
|
2019-04-24 23:12:08 +02:00
|
|
|
import AssignmentIndIcon from '@material-ui/icons/AssignmentInd';
|
2019-04-21 19:45:47 +02:00
|
|
|
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>
|
2019-04-24 23:12:08 +02:00
|
|
|
<LinkableAvatar to={`/profile/${request.id}`} alt={request.username} src={request.avatar_static}/>
|
2019-04-21 19:45:47 +02:00
|
|
|
</ListItemAvatar>
|
|
|
|
<ListItemText primary={request.display_name || request.acct} secondary={request.acct}/>
|
|
|
|
<ListItemSecondaryAction>
|
2019-04-21 20:18:25 +02:00
|
|
|
<Tooltip title="Accept request">
|
|
|
|
<IconButton onClick={() => this.handleFollowRequest(request, "authorize")}>
|
|
|
|
<CheckIcon/>
|
|
|
|
</IconButton>
|
|
|
|
</Tooltip>
|
|
|
|
<Tooltip title="Reject request">
|
|
|
|
<IconButton onClick={() => this.handleFollowRequest(request, "reject")}>
|
|
|
|
<CloseIcon/>
|
|
|
|
</IconButton>
|
|
|
|
</Tooltip>
|
|
|
|
<Tooltip title="View profile">
|
|
|
|
<LinkableIconButton to={`/profile/${request.id}`}>
|
|
|
|
<AccountCircleIcon/>
|
|
|
|
</LinkableIconButton>
|
|
|
|
</Tooltip>
|
2019-04-21 19:45:47 +02:00
|
|
|
</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>
|
2019-04-24 23:12:08 +02:00
|
|
|
<LinkableAvatar to={`/profile/${suggestion.id}`} alt={suggestion.username} src={suggestion.avatar_static}/>
|
2019-04-21 19:45:47 +02:00
|
|
|
</ListItemAvatar>
|
|
|
|
<ListItemText primary={suggestion.display_name || suggestion.acct} secondary={suggestion.acct}/>
|
|
|
|
<ListItemSecondaryAction>
|
2019-04-24 23:12:08 +02:00
|
|
|
<Tooltip title="View profile">
|
|
|
|
<LinkableIconButton to={`/profile/${suggestion.id}`}>
|
|
|
|
<AssignmentIndIcon/>
|
|
|
|
</LinkableIconButton>
|
|
|
|
</Tooltip>
|
2019-04-21 20:18:25 +02:00
|
|
|
<Tooltip title="Follow">
|
|
|
|
<IconButton onClick={() => this.followMember(suggestion)}>
|
|
|
|
<PersonAddIcon/>
|
|
|
|
</IconButton>
|
|
|
|
</Tooltip>
|
2019-04-21 19:45:47 +02:00
|
|
|
</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():
|
2019-04-27 18:43:43 +02:00
|
|
|
<div className={classes.pageLayoutEmptyTextConstraints}>
|
2019-04-21 19:45:47 +02:00
|
|
|
<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():
|
2019-04-27 18:43:43 +02:00
|
|
|
<div className={classes.pageLayoutEmptyTextConstraints}>
|
2019-04-21 19:45:47 +02:00
|
|
|
<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));
|