SettingsView.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import React, { Component } from 'react'
  2. import { connect } from 'react-redux'
  3. import PropTypes from 'prop-types'
  4. import { setInstallationDirectory, setInstallationType, setGameVersion, setAutoLoadMore, setOfflineMode, setTheme, setThemeImage, setFolderStructure, setUpdateChannel, setLatestReleaseNotes } from '../actions/settingsActions'
  5. import { checkDownloadedSongs } from '../actions/queueActions'
  6. import { checkInstalledMods, checkModsForUpdates } from '../actions/modActions'
  7. import { resetApp } from '../actions/appActions'
  8. import '../css/SettingsView.scss'
  9. import Button from './Button'
  10. import Toggle from './Toggle'
  11. const { ipcRenderer } = window.require('electron')
  12. class SettingsView extends Component {
  13. constructor(props) {
  14. super(props)
  15. this.state = {
  16. updateStatus: '',
  17. gameVersions: []
  18. }
  19. }
  20. componentDidMount() {
  21. ipcRenderer.on('electron-updater', (_, event, message) => {
  22. if(event === 'checking-for-update' || event === 'update-not-available' || event === 'update-available' || event === 'error') {
  23. this.setState({ updateStatus: event })
  24. }
  25. })
  26. fetch('https://beatmods.com/api/v1/version')
  27. .then(res => res.json())
  28. .then(gameVersions => this.setState({ gameVersions }))
  29. }
  30. updateValue() {
  31. switch(this.state.updateStatus) {
  32. case 'checking-for-update':
  33. return 'Checking...'
  34. case 'update-not-available':
  35. return 'Up to Date!'
  36. case 'update-available':
  37. return 'Update Available!'
  38. case 'error':
  39. return 'Update Error!'
  40. default:
  41. return 'Check for Updates'
  42. }
  43. }
  44. render() {
  45. return (
  46. <div id='settings-view'>
  47. <h1>Settings</h1>
  48. <h2>Beat Saber Installation</h2>
  49. <label htmlFor="dl-location">Installation Directory</label><br /><br />
  50. <span id="installation-directory-display"><input className="text-box" type="text" id="dl-loc-box" value={ this.props.settings.installationDirectory } disabled /></span>
  51. <input type="file" id="dl-location" webkitdirectory="" onChange={ (e) => {this.props.setInstallationDirectory(e.target.files[0].path || this.props.settings.installationDirectory)} } /><br />
  52. <label htmlFor="dl-location"><Button type="primary" onClick={ () => {} }>Choose Folder</Button></label><Button onClick={ () => {window.require('child_process').exec('start "" "' + this.props.settings.installationDirectory + '"')} }>Open Folder</Button><br /><br />
  53. <table>
  54. <tr>
  55. <th>Installation Type&emsp;</th>
  56. <th>Game Version</th>
  57. </tr>
  58. <br />
  59. <tr>
  60. <td>
  61. <select id="installation-type-select" name="installation-type-select" value={ this.props.settings.installationType } onChange={ (e) => { this.props.setInstallationType(e.target.value) } }>
  62. <option value="steam">Steam</option>
  63. <option value="oculus">Oculus</option>
  64. </select>
  65. </td>
  66. <td>
  67. <select id="game-version-select" name="game-version-select" value={ this.props.settings.gameVersion } onChange={ (e) => { this.props.setGameVersion(e.target.value) } }>
  68. { this.state.gameVersions.map(version => <option key={ version } value={ version }>{ version }</option>) }
  69. </select>
  70. </td>
  71. </tr>
  72. </table>
  73. { this.props.settings.installationType === 'steam' && this.props.settings.installationDirectory.includes('Oculus') ? <><br /><br /><span style={ { fontWeight: 'bold', color: 'salmon' } }>Warning: BeatDrop has detected that you may be using the Oculus version of BeatSaber. If this is the case, please set "Installation Type" to "Oculus". Otherwise, you can ignore this message.</span><br /><br /></> : null }
  74. { this.props.settings.installationType === 'oculus' && this.props.settings.installationDirectory.includes('Steam') ? <><br /><br /><span style={ { fontWeight: 'bold', color: 'salmon' } }>Warning: BeatDrop has detected that you may be using the Steam version of BeatSaber. If this is the case, please set "Installation Type" to "Steam". Otherwise, you can ignore this message.</span><br /><br /></> : null }
  75. <hr />
  76. <h2>Song List</h2>
  77. <Toggle toggled={ this.props.settings.autoLoadMore } onToggle={ () => this.props.setAutoLoadMore(!this.props.settings.autoLoadMore) } /><label htmlFor="auto-load-more">Auto Load More</label><br /><br />
  78. <Toggle toggled={ this.props.settings.offlineMode } onToggle={ () => this.props.setOfflineMode(!this.props.settings.offlineMode) } /><label htmlFor="offline-mode">Offline Mode</label><br />
  79. <hr />
  80. <h2>Downloads</h2>
  81. <Button onClick={ this.props.checkDownloadedSongs }>Scan for Songs</Button><Button onClick={ this.props.checkInstalledMods }>{ this.props.scanningForMods ? 'Scanning...' : 'Scan for Mods' }</Button><br /><br />
  82. <Button onClick={ () => { this.props.checkModsForUpdates(true) } }>Check for Mod Updates</Button><br /><br />
  83. <label htmlFor="folder-structure-select">Folder Structure</label><br /><br />
  84. <select id="folder-structure-select" name="folder-structure-select" value={ this.props.settings.folderStructure } onChange={ (e) => { this.props.setFolderStructure(e.target.value) } }>
  85. <option value="keySongNameArtistName">Key ( Song Name - Song Artist )</option>
  86. <option value="idKey">Key</option>
  87. <option value="songName">Song Name</option>
  88. </select>
  89. <hr />
  90. <h2>Theme</h2>
  91. <select id="theme-select" name="theme-select" value={ this.props.settings.theme } onChange={ (e) => { this.props.setTheme(e.target.value) } }>
  92. <option value="light">Light</option>
  93. <option value="dark">Dark</option>
  94. <option value="hc">High Contrast</option>
  95. </select><br /><br />
  96. <label>Custom Theme Image</label><br /><br />
  97. <span id="installation-directory-display"><input className="text-box" type="text" id="theme-image-box" placeholder="No Image Set" value={ this.props.settings.themeImagePath } disabled /></span>
  98. <input type="file" id="theme-image-path" onChange={ (e) => {this.props.setThemeImage(e.target.files[0].path || this.props.settings.themeImagePath)} } /><br />
  99. <label htmlFor="theme-image-path"><Button type="primary" onClick={ () => {} }>Choose Image</Button></label><Button onClick={ () => { this.props.setThemeImage('') } }>Clear Image</Button><br /><br />
  100. <hr />
  101. <h2>Updates</h2>
  102. <b>Current Version: </b>{ require('../../package.json').version }<br /><br />
  103. <Button onClick={ () => { this.props.setLatestReleaseNotes('0.0.0') } }>View Release Notes</Button><br /><br />
  104. <label htmlFor="update-channel-select">Update Channel</label><br /><br />
  105. <select id="update-channel-select" name="update-channel-select" value={ this.props.settings.updateChannel } onChange={ (e) => { this.props.setUpdateChannel(e.target.value) } }>
  106. <option value="latest">Stable</option>
  107. <option value="beta">Beta</option>
  108. </select><br /><br />
  109. {this.props.settings.updateChannel === 'beta' ? <><span style={ { fontWeight: 'bold', color: 'salmon' } }>Warning: Beta builds are unstable, untested and may result in unexpected crashes, loss of files and other adverse effects! By updating to a beta build, you understand and accept these risks.</span><br /><br /></> : null}
  110. <Button type={ this.state.updateStatus === 'error' ? 'destructive' : null } onClick={ () => { ipcRenderer.send('electron-updater', 'check-for-updates') } }>{ this.updateValue() }</Button>
  111. <br /><br />
  112. <hr />
  113. <h2>DANGER ZONE</h2>
  114. <i>Please don't touch these unless you know what you're doing.</i><br /><br />
  115. <Button type="destructive" onClick={ () => { this.props.resetApp() } }>Reset App</Button>
  116. <br /><br />
  117. <hr />
  118. <h2>Credits</h2>
  119. <b>BeatDrop Developers</b><br />
  120. <ul>
  121. <li>StarGazer1258</li>
  122. <li>Yuuki</li>
  123. <li><a href="https://github.com/StarGazer1258/BeatDrop/graphs/contributors" onClick={ (e) => { e.preventDefault(); e.stopPropagation(); window.require('electron').shell.openExternal(e.target.href) } }>The GitHub Community</a></li>
  124. </ul>
  125. <br />
  126. <b>Icon and Animation Designer, BeastSaber Developer</b><br />
  127. <ul>
  128. <li>Elliotttate</li>
  129. </ul>
  130. <br />
  131. <b>BeatMods Developers</b><br />
  132. <ul>
  133. <li>vanZeben</li>
  134. <li>raftario</li>
  135. </ul>
  136. <br />
  137. <b>Additional Icons Provided by</b><br />
  138. <ul>
  139. <li><a href="https://icons8.com/" onClick={ (e) => { e.preventDefault(); e.stopPropagation(); window.require('electron').shell.openExternal(e.target.href) } }>Icons8</a></li>
  140. </ul>
  141. <br />
  142. <h3>Patreon Supporter (Lifetime Pledge)</h3>
  143. <ul>
  144. <li>
  145. <b>Wave Tier</b>
  146. <ul>
  147. <li>Shane R. Monroe ($50)</li>
  148. <li>Carize ($30)</li>
  149. <li>Myles Hecht ($30)</li>
  150. <li>Iryna Pavlova ($20)</li>
  151. <li>Marc Smith ($10)</li>
  152. <li>Kirk Miller ($10)</li>
  153. <li></li>
  154. </ul>
  155. </li>
  156. <li>
  157. <b>Hurricane Tier</b>
  158. <ul>
  159. <li><i>Your name here...</i></li>
  160. </ul>
  161. </li>
  162. <li>
  163. <b>Tsunami Tier</b>
  164. <ul>
  165. <li><i>This could be you...</i></li>
  166. </ul>
  167. </li>
  168. </ul>
  169. <br /><br />
  170. </div>
  171. )
  172. }
  173. }
  174. SettingsView.propTypes = {
  175. settings: PropTypes.object.isRequired,
  176. scanningForMods: PropTypes.bool.isRequired,
  177. setInstallationDirectory: PropTypes.func.isRequired,
  178. setInstallationType: PropTypes.func.isRequired,
  179. setAutoLoadMore: PropTypes.func.isRequired,
  180. setTheme: PropTypes.func.isRequired,
  181. setFolderStructure: PropTypes.func.isRequired,
  182. setUpdateChannel: PropTypes.func.isRequired,
  183. checkDownloadedSongs: PropTypes.func.isRequired
  184. }
  185. const mapStateToProps = state => ({
  186. settings: state.settings,
  187. scanningForMods: state.mods.scanning
  188. })
  189. export default connect(mapStateToProps, { setInstallationDirectory, setInstallationType, setGameVersion, setAutoLoadMore, setOfflineMode, setTheme, setThemeImage, setFolderStructure, setUpdateChannel, setLatestReleaseNotes, checkDownloadedSongs, checkInstalledMods, checkModsForUpdates, resetApp })(SettingsView)
  190. //<input type="checkbox" name="auto-refresh" id="auto-refresh" checked={this.props.settings.autoRefresh} onClick={() => this.props.setAutoLoadMore(!this.props.settings.autoLoadMore)} /><label htmlFor="auto-refresh">Refresh feed every </label><input type="number" name="auto-refresh-interval" id="auto-refresh-interval"/><label htmlFor="auto-refresh-interval"> seconds</label>