2019-03-27 22:39:25 +01:00
import React , { Component } from 'react' ;
import {
List ,
2019-04-28 18:46:26 +02:00
ListItem ,
ListItemAvatar ,
2019-03-27 22:39:25 +01:00
ListItemText ,
ListSubheader ,
ListItemSecondaryAction ,
Paper ,
IconButton ,
withStyles ,
Button ,
Switch ,
Dialog ,
DialogTitle ,
DialogContent ,
RadioGroup ,
FormControlLabel ,
Radio ,
2019-04-08 20:31:20 +02:00
DialogActions ,
2019-04-20 22:35:38 +02:00
DialogContentText ,
Grid ,
2019-04-21 21:32:12 +02:00
Theme ,
Typography
2019-03-27 22:39:25 +01:00
} from '@material-ui/core' ;
import { styles } from './PageLayout.styles' ;
2019-04-27 19:21:24 +02:00
import { setUserDefaultBool , getUserDefaultBool , getUserDefaultTheme , setUserDefaultTheme , getUserDefaultVisibility , setUserDefaultVisibility , getConfig } from '../utilities/settings' ;
2019-03-28 14:24:38 +01:00
import { canSendNotifications , browserSupportsNotificationRequests } from '../utilities/notifications' ;
2019-04-20 22:35:38 +02:00
import { themes , defaultTheme } from '../types/HyperspaceTheme' ;
import ThemePreview from '../components/ThemePreview' ;
import { setHyperspaceTheme , getHyperspaceTheme } from '../utilities/themes' ;
2019-04-21 18:39:22 +02:00
import { Visibility } from '../types/Visibility' ;
2019-04-27 20:33:56 +02:00
import { LinkableButton } from '../interfaces/overrides' ;
2019-03-27 22:39:25 +01:00
2019-04-28 18:46:26 +02:00
import OpenInNewIcon from '@material-ui/icons/OpenInNew' ;
import DevicesIcon from '@material-ui/icons/Devices' ;
import Brightness3Icon from '@material-ui/icons/Brightness3' ;
import PaletteIcon from '@material-ui/icons/Palette' ;
import MastodonIcon from 'mdi-material-ui/Mastodon' ;
import VisibilityIcon from '@material-ui/icons/Visibility' ;
import NotificationsIcon from '@material-ui/icons/Notifications' ;
2019-05-03 20:23:22 +02:00
import BellAlertIcon from 'mdi-material-ui/BellAlert' ;
2019-04-28 18:46:26 +02:00
import RefreshIcon from '@material-ui/icons/Refresh' ;
import UndoIcon from '@material-ui/icons/Undo' ;
2019-03-27 22:39:25 +01:00
interface ISettingsState {
darkModeEnabled : boolean ;
2019-04-21 18:39:22 +02:00
systemDecidesDarkMode : boolean ;
2019-03-27 22:39:25 +01:00
pushNotificationsEnabled : boolean ;
2019-04-19 20:57:35 +02:00
badgeDisplaysAllNotifs : boolean ;
2019-03-27 22:39:25 +01:00
selectThemeName : string ;
themeDialogOpen : boolean ;
2019-04-20 23:14:47 +02:00
visibilityDialogOpen : boolean ;
2019-04-08 20:31:20 +02:00
resetHyperspaceDialog : boolean ;
2019-04-12 19:39:24 +02:00
resetSettingsDialog : boolean ;
2019-04-20 22:35:38 +02:00
previewTheme : Theme ;
2019-04-20 23:14:47 +02:00
defaultVisibility : Visibility ;
2019-04-28 21:38:44 +02:00
brandName : string ;
2019-04-27 19:21:24 +02:00
federated : boolean ;
2019-03-27 22:39:25 +01:00
}
class SettingsPage extends Component < any , ISettingsState > {
constructor ( props : any ) {
super ( props ) ;
this . state = {
darkModeEnabled : getUserDefaultBool ( 'darkModeEnabled' ) ,
2019-04-21 18:39:22 +02:00
systemDecidesDarkMode : getUserDefaultBool ( 'systemDecidesDarkMode' ) ,
2019-03-28 14:24:38 +01:00
pushNotificationsEnabled : canSendNotifications ( ) ,
2019-04-19 20:57:35 +02:00
badgeDisplaysAllNotifs : getUserDefaultBool ( 'displayAllOnNotificationBadge' ) ,
2019-03-27 22:39:25 +01:00
selectThemeName : getUserDefaultTheme ( ) . key ,
2019-04-08 20:31:20 +02:00
themeDialogOpen : false ,
2019-04-20 23:14:47 +02:00
visibilityDialogOpen : false ,
2019-04-12 19:39:24 +02:00
resetHyperspaceDialog : false ,
2019-04-20 22:35:38 +02:00
resetSettingsDialog : false ,
2019-04-20 23:14:47 +02:00
previewTheme : setHyperspaceTheme ( getUserDefaultTheme ( ) ) || setHyperspaceTheme ( defaultTheme ) ,
2019-04-27 19:21:24 +02:00
defaultVisibility : getUserDefaultVisibility ( ) || "public" ,
2019-04-28 21:52:08 +02:00
brandName : "Hyperspace" ,
2019-04-27 19:21:24 +02:00
federated : true
2019-03-27 22:39:25 +01:00
}
this . toggleDarkMode = this . toggleDarkMode . bind ( this ) ;
2019-04-21 18:39:22 +02:00
this . toggleSystemDarkMode = this . toggleSystemDarkMode . bind ( this ) ;
2019-03-27 22:39:25 +01:00
this . togglePushNotifications = this . togglePushNotifications . bind ( this ) ;
2019-04-19 20:57:35 +02:00
this . toggleBadgeCount = this . toggleBadgeCount . bind ( this ) ;
2019-03-27 22:39:25 +01:00
this . toggleThemeDialog = this . toggleThemeDialog . bind ( this ) ;
2019-04-20 23:14:47 +02:00
this . toggleVisibilityDialog = this . toggleVisibilityDialog . bind ( this ) ;
2019-03-27 22:39:25 +01:00
this . changeThemeName = this . changeThemeName . bind ( this ) ;
this . changeTheme = this . changeTheme . bind ( this ) ;
2019-04-20 23:14:47 +02:00
this . setVisibility = this . setVisibility . bind ( this ) ;
2019-03-27 22:39:25 +01:00
}
2019-04-27 19:21:24 +02:00
componentDidMount() {
2019-04-28 21:38:44 +02:00
getConfig ( ) . then ( ( config : any ) = > {
this . setState ( {
brandName : config.branding.name
} )
} ) . catch ( ( err : Error ) = > {
console . error ( err . message ) ;
2019-04-28 21:52:08 +02:00
} ) ;
2019-04-27 19:21:24 +02:00
this . getFederatedStatus ( ) ;
}
getFederatedStatus() {
getConfig ( ) . then ( ( config : any ) = > {
this . setState ( { federated : config.federated === "true" } ) ;
} )
}
2019-03-27 22:39:25 +01:00
toggleDarkMode() {
this . setState ( { darkModeEnabled : ! this . state . darkModeEnabled } ) ;
setUserDefaultBool ( 'darkModeEnabled' , ! this . state . darkModeEnabled ) ;
window . location . reload ( ) ;
}
2019-04-21 18:39:22 +02:00
toggleSystemDarkMode() {
this . setState ( { systemDecidesDarkMode : ! this . state . systemDecidesDarkMode } ) ;
setUserDefaultBool ( 'systemDecidesDarkMode' , ! this . state . systemDecidesDarkMode ) ;
window . location . reload ( ) ;
}
2019-03-27 22:39:25 +01:00
togglePushNotifications() {
this . setState ( { pushNotificationsEnabled : ! this . state . pushNotificationsEnabled } ) ;
setUserDefaultBool ( 'enablePushNotifications' , ! this . state . pushNotificationsEnabled ) ;
}
2019-04-19 20:57:35 +02:00
toggleBadgeCount() {
this . setState ( { badgeDisplaysAllNotifs : ! this . state . badgeDisplaysAllNotifs } ) ;
setUserDefaultBool ( 'displayAllOnNotificationBadge' , ! this . state . badgeDisplaysAllNotifs ) ;
}
2019-03-27 22:39:25 +01:00
toggleThemeDialog() {
this . setState ( { themeDialogOpen : ! this . state . themeDialogOpen } ) ;
}
2019-04-20 23:14:47 +02:00
toggleVisibilityDialog() {
this . setState ( { visibilityDialogOpen : ! this . state . visibilityDialogOpen } ) ;
}
2019-04-08 20:31:20 +02:00
toggleResetDialog() {
this . setState ( { resetHyperspaceDialog : ! this . state . resetHyperspaceDialog } ) ;
}
2019-04-12 19:39:24 +02:00
toggleResetSettingsDialog() {
this . setState ( { resetSettingsDialog : ! this . state . resetSettingsDialog } ) ;
}
2019-03-27 22:39:25 +01:00
changeTheme() {
setUserDefaultTheme ( this . state . selectThemeName ) ;
window . location . reload ( ) ;
}
changeThemeName ( theme : string ) {
2019-04-20 22:35:38 +02:00
let previewTheme = setHyperspaceTheme ( getHyperspaceTheme ( theme ) ) ;
this . setState ( { selectThemeName : theme , previewTheme } ) ;
2019-03-27 22:39:25 +01:00
}
2019-04-20 23:14:47 +02:00
changeVisibility ( to : Visibility ) {
this . setState ( { defaultVisibility : to } ) ;
}
setVisibility() {
2019-04-20 23:23:08 +02:00
setUserDefaultVisibility ( this . state . defaultVisibility ) ;
2019-04-20 23:14:47 +02:00
this . toggleVisibilityDialog ( ) ;
}
2019-04-08 20:31:20 +02:00
reset() {
localStorage . clear ( ) ;
window . location . reload ( ) ;
}
2019-04-12 19:39:24 +02:00
refresh() {
2019-04-20 23:23:08 +02:00
let settings = [ 'darkModeEnabled' , 'enablePushNotifications' , 'clearNotificationsOnRead' , 'theme' , 'displayAllOnNotificationBadge' , 'defaultVisibility' ] ;
2019-04-12 19:39:24 +02:00
settings . forEach ( setting = > {
localStorage . removeItem ( setting ) ;
} )
window . location . reload ( ) ;
}
2019-03-27 22:39:25 +01:00
showThemeDialog() {
2019-04-20 22:35:38 +02:00
const { classes } = this . props ;
2019-03-27 22:39:25 +01:00
return (
< Dialog
open = { this . state . themeDialogOpen }
disableBackdropClick
disableEscapeKeyDown
2019-04-20 22:35:38 +02:00
maxWidth = "md"
2019-03-27 22:39:25 +01:00
fullWidth = { true }
aria - labelledby = "confirmation-dialog-title"
>
2019-04-20 22:39:35 +02:00
< DialogTitle id = "confirmation-dialog-title" > Choose a theme < / DialogTitle >
2019-03-27 22:39:25 +01:00
< DialogContent >
2019-04-20 22:35:38 +02:00
< Grid container spacing = { 16 } >
< Grid item xs = { 12 } md = { 6 } >
< RadioGroup
2019-04-20 22:39:35 +02:00
aria - label = "Theme"
2019-04-20 22:35:38 +02:00
name = "colorScheme"
value = { this . state . selectThemeName }
onChange = { ( e , value ) = > this . changeThemeName ( value ) }
>
{ themes . map ( theme = > (
< FormControlLabel value = { theme . key } key = { theme . key } control = { < Radio / > } label = { theme . name } / >
) ) }
) ) }
< / RadioGroup >
< / Grid >
< Grid item xs = { 12 } md = { 6 } className = { classes . desktopOnly } >
2019-04-21 21:32:12 +02:00
< Typography variant = "h6" component = "p" > Theme preview < / Typography >
2019-04-20 22:35:38 +02:00
< ThemePreview theme = { this . state . previewTheme } / >
< / Grid >
< / Grid >
2019-03-27 22:39:25 +01:00
< / DialogContent >
< DialogActions >
2019-03-28 00:24:52 +01:00
< Button onClick = { this . toggleThemeDialog } color = "default" >
2019-03-27 22:39:25 +01:00
Cancel
< / Button >
2019-03-28 00:24:52 +01:00
< Button onClick = { this . changeTheme } color = "secondary" >
2019-04-20 22:35:38 +02:00
Set theme
2019-03-27 22:39:25 +01:00
< / Button >
< / DialogActions >
< / Dialog >
) ;
}
2019-04-20 23:14:47 +02:00
showVisibilityDialog() {
return (
< Dialog
open = { this . state . visibilityDialogOpen }
disableBackdropClick
disableEscapeKeyDown
maxWidth = "xs"
fullWidth = { true }
aria - labelledby = "confirmation-dialog-title"
>
< DialogTitle id = "confirmation-dialog-title" > Set your default visibility < / DialogTitle >
< DialogContent >
< RadioGroup
aria - label = "Visibility"
name = "visibility"
value = { this . state . defaultVisibility }
onChange = { ( e , value ) = > this . changeVisibility ( value as Visibility ) }
>
2019-04-27 19:21:24 +02:00
< FormControlLabel value = { "public" } key = { "public" } control = { < Radio / > } label = { ` Public ${ this . state . federated ? "" : "(disabled by provider)" } ` } disabled = { ! this . state . federated } / >
2019-04-20 23:14:47 +02:00
< FormControlLabel value = { "unlisted" } key = { "unlisted" } control = { < Radio / > } label = { "Unlisted" } / >
< FormControlLabel value = { "private" } key = { "private" } control = { < Radio / > } label = { "Private (followers only)" } / >
< FormControlLabel value = { "direct" } key = { "direct" } control = { < Radio / > } label = { "Direct" } / >
< / RadioGroup >
< / DialogContent >
< DialogActions >
< Button onClick = { this . toggleVisibilityDialog } color = "default" >
Cancel
< / Button >
< Button onClick = { this . setVisibility } color = "secondary" >
Set default
< / Button >
< / DialogActions >
< / Dialog >
) ;
}
2019-04-12 19:39:24 +02:00
showResetSettingsDialog() {
return (
< Dialog
open = { this . state . resetSettingsDialog }
onClose = { ( ) = > this . toggleResetSettingsDialog ( ) }
>
< DialogTitle id = "alert-dialog-title" > Are you sure you want to refresh settings ? < / DialogTitle >
< DialogActions >
< Button onClick = { ( ) = > this . toggleResetSettingsDialog ( ) } color = "primary" autoFocus >
Cancel
< / Button >
< Button onClick = { ( ) = > {
this . refresh ( ) ;
} } color = "primary" >
Refresh
< / Button >
< / DialogActions >
< / Dialog >
) ;
}
2019-04-08 20:31:20 +02:00
showResetDialog() {
return (
< Dialog
open = { this . state . resetHyperspaceDialog }
onClose = { ( ) = > this . toggleResetDialog ( ) }
>
2019-04-28 21:38:44 +02:00
< DialogTitle id = "alert-dialog-title" > Reset { this . state . brandName } ? < / DialogTitle >
2019-04-08 20:31:20 +02:00
< DialogContent >
< DialogContentText id = "alert-dialog-description" >
2019-04-28 21:38:44 +02:00
Are you sure you want to reset { this . state . brandName } ? You ' ll need to re - authorize { this . state . brandName } access again .
2019-04-08 20:31:20 +02:00
< / DialogContentText >
< / DialogContent >
< DialogActions >
< Button onClick = { ( ) = > this . toggleResetDialog ( ) } color = "primary" autoFocus >
Cancel
< / Button >
< Button onClick = { ( ) = > {
this . reset ( ) ;
} } color = "primary" >
Reset
< / Button >
< / DialogActions >
< / Dialog >
) ;
}
2019-03-27 22:39:25 +01:00
render() {
const { classes } = this . props ;
return (
< div className = { classes . pageLayoutConstraints } >
< ListSubheader > Appearance < / ListSubheader >
< Paper className = { classes . pageListConstraints } >
< List >
2019-04-21 18:39:22 +02:00
< ListItem >
2019-04-28 18:46:26 +02:00
< ListItemAvatar >
< DevicesIcon color = "action" / >
< / ListItemAvatar >
2019-04-21 18:42:02 +02:00
< ListItemText primary = "Match system appearance" secondary = "Obey light/dark theme from your system" / >
2019-04-21 18:39:22 +02:00
< ListItemSecondaryAction >
< Switch
checked = { this . state . systemDecidesDarkMode }
onChange = { this . toggleSystemDarkMode }
/ >
< / ListItemSecondaryAction >
< / ListItem >
2019-03-27 22:39:25 +01:00
< ListItem >
2019-04-28 18:46:26 +02:00
< ListItemAvatar >
< Brightness3Icon color = "action" / >
< / ListItemAvatar >
2019-04-21 18:42:02 +02:00
< ListItemText primary = "Dark mode" secondary = "Toggles light or dark theme" / >
2019-03-27 22:39:25 +01:00
< ListItemSecondaryAction >
2019-04-21 18:39:22 +02:00
< Switch
disabled = { this . state . systemDecidesDarkMode }
checked = { this . state . darkModeEnabled }
onChange = { this . toggleDarkMode }
/ >
2019-03-27 22:39:25 +01:00
< / ListItemSecondaryAction >
< / ListItem >
< ListItem >
2019-04-28 18:46:26 +02:00
< ListItemAvatar >
< PaletteIcon color = "action" / >
< / ListItemAvatar >
2019-04-21 18:42:02 +02:00
< ListItemText primary = "Interface theme" secondary = "The color palette used for the interface" / >
2019-03-27 22:39:25 +01:00
< ListItemSecondaryAction >
< Button onClick = { this . toggleThemeDialog } >
Set theme
< / Button >
< / ListItemSecondaryAction >
< / ListItem >
< / List >
< / Paper >
< br / >
2019-04-27 20:33:56 +02:00
< ListSubheader > Your Account < / ListSubheader >
2019-04-20 23:14:47 +02:00
< Paper className = { classes . pageListConstraints } >
< List >
< ListItem >
2019-04-28 18:46:26 +02:00
< ListItemAvatar >
< MastodonIcon color = "action" / >
< / ListItemAvatar >
2019-04-27 20:33:56 +02:00
< ListItemText primary = "Edit your profile" secondary = "Change your bio, display name, and images" / >
< ListItemSecondaryAction >
< LinkableButton to = "/you" > Edit < / LinkableButton >
< / ListItemSecondaryAction >
< / ListItem >
2019-04-20 23:14:47 +02:00
< ListItem >
< ListItemText primary = "Configure on Mastodon" / >
< ListItemSecondaryAction >
< IconButton href = { ( localStorage . getItem ( "baseurl" ) as string ) + "/settings/preferences" } target = "_blank" rel = "noreferrer" >
< OpenInNewIcon / >
< / IconButton >
< / ListItemSecondaryAction >
< / ListItem >
< / List >
< / Paper >
< br / >
< ListSubheader > Composer < / ListSubheader >
< Paper className = { classes . pageListConstraints } >
< List >
< ListItem >
2019-04-28 18:46:26 +02:00
< ListItemAvatar >
< VisibilityIcon color = "action" / >
< / ListItemAvatar >
2019-04-20 23:14:47 +02:00
< ListItemText primary = "Default visibility" secondary = "New posts in composer will use this visiblity" / >
< ListItemSecondaryAction >
< Button onClick = { this . toggleVisibilityDialog } >
Change
< / Button >
< / ListItemSecondaryAction >
< / ListItem >
< / List >
< / Paper >
< br / >
2019-03-27 22:39:25 +01:00
< ListSubheader > Notifications < / ListSubheader >
< Paper className = { classes . pageListConstraints } >
< List >
< ListItem >
2019-04-28 18:46:26 +02:00
< ListItemAvatar >
< NotificationsIcon color = "action" / >
< / ListItemAvatar >
2019-03-28 14:24:38 +01:00
< ListItemText
primary = "Enable push notifications"
secondary = {
2019-04-19 20:43:06 +02:00
getUserDefaultBool ( 'userDeniedNotification' ) ?
"Check your browser's notification permissions." :
browserSupportsNotificationRequests ( ) ?
2019-04-19 20:57:35 +02:00
"Send a push notification when not focused." :
2019-04-19 20:45:27 +02:00
"Notifications aren't supported."
2019-03-28 14:24:38 +01:00
}
/ >
2019-03-27 22:39:25 +01:00
< ListItemSecondaryAction >
2019-03-28 14:24:38 +01:00
< Switch
checked = { this . state . pushNotificationsEnabled }
onChange = { this . togglePushNotifications }
2019-04-19 20:43:06 +02:00
disabled = { ! browserSupportsNotificationRequests ( ) || getUserDefaultBool ( 'userDeniedNotification' ) }
2019-03-28 14:24:38 +01:00
/ >
2019-03-27 22:39:25 +01:00
< / ListItemSecondaryAction >
< / ListItem >
2019-04-21 19:56:18 +02:00
< ListItem >
2019-04-28 18:46:26 +02:00
< ListItemAvatar >
2019-05-03 20:23:22 +02:00
< BellAlertIcon color = "action" / >
2019-04-28 18:46:26 +02:00
< / ListItemAvatar >
2019-04-21 19:56:18 +02:00
< ListItemText
primary = "Notification badge counts all notifications"
secondary = {
"Counts all notifications, read or unread."
}
/ >
< ListItemSecondaryAction >
< Switch
checked = { this . state . badgeDisplaysAllNotifs }
onChange = { this . toggleBadgeCount }
2019-04-19 20:57:35 +02:00
/ >
2019-04-21 19:56:18 +02:00
< / ListItemSecondaryAction >
< / ListItem >
2019-03-27 22:39:25 +01:00
< / List >
< / Paper >
< br / >
< ListSubheader > Advanced < / ListSubheader >
< Paper className = { classes . pageListConstraints } >
< List >
2019-04-12 19:39:24 +02:00
< ListItem >
2019-04-28 18:46:26 +02:00
< ListItemAvatar >
< RefreshIcon color = "action" / >
< / ListItemAvatar >
2019-04-20 21:18:11 +02:00
< ListItemText primary = "Refresh settings" secondary = "Reset the settings to defaults." / >
2019-04-12 19:39:24 +02:00
< ListItemSecondaryAction >
< Button onClick = { ( ) = > this . toggleResetSettingsDialog ( ) } >
Refresh
< / Button >
< / ListItemSecondaryAction >
< / ListItem >
2019-03-27 22:39:25 +01:00
< ListItem >
2019-04-28 18:46:26 +02:00
< ListItemAvatar >
< UndoIcon color = "action" / >
< / ListItemAvatar >
2019-04-28 21:38:44 +02:00
< ListItemText primary = { ` Reset ${ this . state . brandName } ` } secondary = "Deletes all data and resets the app" / >
2019-03-27 22:39:25 +01:00
< ListItemSecondaryAction >
2019-04-08 20:31:20 +02:00
< Button onClick = { ( ) = > this . toggleResetDialog ( ) } >
2019-03-27 22:39:25 +01:00
Reset
< / Button >
< / ListItemSecondaryAction >
< / ListItem >
< / List >
< / Paper >
{ this . showThemeDialog ( ) }
2019-04-20 23:14:47 +02:00
{ this . showVisibilityDialog ( ) }
2019-04-08 20:31:20 +02:00
{ this . showResetDialog ( ) }
2019-04-12 19:39:24 +02:00
{ this . showResetSettingsDialog ( ) }
2019-03-27 22:39:25 +01:00
< / div >
) ;
}
}
export default withStyles ( styles ) ( SettingsPage ) ;