Merge pull request #85 from hyperspacedev/small-refinements-b

Small refinements and touch-ups
This commit is contained in:
Marquis Kurt 2019-09-23 13:34:49 -04:00 committed by GitHub
commit 1a4d4120aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 6585 additions and 6097 deletions

94
package-lock.json generated
View File

@ -1297,6 +1297,12 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"@types/events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
"integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==",
"dev": true
},
"@types/history": { "@types/history": {
"version": "4.7.2", "version": "4.7.2",
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.2.tgz", "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.2.tgz",
@ -1491,12 +1497,13 @@
"@types/unist": "*" "@types/unist": "*"
} }
}, },
"@types/ws": { "@types/websocket": {
"version": "6.0.3", "version": "0.0.40",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-0.0.40.tgz",
"integrity": "sha512-yBTM0P05Tx9iXGq00BbJPo37ox68R5vaGTXivs6RGh/BQ6QP5zqZDGWdAO6JbRE/iR1l80xeGAwCQS2nMV9S/w==", "integrity": "sha512-ldteZwWIgl9cOy7FyvYn+39Ah4+PfpVE72eYKw75iy2L0zTbhbcwvzeJ5IOu6DQP93bjfXq0NGHY6FYtmYoqFQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/events": "*",
"@types/node": "*" "@types/node": "*"
} }
}, },
@ -12191,19 +12198,19 @@
"dev": true "dev": true
}, },
"megalodon": { "megalodon": {
"version": "1.0.3", "version": "0.6.4",
"resolved": "https://registry.npmjs.org/megalodon/-/megalodon-1.0.3.tgz", "resolved": "https://registry.npmjs.org/megalodon/-/megalodon-0.6.4.tgz",
"integrity": "sha512-RcJT3HRWCXQcE5ZQUpLEjJ+HgWvwoTpXr4XLUf0tWrtmxrDnIW43pOASDb3G7MOBKYonzM1pMfAmR2Yfh3Qw/g==", "integrity": "sha512-WGYhcSxGYlBwZSm5VebxLqnbpPemum9/6lJUi1HBsVzF5jXc9fdumhXH0vqGhWdovdqRT86iXBDJl5SwUrbr2A==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/oauth": "^0.9.0", "@types/oauth": "^0.9.0",
"@types/request": "^2.47.0", "@types/request": "^2.47.0",
"@types/ws": "^6.0.1", "@types/websocket": "0.0.40",
"axios": "^0.18.1", "axios": "^0.18.0",
"oauth": "^0.9.15", "oauth": "^0.9.15",
"request": "^2.87.0", "request": "^2.87.0",
"typescript": "^3.4.5", "typescript": "^2.9.1",
"ws": "^7.0.1" "websocket": "^1.0.28"
}, },
"dependencies": { "dependencies": {
"axios": { "axios": {
@ -12247,19 +12254,10 @@
"dev": true "dev": true
}, },
"typescript": { "typescript": {
"version": "3.6.3", "version": "2.9.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
"integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==", "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
"dev": true "dev": true
},
"ws": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.1.2.tgz",
"integrity": "sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg==",
"dev": true,
"requires": {
"async-limiter": "^1.0.0"
}
} }
} }
}, },
@ -19851,6 +19849,15 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true "dev": true
}, },
"typedarray-to-buffer": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
"dev": true,
"requires": {
"is-typedarray": "^1.0.0"
}
},
"typescript": { "typescript": {
"version": "3.4.1", "version": "3.4.1",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.1.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.1.tgz",
@ -21010,6 +21017,41 @@
} }
} }
}, },
"websocket": {
"version": "1.0.30",
"resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.30.tgz",
"integrity": "sha512-aO6klgaTdSMkhfl5VVJzD5fm+Srhh5jLYbS15+OiI1sN6h/RU/XW6WN9J1uVIpUKNmsTvT3Hs35XAFjn9NMfOw==",
"dev": true,
"requires": {
"debug": "^2.2.0",
"nan": "^2.14.0",
"typedarray-to-buffer": "^3.1.5",
"yaeti": "^0.0.6"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"nan": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
"dev": true
}
}
},
"websocket-driver": { "websocket-driver": {
"version": "0.7.0", "version": "0.7.0",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz",
@ -21368,6 +21410,12 @@
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
"dev": true "dev": true
}, },
"yaeti": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
"integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=",
"dev": true
},
"yallist": { "yallist": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",

View File

@ -25,10 +25,10 @@
"file-dialog": "^0.0.7", "file-dialog": "^0.0.7",
"material-ui-pickers": "^2.2.4", "material-ui-pickers": "^2.2.4",
"mdi-material-ui": "^5.13.0", "mdi-material-ui": "^5.13.0",
"megalodon": "^1.0.3", "megalodon": "^0.6.4",
"moment": "^2.24.0", "moment": "^2.24.0",
"notistack": "^0.5.1", "notistack": "^0.5.1",
"prettier": "^1.18.2", "prettier": "1.18.2",
"query-string": "^6.8.2", "query-string": "^6.8.2",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

After

Width:  |  Height:  |  Size: 3.3 MiB

View File

@ -20,7 +20,6 @@ import MessagesPage from "./pages/Messages";
import RecommendationsPage from "./pages/Recommendations"; import RecommendationsPage from "./pages/Recommendations";
import Missingno from "./pages/Missingno"; import Missingno from "./pages/Missingno";
import You from "./pages/You"; import You from "./pages/You";
import Blocked from "./pages/Blocked";
import { withSnackbar } from "notistack"; import { withSnackbar } from "notistack";
import { PrivateRoute } from "./interfaces/overrides"; import { PrivateRoute } from "./interfaces/overrides";
import { userLoggedIn } from "./utilities/accounts"; import { userLoggedIn } from "./utilities/accounts";
@ -57,8 +56,6 @@ class App extends Component<any, any> {
removeBodyBackground() { removeBodyBackground() {
if (isDarwinApp()) { if (isDarwinApp()) {
document.body.style.backgroundColor = "transparent"; document.body.style.backgroundColor = "transparent";
console.log("Changed!");
console.log(`New color: ${document.body.style.backgroundColor}`);
} }
} }
@ -91,9 +88,7 @@ class App extends Component<any, any> {
component={Conversation} component={Conversation}
/> />
<PrivateRoute path="/search" component={SearchPage} /> <PrivateRoute path="/search" component={SearchPage} />
<PrivateRoute path="/blocked" component={Blocked} />
<PrivateRoute path="/settings" component={Settings} /> <PrivateRoute path="/settings" component={Settings} />
<PrivateRoute path="/you" component={You} /> <PrivateRoute path="/you" component={You} />
<PrivateRoute path="/about" component={AboutPage} /> <PrivateRoute path="/about" component={AboutPage} />
<PrivateRoute path="/compose" component={Composer} /> <PrivateRoute path="/compose" component={Composer} />

View File

@ -46,7 +46,7 @@ export const styles = (theme: Theme) =>
borderBottomColor: darken(theme.palette.primary.dark, 0.2), borderBottomColor: darken(theme.palette.primary.dark, 0.2),
borderBottomWidth: 1, borderBottomWidth: 1,
borderBottomStyle: isDarwinApp() ? "solid" : "none", borderBottomStyle: isDarwinApp() ? "solid" : "none",
boxShadow: isDarwinApp() ? "none" : "inherit" boxShadow: isDarwinApp() ? "none" : theme.shadows["4"]
}, },
appBarMenuButton: { appBarMenuButton: {
marginLeft: -12, marginLeft: -12,

View File

@ -103,7 +103,9 @@ export class AppLayout extends Component<any, IAppLayoutState> {
this.setState({ currentUser: data }); this.setState({ currentUser: data });
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.enqueueSnackbar("Couldn't find profile info: " + err.name); this.props.enqueueSnackbar(
"Couldn't find profile info: " + err.name
);
console.error(err.message); console.error(err.message);
}); });
} }
@ -113,7 +115,9 @@ export class AppLayout extends Component<any, IAppLayoutState> {
let config: Config = result; let config: Config = result;
this.setState({ this.setState({
enableFederation: config.federation.enablePublicTimeline, enableFederation: config.federation.enablePublicTimeline,
brandName: config.branding ? config.branding.name : "Hyperspace", brandName: config.branding
? config.branding.name
: "Hyperspace",
developerMode: config.developer developerMode: config.developer
}); });
} }
@ -142,40 +146,53 @@ export class AppLayout extends Component<any, IAppLayoutState> {
switch (notif.type) { switch (notif.type) {
case "favourite": case "favourite":
primaryMessage = primaryMessage =
(notif.account.display_name || "@" + notif.account.username) + (notif.account.display_name ||
"@" + notif.account.username) +
" favorited your post."; " favorited your post.";
if (notif.status) { if (notif.status) {
const div = document.createElement("div"); const div = document.createElement("div");
div.innerHTML = notif.status.content; div.innerHTML = notif.status.content;
secondaryMessage = secondaryMessage =
(div.textContent || div.innerText || "").slice(0, 100) + "..."; (div.textContent || div.innerText || "").slice(
0,
100
) + "...";
} }
break; break;
case "follow": case "follow":
primaryMessage = primaryMessage =
(notif.account.display_name || "@" + notif.account.username) + (notif.account.display_name ||
"@" + notif.account.username) +
" is now following you."; " is now following you.";
break; break;
case "mention": case "mention":
primaryMessage = primaryMessage =
(notif.account.display_name || "@" + notif.account.username) + (notif.account.display_name ||
"@" + notif.account.username) +
" mentioned you in a post."; " mentioned you in a post.";
if (notif.status) { if (notif.status) {
const div = document.createElement("div"); const div = document.createElement("div");
div.innerHTML = notif.status.content; div.innerHTML = notif.status.content;
secondaryMessage = secondaryMessage =
(div.textContent || div.innerText || "").slice(0, 100) + "..."; (div.textContent || div.innerText || "").slice(
0,
100
) + "...";
} }
break; break;
case "reblog": case "reblog":
primaryMessage = primaryMessage =
(notif.account.display_name || "@" + notif.account.username) + (notif.account.display_name ||
"@" + notif.account.username) +
" reblogged your post."; " reblogged your post.";
if (notif.status) { if (notif.status) {
const div = document.createElement("div"); const div = document.createElement("div");
div.innerHTML = notif.status.content; div.innerHTML = notif.status.content;
secondaryMessage = secondaryMessage =
(div.textContent || div.innerText || "").slice(0, 100) + "..."; (div.textContent || div.innerText || "").slice(
0,
100
) + "...";
} }
break; break;
} }
@ -229,15 +246,14 @@ export class AppLayout extends Component<any, IAppLayoutState> {
return ( return (
<div className={classes.titleBarRoot}> <div className={classes.titleBarRoot}>
<Typography className={classes.titleBarText}> <Typography className={classes.titleBarText}>
{this.state.brandName ? this.state.brandName : "Hyperspace"}{" "} {this.state.brandName
{this.state.developerMode ? "(beta)" : null} ? this.state.brandName
: "Hyperspace"}{" "}
{this.state.developerMode ? "(Beta)" : null}
</Typography> </Typography>
</div> </div>
); );
} else if ( } else if (process.env.NODE_ENV === "development") {
this.state.developerMode ||
process.env.NODE_ENV === "development"
) {
return ( return (
<div className={classes.titleBarRoot}> <div className={classes.titleBarRoot}>
<Typography className={classes.titleBarText}> <Typography className={classes.titleBarText}>
@ -258,7 +274,9 @@ export class AppLayout extends Component<any, IAppLayoutState> {
button button
key="profile-mobile" key="profile-mobile"
to={`/profile/${ to={`/profile/${
this.state.currentUser ? this.state.currentUser.id : "1" this.state.currentUser
? this.state.currentUser.id
: "1"
}`} }`}
> >
<ListItemAvatar> <ListItemAvatar>
@ -266,7 +284,8 @@ export class AppLayout extends Component<any, IAppLayoutState> {
alt="You" alt="You"
src={ src={
this.state.currentUser this.state.currentUser
? this.state.currentUser.avatar_static ? this.state.currentUser
.avatar_static
: "" : ""
} }
/> />
@ -326,7 +345,10 @@ export class AppLayout extends Component<any, IAppLayoutState> {
<ListItemIcon> <ListItemIcon>
<PublicIcon /> <PublicIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="Public" secondary="Disabled by admin" /> <ListItemText
primary="Public"
secondary="Disabled by admin"
/>
</ListItem> </ListItem>
)} )}
<Divider /> <Divider />
@ -351,7 +373,11 @@ export class AppLayout extends Component<any, IAppLayoutState> {
</ListItemIcon> </ListItemIcon>
<ListItemText primary="Notifications" /> <ListItemText primary="Notifications" />
</LinkableListItem> </LinkableListItem>
<LinkableListItem button key="messages-mobile" to="/messages"> <LinkableListItem
button
key="messages-mobile"
to="/messages"
>
<ListItemIcon> <ListItemIcon>
<MailIcon /> <MailIcon />
</ListItemIcon> </ListItemIcon>
@ -360,7 +386,11 @@ export class AppLayout extends Component<any, IAppLayoutState> {
<Divider /> <Divider />
</div> </div>
<ListSubheader>More</ListSubheader> <ListSubheader>More</ListSubheader>
<LinkableListItem button key="recommended" to="/recommended"> <LinkableListItem
button
key="recommended"
to="/recommended"
>
<ListItemIcon> <ListItemIcon>
<GroupIcon /> <GroupIcon />
</ListItemIcon> </ListItemIcon>
@ -405,7 +435,9 @@ export class AppLayout extends Component<any, IAppLayoutState> {
color="inherit" color="inherit"
noWrap noWrap
> >
{this.state.brandName ? this.state.brandName : "Hyperspace"} {this.state.brandName
? this.state.brandName
: "Hyperspace"}
</Typography> </Typography>
<div className={classes.appBarFlexGrow} /> <div className={classes.appBarFlexGrow} />
<div className={classes.appBarSearch}> <div className={classes.appBarSearch}>
@ -420,7 +452,9 @@ export class AppLayout extends Component<any, IAppLayoutState> {
}} }}
onKeyUp={event => { onKeyUp={event => {
if (event.keyCode === 13) { if (event.keyCode === 13) {
this.searchForQuery(event.currentTarget.value); this.searchForQuery(
event.currentTarget.value
);
} }
}} }}
/> />
@ -436,7 +470,8 @@ export class AppLayout extends Component<any, IAppLayoutState> {
<Badge <Badge
badgeContent={ badgeContent={
this.state.notificationCount > 0 this.state.notificationCount > 0
? this.state.notificationCount ? this.state
.notificationCount
: "" : ""
} }
color="secondary" color="secondary"
@ -446,18 +481,27 @@ export class AppLayout extends Component<any, IAppLayoutState> {
</LinkableIconButton> </LinkableIconButton>
</Tooltip> </Tooltip>
<Tooltip title="Direct messages"> <Tooltip title="Direct messages">
<LinkableIconButton color="inherit" to="/messages"> <LinkableIconButton
color="inherit"
to="/messages"
>
<MailIcon /> <MailIcon />
</LinkableIconButton> </LinkableIconButton>
</Tooltip> </Tooltip>
<Tooltip title="Your account"> <Tooltip title="Your account">
<IconButton id="acctMenuBtn" onClick={this.toggleAcctMenu}> <IconButton
id="acctMenuBtn"
onClick={this.toggleAcctMenu}
>
<Avatar <Avatar
className={classes.appBarAcctMenuIcon} className={
classes.appBarAcctMenuIcon
}
alt="You" alt="You"
src={ src={
this.state.currentUser this.state.currentUser
? this.state.currentUser.avatar_static ? this.state.currentUser
.avatar_static
: "" : ""
} }
/> />
@ -466,16 +510,21 @@ export class AppLayout extends Component<any, IAppLayoutState> {
<Menu <Menu
id="acct-menu" id="acct-menu"
anchorEl={document.getElementById("acctMenuBtn")} anchorEl={document.getElementById(
"acctMenuBtn"
)}
open={this.state.acctMenuOpen} open={this.state.acctMenuOpen}
className={classes.acctMenu} className={classes.acctMenu}
> >
<ClickAwayListener onClickAway={this.toggleAcctMenu}> <ClickAwayListener
onClickAway={this.toggleAcctMenu}
>
<div> <div>
<LinkableListItem <LinkableListItem
to={`/profile/${ to={`/profile/${
this.state.currentUser this.state.currentUser
? this.state.currentUser.id ? this.state.currentUser
.id
: "1" : "1"
}`} }`}
> >
@ -483,8 +532,11 @@ export class AppLayout extends Component<any, IAppLayoutState> {
<Avatar <Avatar
alt="You" alt="You"
src={ src={
this.state.currentUser this.state
? this.state.currentUser.avatar_static .currentUser
? this.state
.currentUser
.avatar_static
: "" : ""
} }
/> />
@ -492,21 +544,36 @@ export class AppLayout extends Component<any, IAppLayoutState> {
<ListItemText <ListItemText
primary={ primary={
this.state.currentUser this.state.currentUser
? this.state.currentUser.display_name || ? this.state
this.state.currentUser.acct .currentUser
.display_name ||
this.state
.currentUser
.acct
: "Loading..." : "Loading..."
} }
secondary={ secondary={
"@" + "@" +
(this.state.currentUser (this.state.currentUser
? this.state.currentUser.acct ? this.state
.currentUser
.acct
: "Loading...") : "Loading...")
} }
/> />
</LinkableListItem> </LinkableListItem>
<Divider /> <Divider />
<LinkableListItem to={"/you"}>
<ListItemText>
Edit profile
</ListItemText>
</LinkableListItem>
{/* <MenuItem>Switch account</MenuItem> */} {/* <MenuItem>Switch account</MenuItem> */}
<MenuItem onClick={() => this.toggleLogOutDialog()}> <MenuItem
onClick={() =>
this.toggleLogOutDialog()
}
>
Log out Log out
</MenuItem> </MenuItem>
</div> </div>
@ -549,14 +616,22 @@ export class AppLayout extends Component<any, IAppLayoutState> {
> >
<DialogTitle id="alert-dialog-title"> <DialogTitle id="alert-dialog-title">
Log out of{" "} Log out of{" "}
{this.state.brandName ? this.state.brandName : "Hyperspace"} {this.state.brandName
? this.state.brandName
: "Hyperspace"}
</DialogTitle> </DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText id="alert-dialog-description"> <DialogContentText id="alert-dialog-description">
You'll need to remove{" "} You'll need to remove{" "}
{this.state.brandName ? this.state.brandName : "Hyperspace"} from {this.state.brandName
your list of authorized apps and log in again if you want to use{" "} ? this.state.brandName
{this.state.brandName ? this.state.brandName : "Hyperspace"}. : "Hyperspace"}{" "}
from your list of authorized apps and log in again
if you want to use{" "}
{this.state.brandName
? this.state.brandName
: "Hyperspace"}
.
</DialogContentText> </DialogContentText>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>

View File

@ -85,7 +85,9 @@ class AttachmentComponent extends Component<
/> />
); );
case "unknown": case "unknown":
return <object data={slide.url} className={classes.mediaObject} />; return (
<object data={slide.url} className={classes.mediaObject} />
);
} }
} }
@ -113,7 +115,10 @@ class AttachmentComponent extends Component<
<Button <Button
size="small" size="small"
onClick={() => this.moveForward()} onClick={() => this.moveForward()}
disabled={this.state.currentStep === this.state.totalSteps - 1} disabled={
this.state.currentStep ===
this.state.totalSteps - 1
}
> >
Next Next
</Button> </Button>

View File

@ -48,7 +48,9 @@ class ComposeMediaAttachment extends Component<
this.props.enqueueSnackbar("Description updated."); this.props.enqueueSnackbar("Description updated.");
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.enqueueSnackbar("Couldn't update description: " + err.name); this.props.enqueueSnackbar(
"Couldn't update description: " + err.name
);
}); });
} }
@ -59,7 +61,9 @@ class ComposeMediaAttachment extends Component<
{attachment.type === "image" || attachment.type === "gifv" ? ( {attachment.type === "image" || attachment.type === "gifv" ? (
<img <img
src={attachment.url} src={attachment.url}
alt={attachment.description ? attachment.description : ""} alt={
attachment.description ? attachment.description : ""
}
/> />
) : attachment.type === "video" ? ( ) : attachment.type === "video" ? (
<video autoPlay={false} src={attachment.url} /> <video autoPlay={false} src={attachment.url} />
@ -74,13 +78,19 @@ class ComposeMediaAttachment extends Component<
label="Description" label="Description"
margin="dense" margin="dense"
className={classes.attachmentText} className={classes.attachmentText}
onBlur={event => this.updateAttachmentText(event.target.value)} onBlur={event =>
this.updateAttachmentText(event.target.value)
}
></TextField> ></TextField>
} }
actionIcon={ actionIcon={
<IconButton <IconButton
color="inherit" color="inherit"
onClick={() => this.props.onDeleteCallback(this.state.attachment)} onClick={() =>
this.props.onDeleteCallback(
this.state.attachment
)
}
> >
<DeleteIcon /> <DeleteIcon />
</IconButton> </IconButton>

View File

@ -106,7 +106,9 @@ export class Post extends React.Component<any, IPostState> {
this.client this.client
.del("/statuses/" + this.state.post.id) .del("/statuses/" + this.state.post.id)
.then((resp: any) => { .then((resp: any) => {
this.props.enqueueSnackbar("Post deleted. Refresh to see changes."); this.props.enqueueSnackbar(
"Post deleted. Refresh to see changes."
);
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.enqueueSnackbar("Couldn't delete post: " + err.name); this.props.enqueueSnackbar("Couldn't delete post: " + err.name);
@ -193,7 +195,9 @@ export class Post extends React.Component<any, IPostState> {
<div className={classes.mediaContainer}> <div className={classes.mediaContainer}>
<Typography <Typography
paragraph paragraph
dangerouslySetInnerHTML={{ __html: oldContent.innerHTML }} dangerouslySetInnerHTML={{
__html: oldContent.innerHTML
}}
/> />
{status.card ? ( {status.card ? (
<div className={classes.postCard}> <div className={classes.postCard}>
@ -204,16 +208,24 @@ export class Post extends React.Component<any, IPostState> {
rel="noreferrer" rel="noreferrer"
> >
<CardContent> <CardContent>
<Typography gutterBottom variant="h6" component="h2"> <Typography
gutterBottom
variant="h6"
component="h2"
>
{status.card.title} {status.card.title}
</Typography> </Typography>
<Typography> <Typography>
{status.card.description.slice(0, 500) + {status.card.description.slice(0, 500) +
(status.card.description.length > 500 ? "..." : "") || (status.card.description.length >
500
? "..."
: "") ||
"No description provided. Click with caution."} "No description provided. Click with caution."}
</Typography> </Typography>
</CardContent> </CardContent>
{status.card.image && status.media_attachments.length <= 0 ? ( {status.card.image &&
status.media_attachments.length <= 0 ? (
<CardMedia <CardMedia
className={classes.postMedia} className={classes.postMedia}
image={status.card.image} image={status.card.image}
@ -243,22 +255,27 @@ export class Post extends React.Component<any, IPostState> {
status.poll.voted || status.poll.expired ? ( status.poll.voted || status.poll.expired ? (
<div> <div>
<Typography variant="caption"> <Typography variant="caption">
You can't vote on this poll. Below are the results of the You can't vote on this poll. Below are the
poll. results of the poll.
</Typography> </Typography>
<RadioGroup value={this.findBiggestVote()}> <RadioGroup value={this.findBiggestVote()}>
{status.poll.options.map((pollOption: PollOption) => { {status.poll.options.map(
(pollOption: PollOption) => {
let x = ( let x = (
<FormControlLabel <FormControlLabel
disabled disabled
value={pollOption.title} value={pollOption.title}
control={<Radio />} control={<Radio />}
label={`${pollOption.title} (${pollOption.votes_count} votes)`} label={`${pollOption.title} (${pollOption.votes_count} votes)`}
key={pollOption.title + pollOption.votes_count} key={
pollOption.title +
pollOption.votes_count
}
/> />
); );
return x; return x;
})} }
)}
</RadioGroup> </RadioGroup>
{status.poll && status.poll.expired ? ( {status.poll && status.poll.expired ? (
<Typography variant="caption"> <Typography variant="caption">
@ -268,7 +285,9 @@ export class Post extends React.Component<any, IPostState> {
<Typography variant="caption"> <Typography variant="caption">
This poll will expire on{" "} This poll will expire on{" "}
{moment( {moment(
status.poll.expires_at ? status.poll.expires_at : "" status.poll.expires_at
? status.poll.expires_at
: ""
).format("MMMM Do YYYY, [at] h:mm A")} ).format("MMMM Do YYYY, [at] h:mm A")}
. .
</Typography> </Typography>
@ -281,17 +300,22 @@ export class Post extends React.Component<any, IPostState> {
this.captureVote(option) this.captureVote(option)
} }
> >
{status.poll.options.map((pollOption: PollOption) => { {status.poll.options.map(
(pollOption: PollOption) => {
let x = ( let x = (
<FormControlLabel <FormControlLabel
value={pollOption.title} value={pollOption.title}
control={<Radio />} control={<Radio />}
label={pollOption.title} label={pollOption.title}
key={pollOption.title + pollOption.votes_count} key={
pollOption.title +
pollOption.votes_count
}
/> />
); );
return x; return x;
})} }
)}
</RadioGroup> </RadioGroup>
<Button <Button
color="primary" color="primary"
@ -338,11 +362,17 @@ export class Post extends React.Component<any, IPostState> {
} }
color="inherit" color="inherit"
> >
<ExpansionPanelSummary expandIcon={<ExpandMoreIcon />} color="inherit"> <ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
color="inherit"
>
{icon} {icon}
<Typography>{warningText}</Typography> <Typography>{warningText}</Typography>
</ExpansionPanelSummary> </ExpansionPanelSummary>
<ExpansionPanelDetails className={classes.postContent} color="inherit"> <ExpansionPanelDetails
className={classes.postContent}
color="inherit"
>
{this.materializeContent(content)} {this.materializeContent(content)}
</ExpansionPanelDetails> </ExpansionPanelDetails>
</ExpansionPanel> </ExpansionPanel>
@ -364,18 +394,21 @@ export class Post extends React.Component<any, IPostState> {
const { classes } = this.props; const { classes } = this.props;
if (post.reblog) { if (post.reblog) {
let author = post.reblog.account; let author = post.reblog.account;
let origString = `<span>${author.display_name || author.username} (@${ let origString = `<span>${author.display_name ||
author.acct author.username} (@${author.acct}) 🔄 ${post.account
}) 🔄 ${post.account.display_name || post.account.username}</span>`; .display_name || post.account.username}</span>`;
let emojis = author.emojis; let emojis = author.emojis;
emojis.concat(post.account.emojis); emojis.concat(post.account.emojis);
return emojifyString(origString, emojis, classes.postAuthorEmoji); return emojifyString(origString, emojis, classes.postAuthorEmoji);
} else { } else {
let author = post.account; let author = post.account;
let origString = `<span>${author.display_name || author.username} (@${ let origString = `<span>${author.display_name ||
author.acct author.username} (@${author.acct})</span>`;
})</span>`; return emojifyString(
return emojifyString(origString, author.emojis, classes.postAuthorEmoji); origString,
author.emojis,
classes.postAuthorEmoji
);
} }
} }
@ -394,7 +427,9 @@ export class Post extends React.Component<any, IPostState> {
</Avatar> </Avatar>
} }
label={person.username} label={person.username}
key={this.state.post.id + "_mention_" + person.id} key={
this.state.post.id + "_mention_" + person.id
}
to={`/profile/${person.id}`} to={`/profile/${person.id}`}
className={classes.postMention} className={classes.postMention}
clickable clickable
@ -481,9 +516,12 @@ export class Post extends React.Component<any, IPostState> {
this.setState({ post }); this.setState({ post });
}) })
.catch((err: Error) => { .catch((err: Error) => {
_this.props.enqueueSnackbar(`Couldn't unfavorite post: ${err.name}`, { _this.props.enqueueSnackbar(
`Couldn't unfavorite post: ${err.name}`,
{
variant: "error" variant: "error"
}); }
);
console.log(err.message); console.log(err.message);
}); });
} else { } else {
@ -494,9 +532,12 @@ export class Post extends React.Component<any, IPostState> {
this.setState({ post }); this.setState({ post });
}) })
.catch((err: Error) => { .catch((err: Error) => {
_this.props.enqueueSnackbar(`Couldn't favorite post: ${err.name}`, { _this.props.enqueueSnackbar(
`Couldn't favorite post: ${err.name}`,
{
variant: "error" variant: "error"
}); }
);
console.log(err.message); console.log(err.message);
}); });
} }
@ -511,9 +552,12 @@ export class Post extends React.Component<any, IPostState> {
this.setState({ post }); this.setState({ post });
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.enqueueSnackbar(`Couldn't unboost post: ${err.name}`, { this.props.enqueueSnackbar(
`Couldn't unboost post: ${err.name}`,
{
variant: "error" variant: "error"
}); }
);
console.log(err.message); console.log(err.message);
}); });
} else { } else {
@ -524,9 +568,12 @@ export class Post extends React.Component<any, IPostState> {
this.setState({ post }); this.setState({ post });
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.enqueueSnackbar(`Couldn't boost post: ${err.name}`, { this.props.enqueueSnackbar(
`Couldn't boost post: ${err.name}`,
{
variant: "error" variant: "error"
}); }
);
console.log(err.message); console.log(err.message);
}); });
} }
@ -538,11 +585,13 @@ export class Post extends React.Component<any, IPostState> {
open={this.state.deletePostDialog} open={this.state.deletePostDialog}
onClose={() => this.togglePostDeleteDialog()} onClose={() => this.togglePostDeleteDialog()}
> >
<DialogTitle id="alert-dialog-title">Delete this post?</DialogTitle> <DialogTitle id="alert-dialog-title">
Delete this post?
</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText id="alert-dialog-description"> <DialogContentText id="alert-dialog-description">
Are you sure you want to delete this post? This action cannot be Are you sure you want to delete this post? This action
undone. cannot be undone.
</DialogContentText> </DialogContentText>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
@ -577,7 +626,9 @@ export class Post extends React.Component<any, IPostState> {
avatar={ avatar={
<LinkableAvatar <LinkableAvatar
to={`/profile/${ to={`/profile/${
post.reblog ? post.reblog.account.id : post.account.id post.reblog
? post.reblog.account.id
: post.account.id
}`} }`}
src={ src={
post.reblog post.reblog
@ -626,17 +677,23 @@ export class Post extends React.Component<any, IPostState> {
to={`/compose?reply=${ to={`/compose?reply=${
post.reblog ? post.reblog.id : post.id post.reblog ? post.reblog.id : post.id
}&visibility=${post.visibility}&acct=${ }&visibility=${post.visibility}&acct=${
post.reblog ? post.reblog.account.acct : post.account.acct post.reblog
? post.reblog.account.acct
: post.account.acct
}`} }`}
> >
<ReplyIcon /> <ReplyIcon />
</LinkableIconButton> </LinkableIconButton>
</Tooltip> </Tooltip>
<Typography> <Typography>
{post.reblog ? post.reblog.replies_count : post.replies_count} {post.reblog
? post.reblog.replies_count
: post.replies_count}
</Typography> </Typography>
<Tooltip title="Favorite"> <Tooltip title="Favorite">
<IconButton onClick={() => this.toggleFavorited(post)}> <IconButton
onClick={() => this.toggleFavorited(post)}
>
<FavoriteIcon <FavoriteIcon
className={ className={
post.reblog post.reblog
@ -656,7 +713,9 @@ export class Post extends React.Component<any, IPostState> {
: post.favourites_count} : post.favourites_count}
</Typography> </Typography>
<Tooltip title="Boost"> <Tooltip title="Boost">
<IconButton onClick={() => this.toggleReblogged(post)}> <IconButton
onClick={() => this.toggleReblogged(post)}
>
<AutorenewIcon <AutorenewIcon
className={ className={
post.reblog post.reblog
@ -671,16 +730,26 @@ export class Post extends React.Component<any, IPostState> {
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Typography> <Typography>
{post.reblog ? post.reblog.reblogs_count : post.reblogs_count} {post.reblog
? post.reblog.reblogs_count
: post.reblogs_count}
</Typography> </Typography>
<Tooltip className={classes.desktopOnly} title="View thread"> <Tooltip
className={classes.desktopOnly}
title="View thread"
>
<LinkableIconButton <LinkableIconButton
to={`/conversation/${post.reblog ? post.reblog.id : post.id}`} to={`/conversation/${
post.reblog ? post.reblog.id : post.id
}`}
> >
<ForumIcon /> <ForumIcon />
</LinkableIconButton> </LinkableIconButton>
</Tooltip> </Tooltip>
<Tooltip className={classes.desktopOnly} title="Open in Web"> <Tooltip
className={classes.desktopOnly}
title="Open in Web"
>
<IconButton <IconButton
href={this.getMastodonUrl(post)} href={this.getMastodonUrl(post)}
rel="noreferrer" rel="noreferrer"
@ -722,22 +791,30 @@ export class Post extends React.Component<any, IPostState> {
/> />
{post.reblog ? ( {post.reblog ? (
<div> <div>
<LinkableMenuItem to={`/profile/${post.reblog.account.id}`}> <LinkableMenuItem
to={`/profile/${post.reblog.account.id}`}
>
View author profile View author profile
</LinkableMenuItem> </LinkableMenuItem>
<LinkableMenuItem to={`/profile/${post.account.id}`}> <LinkableMenuItem
to={`/profile/${post.account.id}`}
>
View reblogger profile View reblogger profile
</LinkableMenuItem> </LinkableMenuItem>
</div> </div>
) : ( ) : (
<LinkableMenuItem to={`/profile/${post.account.id}`}> <LinkableMenuItem
to={`/profile/${post.account.id}`}
>
View profile View profile
</LinkableMenuItem> </LinkableMenuItem>
)} )}
<div className={classes.mobileOnly}> <div className={classes.mobileOnly}>
<Divider /> <Divider />
<LinkableMenuItem <LinkableMenuItem
to={`/conversation/${post.reblog ? post.reblog.id : post.id}`} to={`/conversation/${
post.reblog ? post.reblog.id : post.id
}`}
> >
View thread View thread
</LinkableMenuItem> </LinkableMenuItem>
@ -751,10 +828,15 @@ export class Post extends React.Component<any, IPostState> {
</MenuItem> </MenuItem>
</div> </div>
{post.account.id == {post.account.id ==
JSON.parse(localStorage.getItem("account") as string).id ? ( JSON.parse(localStorage.getItem("account") as string)
.id ? (
<div> <div>
<Divider /> <Divider />
<MenuItem onClick={() => this.togglePostDeleteDialog()}> <MenuItem
onClick={() =>
this.togglePostDeleteDialog()
}
>
Delete Delete
</MenuItem> </MenuItem>
</div> </div>

View File

@ -37,7 +37,9 @@ class ThemePreview extends Component<IThemePreviewProps, IThemePreviewState> {
<Paper> <Paper>
<AppBar color="primary" position="static"> <AppBar color="primary" position="static">
<Toolbar> <Toolbar>
<MenuIcon style={{ marginRight: 20, marginLeft: -4 }} /> <MenuIcon
style={{ marginRight: 20, marginLeft: -4 }}
/>
<Typography variant="h6" color="inherit"> <Typography variant="h6" color="inherit">
Hyperspace Hyperspace
</Typography> </Typography>
@ -57,17 +59,20 @@ class ThemePreview extends Component<IThemePreviewProps, IThemePreviewState> {
</Typography> </Typography>
<br /> <br />
<Typography paragraph> <Typography paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc Lorem ipsum dolor sit amet, consectetur
vestibulum congue sem ac ornare. In nec imperdiet neque. In adipiscing elit. Nunc vestibulum congue sem ac
eleifend laoreet efficitur. Vestibulum vel odio mattis, ornare. In nec imperdiet neque. In eleifend
scelerisque nibh a, ornare lectus. Phasellus sollicitudin erat laoreet efficitur. Vestibulum vel odio mattis,
et turpis pellentesque consequat. In maximus luctus purus, eu scelerisque nibh a, ornare lectus. Phasellus
molestie elit euismod eu. Pellentesque quam lectus, sagittis sollicitudin erat et turpis pellentesque
eget accumsan in, consequat ut sapien. Morbi aliquet ligula consequat. In maximus luctus purus, eu molestie
erat, id dapibus nunc laoreet at. Integer sodales lacinia elit euismod eu. Pellentesque quam lectus,
finibus. Aliquam augue nibh, eleifend quis consectetur et, sagittis eget accumsan in, consequat ut sapien.
rhoncus ut odio. Lorem ipsum dolor sit amet, consectetur Morbi aliquet ligula erat, id dapibus nunc
adipiscing elit. laoreet at. Integer sodales lacinia finibus.
Aliquam augue nibh, eleifend quis consectetur
et, rhoncus ut odio. Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
</Typography> </Typography>
</div> </div>
<div style={{ textAlign: "right" }}> <div style={{ textAlign: "right" }}>

View File

@ -110,6 +110,135 @@ class AboutPage extends Component<any, IAboutPageState> {
return ( return (
<div className={classes.pageLayoutConstraints}> <div className={classes.pageLayoutConstraints}>
<Paper>
<div
className={classes.instanceHeaderPaper}
style={{
backgroundImage: `url("${
this.state.brandBg ? this.state.brandBg : ""
}")`
}}
>
<div className={classes.instanceToolbar}>
{this.state.repository ? (
<Tooltip title="View source code">
<IconButton
href={this.state.repository}
target="_blank"
rel="noreferrer"
color="inherit"
>
<CodeIcon />
</IconButton>
</Tooltip>
) : null}
</div>
<div className={classes.instanceHeaderText}>
<Typography variant="h4" component="p">
{this.state.brandName? this.state.brandName: "Hyperspace"}
</Typography>
<Typography>Version {`${this.state? this.state.versionNumber: "1.0.x"} ${this.state && this.state.brandName !== "Hyperspace"? "(Hyperspace-like)": ""}`}</Typography>
</div>
</div>
<List className={classes.pageListConstraints}>
<ListItem>
<ListItemAvatar>
<LinkableAvatar
to={`/profile/${
this.state.hyperspaceAdmin
? this.state.hyperspaceAdmin.id
: 0
}`}
src={
this.state.hyperspaceAdmin
? this.state.hyperspaceAdmin.avatar_static
: ""
}
>
<PersonIcon />
</LinkableAvatar>
</ListItemAvatar>
<ListItemText
primary="App provider"
secondary={
this.state.hyperspaceAdmin && this.state.hyperspaceAdminName
? this.state.hyperspaceAdminName ||
this.state.hyperspaceAdmin.display_name ||
"@" + this.state.hyperspaceAdmin.acct
: "No provider set in config"
}
/>
<ListItemSecondaryAction>
<Tooltip title="Send a post or message">
<LinkableIconButton
to={`/compose?visibility=${
this.state.federated ? "public" : "private"
}&acct=${
this.state.hyperspaceAdmin
? this.state.hyperspaceAdmin.acct
: ""
}`}
>
<ChatIcon />
</LinkableIconButton>
</Tooltip>
<Tooltip title="View profile">
<LinkableIconButton
to={`/profile/${
this.state.hyperspaceAdmin
? this.state.hyperspaceAdmin.id
: 0
}`}
>
<AssignmentIndIcon />
</LinkableIconButton>
</Tooltip>
</ListItemSecondaryAction>
</ListItem>
<ListItem>
<ListItemAvatar>
<Avatar>
<NotesIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary="License"
secondary={this.state.license.name}
/>
<ListItemSecondaryAction>
<Tooltip title="View license">
<IconButton
href={this.state.license.url}
target="_blank"
rel="noreferrer"
>
<OpenInNewIcon />
</IconButton>
</Tooltip>
</ListItemSecondaryAction>
</ListItem>
<ListItem>
<ListItemAvatar>
<Avatar>
<UpdateIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary="Release channel"
secondary={
this.state
? this.state.developer
? "Developer"
: "Release"
: "Loading..."
}
/>
</ListItem>
</List>
</Paper>
<br />
<Paper> <Paper>
<div <div
className={classes.instanceHeaderPaper} className={classes.instanceHeaderPaper}
@ -130,13 +259,10 @@ class AboutPage extends Component<any, IAboutPageState> {
> >
<OpenInNewIcon /> <OpenInNewIcon />
</IconButton> </IconButton>
<Typography <div className={classes.instanceHeaderText}>
className={classes.instanceHeaderText} <Typography variant="h4" component="p">{this.state.instance ? this.state.instance.uri: "Loading..."}</Typography>
variant="h4" <Typography>Server version {this.state.instance? this.state.instance.version: "x.x.x"}</Typography>
component="p" </div>
>
{this.state.instance ? this.state.instance.uri : "Loading..."}
</Typography>
</div> </div>
<List className={classes.pageListConstraints}> <List className={classes.pageListConstraints}>
{localStorage["isPleroma"] == "false" && ( {localStorage["isPleroma"] == "false" && (
@ -238,168 +364,9 @@ class AboutPage extends Component<any, IAboutPageState> {
</Tooltip> </Tooltip>
</ListItemSecondaryAction> </ListItemSecondaryAction>
</ListItem> </ListItem>
<ListItem>
<ListItemAvatar>
<Avatar>
<MastodonIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary="Mastodon version"
secondary={
this.state.instance ? this.state.instance.version : "x.x.x"
}
/>
</ListItem>
</List> </List>
</Paper> </Paper>
<br />
<Paper>
<div
className={classes.instanceHeaderPaper}
style={{
backgroundImage: `url("${
this.state.brandBg ? this.state.brandBg : ""
}")`
}}
>
<div className={classes.instanceToolbar}>
{this.state.repository ? (
<Tooltip title="View source code">
<IconButton
href={this.state.repository}
target="_blank"
rel="noreferrer"
color="inherit"
>
<CodeIcon />
</IconButton>
</Tooltip>
) : null}
</div>
<Typography
className={classes.instanceHeaderText}
variant="h4"
component="p"
>
{this.state.brandName ? this.state.brandName : "Hyperspace"}
</Typography>
</div>
<List className={classes.pageListConstraints}>
<ListItem>
<ListItemAvatar>
<LinkableAvatar
to={`/profile/${
this.state.hyperspaceAdmin
? this.state.hyperspaceAdmin.id
: 0
}`}
src={
this.state.hyperspaceAdmin
? this.state.hyperspaceAdmin.avatar_static
: ""
}
>
<PersonIcon />
</LinkableAvatar>
</ListItemAvatar>
<ListItemText
primary="App provider"
secondary={
this.state.hyperspaceAdmin && this.state.hyperspaceAdminName
? this.state.hyperspaceAdminName ||
this.state.hyperspaceAdmin.display_name ||
"@" + this.state.hyperspaceAdmin.acct
: "No provider set in config"
}
/>
<ListItemSecondaryAction>
<Tooltip title="Send a post or message">
<LinkableIconButton
to={`/compose?visibility=${
this.state.federated ? "public" : "private"
}&acct=${
this.state.hyperspaceAdmin
? this.state.hyperspaceAdmin.acct
: ""
}`}
>
<ChatIcon />
</LinkableIconButton>
</Tooltip>
<Tooltip title="View profile">
<LinkableIconButton
to={`/profile/${
this.state.hyperspaceAdmin
? this.state.hyperspaceAdmin.id
: 0
}`}
>
<AssignmentIndIcon />
</LinkableIconButton>
</Tooltip>
</ListItemSecondaryAction>
</ListItem>
<ListItem>
<ListItemAvatar>
<Avatar>
<NotesIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary="License"
secondary={this.state.license.name}
/>
<ListItemSecondaryAction>
<Tooltip title="View license">
<IconButton
href={this.state.license.url}
target="_blank"
rel="noreferrer"
>
<OpenInNewIcon />
</IconButton>
</Tooltip>
</ListItemSecondaryAction>
</ListItem>
<ListItem>
<ListItemAvatar>
<Avatar>
<UpdateIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary="Release channel"
secondary={
this.state
? this.state.developer
? "Developer"
: "Release"
: "Loading..."
}
/>
</ListItem>
<ListItem>
<ListItemAvatar>
<Avatar>
<InfoIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary="App version"
secondary={`${
this.state ? this.state.brandName : "Hyperspace"
} v${this.state ? this.state.versionNumber : "1.0.x"} ${
this.state && this.state.brandName !== "Hyperspace"
? "(Hyperspace-like)"
: ""
}`}
/>
</ListItem>
</List>
</Paper>
<br /> <br />
<ListSubheader>Federation status</ListSubheader> <ListSubheader>Federation status</ListSubheader>
<Paper> <Paper>

View File

@ -180,9 +180,10 @@ class Composer extends Component<any, IComposerState> {
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.closeSnackbar("media-upload"); this.props.closeSnackbar("media-upload");
this.props.enqueueSnackbar("Couldn't upload media: " + err.name, { this.props.enqueueSnackbar(
variant: "error" "Couldn't upload media: " + err.name,
}); { variant: "error" }
);
}); });
}) })
.catch((err: Error) => { .catch((err: Error) => {
@ -249,7 +250,8 @@ class Composer extends Component<any, IComposerState> {
let expiration = new Date(); let expiration = new Date();
let current = new Date(); let current = new Date();
expiration.setMinutes(expiration.getMinutes() + 30); expiration.setMinutes(expiration.getMinutes() + 30);
let expiryDifference = expiration.getTime() - current.getTime() / 1000; let expiryDifference =
expiration.getTime() - current.getTime() / 1000;
let temporaryPoll: PollWizard = { let temporaryPoll: PollWizard = {
expires_at: expiryDifference.toString(), expires_at: expiryDifference.toString(),
multiple: false, multiple: false,
@ -263,7 +265,10 @@ class Composer extends Component<any, IComposerState> {
} }
addPollItem() { addPollItem() {
if (this.state.poll !== undefined && this.state.poll.options.length < 4) { if (
this.state.poll !== undefined &&
this.state.poll.options.length < 4
) {
let newOption = { title: "New option" }; let newOption = { title: "New option" };
let options = this.state.poll.options; let options = this.state.poll.options;
let poll = this.state.poll; let poll = this.state.poll;
@ -299,7 +304,10 @@ class Composer extends Component<any, IComposerState> {
} }
removePollItem(item: string) { removePollItem(item: string) {
if (this.state.poll !== undefined && this.state.poll.options.length > 2) { if (
this.state.poll !== undefined &&
this.state.poll.options.length > 2
) {
let options = this.state.poll.options; let options = this.state.poll.options;
let poll = this.state.poll; let poll = this.state.poll;
options.forEach((option: PollWizardOption) => { options.forEach((option: PollWizardOption) => {
@ -427,16 +435,18 @@ class Composer extends Component<any, IComposerState> {
label="Content warning" label="Content warning"
margin="dense" margin="dense"
onChange={event => onChange={event =>
this.updateWarningFromField(event.target.value) this.updateWarningFromField(
event.target.value
)
} }
></TextField> ></TextField>
</Fade> </Fade>
) : null} ) : null}
{this.state.visibility === "direct" ? ( {this.state.visibility === "direct" ? (
<Typography variant="caption"> <Typography variant="caption">
<WarningIcon className={classes.warningCaption} /> Don't forget to <WarningIcon className={classes.warningCaption} />{" "}
add the usernames of the accounts you want to message in your Don't forget to add the usernames of the accounts
post. you want to message in your post.
</Typography> </Typography>
) : null} ) : null}
@ -446,7 +456,9 @@ class Composer extends Component<any, IComposerState> {
fullWidth fullWidth
placeholder="What's on your mind?" placeholder="What's on your mind?"
margin="normal" margin="normal"
onChange={event => this.updateTextFromField(event.target.value)} onChange={event =>
this.updateTextFromField(event.target.value)
}
onKeyDown={event => this.postViaKeyboard(event)} onKeyDown={event => this.postViaKeyboard(event)}
inputProps={{ inputProps={{
maxLength: 500 maxLength: 500
@ -465,11 +477,14 @@ class Composer extends Component<any, IComposerState> {
this.state.remainingChars === 1 ? "" : "s" this.state.remainingChars === 1 ? "" : "s"
} remaining`} } remaining`}
</Typography> </Typography>
{this.state.attachments && this.state.attachments.length > 0 ? ( {this.state.attachments &&
this.state.attachments.length > 0 ? (
<div className={classes.composeAttachmentArea}> <div className={classes.composeAttachmentArea}>
<GridList <GridList
cellHeight={48} cellHeight={48}
className={classes.composeAttachmentAreaGridList} className={
classes.composeAttachmentAreaGridList
}
> >
<GridListTile <GridListTile
key="Subheader-composer" key="Subheader-composer"
@ -478,21 +493,31 @@ class Composer extends Component<any, IComposerState> {
> >
<ListSubheader>Attachments</ListSubheader> <ListSubheader>Attachments</ListSubheader>
</GridListTile> </GridListTile>
{this.state.attachments.map((attachment: Attachment) => { {this.state.attachments.map(
(attachment: Attachment) => {
let c = ( let c = (
<ComposeMediaAttachment <ComposeMediaAttachment
client={this.client} client={this.client}
attachment={attachment} attachment={attachment}
onAttachmentUpdate={(attachment: Attachment) => onAttachmentUpdate={(
this.fetchAttachmentAfterUpdate(attachment) attachment: Attachment
) =>
this.fetchAttachmentAfterUpdate(
attachment
)
} }
onDeleteCallback={(attachment: Attachment) => onDeleteCallback={(
this.deleteMediaAttachment(attachment) attachment: Attachment
) =>
this.deleteMediaAttachment(
attachment
)
} }
/> />
); );
return c; return c;
})} }
)}
</GridList> </GridList>
</div> </div>
) : null} ) : null}
@ -500,25 +525,46 @@ class Composer extends Component<any, IComposerState> {
<div style={{ marginTop: 4 }}> <div style={{ marginTop: 4 }}>
{this.state.poll {this.state.poll
? this.state.poll.options.map( ? this.state.poll.options.map(
(option: PollWizardOption, index: number) => { (
option: PollWizardOption,
index: number
) => {
let c = ( let c = (
<div <div
style={{ display: "flex" }} style={{ display: "flex" }}
key={"compose_option_" + index.toString()} key={
"compose_option_" +
index.toString()
}
> >
<RadioButtonCheckedIcon <RadioButtonCheckedIcon
className={classes.pollWizardOptionIcon} className={
classes.pollWizardOptionIcon
}
/> />
<TextField <TextField
onBlur={(event: any) => onBlur={(event: any) =>
this.editPollItem(index, event) this.editPollItem(
index,
event
)
}
defaultValue={
option.title
}
/>
<div
className={
classes.pollWizardFlexGrow
} }
defaultValue={option.title}
/> />
<div className={classes.pollWizardFlexGrow} />
<Tooltip title="Remove poll option"> <Tooltip title="Remove poll option">
<IconButton <IconButton
onClick={() => this.removePollItem(option.title)} onClick={() =>
this.removePollItem(
option.title
)
}
> >
<DeleteIcon /> <DeleteIcon />
</IconButton> </IconButton>
@ -538,14 +584,18 @@ class Composer extends Component<any, IComposerState> {
: new Date() : new Date()
} }
onChange={(date: any) => { onChange={(date: any) => {
this.setPollExpires(date.toISOString()); this.setPollExpires(
date.toISOString()
);
}} }}
label="Poll exipres on" label="Poll exipres on"
disablePast disablePast
/> />
</MuiPickersUtilsProvider> </MuiPickersUtilsProvider>
<div className={classes.pollWizardFlexGrow} /> <div className={classes.pollWizardFlexGrow} />
<Button onClick={() => this.addPollItem()}>Add Option</Button> <Button onClick={() => this.addPollItem()}>
Add Option
</Button>
</div> </div>
</div> </div>
) : null} ) : null}
@ -575,16 +625,21 @@ class Composer extends Component<any, IComposerState> {
onClose={() => this.toggleEmojis()} onClose={() => this.toggleEmojis()}
className={classes.composeEmoji} className={classes.composeEmoji}
> >
<EmojiPicker onGetEmoji={(emoji: any) => this.insertEmoji(emoji)} /> <EmojiPicker
onGetEmoji={(emoji: any) => this.insertEmoji(emoji)}
/>
</Menu> </Menu>
<Tooltip title="Add/remove a poll"> <Tooltip title="Add/remove a poll">
<IconButton <IconButton
disabled={ disabled={
this.state.attachments && this.state.attachments.length > 0 this.state.attachments &&
this.state.attachments.length > 0
} }
id="compose-poll" id="compose-poll"
onClick={() => { onClick={() => {
this.state.poll ? this.removePoll() : this.createPoll(); this.state.poll
? this.removePoll()
: this.createPoll();
}} }}
> >
<HowToVoteIcon /> <HowToVoteIcon />
@ -611,17 +666,25 @@ class Composer extends Component<any, IComposerState> {
anchorEl={document.getElementById("compose-visibility")} anchorEl={document.getElementById("compose-visibility")}
onClose={() => this.toggleVisibilityMenu()} onClose={() => this.toggleVisibilityMenu()}
> >
<MenuItem onClick={() => this.changeVisibility("direct")}> <MenuItem
onClick={() => this.changeVisibility("direct")}
>
Direct (direct message) Direct (direct message)
</MenuItem> </MenuItem>
<MenuItem onClick={() => this.changeVisibility("private")}> <MenuItem
onClick={() => this.changeVisibility("private")}
>
Private (followers only) Private (followers only)
</MenuItem> </MenuItem>
<MenuItem onClick={() => this.changeVisibility("unlisted")}> <MenuItem
onClick={() => this.changeVisibility("unlisted")}
>
Unlisted Unlisted
</MenuItem> </MenuItem>
{this.state.federated ? ( {this.state.federated ? (
<MenuItem onClick={() => this.changeVisibility("public")}> <MenuItem
onClick={() => this.changeVisibility("public")}
>
Public Public
</MenuItem> </MenuItem>
) : null} ) : null}

View File

@ -52,9 +52,10 @@ class Conversation extends Component<any, IConversationPageState> {
viewDidError: true, viewDidError: true,
viewDidErrorCode: err.message viewDidErrorCode: err.message
}); });
this.props.enqueueSnackbar("Couldn't get conversation: " + err.name, { this.props.enqueueSnackbar(
variant: "error" "Couldn't get conversation: " + err.name,
}); { variant: "error" }
);
}); });
this.client this.client
.get(`/statuses/${this.state.conversationId}/context`) .get(`/statuses/${this.state.conversationId}/context`)
@ -81,9 +82,10 @@ class Conversation extends Component<any, IConversationPageState> {
viewDidError: true, viewDidError: true,
viewDidErrorCode: err.message viewDidErrorCode: err.message
}); });
this.props.enqueueSnackbar("Couldn't get conversation: " + err.name, { this.props.enqueueSnackbar(
variant: "error" "Couldn't get conversation: " + err.name,
}); { variant: "error" }
);
}); });
} }
@ -117,7 +119,13 @@ class Conversation extends Component<any, IConversationPageState> {
{this.state.posts ? ( {this.state.posts ? (
<div> <div>
{this.state.posts.map((post: Status) => { {this.state.posts.map((post: Status) => {
return <Post key={post.id} post={post} client={this.client} />; return (
<Post
key={post.id}
post={post}
client={this.client}
/>
);
})} })}
</div> </div>
) : ( ) : (
@ -130,7 +138,9 @@ class Conversation extends Component<any, IConversationPageState> {
Something went wrong when loading this conversation. Something went wrong when loading this conversation.
</Typography> </Typography>
<Typography> <Typography>
{this.state.viewDidErrorCode ? this.state.viewDidErrorCode : ""} {this.state.viewDidErrorCode
? this.state.viewDidErrorCode
: ""}
</Typography> </Typography>
</Paper> </Paper>
) : ( ) : (
@ -138,7 +148,10 @@ class Conversation extends Component<any, IConversationPageState> {
)} )}
{this.state.viewIsLoading ? ( {this.state.viewIsLoading ? (
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<CircularProgress className={classes.progress} color="primary" /> <CircularProgress
className={classes.progress}
color="primary"
/>
</div> </div>
) : ( ) : (
<span /> <span />

View File

@ -167,8 +167,12 @@ class HomePage extends Component<any, IHomePageState> {
<ArrowUpwardIcon /> <ArrowUpwardIcon />
</Avatar> </Avatar>
} }
label={`View ${this.state.backlogPosts.length} new post${ label={`View ${
this.state.backlogPosts.length > 1 ? "s" : "" this.state.backlogPosts.length
} new post${
this.state.backlogPosts.length > 1
? "s"
: ""
}`} }`}
color="primary" color="primary"
className={classes.pageTopChip} className={classes.pageTopChip}
@ -182,7 +186,13 @@ class HomePage extends Component<any, IHomePageState> {
{this.state.posts ? ( {this.state.posts ? (
<div> <div>
{this.state.posts.map((post: Status) => { {this.state.posts.map((post: Status) => {
return <Post key={post.id} post={post} client={this.client} />; return (
<Post
key={post.id}
post={post}
client={this.client}
/>
);
})} })}
<br /> <br />
{this.state.viewDidLoad && !this.state.viewDidError ? ( {this.state.viewDidLoad && !this.state.viewDidError ? (
@ -204,7 +214,9 @@ class HomePage extends Component<any, IHomePageState> {
Something went wrong when loading this timeline. Something went wrong when loading this timeline.
</Typography> </Typography>
<Typography> <Typography>
{this.state.viewDidErrorCode ? this.state.viewDidErrorCode : ""} {this.state.viewDidErrorCode
? this.state.viewDidErrorCode
: ""}
</Typography> </Typography>
</Paper> </Paper>
) : ( ) : (
@ -212,7 +224,10 @@ class HomePage extends Component<any, IHomePageState> {
)} )}
{this.state.viewIsLoading ? ( {this.state.viewIsLoading ? (
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<CircularProgress className={classes.progress} color="primary" /> <CircularProgress
className={classes.progress}
color="primary"
/>
</div> </div>
) : ( ) : (
<span /> <span />

View File

@ -168,8 +168,12 @@ class LocalPage extends Component<any, ILocalPageState> {
<ArrowUpwardIcon /> <ArrowUpwardIcon />
</Avatar> </Avatar>
} }
label={`View ${this.state.backlogPosts.length} new post${ label={`View ${
this.state.backlogPosts.length > 1 ? "s" : "" this.state.backlogPosts.length
} new post${
this.state.backlogPosts.length > 1
? "s"
: ""
}`} }`}
color="primary" color="primary"
className={classes.pageTopChip} className={classes.pageTopChip}
@ -183,7 +187,13 @@ class LocalPage extends Component<any, ILocalPageState> {
{this.state.posts ? ( {this.state.posts ? (
<div> <div>
{this.state.posts.map((post: Status) => { {this.state.posts.map((post: Status) => {
return <Post key={post.id} post={post} client={this.client} />; return (
<Post
key={post.id}
post={post}
client={this.client}
/>
);
})} })}
<br /> <br />
{this.state.viewDidLoad && !this.state.viewDidError ? ( {this.state.viewDidLoad && !this.state.viewDidError ? (
@ -205,7 +215,9 @@ class LocalPage extends Component<any, ILocalPageState> {
Something went wrong when loading this timeline. Something went wrong when loading this timeline.
</Typography> </Typography>
<Typography> <Typography>
{this.state.viewDidErrorCode ? this.state.viewDidErrorCode : ""} {this.state.viewDidErrorCode
? this.state.viewDidErrorCode
: ""}
</Typography> </Typography>
</Paper> </Paper>
) : ( ) : (
@ -213,7 +225,10 @@ class LocalPage extends Component<any, ILocalPageState> {
)} )}
{this.state.viewIsLoading ? ( {this.state.viewIsLoading ? (
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<CircularProgress className={classes.progress} color="primary" /> <CircularProgress
className={classes.progress}
color="primary"
/>
</div> </div>
) : ( ) : (
<span /> <span />

View File

@ -80,24 +80,39 @@ class MessagesPage extends Component<any, IMessagesState> {
<Paper className={classes.pageListConstraints}> <Paper className={classes.pageListConstraints}>
<List> <List>
{this.state.posts {this.state.posts
? this.state.posts.map((message: Status) => { ? this.state.posts.map(
(message: Status) => {
return ( return (
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
<LinkableAvatar <LinkableAvatar
to={`/profile/${message.account.id}`} to={`/profile/${message.account.id}`}
alt={message.account.username} alt={
src={message.account.avatar_static} message
.account
.username
}
src={
message
.account
.avatar_static
}
> >
<PersonIcon /> <PersonIcon />
</LinkableAvatar> </LinkableAvatar>
</ListItemAvatar> </ListItemAvatar>
<ListItemText <ListItemText
primary={ primary={
message.account.display_name || message.account
"@" + message.account.acct .display_name ||
"@" +
message
.account
.acct
} }
secondary={this.removeHTMLContent(message.content)} secondary={this.removeHTMLContent(
message.content
)}
/> />
<ListItemSecondaryAction> <ListItemSecondaryAction>
<Tooltip title="View conversation"> <Tooltip title="View conversation">
@ -110,7 +125,8 @@ class MessagesPage extends Component<any, IMessagesState> {
</ListItemSecondaryAction> </ListItemSecondaryAction>
</ListItem> </ListItem>
); );
}) }
)
: null} : null}
</List> </List>
</Paper> </Paper>
@ -119,7 +135,10 @@ class MessagesPage extends Component<any, IMessagesState> {
) : null} ) : null}
{this.state.viewIsLoading ? ( {this.state.viewIsLoading ? (
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<CircularProgress className={classes.progress} color="primary" /> <CircularProgress
className={classes.progress}
color="primary"
/>
</div> </div>
) : null} ) : null}
</div> </div>

View File

@ -16,7 +16,11 @@ class Missingno extends Component<any, any> {
The part of Hyperspace you're looking for isn't here. The part of Hyperspace you're looking for isn't here.
</Typography> </Typography>
<br /> <br />
<LinkableButton to="/home" color="primary" variant="contained"> <LinkableButton
to="/home"
color="primary"
variant="contained"
>
Go back to home timeline Go back to home timeline
</LinkableButton> </LinkableButton>
</div> </div>

View File

@ -115,8 +115,14 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
let notifications = this.state.notifications; let notifications = this.state.notifications;
if (notifications !== undefined && notifications.length > 0) { if (notifications !== undefined && notifications.length > 0) {
notifications.forEach((notification: Notification) => { notifications.forEach((notification: Notification) => {
if (notifications !== undefined && notification.id === id) { if (
notifications.splice(notifications.indexOf(notification), 1); notifications !== undefined &&
notification.id === id
) {
notifications.splice(
notifications.indexOf(notification),
1
);
} }
}); });
} }
@ -206,10 +212,16 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
primary={primary} primary={primary}
secondary={ secondary={
<span> <span>
<Typography color="textSecondary" className={classes.mobileOnly}> <Typography
color="textSecondary"
className={classes.mobileOnly}
>
{secondary.slice(0, 35) + "..."} {secondary.slice(0, 35) + "..."}
</Typography> </Typography>
<Typography color="textSecondary" className={classes.desktopOnly}> <Typography
color="textSecondary"
className={classes.desktopOnly}
>
{secondary} {secondary}
</Typography> </Typography>
</span> </span>
@ -219,12 +231,18 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
{notif.type === "follow" ? ( {notif.type === "follow" ? (
<span> <span>
<Tooltip title="View profile"> <Tooltip title="View profile">
<LinkableIconButton to={`/profile/${notif.account.id}`}> <LinkableIconButton
to={`/profile/${notif.account.id}`}
>
<AssignmentIndIcon /> <AssignmentIndIcon />
</LinkableIconButton> </LinkableIconButton>
</Tooltip> </Tooltip>
<Tooltip title="Follow account"> <Tooltip title="Follow account">
<IconButton onClick={() => this.followMember(notif.account)}> <IconButton
onClick={() =>
this.followMember(notif.account)
}
>
<PersonAddIcon /> <PersonAddIcon />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
@ -232,7 +250,9 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
) : notif.status ? ( ) : notif.status ? (
<span> <span>
<Tooltip title="View conversation"> <Tooltip title="View conversation">
<LinkableIconButton to={`/conversation/${notif.status.id}`}> <LinkableIconButton
to={`/conversation/${notif.status.id}`}
>
<ForumIcon /> <ForumIcon />
</LinkableIconButton> </LinkableIconButton>
</Tooltip> </Tooltip>
@ -243,9 +263,12 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
notif.status.reblog notif.status.reblog
? notif.status.reblog.id ? notif.status.reblog.id
: notif.status.id : notif.status.id
}&visibility=${notif.status.visibility}&acct=${ }&visibility=${
notif.status.visibility
}&acct=${
notif.status.reblog notif.status.reblog
? notif.status.reblog.account.acct ? notif.status.reblog.account
.acct
: notif.status.account.acct : notif.status.account.acct
}`} }`}
> >
@ -256,7 +279,9 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
</span> </span>
) : null} ) : null}
<Tooltip title="Remove notification"> <Tooltip title="Remove notification">
<IconButton onClick={() => this.removeNotification(notif.id)}> <IconButton
onClick={() => this.removeNotification(notif.id)}
>
<DeleteIcon /> <DeleteIcon />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
@ -269,12 +294,15 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
this.client this.client
.post(`/accounts/${acct.id}/follow`) .post(`/accounts/${acct.id}/follow`)
.then((resp: any) => { .then((resp: any) => {
this.props.enqueueSnackbar("You are now following this account."); this.props.enqueueSnackbar(
"You are now following this account."
);
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.enqueueSnackbar("Couldn't follow account: " + err.name, { this.props.enqueueSnackbar(
variant: "error" "Couldn't follow account: " + err.name,
}); { variant: "error" }
);
console.error(err.message); console.error(err.message);
}); });
} }
@ -284,7 +312,8 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
return ( return (
<div className={classes.pageLayoutConstraints}> <div className={classes.pageLayoutConstraints}>
{this.state.viewDidLoad ? ( {this.state.viewDidLoad ? (
this.state.notifications && this.state.notifications.length > 0 ? ( this.state.notifications &&
this.state.notifications.length > 0 ? (
<div> <div>
<ListSubheader>Recent notifications</ListSubheader> <ListSubheader>Recent notifications</ListSubheader>
<Button <Button
@ -299,7 +328,9 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
<List> <List>
{this.state.notifications.map( {this.state.notifications.map(
(notification: Notification) => { (notification: Notification) => {
return this.createNotification(notification); return this.createNotification(
notification
);
} }
)} )}
</List> </List>
@ -309,8 +340,8 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
<div className={classes.pageLayoutEmptyTextConstraints}> <div className={classes.pageLayoutEmptyTextConstraints}>
<Typography variant="h4">All clear!</Typography> <Typography variant="h4">All clear!</Typography>
<Typography paragraph> <Typography paragraph>
It looks like you have no notifications. Why not get the It looks like you have no notifications. Why not
conversation going with a new post? get the conversation going with a new post?
</Typography> </Typography>
</div> </div>
) )
@ -322,7 +353,9 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
Something went wrong when loading this timeline. Something went wrong when loading this timeline.
</Typography> </Typography>
<Typography> <Typography>
{this.state.viewDidErrorCode ? this.state.viewDidErrorCode : ""} {this.state.viewDidErrorCode
? this.state.viewDidErrorCode
: ""}
</Typography> </Typography>
</Paper> </Paper>
) : ( ) : (
@ -330,7 +363,10 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
)} )}
{this.state.viewIsLoading ? ( {this.state.viewIsLoading ? (
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<CircularProgress className={classes.progress} color="primary" /> <CircularProgress
className={classes.progress}
color="primary"
/>
</div> </div>
) : ( ) : (
<span /> <span />
@ -345,8 +381,8 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
</DialogTitle> </DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText id="alert-dialog-description"> <DialogContentText id="alert-dialog-description">
Are you sure you want to delete all notifications? This action Are you sure you want to delete all notifications?
cannot be undone. This action cannot be undone.
</DialogContentText> </DialogContentText>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>

View File

@ -90,7 +90,6 @@ export const styles = (theme: Theme) =>
pageHeroContent: { pageHeroContent: {
padding: 16, padding: 16,
paddingTop: 8, paddingTop: 8,
textAlign: "center",
width: "100%", width: "100%",
height: "100%", height: "100%",
[theme.breakpoints.up("md")]: { [theme.breakpoints.up("md")]: {
@ -254,7 +253,7 @@ export const styles = (theme: Theme) =>
height: 128 height: 128
}, },
instanceHeaderPaper: { instanceHeaderPaper: {
height: 200, height: 150,
backgroundPosition: "center", backgroundPosition: "center",
backgroundRepeat: "no-repeat", backgroundRepeat: "no-repeat",
backgroundSize: "cover", backgroundSize: "cover",
@ -267,9 +266,11 @@ export const styles = (theme: Theme) =>
position: "absolute", position: "absolute",
bottom: theme.spacing.unit, bottom: theme.spacing.unit,
left: theme.spacing.unit * 2, left: theme.spacing.unit * 2,
"& *": {
color: theme.palette.common.white, color: theme.palette.common.white,
textShadow: `0 0 4px ${theme.palette.grey[700]}`, textShadow: `0 0 4px ${theme.palette.grey[700]}`,
fontWeight: 600 fontWeight: 600
}
}, },
instanceToolbar: { instanceToolbar: {
position: "absolute", position: "absolute",

View File

@ -135,7 +135,9 @@ class ProfilePage extends Component<any, IProfilePageState> {
getRelationships() { getRelationships() {
this.client this.client
.get("/accounts/relationships", { id: this.props.match.params.profileId }) .get("/accounts/relationships", {
id: this.props.match.params.profileId
})
.then((resp: any) => { .then((resp: any) => {
let relationship: Relationship = resp.data[0]; let relationship: Relationship = resp.data[0];
this.setState({ relationship }); this.setState({ relationship });
@ -189,7 +191,9 @@ class ProfilePage extends Component<any, IProfilePageState> {
}); });
}); });
} else { } else {
this.props.enqueueSnackbar("Reached end of posts", { variant: "error" }); this.props.enqueueSnackbar("Reached end of posts", {
variant: "error"
});
this.setState({ this.setState({
viewIsLoading: false, viewIsLoading: false,
viewDidLoad: true viewDidLoad: true
@ -234,12 +238,15 @@ class ProfilePage extends Component<any, IProfilePageState> {
.then((resp: any) => { .then((resp: any) => {
let relationship: Relationship = resp.data; let relationship: Relationship = resp.data;
this.setState({ relationship }); this.setState({ relationship });
this.props.enqueueSnackbar("You are now following this account."); this.props.enqueueSnackbar(
"You are now following this account."
);
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.enqueueSnackbar("Couldn't follow account: " + err.name, { this.props.enqueueSnackbar(
variant: "error" "Couldn't follow account: " + err.name,
}); { variant: "error" }
);
console.error(err.message); console.error(err.message);
}); });
} }
@ -283,12 +290,15 @@ class ProfilePage extends Component<any, IProfilePageState> {
.then((resp: any) => { .then((resp: any) => {
let relationship: Relationship = resp.data; let relationship: Relationship = resp.data;
this.setState({ relationship }); this.setState({ relationship });
this.props.enqueueSnackbar("You are now blocking this account."); this.props.enqueueSnackbar(
"You are now blocking this account."
);
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.enqueueSnackbar("Couldn't block account: " + err.name, { this.props.enqueueSnackbar(
variant: "error" "Couldn't block account: " + err.name,
}); { variant: "error" }
);
console.error(err.message); console.error(err.message);
}); });
} }
@ -314,7 +324,8 @@ class ProfilePage extends Component<any, IProfilePageState> {
title={ title={
this.isItMe() this.isItMe()
? "You can't follow yourself." ? "You can't follow yourself."
: this.state.relationship && this.state.relationship.following : this.state.relationship &&
this.state.relationship.following
? "Unfollow" ? "Unfollow"
: "Follow" : "Follow"
} }
@ -337,7 +348,9 @@ class ProfilePage extends Component<any, IProfilePageState> {
<Tooltip title={"Send a message or post"}> <Tooltip title={"Send a message or post"}>
<LinkableIconButton <LinkableIconButton
to={`/compose?acct=${ to={`/compose?acct=${
this.state.account ? this.state.account.acct : "" this.state.account
? this.state.account.acct
: ""
}`} }`}
color={"inherit"} color={"inherit"}
> >
@ -346,7 +359,8 @@ class ProfilePage extends Component<any, IProfilePageState> {
</Tooltip> </Tooltip>
<Tooltip <Tooltip
title={ title={
this.state.relationship && this.state.relationship.blocking this.state.relationship &&
this.state.relationship.blocking
? "Unblock this account" ? "Unblock this account"
: "Block this account" : "Block this account"
} }
@ -356,7 +370,8 @@ class ProfilePage extends Component<any, IProfilePageState> {
disabled={this.isItMe()} disabled={this.isItMe()}
onClick={() => this.toggleBlockDialog()} onClick={() => this.toggleBlockDialog()}
> >
{this.state.relationship && this.state.relationship.blocking ? ( {this.state.relationship &&
this.state.relationship.blocking ? (
<AccountHeartIcon /> <AccountHeartIcon />
) : ( ) : (
<AccountRemoveIcon /> <AccountRemoveIcon />
@ -365,7 +380,11 @@ class ProfilePage extends Component<any, IProfilePageState> {
</Tooltip> </Tooltip>
<Tooltip title="Open in web"> <Tooltip title="Open in web">
<IconButton <IconButton
href={this.state.account ? this.state.account.url : ""} href={
this.state.account
? this.state.account.url
: ""
}
target="_blank" target="_blank"
rel={"nofollower noreferrer noopener"} rel={"nofollower noreferrer noopener"}
color={"inherit"} color={"inherit"}
@ -384,7 +403,11 @@ class ProfilePage extends Component<any, IProfilePageState> {
<div className={classes.profileContent}> <div className={classes.profileContent}>
<Avatar <Avatar
className={classes.profileAvatar} className={classes.profileAvatar}
src={this.state.account ? this.state.account.avatar : ""} src={
this.state.account
? this.state.account.avatar
: ""
}
/> />
<div className={classes.profileUserBox}> <div className={classes.profileUserBox}>
<Typography <Typography
@ -394,7 +417,8 @@ class ProfilePage extends Component<any, IProfilePageState> {
__html: this.state.account __html: this.state.account
? this.state.account.display_name ? this.state.account.display_name
? emojifyString( ? emojifyString(
this.state.account.display_name, this.state.account
.display_name,
this.state.account.emojis, this.state.account.emojis,
classes.pageProfileNameEmoji classes.pageProfileNameEmoji
) )
@ -404,7 +428,9 @@ class ProfilePage extends Component<any, IProfilePageState> {
className={classes.pageProfileNameEmoji} className={classes.pageProfileNameEmoji}
/> />
<Typography variant="caption" color="inherit"> <Typography variant="caption" color="inherit">
{this.state.account ? "@" + this.state.account.acct : ""} {this.state.account
? "@" + this.state.account.acct
: ""}
</Typography> </Typography>
<Typography paragraph color="inherit"> <Typography paragraph color="inherit">
{this.state.account {this.state.account
@ -414,11 +440,17 @@ class ProfilePage extends Component<any, IProfilePageState> {
: "No bio available."} : "No bio available."}
</Typography> </Typography>
<Typography color={"inherit"}> <Typography color={"inherit"}>
{this.state.account ? this.state.account.followers_count : 0}{" "} {this.state.account
? this.state.account.followers_count
: 0}{" "}
followers |{" "} followers |{" "}
{this.state.account ? this.state.account.following_count : 0}{" "} {this.state.account
? this.state.account.following_count
: 0}{" "}
following |{" "} following |{" "}
{this.state.account ? this.state.account.statuses_count : 0}{" "} {this.state.account
? this.state.account.statuses_count
: 0}{" "}
posts posts
</Typography> </Typography>
</div> </div>
@ -432,7 +464,9 @@ class ProfilePage extends Component<any, IProfilePageState> {
Something went wrong when loading this profile. Something went wrong when loading this profile.
</Typography> </Typography>
<Typography> <Typography>
{this.state.viewDidErrorCode ? this.state.viewDidErrorCode : ""} {this.state.viewDidErrorCode
? this.state.viewDidErrorCode
: ""}
</Typography> </Typography>
</Paper> </Paper>
) : ( ) : (
@ -441,15 +475,26 @@ class ProfilePage extends Component<any, IProfilePageState> {
{this.state.posts ? ( {this.state.posts ? (
<div> <div>
{this.state.posts.map((post: Status) => { {this.state.posts.map((post: Status) => {
return <Post key={post.id} post={post} client={this.client} />; return (
<Post
key={post.id}
post={post}
client={this.client}
/>
);
})} })}
<br /> <br />
{this.state.viewDidLoad && !this.state.viewDidError ? ( {this.state.viewDidLoad &&
!this.state.viewDidError ? (
<div <div
style={{ textAlign: "center" }} style={{ textAlign: "center" }}
onClick={() => this.loadMoreTimelinePieces()} onClick={() =>
this.loadMoreTimelinePieces()
}
> >
<Button variant="contained">Load more</Button> <Button variant="contained">
Load more
</Button>
</div> </div>
) : null} ) : null}
</div> </div>
@ -458,7 +503,10 @@ class ProfilePage extends Component<any, IProfilePageState> {
)} )}
{this.state.viewIsLoading ? ( {this.state.viewIsLoading ? (
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<CircularProgress className={classes.progress} color="primary" /> <CircularProgress
className={classes.progress}
color="primary"
/>
</div> </div>
) : ( ) : (
<span /> <span />
@ -472,8 +520,9 @@ class ProfilePage extends Component<any, IProfilePageState> {
</DialogTitle> </DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText id="alert-dialog-description"> <DialogContentText id="alert-dialog-description">
Are you sure you want to block this person? You won't see their Are you sure you want to block this person? You
posts on your home feed, local timeline, or public timeline. won't see their posts on your home feed, local
timeline, or public timeline.
</DialogContentText> </DialogContentText>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>

View File

@ -167,8 +167,12 @@ class PublicPage extends Component<any, IPublicPageState> {
<ArrowUpwardIcon /> <ArrowUpwardIcon />
</Avatar> </Avatar>
} }
label={`View ${this.state.backlogPosts.length} new post${ label={`View ${
this.state.backlogPosts.length > 1 ? "s" : "" this.state.backlogPosts.length
} new post${
this.state.backlogPosts.length > 1
? "s"
: ""
}`} }`}
color="primary" color="primary"
className={classes.pageTopChip} className={classes.pageTopChip}
@ -182,7 +186,13 @@ class PublicPage extends Component<any, IPublicPageState> {
{this.state.posts ? ( {this.state.posts ? (
<div> <div>
{this.state.posts.map((post: Status) => { {this.state.posts.map((post: Status) => {
return <Post key={post.id} post={post} client={this.client} />; return (
<Post
key={post.id}
post={post}
client={this.client}
/>
);
})} })}
<br /> <br />
{this.state.viewDidLoad && !this.state.viewDidError ? ( {this.state.viewDidLoad && !this.state.viewDidError ? (
@ -204,7 +214,9 @@ class PublicPage extends Component<any, IPublicPageState> {
Something went wrong when loading this timeline. Something went wrong when loading this timeline.
</Typography> </Typography>
<Typography> <Typography>
{this.state.viewDidErrorCode ? this.state.viewDidErrorCode : ""} {this.state.viewDidErrorCode
? this.state.viewDidErrorCode
: ""}
</Typography> </Typography>
</Paper> </Paper>
) : ( ) : (
@ -212,7 +224,10 @@ class PublicPage extends Component<any, IPublicPageState> {
)} )}
{this.state.viewIsLoading ? ( {this.state.viewIsLoading ? (
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<CircularProgress className={classes.progress} color="primary" /> <CircularProgress
className={classes.progress}
color="primary"
/>
</div> </div>
) : ( ) : (
<span /> <span />

View File

@ -96,23 +96,31 @@ class RecommendationsPage extends Component<
this.client this.client
.post(`/accounts/${acct.id}/follow`) .post(`/accounts/${acct.id}/follow`)
.then((resp: any) => { .then((resp: any) => {
this.props.enqueueSnackbar("You are now following this account."); this.props.enqueueSnackbar(
"You are now following this account."
);
this.client.del(`/suggestions/${acct.id}`).then((resp: any) => { this.client.del(`/suggestions/${acct.id}`).then((resp: any) => {
let followSuggestions = this.state.followSuggestions; let followSuggestions = this.state.followSuggestions;
if (followSuggestions) { if (followSuggestions) {
followSuggestions.forEach((suggestion: Account, index: number) => { followSuggestions.forEach(
if (followSuggestions && suggestion.id === acct.id) { (suggestion: Account, index: number) => {
if (
followSuggestions &&
suggestion.id === acct.id
) {
followSuggestions.splice(index, 1); followSuggestions.splice(index, 1);
} }
}); }
);
this.setState({ followSuggestions }); this.setState({ followSuggestions });
} }
}); });
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.enqueueSnackbar("Couldn't follow account: " + err.name, { this.props.enqueueSnackbar(
variant: "error" "Couldn't follow account: " + err.name,
}); { variant: "error" }
);
console.error(err.message); console.error(err.message);
}); });
} }
@ -123,16 +131,20 @@ class RecommendationsPage extends Component<
.then((resp: any) => { .then((resp: any) => {
let requestedFollows = this.state.requestedFollows; let requestedFollows = this.state.requestedFollows;
if (requestedFollows) { if (requestedFollows) {
requestedFollows.forEach((request: Account, index: number) => { requestedFollows.forEach(
(request: Account, index: number) => {
if (requestedFollows && request.id === acct.id) { if (requestedFollows && request.id === acct.id) {
requestedFollows.splice(index, 1); requestedFollows.splice(index, 1);
} }
}); }
);
} }
this.setState({ requestedFollows }); this.setState({ requestedFollows });
let verb: string = type; let verb: string = type;
verb === "authorize" ? (verb = "authorized") : (verb = "rejected"); verb === "authorize"
? (verb = "authorized")
: (verb = "rejected");
this.props.enqueueSnackbar(`You have ${verb} this request.`); this.props.enqueueSnackbar(`You have ${verb} this request.`);
}) })
.catch((err: Error) => { .catch((err: Error) => {
@ -152,25 +164,34 @@ class RecommendationsPage extends Component<
<Paper className={classes.pageListConstraints}> <Paper className={classes.pageListConstraints}>
<List> <List>
{this.state.requestedFollows {this.state.requestedFollows
? this.state.requestedFollows.map((request: Account) => { ? this.state.requestedFollows.map(
(request: Account) => {
return ( return (
<ListItem key={request.id}> <ListItem key={request.id}>
<ListItemAvatar> <ListItemAvatar>
<LinkableAvatar <LinkableAvatar
to={`/profile/${request.id}`} to={`/profile/${request.id}`}
alt={request.username} alt={request.username}
src={request.avatar_static} src={
request.avatar_static
}
/> />
</ListItemAvatar> </ListItemAvatar>
<ListItemText <ListItemText
primary={request.display_name || request.acct} primary={
request.display_name ||
request.acct
}
secondary={request.acct} secondary={request.acct}
/> />
<ListItemSecondaryAction> <ListItemSecondaryAction>
<Tooltip title="Accept request"> <Tooltip title="Accept request">
<IconButton <IconButton
onClick={() => onClick={() =>
this.handleFollowRequest(request, "authorize") this.handleFollowRequest(
request,
"authorize"
)
} }
> >
<CheckIcon /> <CheckIcon />
@ -179,21 +200,27 @@ class RecommendationsPage extends Component<
<Tooltip title="Reject request"> <Tooltip title="Reject request">
<IconButton <IconButton
onClick={() => onClick={() =>
this.handleFollowRequest(request, "reject") this.handleFollowRequest(
request,
"reject"
)
} }
> >
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Tooltip title="View profile"> <Tooltip title="View profile">
<LinkableIconButton to={`/profile/${request.id}`}> <LinkableIconButton
to={`/profile/${request.id}`}
>
<AccountCircleIcon /> <AccountCircleIcon />
</LinkableIconButton> </LinkableIconButton>
</Tooltip> </Tooltip>
</ListItemSecondaryAction> </ListItemSecondaryAction>
</ListItem> </ListItem>
); );
}) }
)
: null} : null}
</List> </List>
</Paper> </Paper>
@ -210,29 +237,41 @@ class RecommendationsPage extends Component<
<Paper className={classes.pageListConstraints}> <Paper className={classes.pageListConstraints}>
<List> <List>
{this.state.followSuggestions {this.state.followSuggestions
? this.state.followSuggestions.map((suggestion: Account) => { ? this.state.followSuggestions.map(
(suggestion: Account) => {
return ( return (
<ListItem key={suggestion.id}> <ListItem key={suggestion.id}>
<ListItemAvatar> <ListItemAvatar>
<LinkableAvatar <LinkableAvatar
to={`/profile/${suggestion.id}`} to={`/profile/${suggestion.id}`}
alt={suggestion.username} alt={suggestion.username}
src={suggestion.avatar_static} src={
suggestion.avatar_static
}
/> />
</ListItemAvatar> </ListItemAvatar>
<ListItemText <ListItemText
primary={suggestion.display_name || suggestion.acct} primary={
suggestion.display_name ||
suggestion.acct
}
secondary={suggestion.acct} secondary={suggestion.acct}
/> />
<ListItemSecondaryAction> <ListItemSecondaryAction>
<Tooltip title="View profile"> <Tooltip title="View profile">
<LinkableIconButton to={`/profile/${suggestion.id}`}> <LinkableIconButton
to={`/profile/${suggestion.id}`}
>
<AssignmentIndIcon /> <AssignmentIndIcon />
</LinkableIconButton> </LinkableIconButton>
</Tooltip> </Tooltip>
<Tooltip title="Follow"> <Tooltip title="Follow">
<IconButton <IconButton
onClick={() => this.followMember(suggestion)} onClick={() =>
this.followMember(
suggestion
)
}
> >
<PersonAddIcon /> <PersonAddIcon />
</IconButton> </IconButton>
@ -240,7 +279,8 @@ class RecommendationsPage extends Component<
</ListItemSecondaryAction> </ListItemSecondaryAction>
</ListItem> </ListItem>
); );
}) }
)
: null} : null}
</List> </List>
</Paper> </Paper>
@ -259,7 +299,11 @@ class RecommendationsPage extends Component<
this.state.requestedFollows.length > 0 ? ( this.state.requestedFollows.length > 0 ? (
this.showFollowRequests() this.showFollowRequests()
) : ( ) : (
<div className={classes.pageLayoutEmptyTextConstraints}> <div
className={
classes.pageLayoutEmptyTextConstraints
}
>
<Typography variant="h6"> <Typography variant="h6">
You don't have any follow requests. You don't have any follow requests.
</Typography> </Typography>
@ -272,13 +316,17 @@ class RecommendationsPage extends Component<
this.state.followSuggestions.length > 0 ? ( this.state.followSuggestions.length > 0 ? (
this.showFollowSuggestions() this.showFollowSuggestions()
) : ( ) : (
<div className={classes.pageLayoutEmptyTextConstraints}> <div
className={
classes.pageLayoutEmptyTextConstraints
}
>
<Typography variant="h5"> <Typography variant="h5">
We don't have any suggestions for you. We don't have any suggestions for you.
</Typography> </Typography>
<Typography paragraph> <Typography paragraph>
Why not interact with the fediverse a bit by creating a new Why not interact with the fediverse a bit by
post? creating a new post?
</Typography> </Typography>
</div> </div>
)} )}
@ -291,7 +339,9 @@ class RecommendationsPage extends Component<
Something went wrong when loading this timeline. Something went wrong when loading this timeline.
</Typography> </Typography>
<Typography> <Typography>
{this.state.viewDidErrorCode ? this.state.viewDidErrorCode : ""} {this.state.viewDidErrorCode
? this.state.viewDidErrorCode
: ""}
</Typography> </Typography>
</Paper> </Paper>
) : ( ) : (
@ -299,7 +349,10 @@ class RecommendationsPage extends Component<
)} )}
{this.state.viewIsLoading ? ( {this.state.viewIsLoading ? (
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<CircularProgress className={classes.progress} color="primary" /> <CircularProgress
className={classes.progress}
color="primary"
/>
</div> </div>
) : ( ) : (
<span /> <span />

View File

@ -179,12 +179,15 @@ class SearchPage extends Component<any, ISearchPageState> {
client client
.post(`/accounts/${acct.id}/follow`) .post(`/accounts/${acct.id}/follow`)
.then((resp: any) => { .then((resp: any) => {
this.props.enqueueSnackbar("You are now following this account."); this.props.enqueueSnackbar(
"You are now following this account."
);
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.enqueueSnackbar("Couldn't follow account: " + err.name, { this.props.enqueueSnackbar(
variant: "error" "Couldn't follow account: " + err.name,
}); { variant: "error" }
);
console.error(err.message); console.error(err.message);
}); });
} }
@ -195,10 +198,12 @@ class SearchPage extends Component<any, ISearchPageState> {
<div> <div>
<ListSubheader>Accounts</ListSubheader> <ListSubheader>Accounts</ListSubheader>
{this.state.results && this.state.results.accounts.length > 0 ? ( {this.state.results &&
this.state.results.accounts.length > 0 ? (
<Paper className={classes.pageListConstraints}> <Paper className={classes.pageListConstraints}>
<List> <List>
{this.state.results.accounts.map((acct: Account) => { {this.state.results.accounts.map(
(acct: Account) => {
return ( return (
<ListItem key={acct.id}> <ListItem key={acct.id}>
<ListItemAvatar> <ListItemAvatar>
@ -209,18 +214,27 @@ class SearchPage extends Component<any, ISearchPageState> {
/> />
</ListItemAvatar> </ListItemAvatar>
<ListItemText <ListItemText
primary={acct.display_name || acct.acct} primary={
acct.display_name ||
acct.acct
}
secondary={acct.acct} secondary={acct.acct}
/> />
<ListItemSecondaryAction> <ListItemSecondaryAction>
<Tooltip title="View profile"> <Tooltip title="View profile">
<LinkableIconButton to={`/profile/${acct.id}`}> <LinkableIconButton
to={`/profile/${acct.id}`}
>
<AssignmentIndIcon /> <AssignmentIndIcon />
</LinkableIconButton> </LinkableIconButton>
</Tooltip> </Tooltip>
<Tooltip title="Follow"> <Tooltip title="Follow">
<IconButton <IconButton
onClick={() => this.followMemberFromQuery(acct)} onClick={() =>
this.followMemberFromQuery(
acct
)
}
> >
<PersonAddIcon /> <PersonAddIcon />
</IconButton> </IconButton>
@ -228,7 +242,8 @@ class SearchPage extends Component<any, ISearchPageState> {
</ListItemSecondaryAction> </ListItemSecondaryAction>
</ListItem> </ListItem>
); );
})} }
)}
</List> </List>
</Paper> </Paper>
) : ( ) : (
@ -253,7 +268,13 @@ class SearchPage extends Component<any, ISearchPageState> {
{this.state.results ? ( {this.state.results ? (
this.state.results.statuses.length > 0 ? ( this.state.results.statuses.length > 0 ? (
this.state.results.statuses.map((post: Status) => { this.state.results.statuses.map((post: Status) => {
return <Post key={post.id} post={post} client={this.client} />; return (
<Post
key={post.id}
post={post}
client={this.client}
/>
);
}) })
) : ( ) : (
<Typography <Typography
@ -276,7 +297,13 @@ class SearchPage extends Component<any, ISearchPageState> {
{this.state.tagResults ? ( {this.state.tagResults ? (
this.state.tagResults.length > 0 ? ( this.state.tagResults.length > 0 ? (
this.state.tagResults.map((post: Status) => { this.state.tagResults.map((post: Status) => {
return <Post key={post.id} post={post} client={this.client} />; return (
<Post
key={post.id}
post={post}
client={this.client}
/>
);
}) })
) : ( ) : (
<Typography <Typography
@ -310,7 +337,9 @@ class SearchPage extends Component<any, ISearchPageState> {
Something went wrong when loading this timeline. Something went wrong when loading this timeline.
</Typography> </Typography>
<Typography> <Typography>
{this.state.viewDidErrorCode ? this.state.viewDidErrorCode : ""} {this.state.viewDidErrorCode
? this.state.viewDidErrorCode
: ""}
</Typography> </Typography>
</Paper> </Paper>
) : ( ) : (
@ -318,7 +347,10 @@ class SearchPage extends Component<any, ISearchPageState> {
)} )}
{this.state.viewIsLoading ? ( {this.state.viewIsLoading ? (
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<CircularProgress className={classes.progress} color="primary" /> <CircularProgress
className={classes.progress}
color="primary"
/>
</div> </div>
) : ( ) : (
<span /> <span />

View File

@ -46,7 +46,6 @@ import {
} from "../utilities/themes"; } from "../utilities/themes";
import { Visibility } from "../types/Visibility"; import { Visibility } from "../types/Visibility";
import { LinkableButton } from "../interfaces/overrides"; import { LinkableButton } from "../interfaces/overrides";
import { Config } from "../types/Config";
import OpenInNewIcon from "@material-ui/icons/OpenInNew"; import OpenInNewIcon from "@material-ui/icons/OpenInNew";
import DevicesIcon from "@material-ui/icons/Devices"; import DevicesIcon from "@material-ui/icons/Devices";
@ -59,7 +58,7 @@ import NotificationsIcon from "@material-ui/icons/Notifications";
import BellAlertIcon from "mdi-material-ui/BellAlert"; import BellAlertIcon from "mdi-material-ui/BellAlert";
import RefreshIcon from "@material-ui/icons/Refresh"; import RefreshIcon from "@material-ui/icons/Refresh";
import UndoIcon from "@material-ui/icons/Undo"; import UndoIcon from "@material-ui/icons/Undo";
import DomainDisablbedIcon from "@material-ui/icons/DomainDisabled"; import { Config } from "../types/Config";
interface ISettingsState { interface ISettingsState {
darkModeEnabled: boolean; darkModeEnabled: boolean;
@ -500,20 +499,6 @@ class SettingsPage extends Component<any, ISettingsState> {
<LinkableButton to="/you">Edit</LinkableButton> <LinkableButton to="/you">Edit</LinkableButton>
</ListItemSecondaryAction> </ListItemSecondaryAction>
</ListItem> </ListItem>
<ListItem>
<ListItemAvatar>
<DomainDisablbedIcon color="action" />
</ListItemAvatar>
<ListItemText
primary="Manage blocked servers"
secondary="View and manage servers that you've blocked"
/>
<ListItemSecondaryAction>
<LinkableButton to="/blocked">
Manage
</LinkableButton>
</ListItemSecondaryAction>
</ListItem>
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
<MastodonIcon color="action" /> <MastodonIcon color="action" />

View File

@ -17,11 +17,7 @@ import {
import { styles } from "./WelcomePage.styles"; import { styles } from "./WelcomePage.styles";
import Mastodon from "megalodon"; import Mastodon from "megalodon";
import { SaveClientSession } from "../types/SessionData"; import { SaveClientSession } from "../types/SessionData";
import { import { createHyperspaceApp, getRedirectAddress } from "../utilities/login";
createHyperspaceApp,
getRedirectAddress,
inDisallowedDomains
} from "../utilities/login";
import { parseUrl } from "query-string"; import { parseUrl } from "query-string";
import { getConfig } from "../utilities/settings"; import { getConfig } from "../utilities/settings";
import { isDarwinApp } from "../utilities/desktop"; import { isDarwinApp } from "../utilities/desktop";
@ -83,17 +79,9 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
let config: Config = result; let config: Config = result;
if (result.location === "dynamic") { if (result.location === "dynamic") {
console.warn( console.warn(
"Redirect URI is set to dynamic, which may affect how sign-in works for some users. Careful!" "Recirect URI is set to dynamic, which may affect how sign-in works for some users. Careful!"
); );
} }
if (
inDisallowedDomains(result.registration.defaultInstance)
) {
console.warn(
`The default instance field in config.json contains an unsupported domain (${result.registration.defaultInstance}), so it's been reset to mastodon.social.`
);
result.registration.defaultInstance = "mastodon.social";
}
this.setState({ this.setState({
logoUrl: config.branding logoUrl: config.branding
? result.branding.logo ? result.branding.logo
@ -322,19 +310,8 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
if (this.state.user.includes("@")) { if (this.state.user.includes("@")) {
if (this.state.federates && this.state.federates === true) { if (this.state.federates && this.state.federates === true) {
let baseUrl = this.state.user.split("@")[1]; let baseUrl = this.state.user.split("@")[1];
if (inDisallowedDomains(baseUrl)) {
this.setState({
userInputError: true,
userInputErrorMessage: `Signing in with an account from ${baseUrl} isn't supported.`
});
return true;
} else {
axios axios
.get( .get("https://" + baseUrl + "/api/v1/timelines/public")
"https://" +
baseUrl +
"/api/v1/timelines/public"
)
.catch((err: Error) => { .catch((err: Error) => {
let userInputError = true; let userInputError = true;
let userInputErrorMessage = let userInputErrorMessage =
@ -345,7 +322,6 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
}); });
return true; return true;
}); });
}
} else if ( } else if (
this.state.user.includes( this.state.user.includes(
this.state.registerBase this.state.registerBase
@ -726,8 +702,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
{this.state.brandName {this.state.brandName
? this.state.brandName ? this.state.brandName
: "Hypersapce"}{" "} : "Hypersapce"}{" "}
v. v.{this.state.version}{" "}
{this.state.version}{" "}
{this.state.brandName && {this.state.brandName &&
this.state.brandName !== "Hyperspace" this.state.brandName !== "Hyperspace"
? "(Hyperspace-like)" ? "(Hyperspace-like)"

View File

@ -71,9 +71,14 @@ class You extends Component<IYouProps, IYouState> {
.then((acct: any) => { .then((acct: any) => {
let currentAccount: Account = acct.data; let currentAccount: Account = acct.data;
this.setState({ currentAccount }); this.setState({ currentAccount });
localStorage.setItem("account", JSON.stringify(currentAccount)); localStorage.setItem(
"account",
JSON.stringify(currentAccount)
);
this.props.closeSnackbar("persistAvatar"); this.props.closeSnackbar("persistAvatar");
this.props.enqueueSnackbar("Avatar updated successfully."); this.props.enqueueSnackbar(
"Avatar updated successfully."
);
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.closeSnackbar("persistAvatar"); this.props.closeSnackbar("persistAvatar");
@ -85,7 +90,9 @@ class You extends Component<IYouProps, IYouState> {
} }
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.enqueueSnackbar("Couldn't update avatar: " + err.name); this.props.enqueueSnackbar(
"Couldn't update avatar: " + err.name
);
}); });
} }
@ -107,9 +114,14 @@ class You extends Component<IYouProps, IYouState> {
.then((acct: any) => { .then((acct: any) => {
let currentAccount: Account = acct.data; let currentAccount: Account = acct.data;
this.setState({ currentAccount }); this.setState({ currentAccount });
localStorage.setItem("account", JSON.stringify(currentAccount)); localStorage.setItem(
"account",
JSON.stringify(currentAccount)
);
this.props.closeSnackbar("persistHeader"); this.props.closeSnackbar("persistHeader");
this.props.enqueueSnackbar("Header updated successfully."); this.props.enqueueSnackbar(
"Header updated successfully."
);
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.closeSnackbar("persistHeader"); this.props.closeSnackbar("persistHeader");
@ -121,7 +133,9 @@ class You extends Component<IYouProps, IYouState> {
} }
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.enqueueSnackbar("Couldn't update header: " + err.name); this.props.enqueueSnackbar(
"Couldn't update header: " + err.name
);
}); });
} }
@ -157,7 +171,7 @@ class You extends Component<IYouProps, IYouState> {
}); });
} }
updateDisplayname(name: string) { updateDisplayName(name: string) {
this.setState({ newDisplayName: name }); this.setState({ newDisplayName: name });
} }
changeBio() { changeBio() {
@ -198,15 +212,27 @@ class You extends Component<IYouProps, IYouState> {
backgroundImage: `url("${this.state.currentAccount.header_static}")` backgroundImage: `url("${this.state.currentAccount.header_static}")`
}} }}
/> />
<div className={classes.pageHeroContent}> <div className={classes.profileContent}>
<br />
<Avatar <Avatar
className={classes.pageProfileAvatar} className={classes.profileAvatar}
src={this.state.currentAccount.avatar_static} src={this.state.currentAccount.avatar_static}
/> />
<Typography variant="h4" color="inherit" component="h1"> <div
className={classes.profileUserBox}
style={{ paddingTop: 8, paddingBottom: 8 }}
>
<Typography
variant="h4"
color="inherit"
component="h1"
>
Edit your profile Edit your profile
</Typography> </Typography>
<br /> <Typography color="inherit">
Change information such as your display name,
bio, and images used here.
</Typography>
<div> <div>
<Button <Button
className={classes.pageProfileFollowButton} className={classes.pageProfileFollowButton}
@ -223,7 +249,7 @@ class You extends Component<IYouProps, IYouState> {
Change Header Change Header
</Button> </Button>
</div> </div>
<br /> </div>
</div> </div>
</div> </div>
<div className={classes.pageContentLayoutConstraints}> <div className={classes.pageContentLayoutConstraints}>
@ -234,14 +260,16 @@ class You extends Component<IYouProps, IYouState> {
<br /> <br />
<TextField <TextField
className={classes.TextField} className={classes.TextField}
defaultValue={this.state.currentAccount.display_name} defaultValue={
this.state.currentAccount.display_name
}
rowsMax="1" rowsMax="1"
variant="outlined" variant="outlined"
fullWidth fullWidth
onChange={(event: any) => onChange={(event: any) =>
this.updateDisplayname(event.target.value) this.updateDisplayName(event.target.value)
} }
></TextField> />
<div style={{ textAlign: "right" }}> <div style={{ textAlign: "right" }}>
<Button <Button
className={classes.pageProfileFollowButton} className={classes.pageProfileFollowButton}
@ -262,7 +290,9 @@ class You extends Component<IYouProps, IYouState> {
className={classes.TextField} className={classes.TextField}
defaultValue={ defaultValue={
this.state.currentAccount.note this.state.currentAccount.note
? this.removeHTMLContent(this.state.currentAccount.note) ? this.removeHTMLContent(
this.state.currentAccount.note
)
: "Tell a little bit about yourself" : "Tell a little bit about yourself"
} }
multiline multiline
@ -270,8 +300,10 @@ class You extends Component<IYouProps, IYouState> {
rows="2" rows="2"
rowsMax="5" rowsMax="5"
fullWidth fullWidth
onChange={(event: any) => this.updateBio(event.target.value)} onChange={(event: any) =>
></TextField> this.updateBio(event.target.value)
}
/>
<div style={{ textAlign: "right" }}> <div style={{ textAlign: "right" }}>
<Button <Button
className={classes.pageProfileFollowButton} className={classes.pageProfileFollowButton}

View File

@ -1,7 +1,10 @@
import Mastodon from "megalodon"; import Mastodon from "megalodon";
export function userLoggedIn(): boolean { export function userLoggedIn(): boolean {
if (localStorage.getItem("baseurl") && localStorage.getItem("access_token")) { if (
localStorage.getItem("baseurl") &&
localStorage.getItem("access_token")
) {
return true; return true;
} else { } else {
return false; return false;

View File

@ -55,13 +55,3 @@ export function getRedirectAddress(
return type; return type;
} }
} }
/**
* Determine whether a base URL is in the 'disallowed' domains section.
* @param domain The URL to test
* @returns Boolean dictating the URL's presence in disallowed domains
*/
export function inDisallowedDomains(domain: string): boolean {
let disallowed = ["gab.com"];
return disallowed.includes(domain);
}

View File

@ -135,7 +135,8 @@ export async function getConfig(): Promise<Config | undefined> {
return config; return config;
} catch (err) { } catch (err) {
console.error( console.error(
"Couldn't configure Hyperspace with the config file. Reason: " + err.name "Couldn't configure Hyperspace with the config file. Reason: " +
err.name
); );
} }
} }