Merge branch 'beta7' into thread-and-settings-redesigns
This commit is contained in:
commit
1d2c77c429
|
@ -3220,6 +3220,14 @@
|
||||||
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
|
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"bindings": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||||
|
"requires": {
|
||||||
|
"file-uri-to-path": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"version": "3.5.4",
|
"version": "3.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz",
|
||||||
|
@ -7262,10 +7270,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslint-utils": {
|
"eslint-utils": {
|
||||||
"version": "1.3.1",
|
"version": "1.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz",
|
||||||
"integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
|
"integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"eslint-visitor-keys": "^1.0.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"eslint-visitor-keys": {
|
"eslint-visitor-keys": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
@ -7325,6 +7336,11 @@
|
||||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
|
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"event-target-shim": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-qG5e5r2qFgVEddp5fM3fDFVphJE="
|
||||||
|
},
|
||||||
"eventemitter3": {
|
"eventemitter3": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz",
|
||||||
|
@ -8022,6 +8038,11 @@
|
||||||
"schema-utils": "^1.0.0"
|
"schema-utils": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"file-uri-to-path": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
||||||
|
},
|
||||||
"filename-regex": {
|
"filename-regex": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
|
||||||
|
@ -9448,9 +9469,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"handlebars": {
|
"handlebars": {
|
||||||
"version": "4.1.2",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.0.tgz",
|
||||||
"integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==",
|
"integrity": "sha512-xkRtOt3/3DzTKMOt3xahj2M/EqNhY988T+imYSlMgs5fVhLN2fmKVVj0LtEGmb+3UUYV5Qmm1052Mm3dIQxOvw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"neo-async": "^2.6.0",
|
"neo-async": "^2.6.0",
|
||||||
|
@ -12840,6 +12861,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node-mac-notifier": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-mac-notifier/-/node-mac-notifier-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-+9FZ01BbPMv3pQVRWgPlaIKbhQl35Pn3WmRg96zIrCJHb4XvClnAqc0+aPfHrWs8o1PYMAQFeYK5tF69ljkKQw==",
|
||||||
|
"requires": {
|
||||||
|
"bindings": "^1.2.1",
|
||||||
|
"event-target-shim": "^1.1.1",
|
||||||
|
"uuid": "^3.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node-notifier": {
|
"node-notifier": {
|
||||||
"version": "5.4.0",
|
"version": "5.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz",
|
||||||
|
@ -20242,8 +20273,7 @@
|
||||||
"uuid": {
|
"uuid": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||||
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
|
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"validate-npm-package-license": {
|
"validate-npm-package-license": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"version": "1.0.0beta7",
|
"version": "1.0.0beta7",
|
||||||
"location": "desktop",
|
"location": "https://localhost:3000",
|
||||||
"branding": {
|
"branding": {
|
||||||
"name": "Hyperspace",
|
"name": "Hyperspace",
|
||||||
"logo": "logo.svg",
|
"logo": "logo.svg",
|
||||||
|
|
|
@ -20,7 +20,7 @@ let mainWindow;
|
||||||
// to when authorizing Hyperspace.
|
// to when authorizing Hyperspace.
|
||||||
protocol.registerSchemesAsPrivileged([
|
protocol.registerSchemesAsPrivileged([
|
||||||
{ scheme: 'hyperspace', privileges: { standard: true, secure: true } }
|
{ scheme: 'hyperspace', privileges: { standard: true, secure: true } }
|
||||||
])
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the desktop app is on macOS
|
* Determine whether the desktop app is on macOS
|
||||||
|
@ -217,15 +217,8 @@ function createMenubar() {
|
||||||
click() {
|
click() {
|
||||||
safelyGoTo("hyperspace://hyperspace/app/#compose")
|
safelyGoTo("hyperspace://hyperspace/app/#compose")
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
{ type: 'separator' },
|
|
||||||
{
|
|
||||||
label: 'Edit Profile',
|
|
||||||
accelerator: "Shift+CmdOrCtrl+P",
|
|
||||||
click() {
|
|
||||||
safelyGoTo("hyperspace://hyperspace/app/#/you")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -283,7 +276,7 @@ function createMenubar() {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Places",
|
label: "Timelines",
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: 'Home',
|
label: 'Home',
|
||||||
|
@ -307,27 +300,53 @@ function createMenubar() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Recommendations',
|
label: 'Messages',
|
||||||
accelerator: "CmdOrCtrl+3",
|
accelerator: "CmdOrCtrl+3",
|
||||||
|
click() {
|
||||||
|
safelyGoTo("hyperspace://hyperspace/app/#/messages")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Account",
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: 'Notifications',
|
||||||
|
accelerator: "Alt+CmdOrCtrl+N",
|
||||||
|
click() {
|
||||||
|
safelyGoTo("hyperspace://hyperspace/app/#/notifications")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Recommendations...',
|
||||||
|
accelerator: "Alt+CmdOrCtrl+R",
|
||||||
click() {
|
click() {
|
||||||
safelyGoTo("hyperspace://hyperspace/app/#/recommended")
|
safelyGoTo("hyperspace://hyperspace/app/#/recommended")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{
|
{
|
||||||
label: 'Notifications',
|
label: 'Edit Profile',
|
||||||
accelerator: "CmdOrCtrl+4",
|
accelerator: "Shift+CmdOrCtrl+P",
|
||||||
click() {
|
click() {
|
||||||
safelyGoTo("hyperspace://hyperspace/app/#/notifications")
|
safelyGoTo("hyperspace://hyperspace/app/#/you")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Messages',
|
label: 'Blocked Servers',
|
||||||
accelerator: "CmdOrCtrl+5",
|
accelerator: "Shift+CmdOrCtrl+B",
|
||||||
click() {
|
click() {
|
||||||
safelyGoTo("hyperspace://hyperspace/app/#/messages")
|
safelyGoTo("hyperspace://hyperspace/app/#/blocked")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{ type: 'separator'},
|
||||||
|
{
|
||||||
|
label: 'Switch Accounts...',
|
||||||
|
click() {
|
||||||
|
safelyGoTo("hyperspace://hyperspace/app/#/welcome")
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -356,7 +375,7 @@ function createMenubar() {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
];
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
menuBar.unshift({
|
menuBar.unshift({
|
||||||
|
@ -385,7 +404,7 @@ function createMenubar() {
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ role: 'quit' }
|
{ role: 'quit' }
|
||||||
]
|
]
|
||||||
})
|
});
|
||||||
|
|
||||||
// Edit menu
|
// Edit menu
|
||||||
menuBar[2].submenu.push(
|
menuBar[2].submenu.push(
|
||||||
|
@ -397,10 +416,10 @@ function createMenubar() {
|
||||||
{ role: 'stopspeaking' }
|
{ role: 'stopspeaking' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
|
|
||||||
// Window menu
|
// Window menu
|
||||||
menuBar[5].submenu = [
|
menuBar[6].submenu = [
|
||||||
{ role: 'close' },
|
{ role: 'close' },
|
||||||
{ role: 'minimize' },
|
{ role: 'minimize' },
|
||||||
{ role: 'zoom' },
|
{ role: 'zoom' },
|
||||||
|
|
39
src/App.tsx
39
src/App.tsx
|
@ -3,7 +3,7 @@ import { MuiThemeProvider, CssBaseline, withStyles } from "@material-ui/core";
|
||||||
import { setHyperspaceTheme, darkMode } from "./utilities/themes";
|
import { setHyperspaceTheme, darkMode } from "./utilities/themes";
|
||||||
import AppLayout from "./components/AppLayout";
|
import AppLayout from "./components/AppLayout";
|
||||||
import { styles } from "./App.styles";
|
import { styles } from "./App.styles";
|
||||||
import { Route } from "react-router-dom";
|
import { Route, withRouter } from "react-router-dom";
|
||||||
import AboutPage from "./pages/About";
|
import AboutPage from "./pages/About";
|
||||||
import Settings from "./pages/Settings";
|
import Settings from "./pages/Settings";
|
||||||
import { getUserDefaultBool, getUserDefaultTheme } from "./utilities/settings";
|
import { getUserDefaultBool, getUserDefaultTheme } from "./utilities/settings";
|
||||||
|
@ -27,14 +27,22 @@ import { userLoggedIn } from "./utilities/accounts";
|
||||||
import { isDarwinApp } from "./utilities/desktop";
|
import { isDarwinApp } from "./utilities/desktop";
|
||||||
let theme = setHyperspaceTheme(getUserDefaultTheme());
|
let theme = setHyperspaceTheme(getUserDefaultTheme());
|
||||||
|
|
||||||
class App extends Component<any, any> {
|
interface IAppState {
|
||||||
|
theme: any;
|
||||||
|
showLayout: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class App extends Component<any, IAppState> {
|
||||||
offline: any;
|
offline: any;
|
||||||
|
unlisten: any;
|
||||||
|
|
||||||
constructor(props: any) {
|
constructor(props: any) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
theme: theme
|
theme: theme,
|
||||||
|
showLayout:
|
||||||
|
userLoggedIn() && !window.location.hash.includes("#/welcome")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,17 +51,33 @@ class App extends Component<any, any> {
|
||||||
this.state.theme,
|
this.state.theme,
|
||||||
getUserDefaultBool("darkModeEnabled")
|
getUserDefaultBool("darkModeEnabled")
|
||||||
);
|
);
|
||||||
this.setState({ theme: newTheme });
|
this.setState({
|
||||||
|
theme: newTheme,
|
||||||
|
showLayout:
|
||||||
|
userLoggedIn() && !window.location.hash.includes("#/welcome")
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.removeBodyBackground();
|
this.removeBodyBackground();
|
||||||
|
this.unlisten = this.props.history.listen(
|
||||||
|
(location: Location, action: any) => {
|
||||||
|
this.setState({
|
||||||
|
showLayout:
|
||||||
|
userLoggedIn() &&
|
||||||
|
!location.pathname.includes("/welcome")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
this.removeBodyBackground();
|
this.removeBodyBackground();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.unlisten();
|
||||||
|
}
|
||||||
|
|
||||||
removeBodyBackground() {
|
removeBodyBackground() {
|
||||||
if (isDarwinApp()) {
|
if (isDarwinApp()) {
|
||||||
|
@ -71,7 +95,7 @@ class App extends Component<any, any> {
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<Route path="/welcome" component={WelcomePage} />
|
<Route path="/welcome" component={WelcomePage} />
|
||||||
<div>
|
<div>
|
||||||
{userLoggedIn() ? <AppLayout /> : null}
|
{this.state.showLayout ? <AppLayout /> : null}
|
||||||
<PrivateRoute exact path="/" component={HomePage} />
|
<PrivateRoute exact path="/" component={HomePage} />
|
||||||
<PrivateRoute path="/home" component={HomePage} />
|
<PrivateRoute path="/home" component={HomePage} />
|
||||||
<PrivateRoute path="/local" component={LocalPage} />
|
<PrivateRoute path="/local" component={LocalPage} />
|
||||||
|
@ -91,7 +115,7 @@ class App extends Component<any, any> {
|
||||||
/>
|
/>
|
||||||
<PrivateRoute path="/search" component={SearchPage} />
|
<PrivateRoute path="/search" component={SearchPage} />
|
||||||
<PrivateRoute path="/settings" component={Settings} />
|
<PrivateRoute path="/settings" component={Settings} />
|
||||||
<PrivateRoute path="/blocked" component={Blocked}/>
|
<PrivateRoute path="/blocked" component={Blocked} />
|
||||||
<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} />
|
||||||
|
@ -105,4 +129,5 @@ class App extends Component<any, any> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withStyles(styles)(withSnackbar(App));
|
// @ts-ignore
|
||||||
|
export default withStyles(styles)(withSnackbar(withRouter(App)));
|
||||||
|
|
|
@ -39,10 +39,10 @@ import GroupIcon from "@material-ui/icons/Group";
|
||||||
import SettingsIcon from "@material-ui/icons/Settings";
|
import SettingsIcon from "@material-ui/icons/Settings";
|
||||||
import InfoIcon from "@material-ui/icons/Info";
|
import InfoIcon from "@material-ui/icons/Info";
|
||||||
import CreateIcon from "@material-ui/icons/Create";
|
import CreateIcon from "@material-ui/icons/Create";
|
||||||
//import SupervisedUserCircleIcon from '@material-ui/icons/SupervisedUserCircle';
|
import SupervisedUserCircleIcon from "@material-ui/icons/SupervisedUserCircle";
|
||||||
import ExitToAppIcon from "@material-ui/icons/ExitToApp";
|
import ExitToAppIcon from "@material-ui/icons/ExitToApp";
|
||||||
import { styles } from "./AppLayout.styles";
|
import { styles } from "./AppLayout.styles";
|
||||||
import { UAccount } from "../../types/Account";
|
import { MultiAccount, UAccount } from "../../types/Account";
|
||||||
import {
|
import {
|
||||||
LinkableListItem,
|
LinkableListItem,
|
||||||
LinkableIconButton,
|
LinkableIconButton,
|
||||||
|
@ -53,8 +53,16 @@ import { Notification } from "../../types/Notification";
|
||||||
import { sendNotificationRequest } from "../../utilities/notifications";
|
import { sendNotificationRequest } from "../../utilities/notifications";
|
||||||
import { withSnackbar } from "notistack";
|
import { withSnackbar } from "notistack";
|
||||||
import { getConfig, getUserDefaultBool } from "../../utilities/settings";
|
import { getConfig, getUserDefaultBool } from "../../utilities/settings";
|
||||||
import { isDesktopApp, isDarwinApp } from "../../utilities/desktop";
|
import {
|
||||||
|
isDesktopApp,
|
||||||
|
isDarwinApp,
|
||||||
|
getElectronApp
|
||||||
|
} from "../../utilities/desktop";
|
||||||
import { Config } from "../../types/Config";
|
import { Config } from "../../types/Config";
|
||||||
|
import {
|
||||||
|
getAccountRegistry,
|
||||||
|
removeAccountFromRegistry
|
||||||
|
} from "../../utilities/accounts";
|
||||||
|
|
||||||
interface IAppLayoutState {
|
interface IAppLayoutState {
|
||||||
acctMenuOpen: boolean;
|
acctMenuOpen: boolean;
|
||||||
|
@ -92,23 +100,7 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
let acct = localStorage.getItem("account");
|
this.getAccountData();
|
||||||
if (acct) {
|
|
||||||
this.setState({ currentUser: JSON.parse(acct) });
|
|
||||||
} else {
|
|
||||||
this.client
|
|
||||||
.get("/accounts/verify_credentials")
|
|
||||||
.then((resp: any) => {
|
|
||||||
let data: UAccount = resp.data;
|
|
||||||
this.setState({ currentUser: data });
|
|
||||||
})
|
|
||||||
.catch((err: Error) => {
|
|
||||||
this.props.enqueueSnackbar(
|
|
||||||
"Couldn't find profile info: " + err.name
|
|
||||||
);
|
|
||||||
console.error(err.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getConfig().then((result: any) => {
|
getConfig().then((result: any) => {
|
||||||
if (result !== undefined) {
|
if (result !== undefined) {
|
||||||
|
@ -126,6 +118,24 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
||||||
this.streamNotifications();
|
this.streamNotifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAccountData() {
|
||||||
|
this.client
|
||||||
|
.get("/accounts/verify_credentials")
|
||||||
|
.then((resp: any) => {
|
||||||
|
let data: UAccount = resp.data;
|
||||||
|
this.setState({ currentUser: data });
|
||||||
|
sessionStorage.setItem("id", data.id);
|
||||||
|
})
|
||||||
|
.catch((err: Error) => {
|
||||||
|
this.props.enqueueSnackbar(
|
||||||
|
"Couldn't find profile info: " + err.name
|
||||||
|
);
|
||||||
|
console.error(err.message);
|
||||||
|
let acct = localStorage.getItem("account") as string;
|
||||||
|
this.setState({ currentUser: JSON.parse(acct) });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
streamNotifications() {
|
streamNotifications() {
|
||||||
this.streamListener = this.client.stream("/streaming/user");
|
this.streamListener = this.client.stream("/streaming/user");
|
||||||
|
|
||||||
|
@ -139,6 +149,11 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
||||||
this.streamListener.on("notification", (notif: Notification) => {
|
this.streamListener.on("notification", (notif: Notification) => {
|
||||||
const notificationCount = this.state.notificationCount + 1;
|
const notificationCount = this.state.notificationCount + 1;
|
||||||
this.setState({ notificationCount });
|
this.setState({ notificationCount });
|
||||||
|
|
||||||
|
if (isDesktopApp()) {
|
||||||
|
getElectronApp().setBadgeCount(notificationCount);
|
||||||
|
}
|
||||||
|
|
||||||
if (!document.hasFocus()) {
|
if (!document.hasFocus()) {
|
||||||
let primaryMessage = "";
|
let primaryMessage = "";
|
||||||
let secondaryMessage = "";
|
let secondaryMessage = "";
|
||||||
|
@ -220,16 +235,28 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
||||||
window.location.href = isDesktopApp()
|
window.location.href = isDesktopApp()
|
||||||
? "hyperspace://hyperspace/app/index.html#/search?query=" + what
|
? "hyperspace://hyperspace/app/index.html#/search?query=" + what
|
||||||
: "/#/search?query=" + what;
|
: "/#/search?query=" + what;
|
||||||
window.location.reload;
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
logOutAndRestart() {
|
logOutAndRestart() {
|
||||||
let loginData = localStorage.getItem("login");
|
let loginData = localStorage.getItem("login");
|
||||||
if (loginData) {
|
if (loginData) {
|
||||||
|
let registry = getAccountRegistry();
|
||||||
|
|
||||||
|
registry.forEach((registryItem: MultiAccount, index: number) => {
|
||||||
|
if (
|
||||||
|
registryItem.access_token ===
|
||||||
|
localStorage.getItem("access_token")
|
||||||
|
) {
|
||||||
|
removeAccountFromRegistry(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let items = ["login", "account", "baseurl", "access_token"];
|
let items = ["login", "account", "baseurl", "access_token"];
|
||||||
items.forEach(entry => {
|
items.forEach(entry => {
|
||||||
localStorage.removeItem(entry);
|
localStorage.removeItem(entry);
|
||||||
});
|
});
|
||||||
|
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,6 +265,10 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
||||||
if (!getUserDefaultBool("displayAllOnNotificationBadge")) {
|
if (!getUserDefaultBool("displayAllOnNotificationBadge")) {
|
||||||
this.setState({ notificationCount: 0 });
|
this.setState({ notificationCount: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isDesktopApp() && getElectronApp().getBadgeCount() > 0) {
|
||||||
|
getElectronApp().setBadgeCount(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
titlebar() {
|
titlebar() {
|
||||||
|
@ -307,10 +338,22 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</LinkableListItem>
|
</LinkableListItem>
|
||||||
{/* <LinkableListItem button key="acctSwitch-module" to="/switchacct">
|
<LinkableListItem
|
||||||
<ListItemIcon><SupervisedUserCircleIcon/></ListItemIcon>
|
button
|
||||||
<ListItemText primary="Switch account"/>
|
key="acctSwitch-module"
|
||||||
</LinkableListItem> */}
|
to="/welcome"
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<SupervisedUserCircleIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText
|
||||||
|
primary={
|
||||||
|
getAccountRegistry().length > 1
|
||||||
|
? "Switch account"
|
||||||
|
: "Add account"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</LinkableListItem>
|
||||||
<ListItem
|
<ListItem
|
||||||
button
|
button
|
||||||
key="acctLogout-mobile"
|
key="acctLogout-mobile"
|
||||||
|
@ -571,7 +614,14 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
||||||
Edit profile
|
Edit profile
|
||||||
</ListItemText>
|
</ListItemText>
|
||||||
</LinkableListItem>
|
</LinkableListItem>
|
||||||
{/* <MenuItem>Switch account</MenuItem> */}
|
<LinkableListItem to={"/welcome"}>
|
||||||
|
<ListItemText>
|
||||||
|
{getAccountRegistry()
|
||||||
|
.length > 1
|
||||||
|
? "Switch account"
|
||||||
|
: "Add account"}
|
||||||
|
</ListItemText>
|
||||||
|
</LinkableListItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
this.toggleLogOutDialog()
|
this.toggleLogOutDialog()
|
||||||
|
@ -613,48 +663,7 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
||||||
</Hidden>
|
</Hidden>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<Dialog
|
{this.logoutDialog()}
|
||||||
open={this.state.logOutOpen}
|
|
||||||
onClose={() => this.toggleLogOutDialog()}
|
|
||||||
>
|
|
||||||
<DialogTitle id="alert-dialog-title">
|
|
||||||
Log out of{" "}
|
|
||||||
{this.state.brandName
|
|
||||||
? this.state.brandName
|
|
||||||
: "Hyperspace"}
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogContentText id="alert-dialog-description">
|
|
||||||
You'll need to remove{" "}
|
|
||||||
{this.state.brandName
|
|
||||||
? this.state.brandName
|
|
||||||
: "Hyperspace"}{" "}
|
|
||||||
from your list of authorized apps and log in again
|
|
||||||
if you want to use{" "}
|
|
||||||
{this.state.brandName
|
|
||||||
? this.state.brandName
|
|
||||||
: "Hyperspace"}
|
|
||||||
.
|
|
||||||
</DialogContentText>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
onClick={() => this.toggleLogOutDialog()}
|
|
||||||
color="primary"
|
|
||||||
autoFocus
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
this.logOutAndRestart();
|
|
||||||
}}
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
Log out
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
<Tooltip title="Create a new post">
|
<Tooltip title="Create a new post">
|
||||||
<LinkableFab
|
<LinkableFab
|
||||||
to="/compose"
|
to="/compose"
|
||||||
|
@ -668,6 +677,57 @@ export class AppLayout extends Component<any, IAppLayoutState> {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logoutDialog() {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={this.state.logOutOpen}
|
||||||
|
onClose={() => this.toggleLogOutDialog()}
|
||||||
|
>
|
||||||
|
<DialogTitle id="alert-dialog-title">
|
||||||
|
Log out of{" "}
|
||||||
|
{this.state.brandName ? this.state.brandName : "Hyperspace"}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText id="alert-dialog-description">
|
||||||
|
<Typography paragraph>
|
||||||
|
You'll need to remove{" "}
|
||||||
|
{this.state.brandName
|
||||||
|
? this.state.brandName
|
||||||
|
: "Hyperspace"}{" "}
|
||||||
|
from your list of authorized apps and log in again
|
||||||
|
if you want to use{" "}
|
||||||
|
{this.state.brandName
|
||||||
|
? this.state.brandName
|
||||||
|
: "Hyperspace"}
|
||||||
|
.
|
||||||
|
</Typography>
|
||||||
|
<Typography paragraph>
|
||||||
|
Logging out will also remove this account from the
|
||||||
|
account list.
|
||||||
|
</Typography>
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
onClick={() => this.toggleLogOutDialog()}
|
||||||
|
color="primary"
|
||||||
|
autoFocus
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
this.logOutAndRestart();
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
Log out
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withStyles(styles)(withSnackbar(AppLayout));
|
export default withStyles(styles)(withSnackbar(AppLayout));
|
||||||
|
|
|
@ -1,33 +1,32 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
Typography,
|
|
||||||
IconButton,
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
Avatar,
|
Avatar,
|
||||||
CardContent,
|
|
||||||
CardActions,
|
|
||||||
withStyles,
|
|
||||||
Menu,
|
|
||||||
MenuItem,
|
|
||||||
Chip,
|
|
||||||
Divider,
|
|
||||||
CardMedia,
|
|
||||||
CardActionArea,
|
|
||||||
ExpansionPanel,
|
|
||||||
ExpansionPanelSummary,
|
|
||||||
ExpansionPanelDetails,
|
|
||||||
Zoom,
|
|
||||||
Tooltip,
|
|
||||||
RadioGroup,
|
|
||||||
Radio,
|
|
||||||
FormControlLabel,
|
|
||||||
Button,
|
Button,
|
||||||
|
Card,
|
||||||
|
CardActionArea,
|
||||||
|
CardActions,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
CardMedia,
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogTitle,
|
DialogActions,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogContentText,
|
DialogContentText,
|
||||||
DialogActions
|
DialogTitle,
|
||||||
|
Divider,
|
||||||
|
ExpansionPanel,
|
||||||
|
ExpansionPanelDetails,
|
||||||
|
ExpansionPanelSummary,
|
||||||
|
FormControlLabel,
|
||||||
|
IconButton,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
Tooltip,
|
||||||
|
Typography,
|
||||||
|
withStyles,
|
||||||
|
Zoom
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import MoreVertIcon from "@material-ui/icons/MoreVert";
|
import MoreVertIcon from "@material-ui/icons/MoreVert";
|
||||||
import ReplyIcon from "@material-ui/icons/Reply";
|
import ReplyIcon from "@material-ui/icons/Reply";
|
||||||
|
@ -51,10 +50,10 @@ import moment from "moment";
|
||||||
import AttachmentComponent from "../Attachment";
|
import AttachmentComponent from "../Attachment";
|
||||||
import Mastodon from "megalodon";
|
import Mastodon from "megalodon";
|
||||||
import {
|
import {
|
||||||
|
LinkableAvatar,
|
||||||
LinkableChip,
|
LinkableChip,
|
||||||
LinkableMenuItem,
|
|
||||||
LinkableIconButton,
|
LinkableIconButton,
|
||||||
LinkableAvatar
|
LinkableMenuItem
|
||||||
} from "../../interfaces/overrides";
|
} from "../../interfaces/overrides";
|
||||||
import { withSnackbar } from "notistack";
|
import { withSnackbar } from "notistack";
|
||||||
import ShareMenu from "./PostShareMenu";
|
import ShareMenu from "./PostShareMenu";
|
||||||
|
@ -74,6 +73,7 @@ interface IPostState {
|
||||||
menuIsOpen: boolean;
|
menuIsOpen: boolean;
|
||||||
myVote?: [number];
|
myVote?: [number];
|
||||||
deletePostDialog: boolean;
|
deletePostDialog: boolean;
|
||||||
|
myAccount?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Post extends React.Component<any, IPostState> {
|
export class Post extends React.Component<any, IPostState> {
|
||||||
|
@ -95,6 +95,12 @@ export class Post extends React.Component<any, IPostState> {
|
||||||
this.client = this.props.client;
|
this.client = this.props.client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.setState({
|
||||||
|
myAccount: sessionStorage.getItem("id") as string
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
togglePostMenu() {
|
togglePostMenu() {
|
||||||
this.setState({ menuIsOpen: !this.state.menuIsOpen });
|
this.setState({ menuIsOpen: !this.state.menuIsOpen });
|
||||||
}
|
}
|
||||||
|
@ -106,7 +112,7 @@ export class Post extends React.Component<any, IPostState> {
|
||||||
deletePost() {
|
deletePost() {
|
||||||
this.client
|
this.client
|
||||||
.del("/statuses/" + this.state.post.id)
|
.del("/statuses/" + this.state.post.id)
|
||||||
.then((resp: any) => {
|
.then(() => {
|
||||||
this.props.enqueueSnackbar(
|
this.props.enqueueSnackbar(
|
||||||
"Post deleted. Refresh to see changes."
|
"Post deleted. Refresh to see changes."
|
||||||
);
|
);
|
||||||
|
@ -262,7 +268,7 @@ export class Post extends React.Component<any, IPostState> {
|
||||||
<RadioGroup value={this.findBiggestVote()}>
|
<RadioGroup value={this.findBiggestVote()}>
|
||||||
{status.poll.options.map(
|
{status.poll.options.map(
|
||||||
(pollOption: PollOption) => {
|
(pollOption: PollOption) => {
|
||||||
let x = (
|
return (
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
disabled
|
disabled
|
||||||
value={pollOption.title}
|
value={pollOption.title}
|
||||||
|
@ -274,7 +280,6 @@ export class Post extends React.Component<any, IPostState> {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
return x;
|
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
@ -303,7 +308,7 @@ export class Post extends React.Component<any, IPostState> {
|
||||||
>
|
>
|
||||||
{status.poll.options.map(
|
{status.poll.options.map(
|
||||||
(pollOption: PollOption) => {
|
(pollOption: PollOption) => {
|
||||||
let x = (
|
return (
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value={pollOption.title}
|
value={pollOption.title}
|
||||||
control={<Radio />}
|
control={<Radio />}
|
||||||
|
@ -314,13 +319,12 @@ export class Post extends React.Component<any, IPostState> {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
return x;
|
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={(event: any) => this.submitVote()}
|
onClick={() => this.submitVote()}
|
||||||
>
|
>
|
||||||
Vote
|
Vote
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -381,7 +385,6 @@ export class Post extends React.Component<any, IPostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getReblogOfPost(of: Status | null) {
|
getReblogOfPost(of: Status | null) {
|
||||||
const { classes } = this.props;
|
|
||||||
if (of !== null) {
|
if (of !== null) {
|
||||||
return of.sensitive
|
return of.sensitive
|
||||||
? this.getSensitiveContent(of.spoiler_text, of)
|
? this.getSensitiveContent(of.spoiler_text, of)
|
||||||
|
@ -832,9 +835,8 @@ export class Post extends React.Component<any, IPostState> {
|
||||||
Open in Web
|
Open in Web
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</div>
|
</div>
|
||||||
{post.account.id ==
|
{this.state.myAccount &&
|
||||||
JSON.parse(localStorage.getItem("account") as string)
|
post.account.id === this.state.myAccount ? (
|
||||||
.id ? (
|
|
||||||
<div>
|
<div>
|
||||||
<Divider />
|
<Divider />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|
|
@ -22,6 +22,12 @@ if (userLoggedIn()) {
|
||||||
refreshUserAccountData();
|
refreshUserAccountData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.onstorage = (event: any) => {
|
||||||
|
if (event.key == "account") {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<HashRouter>
|
<HashRouter>
|
||||||
<SnackbarProvider
|
<SnackbarProvider
|
||||||
|
|
|
@ -84,6 +84,10 @@ class Composer extends Component<any, IComposerState> {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
let state = this.getComposerParams(this.props);
|
let state = this.getComposerParams(this.props);
|
||||||
let text = state.acct ? `@${state.acct}: ` : "";
|
let text = state.acct ? `@${state.acct}: ` : "";
|
||||||
|
this.client.get("/accounts/verify_credentials").then((resp: any) => {
|
||||||
|
let account: UAccount = resp.data;
|
||||||
|
this.setState({ account });
|
||||||
|
});
|
||||||
getConfig().then((config: any) => {
|
getConfig().then((config: any) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
federated: config.federation.allowPublicPosts,
|
federated: config.federation.allowPublicPosts,
|
||||||
|
@ -439,7 +443,7 @@ class Composer extends Component<any, IComposerState> {
|
||||||
event.target.value
|
event.target.value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
></TextField>
|
/>
|
||||||
</Fade>
|
</Fade>
|
||||||
) : null}
|
) : null}
|
||||||
{this.state.visibility === "direct" ? (
|
{this.state.visibility === "direct" ? (
|
||||||
|
|
|
@ -110,7 +110,7 @@ class NotificationsPage extends Component<any, INotificationsPageState> {
|
||||||
|
|
||||||
removeNotification(id: string) {
|
removeNotification(id: string) {
|
||||||
this.client
|
this.client
|
||||||
.post("/notifications/dismiss", { id: id })
|
.post(`/notifications/${id}/dismiss`)
|
||||||
.then((resp: any) => {
|
.then((resp: any) => {
|
||||||
let notifications = this.state.notifications;
|
let notifications = this.state.notifications;
|
||||||
if (notifications !== undefined && notifications.length > 0) {
|
if (notifications !== undefined && notifications.length > 0) {
|
||||||
|
|
|
@ -12,7 +12,13 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
DialogContent
|
DialogContent,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
ListItemText,
|
||||||
|
ListItemAvatar,
|
||||||
|
ListItemSecondaryAction,
|
||||||
|
IconButton
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import { styles } from "./WelcomePage.styles";
|
import { styles } from "./WelcomePage.styles";
|
||||||
import Mastodon from "megalodon";
|
import Mastodon from "megalodon";
|
||||||
|
@ -28,6 +34,16 @@ import { isDarwinApp } from "../utilities/desktop";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { withSnackbar, withSnackbarProps } from "notistack";
|
import { withSnackbar, withSnackbarProps } from "notistack";
|
||||||
import { Config } from "../types/Config";
|
import { Config } from "../types/Config";
|
||||||
|
import {
|
||||||
|
addAccountToRegistry,
|
||||||
|
getAccountRegistry,
|
||||||
|
loginWithAccount,
|
||||||
|
removeAccountFromRegistry
|
||||||
|
} from "../utilities/accounts";
|
||||||
|
import { Account, MultiAccount } from "../types/Account";
|
||||||
|
|
||||||
|
import AccountCircleIcon from "@material-ui/icons/AccountCircle";
|
||||||
|
import CloseIcon from "@material-ui/icons/Close";
|
||||||
|
|
||||||
interface IWelcomeProps extends withSnackbarProps {
|
interface IWelcomeProps extends withSnackbarProps {
|
||||||
classes: any;
|
classes: any;
|
||||||
|
@ -39,7 +55,7 @@ interface IWelcomeState {
|
||||||
brandName?: string;
|
brandName?: string;
|
||||||
registerBase?: string;
|
registerBase?: string;
|
||||||
federates?: boolean;
|
federates?: boolean;
|
||||||
wantsToLogin: boolean;
|
proceedToGetCode: boolean;
|
||||||
user: string;
|
user: string;
|
||||||
userInputError: boolean;
|
userInputError: boolean;
|
||||||
userInputErrorMessage: string;
|
userInputErrorMessage: string;
|
||||||
|
@ -47,7 +63,7 @@ interface IWelcomeState {
|
||||||
clientSecret?: string;
|
clientSecret?: string;
|
||||||
authUrl?: string;
|
authUrl?: string;
|
||||||
foundSavedLogin: boolean;
|
foundSavedLogin: boolean;
|
||||||
authority: boolean;
|
authorizing: boolean;
|
||||||
license?: string;
|
license?: string;
|
||||||
repo?: string;
|
repo?: string;
|
||||||
defaultRedirectAddress: string;
|
defaultRedirectAddress: string;
|
||||||
|
@ -55,6 +71,7 @@ interface IWelcomeState {
|
||||||
authCode: string;
|
authCode: string;
|
||||||
emergencyMode: boolean;
|
emergencyMode: boolean;
|
||||||
version: string;
|
version: string;
|
||||||
|
willAddAccount: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
|
@ -64,17 +81,18 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
wantsToLogin: false,
|
proceedToGetCode: false,
|
||||||
user: "",
|
user: "",
|
||||||
userInputError: false,
|
userInputError: false,
|
||||||
foundSavedLogin: false,
|
foundSavedLogin: false,
|
||||||
authority: false,
|
authorizing: false,
|
||||||
userInputErrorMessage: "",
|
userInputErrorMessage: "",
|
||||||
defaultRedirectAddress: "",
|
defaultRedirectAddress: "",
|
||||||
openAuthDialog: false,
|
openAuthDialog: false,
|
||||||
authCode: "",
|
authCode: "",
|
||||||
emergencyMode: false,
|
emergencyMode: false,
|
||||||
version: ""
|
version: "",
|
||||||
|
willAddAccount: false
|
||||||
};
|
};
|
||||||
|
|
||||||
getConfig()
|
getConfig()
|
||||||
|
@ -155,6 +173,11 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
localStorage.removeItem("access_token");
|
||||||
|
localStorage.removeItem("baseurl");
|
||||||
|
}
|
||||||
|
|
||||||
getSavedSession() {
|
getSavedSession() {
|
||||||
let loginData = localStorage.getItem("login");
|
let loginData = localStorage.getItem("login");
|
||||||
if (loginData) {
|
if (loginData) {
|
||||||
|
@ -253,7 +276,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
clientId: resp.clientId,
|
clientId: resp.clientId,
|
||||||
clientSecret: resp.clientSecret,
|
clientSecret: resp.clientSecret,
|
||||||
authUrl: resp.url,
|
authUrl: resp.url,
|
||||||
wantsToLogin: true
|
proceedToGetCode: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -304,7 +327,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
clientSecret: session.clientSecret,
|
clientSecret: session.clientSecret,
|
||||||
authUrl: session.authUrl,
|
authUrl: session.authUrl,
|
||||||
emergencyMode: session.emergency,
|
emergencyMode: session.emergency,
|
||||||
wantsToLogin: true
|
proceedToGetCode: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -332,8 +355,8 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
axios
|
axios
|
||||||
.get(
|
.get(
|
||||||
"https://" +
|
"https://" +
|
||||||
baseUrl +
|
baseUrl +
|
||||||
"/api/v1/timelines/public"
|
"/api/v1/timelines/public"
|
||||||
)
|
)
|
||||||
.catch((err: Error) => {
|
.catch((err: Error) => {
|
||||||
let userInputError = true;
|
let userInputError = true;
|
||||||
|
@ -375,7 +398,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
let location = window.location.href;
|
let location = window.location.href;
|
||||||
if (location.includes("?code=")) {
|
if (location.includes("?code=")) {
|
||||||
let code = parseUrl(location).query.code as string;
|
let code = parseUrl(location).query.code as string;
|
||||||
this.setState({ authority: true });
|
this.setState({ authorizing: true });
|
||||||
let loginData = localStorage.getItem("login");
|
let loginData = localStorage.getItem("login");
|
||||||
if (loginData) {
|
if (loginData) {
|
||||||
let clientLoginSession: SaveClientSession = JSON.parse(
|
let clientLoginSession: SaveClientSession = JSON.parse(
|
||||||
|
@ -389,12 +412,12 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
this.state.emergencyMode
|
this.state.emergencyMode
|
||||||
? undefined
|
? undefined
|
||||||
: clientLoginSession.authUrl.includes(
|
: clientLoginSession.authUrl.includes(
|
||||||
"urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob"
|
"urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob"
|
||||||
)
|
)
|
||||||
? undefined
|
? undefined
|
||||||
: window.location.protocol === "hyperspace:"
|
: window.location.protocol === "hyperspace:"
|
||||||
? "hyperspace://hyperspace/app/"
|
? "hyperspace://hyperspace/app/"
|
||||||
: `https://${window.location.host}`
|
: `https://${window.location.host}`
|
||||||
)
|
)
|
||||||
.then((tokenData: any) => {
|
.then((tokenData: any) => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
|
@ -436,6 +459,65 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showMultiAccount() {
|
||||||
|
const { classes } = this.props;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Typography variant="h5">Select an account</Typography>
|
||||||
|
<Typography>from the list below or add a new one</Typography>
|
||||||
|
|
||||||
|
<List>
|
||||||
|
{getAccountRegistry().map(
|
||||||
|
(account: MultiAccount, index: number) => (
|
||||||
|
<ListItem
|
||||||
|
onClick={() => {
|
||||||
|
loginWithAccount(account);
|
||||||
|
window.location.href =
|
||||||
|
window.location.protocol ===
|
||||||
|
"hyperspace:"
|
||||||
|
? "hyperspace://hyperspace/app/"
|
||||||
|
: `https://${window.location.host}/#/`;
|
||||||
|
}}
|
||||||
|
button={true}
|
||||||
|
>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<AccountCircleIcon color="action" />
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={`@${account.username}`}
|
||||||
|
secondary={account.host}
|
||||||
|
/>
|
||||||
|
<ListItemSecondaryAction>
|
||||||
|
<IconButton
|
||||||
|
onClick={(e: any) => {
|
||||||
|
e.preventDefault();
|
||||||
|
removeAccountFromRegistry(index);
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
</ListItem>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</List>
|
||||||
|
<div className={classes.middlePadding} />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
this.setState({ willAddAccount: true });
|
||||||
|
this.clear();
|
||||||
|
}}
|
||||||
|
color={"primary"}
|
||||||
|
variant={"contained"}
|
||||||
|
>
|
||||||
|
Add Account
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
showLanding() {
|
showLanding() {
|
||||||
const { classes } = this.props;
|
const { classes } = this.props;
|
||||||
return (
|
return (
|
||||||
|
@ -452,7 +534,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
onKeyDown={event => this.watchUsernameField(event)}
|
onKeyDown={event => this.watchUsernameField(event)}
|
||||||
error={this.state.userInputError}
|
error={this.state.userInputError}
|
||||||
onBlur={() => this.checkForErrors()}
|
onBlur={() => this.checkForErrors()}
|
||||||
></TextField>
|
/>
|
||||||
{this.state.userInputError ? (
|
{this.state.userInputError ? (
|
||||||
<Typography color="error">
|
<Typography color="error">
|
||||||
{this.state.userInputErrorMessage}
|
{this.state.userInputErrorMessage}
|
||||||
|
@ -597,7 +679,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
this.updateAuthCode(event.target.value)
|
this.updateAuthCode(event.target.value)
|
||||||
}
|
}
|
||||||
onKeyDown={event => this.watchAuthField(event)}
|
onKeyDown={event => this.watchAuthField(event)}
|
||||||
></TextField>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => this.toggleAuthDialog()}>
|
<Button onClick={() => this.toggleAuthDialog()}>
|
||||||
|
@ -614,7 +696,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
showAuthority() {
|
showAuthorizationLoader() {
|
||||||
const { classes } = this.props;
|
const { classes } = this.props;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -659,11 +741,14 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
/>
|
/>
|
||||||
<br />
|
<br />
|
||||||
<Fade in={true}>
|
<Fade in={true}>
|
||||||
{this.state.authority
|
{this.state.authorizing
|
||||||
? this.showAuthority()
|
? this.showAuthorizationLoader()
|
||||||
: this.state.wantsToLogin
|
: this.state.proceedToGetCode
|
||||||
? this.showLoginAuth()
|
? this.showLoginAuth()
|
||||||
: this.showLanding()}
|
: getAccountRegistry().length > 0 &&
|
||||||
|
!this.state.willAddAccount
|
||||||
|
? this.showMultiAccount()
|
||||||
|
: this.showLanding()}
|
||||||
</Fade>
|
</Fade>
|
||||||
<br />
|
<br />
|
||||||
<Typography variant="caption">
|
<Typography variant="caption">
|
||||||
|
@ -741,4 +826,4 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withStyles(styles)(withSnackbar(WelcomePage));
|
export default withStyles(styles)(withSnackbar(WelcomePage));
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import {
|
import {
|
||||||
withStyles,
|
|
||||||
Typography,
|
|
||||||
Paper,
|
|
||||||
Avatar,
|
Avatar,
|
||||||
Button,
|
Button,
|
||||||
|
CircularProgress,
|
||||||
|
Paper,
|
||||||
TextField,
|
TextField,
|
||||||
ListItem,
|
Typography,
|
||||||
ListItemText,
|
withStyles
|
||||||
ListItemAvatar,
|
|
||||||
List,
|
|
||||||
Grid
|
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import { withSnackbar, withSnackbarProps } from "notistack";
|
import { withSnackbar, withSnackbarProps } from "notistack";
|
||||||
import { styles } from "./PageLayout.styles";
|
import { styles } from "./PageLayout.styles";
|
||||||
|
@ -18,16 +14,17 @@ import { Account } from "../types/Account";
|
||||||
import Mastodon from "megalodon";
|
import Mastodon from "megalodon";
|
||||||
import filedialog from "file-dialog";
|
import filedialog from "file-dialog";
|
||||||
|
|
||||||
import PersonIcon from "@material-ui/icons/Person";
|
|
||||||
|
|
||||||
interface IYouProps extends withSnackbarProps {
|
interface IYouProps extends withSnackbarProps {
|
||||||
classes: any;
|
classes: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IYouState {
|
interface IYouState {
|
||||||
currentAccount: Account;
|
currentAccount?: Account;
|
||||||
newDisplayName?: string;
|
newDisplayName?: string;
|
||||||
newBio?: string;
|
newBio?: string;
|
||||||
|
viewIsLoading: boolean;
|
||||||
|
viewLoaded: boolean;
|
||||||
|
viewErrored: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class You extends Component<IYouProps, IYouState> {
|
class You extends Component<IYouProps, IYouState> {
|
||||||
|
@ -42,12 +39,42 @@ class You extends Component<IYouProps, IYouState> {
|
||||||
);
|
);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
currentAccount: this.getAccount()
|
viewIsLoading: true,
|
||||||
|
viewLoaded: false,
|
||||||
|
viewErrored: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.client
|
||||||
|
.get("/accounts/verify_credentials")
|
||||||
|
.then((resp: any) => {
|
||||||
|
let currentAccount: Account = resp.data;
|
||||||
|
this.setState({
|
||||||
|
currentAccount,
|
||||||
|
viewIsLoading: false,
|
||||||
|
viewLoaded: true
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (this.getAccount()) {
|
||||||
|
this.setState({
|
||||||
|
currentAccount: this.getAccount(),
|
||||||
|
viewIsLoading: false,
|
||||||
|
viewLoaded: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
viewIsLoading: false,
|
||||||
|
viewErrored: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getAccount() {
|
getAccount() {
|
||||||
let acct = localStorage.getItem("account");
|
let acct = localStorage.getItem("account");
|
||||||
|
console.log(acct);
|
||||||
if (acct) {
|
if (acct) {
|
||||||
return JSON.parse(acct);
|
return JSON.parse(acct);
|
||||||
}
|
}
|
||||||
|
@ -142,15 +169,16 @@ class You extends Component<IYouProps, IYouState> {
|
||||||
removeHTMLContent(text: string) {
|
removeHTMLContent(text: string) {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.innerHTML = text;
|
div.innerHTML = text;
|
||||||
let innerContent = div.textContent || div.innerText || "";
|
return div.textContent || div.innerText || "";
|
||||||
return innerContent;
|
|
||||||
}
|
}
|
||||||
changeDisplayName() {
|
changeDisplayName() {
|
||||||
this.client
|
this.client
|
||||||
.patch("/accounts/update_credentials", {
|
.patch("/accounts/update_credentials", {
|
||||||
display_name: this.state.newDisplayName
|
display_name: this.state.newDisplayName
|
||||||
? this.state.newDisplayName
|
? this.state.newDisplayName
|
||||||
: this.state.currentAccount.display_name
|
: this.state.currentAccount
|
||||||
|
? this.state.currentAccount.display_name
|
||||||
|
: ""
|
||||||
})
|
})
|
||||||
.then((acct: any) => {
|
.then((acct: any) => {
|
||||||
let currentAccount: Account = acct.data;
|
let currentAccount: Account = acct.data;
|
||||||
|
@ -179,7 +207,9 @@ class You extends Component<IYouProps, IYouState> {
|
||||||
.patch("/accounts/update_credentials", {
|
.patch("/accounts/update_credentials", {
|
||||||
note: this.state.newBio
|
note: this.state.newBio
|
||||||
? this.state.newBio
|
? this.state.newBio
|
||||||
: this.state.currentAccount.note
|
: this.state.currentAccount
|
||||||
|
? this.state.currentAccount.note
|
||||||
|
: ""
|
||||||
})
|
})
|
||||||
.then((acct: any) => {
|
.then((acct: any) => {
|
||||||
let currentAccount: Account = acct.data;
|
let currentAccount: Account = acct.data;
|
||||||
|
@ -205,116 +235,155 @@ class You extends Component<IYouProps, IYouState> {
|
||||||
const { classes } = this.props;
|
const { classes } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className={classes.pageLayoutMinimalConstraints}>
|
<div className={classes.pageLayoutMinimalConstraints}>
|
||||||
<div className={classes.pageHeroBackground}>
|
{this.state.viewErrored ? (
|
||||||
<div
|
<Paper className={classes.errorCard}>
|
||||||
className={classes.pageHeroBackgroundImage}
|
<Typography variant="h4">Bummer.</Typography>
|
||||||
style={{
|
<Typography variant="h6">
|
||||||
backgroundImage: `url("${this.state.currentAccount.header_static}")`
|
Something went wrong when trying to get your account
|
||||||
}}
|
information.
|
||||||
/>
|
</Typography>
|
||||||
<div className={classes.profileContent}>
|
</Paper>
|
||||||
<br />
|
) : (
|
||||||
<Avatar
|
<span />
|
||||||
className={classes.profileAvatar}
|
)}
|
||||||
src={this.state.currentAccount.avatar_static}
|
{this.state.currentAccount ? (
|
||||||
/>
|
<div>
|
||||||
<div
|
<div className={classes.pageHeroBackground}>
|
||||||
className={classes.profileUserBox}
|
<div
|
||||||
style={{ paddingTop: 8, paddingBottom: 8 }}
|
className={classes.pageHeroBackgroundImage}
|
||||||
>
|
style={{
|
||||||
<Typography
|
backgroundImage: `url("${this.state.currentAccount.header_static}")`
|
||||||
variant="h4"
|
}}
|
||||||
color="inherit"
|
/>
|
||||||
component="h1"
|
<div className={classes.profileContent}>
|
||||||
>
|
<br />
|
||||||
Edit your profile
|
<Avatar
|
||||||
</Typography>
|
className={classes.profileAvatar}
|
||||||
<Typography color="inherit">
|
src={
|
||||||
Change information such as your display name,
|
this.state.currentAccount.avatar_static
|
||||||
bio, and images used here.
|
}
|
||||||
</Typography>
|
/>
|
||||||
<div>
|
<div
|
||||||
<Button
|
className={classes.profileUserBox}
|
||||||
className={classes.pageProfileFollowButton}
|
style={{ paddingTop: 8, paddingBottom: 8 }}
|
||||||
variant="contained"
|
|
||||||
onClick={() => this.updateAvatar()}
|
|
||||||
>
|
>
|
||||||
Change Avatar
|
<Typography
|
||||||
</Button>
|
variant="h4"
|
||||||
<Button
|
color="inherit"
|
||||||
className={classes.pageProfileFollowButton}
|
component="h1"
|
||||||
variant="contained"
|
>
|
||||||
onClick={() => this.updateHeader()}
|
Edit your profile
|
||||||
>
|
</Typography>
|
||||||
Change Header
|
<Typography color="inherit">
|
||||||
</Button>
|
Change information such as your display
|
||||||
|
name, bio, and images used here.
|
||||||
|
</Typography>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
className={
|
||||||
|
classes.pageProfileFollowButton
|
||||||
|
}
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => this.updateAvatar()}
|
||||||
|
>
|
||||||
|
Change Avatar
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className={
|
||||||
|
classes.pageProfileFollowButton
|
||||||
|
}
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => this.updateHeader()}
|
||||||
|
>
|
||||||
|
Change Header
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className={classes.pageContentLayoutConstraints}>
|
||||||
|
<Paper className={classes.youPaper}>
|
||||||
|
<Typography variant="h5" component="h2">
|
||||||
|
Display Name
|
||||||
|
</Typography>
|
||||||
|
<br />
|
||||||
|
<TextField
|
||||||
|
className={classes.TextField}
|
||||||
|
defaultValue={
|
||||||
|
this.state.currentAccount.display_name
|
||||||
|
}
|
||||||
|
rowsMax="1"
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
onChange={(event: any) =>
|
||||||
|
this.updateDisplayName(
|
||||||
|
event.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div style={{ textAlign: "right" }}>
|
||||||
|
<Button
|
||||||
|
className={
|
||||||
|
classes.pageProfileFollowButton
|
||||||
|
}
|
||||||
|
color="primary"
|
||||||
|
onClick={() => this.changeDisplayName()}
|
||||||
|
>
|
||||||
|
Update display Name
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Paper>
|
||||||
|
<br />
|
||||||
|
<Paper className={classes.youPaper}>
|
||||||
|
<Typography variant="h5" component="h2">
|
||||||
|
About you
|
||||||
|
</Typography>
|
||||||
|
<br />
|
||||||
|
<TextField
|
||||||
|
className={classes.TextField}
|
||||||
|
defaultValue={
|
||||||
|
this.state.currentAccount.note
|
||||||
|
? this.removeHTMLContent(
|
||||||
|
this.state.currentAccount.note
|
||||||
|
)
|
||||||
|
: "Tell a little bit about yourself"
|
||||||
|
}
|
||||||
|
multiline
|
||||||
|
variant="outlined"
|
||||||
|
rows="2"
|
||||||
|
rowsMax="5"
|
||||||
|
fullWidth
|
||||||
|
onChange={(event: any) =>
|
||||||
|
this.updateBio(event.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div style={{ textAlign: "right" }}>
|
||||||
|
<Button
|
||||||
|
className={
|
||||||
|
classes.pageProfileFollowButton
|
||||||
|
}
|
||||||
|
color="primary"
|
||||||
|
onClick={() => this.changeBio()}
|
||||||
|
>
|
||||||
|
Update biography
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Paper>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
<div className={classes.pageContentLayoutConstraints}>
|
"AAA"
|
||||||
<Paper className={classes.youPaper}>
|
)}
|
||||||
<Typography variant="h5" component="h2">
|
{this.state.viewIsLoading ? (
|
||||||
Display Name
|
<div style={{ textAlign: "center" }}>
|
||||||
</Typography>
|
<CircularProgress
|
||||||
<br />
|
className={classes.progress}
|
||||||
<TextField
|
color="primary"
|
||||||
className={classes.TextField}
|
|
||||||
defaultValue={
|
|
||||||
this.state.currentAccount.display_name
|
|
||||||
}
|
|
||||||
rowsMax="1"
|
|
||||||
variant="outlined"
|
|
||||||
fullWidth
|
|
||||||
onChange={(event: any) =>
|
|
||||||
this.updateDisplayName(event.target.value)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<div style={{ textAlign: "right" }}>
|
</div>
|
||||||
<Button
|
) : (
|
||||||
className={classes.pageProfileFollowButton}
|
<span />
|
||||||
color="primary"
|
)}
|
||||||
onClick={() => this.changeDisplayName()}
|
|
||||||
>
|
|
||||||
Update display Name
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Paper>
|
|
||||||
<br />
|
|
||||||
<Paper className={classes.youPaper}>
|
|
||||||
<Typography variant="h5" component="h2">
|
|
||||||
About you
|
|
||||||
</Typography>
|
|
||||||
<br />
|
|
||||||
<TextField
|
|
||||||
className={classes.TextField}
|
|
||||||
defaultValue={
|
|
||||||
this.state.currentAccount.note
|
|
||||||
? this.removeHTMLContent(
|
|
||||||
this.state.currentAccount.note
|
|
||||||
)
|
|
||||||
: "Tell a little bit about yourself"
|
|
||||||
}
|
|
||||||
multiline
|
|
||||||
variant="outlined"
|
|
||||||
rows="2"
|
|
||||||
rowsMax="5"
|
|
||||||
fullWidth
|
|
||||||
onChange={(event: any) =>
|
|
||||||
this.updateBio(event.target.value)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div style={{ textAlign: "right" }}>
|
|
||||||
<Button
|
|
||||||
className={classes.pageProfileFollowButton}
|
|
||||||
color="primary"
|
|
||||||
onClick={() => this.changeBio()}
|
|
||||||
>
|
|
||||||
Update biography
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Paper>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,9 +26,32 @@ export type Account = {
|
||||||
bot: boolean | null;
|
bot: boolean | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Watered-down type for Mastodon accounts
|
||||||
|
*/
|
||||||
export type UAccount = {
|
export type UAccount = {
|
||||||
id: string;
|
id: string;
|
||||||
acct: string;
|
acct: string;
|
||||||
display_name: string;
|
display_name: string;
|
||||||
avatar_static: string;
|
avatar_static: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account type for use with multi-account support
|
||||||
|
*/
|
||||||
|
export type MultiAccount = {
|
||||||
|
/**
|
||||||
|
* The host name of the account (ex.: mastodon.social)
|
||||||
|
*/
|
||||||
|
host: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The username of the account (@test)
|
||||||
|
*/
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The access token generated from the login
|
||||||
|
*/
|
||||||
|
access_token: string;
|
||||||
|
};
|
||||||
|
|
|
@ -1,25 +1,26 @@
|
||||||
import Mastodon from "megalodon";
|
import Mastodon from "megalodon";
|
||||||
|
import { MultiAccount, Account } from "../types/Account";
|
||||||
|
|
||||||
export function userLoggedIn(): boolean {
|
export function userLoggedIn(): boolean {
|
||||||
if (
|
return !!(
|
||||||
localStorage.getItem("baseurl") &&
|
localStorage.getItem("baseurl") && localStorage.getItem("access_token")
|
||||||
localStorage.getItem("access_token")
|
);
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function refreshUserAccountData() {
|
export function refreshUserAccountData() {
|
||||||
let client = new Mastodon(
|
let host = localStorage.getItem("baseurl") as string;
|
||||||
localStorage.getItem("access_token") as string,
|
let token = localStorage.getItem("access_token") as string;
|
||||||
(localStorage.getItem("baseurl") as string) + "/api/v1"
|
|
||||||
);
|
let client = new Mastodon(token, host + "/api/v1");
|
||||||
|
|
||||||
client
|
client
|
||||||
.get("/accounts/verify_credentials")
|
.get("/accounts/verify_credentials")
|
||||||
.then((resp: any) => {
|
.then((resp: any) => {
|
||||||
localStorage.setItem("account", JSON.stringify(resp.data));
|
let account: Account = resp.data;
|
||||||
|
localStorage.setItem("account", JSON.stringify(account));
|
||||||
|
sessionStorage.setItem("id", account.id);
|
||||||
|
|
||||||
|
addAccountToRegistry(host, token, account.acct);
|
||||||
})
|
})
|
||||||
.catch((err: Error) => {
|
.catch((err: Error) => {
|
||||||
console.error(err.message);
|
console.error(err.message);
|
||||||
|
@ -31,3 +32,111 @@ export function refreshUserAccountData() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the access token and base URL to a given multi-account user.
|
||||||
|
* @param account The multi-account from localStorage to use
|
||||||
|
*/
|
||||||
|
export function loginWithAccount(account: MultiAccount) {
|
||||||
|
if (localStorage.getItem("access_token") !== null) {
|
||||||
|
console.info(
|
||||||
|
"Existing login detected. Removing and using assigned token..."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
localStorage.setItem("access_token", account.access_token);
|
||||||
|
localStorage.setItem("baseurl", account.host);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the account registry.
|
||||||
|
* @returns A list of accounts
|
||||||
|
*/
|
||||||
|
export function getAccountRegistry(): MultiAccount[] {
|
||||||
|
let accountRegistry: MultiAccount[] = [];
|
||||||
|
|
||||||
|
let accountRegistryString = localStorage.getItem("accountRegistry");
|
||||||
|
if (accountRegistryString !== null) {
|
||||||
|
accountRegistry = JSON.parse(accountRegistryString);
|
||||||
|
}
|
||||||
|
return accountRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an account to the multi-account registry if it doesn't exist already.
|
||||||
|
* @param base_url The base URL of the user (eg., the instance)
|
||||||
|
* @param access_token The access token for the user
|
||||||
|
* @param username The username of the user
|
||||||
|
*/
|
||||||
|
export function addAccountToRegistry(
|
||||||
|
base_url: string,
|
||||||
|
access_token: string,
|
||||||
|
username: string
|
||||||
|
) {
|
||||||
|
const newAccount: MultiAccount = {
|
||||||
|
host: base_url,
|
||||||
|
username,
|
||||||
|
access_token
|
||||||
|
};
|
||||||
|
|
||||||
|
let accountRegistry = getAccountRegistry();
|
||||||
|
const stringifiedRegistry = accountRegistry.map(account =>
|
||||||
|
JSON.stringify(account)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (stringifiedRegistry.indexOf(JSON.stringify(newAccount)) === -1) {
|
||||||
|
accountRegistry.push(newAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem("accountRegistry", JSON.stringify(accountRegistry));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an account from the multi-account registry, if possible
|
||||||
|
* @param accountIdentifier The index of the account from the registry or the MultiAccount object itself
|
||||||
|
*/
|
||||||
|
export function removeAccountFromRegistry(
|
||||||
|
accountIdentifier: number | MultiAccount
|
||||||
|
) {
|
||||||
|
let accountRegistry = getAccountRegistry();
|
||||||
|
|
||||||
|
if (typeof accountIdentifier === "number") {
|
||||||
|
if (accountRegistry.length > accountIdentifier) {
|
||||||
|
if (
|
||||||
|
localStorage.getItem("access_token") ===
|
||||||
|
accountRegistry[accountIdentifier].access_token
|
||||||
|
) {
|
||||||
|
localStorage.removeItem("baseurl");
|
||||||
|
localStorage.removeItem("access_token");
|
||||||
|
}
|
||||||
|
accountRegistry.splice(accountIdentifier);
|
||||||
|
} else {
|
||||||
|
console.log("Multi account index may be out of range");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const stringifiedRegistry = accountRegistry.map(account =>
|
||||||
|
JSON.stringify(account)
|
||||||
|
);
|
||||||
|
|
||||||
|
const stringifiedAccountId = JSON.stringify(accountIdentifier);
|
||||||
|
|
||||||
|
if (
|
||||||
|
stringifiedRegistry.indexOf(
|
||||||
|
JSON.stringify(stringifiedAccountId)
|
||||||
|
) !== -1
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
localStorage.getItem("access_token") ===
|
||||||
|
accountIdentifier.access_token
|
||||||
|
) {
|
||||||
|
localStorage.removeItem("baseurl");
|
||||||
|
localStorage.removeItem("access_token");
|
||||||
|
}
|
||||||
|
|
||||||
|
accountRegistry.splice(
|
||||||
|
stringifiedRegistry.indexOf(stringifiedAccountId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem("accountRegistry", JSON.stringify(accountRegistry));
|
||||||
|
}
|
||||||
|
|
|
@ -47,3 +47,12 @@ export function getDarwinAccentColor(): number {
|
||||||
);
|
);
|
||||||
return themeInteger === "" ? -2 : parseInt(themeInteger);
|
return themeInteger === "" ? -2 : parseInt(themeInteger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the app component from the desktop app
|
||||||
|
*/
|
||||||
|
export function getElectronApp() {
|
||||||
|
const eWin = window as ElectronWindow;
|
||||||
|
const { remote } = eWin.require("electron");
|
||||||
|
return remote.app;
|
||||||
|
}
|
||||||
|
|
|
@ -45,9 +45,7 @@ export function canSendNotifications() {
|
||||||
*/
|
*/
|
||||||
export function sendNotificationRequest(title: string, body: string) {
|
export function sendNotificationRequest(title: string, body: string) {
|
||||||
if (canSendNotifications()) {
|
if (canSendNotifications()) {
|
||||||
let notif = new Notification(title, {
|
let notif = new Notification(title, { body });
|
||||||
body: body
|
|
||||||
});
|
|
||||||
|
|
||||||
notif.onclick = () => {
|
notif.onclick = () => {
|
||||||
window.focus();
|
window.focus();
|
||||||
|
|
Loading…
Reference in New Issue