Configuration settings
This commit is contained in:
parent
2397dd6b56
commit
e04fd587c8
|
@ -0,0 +1,45 @@
|
|||
# Hyperspace 1.0 Configuration File
|
||||
|
||||
Hyperspace 1.0 comes with a new configuration file app providers can use to create a custom experience relatively easy. This is inspired from the way the [Riot](https://github.com/vector-im/riot-web) project handles configurations for their app. The following fields should be in the `config.json` folder at the root of the Hyperspace installation.
|
||||
|
||||
- `version`: The app's version using semantic versioning. This can be used to differentiate between versions of the main Hyperspace app or the custom deployment.
|
||||
- `branding`: The custom branding for Hyperspace.
|
||||
- `name`: The name for the brand/app. Affects title bar, about screens, and main interface by replacing the "Hyperspace" text.
|
||||
- `logo`: The filepath of the brand's logo, relative to the deployment folder. Can be a relative path (`brand/logo.png`) or a URL (`https://www.test.com/brands/logo-hs.png`).
|
||||
- `background`: The background used on the login page. Can be a relative path (`brand/bg.png`) or a URL (`https://www.test.com/brands/bg-hs.png`)
|
||||
- `developer`: Whether the version is a developer version or should be put in developer mode. Used to signify unstable releases with new features to play around with.
|
||||
- `federated`: Whether Hyperspace should enable federating features in its interface for Mastodon. Disabling federation disables the public timeline.
|
||||
- `registration`: Information regarding registration of accounts.
|
||||
- `defaultInstance`: The host name of the instance to default to when making accounts. Affects "well-known" sign-in and 'Create account' buttons
|
||||
- `admin`: Information about the app provider/administrator:
|
||||
- `name`: The name of the app provider
|
||||
- `account`: The Account ID of the app provider on Mastodon, in-instance or not
|
||||
- `license`: Licensing information about the app. Will default to Apache 2.0 if not listed (the standard license for Hyperspace source code).
|
||||
- `name`: The name of the license.
|
||||
- `url`: The link to the license for reviewing.
|
||||
|
||||
## Example Config File
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0.0beta1",
|
||||
"branding": {
|
||||
"name": "Hyperspace",
|
||||
"logo": "logo.svg",
|
||||
"background": "background.png"
|
||||
},
|
||||
"developer": "true",
|
||||
"federated": "true",
|
||||
"registration": {
|
||||
"defaultInstance": "mastodon.social"
|
||||
},
|
||||
"admin": {
|
||||
"name": "Eugen",
|
||||
"account": "1"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0 License",
|
||||
"url": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
}
|
||||
}
|
||||
```
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"version": "1.0.0beta1",
|
||||
"branding": {
|
||||
"name": "Hyperspace",
|
||||
"logo": "logo.svg",
|
||||
|
@ -11,6 +12,10 @@
|
|||
},
|
||||
"admin": {
|
||||
"name": "Marquis Kurt",
|
||||
"account": "alicerunsonfedora@mastodon.social"
|
||||
"account": "367895"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0 License",
|
||||
"url": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import Mastodon from 'megalodon';
|
|||
import { Notification } from '../../types/Notification';
|
||||
import {sendNotificationRequest} from '../../utilities/notifications';
|
||||
import {withSnackbar} from 'notistack';
|
||||
import { getConfig } from '../../utilities/settings';
|
||||
|
||||
interface IAppLayoutState {
|
||||
acctMenuOpen: boolean;
|
||||
|
@ -27,6 +28,9 @@ interface IAppLayoutState {
|
|||
currentUser?: UAccount;
|
||||
notificationCount: number;
|
||||
logOutOpen: boolean;
|
||||
enableFederation?: boolean;
|
||||
brandName?: string;
|
||||
developerMode?: boolean;
|
||||
}
|
||||
|
||||
export class AppLayout extends Component<any, IAppLayoutState> {
|
||||
|
@ -65,6 +69,18 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
|||
})
|
||||
}
|
||||
|
||||
getConfig().then((config: any) => {
|
||||
this.setState({
|
||||
enableFederation: config.federated === "true",
|
||||
brandName: config.branding? config.branding.name: "Hyperspace",
|
||||
developerMode: config.developer === "true"
|
||||
});
|
||||
})
|
||||
|
||||
this.streamNotifications()
|
||||
}
|
||||
|
||||
streamNotifications() {
|
||||
this.streamListener = this.client.stream('/streaming/user');
|
||||
|
||||
this.streamListener.on('notification', (notif: Notification) => {
|
||||
|
@ -131,14 +147,17 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
|||
logOutAndRestart() {
|
||||
let loginData = localStorage.getItem("login");
|
||||
if (loginData) {
|
||||
localStorage.clear();
|
||||
let items = ["login", "account", "baseurl", "access_token"];
|
||||
items.forEach((entry) => {
|
||||
localStorage.removeItem(entry);
|
||||
})
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
titlebar() {
|
||||
const { classes } = this.props;
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
if (this.state.developerMode || process.env.NODE_ENV === "development") {
|
||||
return (
|
||||
<div className={classes.titleBarRoot}>
|
||||
<Typography className={classes.titleBarText}>Careful: you're running in developer mode.</Typography>
|
||||
|
@ -192,15 +211,22 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
|||
<ListItemIcon><DomainIcon/></ListItemIcon>
|
||||
<ListItemText primary="Local"/>
|
||||
</LinkableListItem>
|
||||
<LinkableListItem button key="public" to="/public">
|
||||
<ListItemIcon><PublicIcon/></ListItemIcon>
|
||||
<ListItemText primary="Public"/>
|
||||
</LinkableListItem>
|
||||
{
|
||||
this.state.enableFederation?
|
||||
<LinkableListItem button key="public" to="/public">
|
||||
<ListItemIcon><PublicIcon/></ListItemIcon>
|
||||
<ListItemText primary="Public"/>
|
||||
</LinkableListItem>:
|
||||
<ListItem disabled>
|
||||
<ListItemIcon><PublicIcon/></ListItemIcon>
|
||||
<ListItemText primary="Public" secondary="Disabled by admin"/>
|
||||
</ListItem>
|
||||
}
|
||||
<Divider/>
|
||||
<ListSubheader>More</ListSubheader>
|
||||
<LinkableListItem button key="recommended" to="/recommended">
|
||||
<LinkableListItem button key="recommended" to="/recommended" disabled>
|
||||
<ListItemIcon><GroupIcon/></ListItemIcon>
|
||||
<ListItemText primary="Who to follow"/>
|
||||
<ListItemText primary="Who to follow" secondary="Coming soon!"/>
|
||||
</LinkableListItem>
|
||||
<LinkableListItem button key="settings" to="/settings">
|
||||
<ListItemIcon><SettingsIcon/></ListItemIcon>
|
||||
|
@ -232,7 +258,7 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
|||
<MenuIcon/>
|
||||
</IconButton>
|
||||
<Typography className={classes.appBarTitle} variant="h6" color="inherit" noWrap>
|
||||
Hyperspace
|
||||
{this.state.brandName? this.state.brandName: "Hyperspace"}
|
||||
</Typography>
|
||||
<div className={classes.appBarFlexGrow}/>
|
||||
<div className={classes.appBarSearch}>
|
||||
|
|
|
@ -17,28 +17,71 @@ import OpenInNewIcon from '@material-ui/icons/OpenInNew';
|
|||
import DomainIcon from '@material-ui/icons/Domain';
|
||||
import ChatIcon from '@material-ui/icons/Chat';
|
||||
import PersonIcon from '@material-ui/icons/Person';
|
||||
import NetworkCheckIcon from '@material-ui/icons/NetworkCheck';
|
||||
import UpdateIcon from '@material-ui/icons/Update';
|
||||
import InfoIcon from '@material-ui/icons/Info';
|
||||
import NotesIcon from '@material-ui/icons/Notes';
|
||||
import {styles} from './PageLayout.styles';
|
||||
import {Instance} from '../types/Instance';
|
||||
import {LinkableIconButton} from '../interfaces/overrides';
|
||||
import Mastodon from 'megalodon';
|
||||
import { UAccount } from '../types/Account';
|
||||
import { getConfig } from '../utilities/settings';
|
||||
import { License } from '../types/Config';
|
||||
|
||||
interface IAboutPageState {
|
||||
instance: Instance | any;
|
||||
instance?: Instance | any;
|
||||
federated?: boolean;
|
||||
developer?: boolean;
|
||||
hyperspaceAdmin?: UAccount;
|
||||
versionNumber?: string;
|
||||
brandName?: string;
|
||||
license: License;
|
||||
}
|
||||
|
||||
class AboutPage extends Component<any, IAboutPageState> {
|
||||
|
||||
client: Mastodon;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.client = new Mastodon(localStorage.getItem('access_token') as string, localStorage.getItem('baseurl') + "/api/v1");
|
||||
|
||||
this.state = {
|
||||
license: {
|
||||
name: "Apache 2.0 License (inherited)",
|
||||
url: "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
let client = new Mastodon(localStorage.getItem('access_token') as string, localStorage.getItem('baseurl') + "/api/v1");
|
||||
client.get('/instance').then((resp: any) => {
|
||||
this.client.get('/instance').then((resp: any) => {
|
||||
this.setState({
|
||||
instance: resp.data
|
||||
})
|
||||
})
|
||||
|
||||
getConfig().then((config: any) => {
|
||||
this.client.get('/accounts/' + (config.admin? config.admin.account: "0")).then((resp: any) => {
|
||||
let account = resp.data;
|
||||
console.log(config);
|
||||
this.setState({
|
||||
hyperspaceAdmin: account,
|
||||
federated: config.federated? config.federated === "true": false,
|
||||
developer: config.developer? config.developer === "true": false,
|
||||
versionNumber: config.version,
|
||||
brandName: config.branding? config.branding.name: "Hyperspace",
|
||||
license: {
|
||||
name: config.license.name,
|
||||
url: config.license.url
|
||||
}
|
||||
})
|
||||
}).catch((err: Error) => {
|
||||
console.error(err.message);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -54,7 +97,7 @@ class AboutPage extends Component<any, IAboutPageState> {
|
|||
<DomainIcon/>
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary="Instance location (URL)" secondary={this.state ? this.state.instance.uri: "Loading..."}/>
|
||||
<ListItemText primary="Instance location (URL)" secondary={this.state.instance ? this.state.instance.uri: "Loading..."}/>
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton href={localStorage.getItem("baseurl") as string} target="_blank" rel="noreferrer">
|
||||
<OpenInNewIcon/>
|
||||
|
@ -63,17 +106,17 @@ class AboutPage extends Component<any, IAboutPageState> {
|
|||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar alt="Instance admin" src={this.state? this.state.instance.contact_account.avatar_static: ""}/>
|
||||
<Avatar alt="Instance admin" src={this.state.instance? this.state.instance.contact_account.avatar_static: ""}/>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary="Instance admin" secondary={
|
||||
this.state ? `${this.state.instance.contact_account.display_name} (@${this.state.instance.contact_account.acct})`:
|
||||
this.state.instance ? `${this.state.instance.contact_account.display_name} (@${this.state.instance.contact_account.acct})`:
|
||||
"Loading..."
|
||||
}/>
|
||||
<ListItemSecondaryAction>
|
||||
<LinkableIconButton to={`/compose?visibility=public&acct=${this.state? this.state.instance.contact_account.acct: ""}`}>
|
||||
<LinkableIconButton to={`/compose?visibility=public&acct=${this.state.instance? this.state.instance.contact_account.acct: ""}`}>
|
||||
<ChatIcon/>
|
||||
</LinkableIconButton>
|
||||
<LinkableIconButton to={`/profile/${this.state? this.state.instance.contact_account.id: 0}`}>
|
||||
<LinkableIconButton to={`/profile/${this.state.instance? this.state.instance.contact_account.id: 0}`}>
|
||||
<PersonIcon/>
|
||||
</LinkableIconButton>
|
||||
</ListItemSecondaryAction>
|
||||
|
@ -85,20 +128,70 @@ class AboutPage extends Component<any, IAboutPageState> {
|
|||
<Paper className={classes.pageListConstraints}>
|
||||
<List>
|
||||
<ListItem>
|
||||
<ListItemText primary="App version" secondary="Hyperspace v1.0.0"/>
|
||||
<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>
|
||||
<ListItem>
|
||||
<ListItemText primary="Release channel" secondary="Developer"/>
|
||||
<ListItemAvatar>
|
||||
<Avatar src={this.state.hyperspaceAdmin? this.state.hyperspaceAdmin.avatar_static: ""}>
|
||||
<PersonIcon/>
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary="App provider" secondary={this.state.hyperspaceAdmin? (this.state.hyperspaceAdmin.display_name || "@" + this.state.hyperspaceAdmin.acct): "No provider set in config"}/>
|
||||
<ListItemSecondaryAction>
|
||||
<LinkableIconButton to={`/compose?visibility=${this.state.federated? "public": "private"}&acct=${this.state.hyperspaceAdmin? this.state.hyperspaceAdmin.acct: ""}`}>
|
||||
<ChatIcon/>
|
||||
</LinkableIconButton>
|
||||
<LinkableIconButton to={`/profile/${this.state.hyperspaceAdmin? this.state.hyperspaceAdmin.id: 0}`}>
|
||||
<PersonIcon/>
|
||||
</LinkableIconButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText primary="License" secondary="Apache 2.0 License (inherited)"/>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
<NetworkCheckIcon/>
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary="Federation status" secondary={`This instance of ${this.state? this.state.brandName: "Hyperspace"} is ${this.state? this.state.federated? "": "not": "unknown"} federated.`}/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
<UpdateIcon/>
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary="Release channel" secondary={
|
||||
this.state?
|
||||
this.state.developer?
|
||||
"Developer":
|
||||
"Release":
|
||||
"Loading..."
|
||||
}/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
<NotesIcon/>
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary="License" secondary={this.state.license.name}/>
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton href={this.state.license.url} target="_blank" rel="noreferrer">
|
||||
<OpenInNewIcon/>
|
||||
</IconButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Paper>
|
||||
<br/>
|
||||
<div>
|
||||
<Typography variant="caption">(C) 2019 Hyperspace developers. All rights reserved.</Typography>
|
||||
<Typography variant="caption" paragraph>Hyperspace is made possible by the <Link href={"https://material-ui.com"} target="_blank" rel="noreferrer">Material UI</Link> project, <Link href={"https://www.npmjs.com/package/megalodon"} target="_blank" rel="noreferrer">Megalodon</Link> library, and other <Link href={"https://github.com/hyperspacedev/hyperspace/blob/master/package.json"} target="_blank" rel="noreferrer">open source software</Link>.</Typography>
|
||||
<Typography variant="caption">(C) 2019 {this.state? this.state.brandName: "Hyperspace"} developers. All rights reserved.</Typography>
|
||||
<Typography variant="caption" paragraph>{this.state? this.state.brandName: "Hyperspace"} is made possible by the <Link href={"https://material-ui.com"} target="_blank" rel="noreferrer">Material UI</Link> project, <Link href={"https://www.npmjs.com/package/megalodon"} target="_blank" rel="noreferrer">Megalodon</Link> library, and other <Link href={"https://github.com/hyperspacedev/hyperspace/blob/master/package.json"} target="_blank" rel="noreferrer">open source software</Link>.</Typography>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -16,7 +16,8 @@ import {
|
|||
RadioGroup,
|
||||
FormControlLabel,
|
||||
Radio,
|
||||
DialogActions
|
||||
DialogActions,
|
||||
DialogContentText
|
||||
} from '@material-ui/core';
|
||||
import OpenInNewIcon from '@material-ui/icons/OpenInNew';
|
||||
import {styles} from './PageLayout.styles';
|
||||
|
@ -29,6 +30,7 @@ interface ISettingsState {
|
|||
pushNotificationsEnabled: boolean;
|
||||
selectThemeName: string;
|
||||
themeDialogOpen: boolean;
|
||||
resetHyperspaceDialog: boolean;
|
||||
}
|
||||
|
||||
class SettingsPage extends Component<any, ISettingsState> {
|
||||
|
@ -40,7 +42,8 @@ class SettingsPage extends Component<any, ISettingsState> {
|
|||
darkModeEnabled: getUserDefaultBool('darkModeEnabled'),
|
||||
pushNotificationsEnabled: canSendNotifications(),
|
||||
selectThemeName: getUserDefaultTheme().key,
|
||||
themeDialogOpen: false
|
||||
themeDialogOpen: false,
|
||||
resetHyperspaceDialog: false
|
||||
}
|
||||
|
||||
this.toggleDarkMode = this.toggleDarkMode.bind(this);
|
||||
|
@ -65,6 +68,10 @@ class SettingsPage extends Component<any, ISettingsState> {
|
|||
this.setState({ themeDialogOpen: !this.state.themeDialogOpen });
|
||||
}
|
||||
|
||||
toggleResetDialog() {
|
||||
this.setState({ resetHyperspaceDialog: !this.state.resetHyperspaceDialog });
|
||||
}
|
||||
|
||||
changeTheme() {
|
||||
setUserDefaultTheme(this.state.selectThemeName);
|
||||
window.location.reload();
|
||||
|
@ -74,6 +81,11 @@ class SettingsPage extends Component<any, ISettingsState> {
|
|||
this.setState({ selectThemeName: theme});
|
||||
}
|
||||
|
||||
reset() {
|
||||
localStorage.clear();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
showThemeDialog() {
|
||||
return (
|
||||
<Dialog
|
||||
|
@ -110,6 +122,32 @@ class SettingsPage extends Component<any, ISettingsState> {
|
|||
);
|
||||
}
|
||||
|
||||
showResetDialog() {
|
||||
return (
|
||||
<Dialog
|
||||
open={this.state.resetHyperspaceDialog}
|
||||
onClose={() => this.toggleResetDialog()}
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">Reset Hyperspace?</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Are you sure you want to reset Hyperspace? You'll need to sign in again and grant Hyperspace access to use it again.
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => this.toggleResetDialog()} color="primary" autoFocus>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={() => {
|
||||
this.reset();
|
||||
}} color="primary">
|
||||
Reset
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
return (
|
||||
|
@ -177,7 +215,7 @@ class SettingsPage extends Component<any, ISettingsState> {
|
|||
<ListItem>
|
||||
<ListItemText primary="Reset Hyperspace" secondary="Deletes all data and resets the app"/>
|
||||
<ListItemSecondaryAction>
|
||||
<Button>
|
||||
<Button onClick={() => this.toggleResetDialog()}>
|
||||
Reset
|
||||
</Button>
|
||||
</ListItemSecondaryAction>
|
||||
|
@ -185,6 +223,7 @@ class SettingsPage extends Component<any, ISettingsState> {
|
|||
</List>
|
||||
</Paper>
|
||||
{this.showThemeDialog()}
|
||||
{this.showResetDialog()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import React, { Component } from 'react';
|
||||
import {withStyles, Paper, Typography, Button, TextField, Fade, Checkbox, FormControlLabel, Link, CircularProgress} from '@material-ui/core';
|
||||
import {withStyles, Paper, Typography, Button, TextField, Fade, Link, CircularProgress} from '@material-ui/core';
|
||||
import {styles} from './WelcomePage.styles';
|
||||
import axios from 'axios';
|
||||
import Mastodon from 'megalodon';
|
||||
import {Config} from '../types/Config';
|
||||
import {SaveClientSession} from '../types/SessionData';
|
||||
import { createHyperspaceApp } from '../utilities/login';
|
||||
import {parseUrl} from 'query-string';
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export type Config = {
|
||||
version: string;
|
||||
branding?: {
|
||||
name?: string;
|
||||
logo?: string;
|
||||
|
@ -13,4 +14,10 @@ export type Config = {
|
|||
name?: string;
|
||||
account?: string;
|
||||
};
|
||||
license: License;
|
||||
}
|
||||
|
||||
export type License = {
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
Loading…
Reference in New Issue