hyperspace-desktop-client-w.../src/pages/Recommendations.tsx

366 lines
16 KiB
TypeScript

import React, { Component } from "react";
import {
withStyles,
Typography,
List,
ListItem,
Paper,
ListItemText,
Avatar,
ListItemSecondaryAction,
ListItemAvatar,
ListSubheader,
CircularProgress,
IconButton,
Divider,
Tooltip
} from "@material-ui/core";
import { styles } from "./PageLayout.styles";
import Mastodon from "megalodon";
import { Account } from "../types/Account";
import { LinkableIconButton, LinkableAvatar } from "../interfaces/overrides";
import AccountCircleIcon from "@material-ui/icons/AccountCircle";
import AssignmentIndIcon from "@material-ui/icons/AssignmentInd";
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>
<LinkableAvatar
to={`/profile/${request.id}`}
alt={request.username}
src={
request.avatar_static
}
/>
</ListItemAvatar>
<ListItemText
primary={
request.display_name ||
request.acct
}
secondary={request.acct}
/>
<ListItemSecondaryAction>
<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>
</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>
<LinkableAvatar
to={`/profile/${suggestion.id}`}
alt={suggestion.username}
src={
suggestion.avatar_static
}
/>
</ListItemAvatar>
<ListItemText
primary={
suggestion.display_name ||
suggestion.acct
}
secondary={suggestion.acct}
/>
<ListItemSecondaryAction>
<Tooltip title="View profile">
<LinkableIconButton
to={`/profile/${suggestion.id}`}
>
<AssignmentIndIcon />
</LinkableIconButton>
</Tooltip>
<Tooltip title="Follow">
<IconButton
onClick={() =>
this.followMember(
suggestion
)
}
>
<PersonAddIcon />
</IconButton>
</Tooltip>
</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
className={
classes.pageLayoutEmptyTextConstraints
}
>
<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
className={
classes.pageLayoutEmptyTextConstraints
}
>
<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));