From 49cba89a113a530ded7404fa9fb648db79716a0f Mon Sep 17 00:00:00 2001
From: Geomitron <22552797+Geomitron@users.noreply.github.com>
Date: Thu, 13 Feb 2020 22:33:14 -0500
Subject: [PATCH] Initial settings tab
---
src/app/app-routing.module.ts | 3 +-
src/app/app.module.ts | 6 ++-
.../search-bar/search-bar.component.html | 4 +-
.../downloads-modal.component.ts | 2 +-
.../settings/settings.component.html | 29 +++++++++++
.../settings/settings.component.scss | 10 ++++
.../components/settings/settings.component.ts | 52 +++++++++++++++++++
.../components/toolbar/toolbar.component.ts | 6 +--
src/app/core/services/electron.service.ts | 51 ++++++++++--------
src/app/core/services/settings.service.ts | 45 ++++++++++++++++
src/electron/ipc/InitSettingsHandler.ipc.ts | 7 ++-
src/electron/ipc/OpenFolderHandler.ipc.ts | 10 ----
src/electron/ipc/SaveSettingsHandler.ipc.ts | 20 +++++++
src/electron/shared/IPCHandler.ts | 6 +--
14 files changed, 204 insertions(+), 47 deletions(-)
create mode 100644 src/app/components/settings/settings.component.html
create mode 100644 src/app/components/settings/settings.component.scss
create mode 100644 src/app/components/settings/settings.component.ts
delete mode 100644 src/electron/ipc/OpenFolderHandler.ipc.ts
create mode 100644 src/electron/ipc/SaveSettingsHandler.ipc.ts
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 6d40c76..8e170c7 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -1,12 +1,13 @@
import { NgModule } from '@angular/core'
import { Routes, RouterModule } from '@angular/router'
import { BrowseComponent } from './components/browse/browse.component'
+import { SettingsComponent } from './components/settings/settings.component'
const routes: Routes = [
{ path: 'browse', component: BrowseComponent },
{ path: 'library', component: BrowseComponent },
- { path: 'settings', component: BrowseComponent },
+ { path: 'settings', component: SettingsComponent },
{ path: 'about', component: BrowseComponent }, // TODO: replace these with the correct components
{ path: '**', redirectTo: '/browse'}
]
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index f16e5e2..9539275 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -12,7 +12,8 @@ import { ChartSidebarComponent } from './components/browse/chart-sidebar/chart-s
import { ResultTableRowComponent } from './components/browse/result-table/result-table-row/result-table-row.component';
import { DownloadsModalComponent } from './components/browse/status-bar/downloads-modal/downloads-modal.component';
import { ProgressBarDirective } from './core/directives/progress-bar.directive';
-import { CheckboxDirective } from './core/directives/checkbox.directive'
+import { CheckboxDirective } from './core/directives/checkbox.directive';
+import { SettingsComponent } from './components/settings/settings.component'
@NgModule({
declarations: [
@@ -26,7 +27,8 @@ import { CheckboxDirective } from './core/directives/checkbox.directive'
ResultTableRowComponent,
DownloadsModalComponent,
ProgressBarDirective,
- CheckboxDirective
+ CheckboxDirective,
+ SettingsComponent
],
imports: [
BrowserModule,
diff --git a/src/app/components/browse/search-bar/search-bar.component.html b/src/app/components/browse/search-bar/search-bar.component.html
index b5668f9..cadc882 100644
--- a/src/app/components/browse/search-bar/search-bar.component.html
+++ b/src/app/components/browse/search-bar/search-bar.component.html
@@ -19,7 +19,7 @@
Charter
-
+
\ No newline at end of file
diff --git a/src/app/components/browse/status-bar/downloads-modal/downloads-modal.component.ts b/src/app/components/browse/status-bar/downloads-modal/downloads-modal.component.ts
index d626629..9359dda 100644
--- a/src/app/components/browse/status-bar/downloads-modal/downloads-modal.component.ts
+++ b/src/app/components/browse/status-bar/downloads-modal/downloads-modal.component.ts
@@ -53,6 +53,6 @@ export class DownloadsModalComponent {
}
openFolder(filepath: string) {
- this.electronService.sendIPC('open-folder', filepath)
+ this.electronService.showFolder(filepath)
}
}
\ No newline at end of file
diff --git a/src/app/components/settings/settings.component.html b/src/app/components/settings/settings.component.html
new file mode 100644
index 0000000..fb639bb
--- /dev/null
+++ b/src/app/components/settings/settings.component.html
@@ -0,0 +1,29 @@
+
+
+
+
+Current Cache Size:
{{cacheSize}}
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/components/settings/settings.component.scss b/src/app/components/settings/settings.component.scss
new file mode 100644
index 0000000..b1ac3b1
--- /dev/null
+++ b/src/app/components/settings/settings.component.scss
@@ -0,0 +1,10 @@
+:host {
+ flex: 1;
+ padding: 2em;
+ overflow-y: scroll;
+}
+
+.default-cursor {
+ cursor: default;
+ pointer-events: none;
+}
\ No newline at end of file
diff --git a/src/app/components/settings/settings.component.ts b/src/app/components/settings/settings.component.ts
new file mode 100644
index 0000000..f3a0420
--- /dev/null
+++ b/src/app/components/settings/settings.component.ts
@@ -0,0 +1,52 @@
+import { Component, OnInit, ViewChild, ElementRef, AfterViewInit } from '@angular/core'
+import { ElectronService } from 'src/app/core/services/electron.service'
+import { SettingsService } from 'src/app/core/services/settings.service'
+
+@Component({
+ selector: 'app-settings',
+ templateUrl: './settings.component.html',
+ styleUrls: ['./settings.component.scss']
+})
+export class SettingsComponent implements OnInit, AfterViewInit {
+ @ViewChild('themeDropdown', { static: true }) themeDropdown: ElementRef
+
+ cacheSize = 'Calculating...'
+
+ constructor(private settingsService: SettingsService, private electronService: ElectronService) { }
+
+ async ngOnInit() {
+ const cacheSize = await this.settingsService.getCacheSize()
+ this.cacheSize = Math.round(cacheSize / 1000000) + ' MB'
+ }
+
+ ngAfterViewInit() {
+ $(this.themeDropdown.nativeElement).dropdown({
+ onChange: (_value: string, text: string) => {
+ this.settingsService.theme = text
+ }
+ })
+ }
+
+ async clearCache() {
+ this.cacheSize = 'Please wait...'
+ await this.settingsService.clearCache()
+ this.cacheSize = 'Cleared!'
+ }
+
+ async getLibraryDirectory() {
+ const result = await this.electronService.showOpenDialog({
+ title: 'Choose library folder',
+ buttonLabel: 'This is where my charts are!',
+ defaultPath: this.settingsService.libraryDirectory || '',
+ properties: ['openDirectory']
+ })
+
+ if (result.canceled == false) {
+ this.settingsService.libraryDirectory = result.filePaths[0]
+ }
+ }
+
+ async openLibraryDirectory() {
+ this.electronService.openFolder(this.settingsService.libraryDirectory)
+ }
+}
\ No newline at end of file
diff --git a/src/app/components/toolbar/toolbar.component.ts b/src/app/components/toolbar/toolbar.component.ts
index d2f492e..68ac604 100644
--- a/src/app/components/toolbar/toolbar.component.ts
+++ b/src/app/components/toolbar/toolbar.component.ts
@@ -11,14 +11,14 @@ export class ToolbarComponent {
constructor(private electronService: ElectronService) { }
minimize() {
- this.electronService.remote.getCurrentWindow().minimize()
+ this.electronService.currentWindow.minimize()
}
maximize() {
- this.electronService.remote.getCurrentWindow().maximize()
+ this.electronService.currentWindow.maximize()
}
close() {
- this.electronService.remote.app.quit()
+ this.electronService.quit()
}
}
\ No newline at end of file
diff --git a/src/app/core/services/electron.service.ts b/src/app/core/services/electron.service.ts
index a1f3f31..1054fc0 100644
--- a/src/app/core/services/electron.service.ts
+++ b/src/app/core/services/electron.service.ts
@@ -2,40 +2,29 @@ import { Injectable } from '@angular/core'
// If you import a module but never use any of the imported values other than as TypeScript types,
// the resulting javascript file will look as if you never imported the module at all.
-import { ipcRenderer, webFrame, remote } from 'electron'
-import * as childProcess from 'child_process'
-import * as fs from 'fs'
-import * as util from 'util'
+import * as electron from 'electron'
import { IPCInvokeEvents, IPCEmitEvents } from '../../../electron/shared/IPCHandler'
@Injectable({
providedIn: 'root'
})
export class ElectronService {
- ipcRenderer: typeof ipcRenderer
- webFrame: typeof webFrame
- remote: typeof remote
- childProcess: typeof childProcess
- fs: typeof fs
- util: typeof util
+ electron: typeof electron
get isElectron() {
return !!(window && window.process && window.process.type)
}
constructor() {
- // Conditional imports
if (this.isElectron) {
- this.ipcRenderer = window.require('electron').ipcRenderer
- this.webFrame = window.require('electron').webFrame
- this.remote = window.require('electron').remote
-
- this.childProcess = window.require('child_process')
- this.fs = window.require('fs')
- this.util = window.require('util')
+ this.electron = window.require('electron')
}
}
+ get currentWindow() {
+ return this.electron.remote.getCurrentWindow()
+ }
+
/**
* Calls an async function in the main process.
* @param event The name of the IPC event to invoke.
@@ -43,7 +32,7 @@ export class ElectronService {
* @returns A promise that resolves to the output data.
*/
async invoke(event: E, data: IPCInvokeEvents[E]['input']) {
- return this.ipcRenderer.invoke(event, data) as Promise
+ return this.electron.ipcRenderer.invoke(event, data) as Promise
}
/**
@@ -52,7 +41,7 @@ export class ElectronService {
* @param data The data object to send across IPC.
*/
sendIPC(event: E, data: IPCEmitEvents[E]) {
- this.ipcRenderer.send(event, data)
+ this.electron.ipcRenderer.send(event, data)
}
/**
@@ -61,8 +50,28 @@ export class ElectronService {
* @param callback The data object to receive across IPC.
*/
receiveIPC(event: E, callback: (result: IPCEmitEvents[E]) => void) {
- this.ipcRenderer.on(event, (_event, ...results) => {
+ this.electron.ipcRenderer.on(event, (_event, ...results) => {
callback(results[0])
})
}
+
+ quit() {
+ this.electron.remote.app.quit()
+ }
+
+ openFolder(filepath: string) {
+ this.electron.shell.openItem(filepath)
+ }
+
+ showFolder(filepath: string) {
+ this.electron.shell.showItemInFolder(filepath)
+ }
+
+ showOpenDialog(options: Electron.OpenDialogOptions) {
+ return this.electron.remote.dialog.showOpenDialog(this.currentWindow, options)
+ }
+
+ get defaultSession() {
+ return this.electron.remote.session.defaultSession
+ }
}
\ No newline at end of file
diff --git a/src/app/core/services/settings.service.ts b/src/app/core/services/settings.service.ts
index 399cd1b..3feea91 100644
--- a/src/app/core/services/settings.service.ts
+++ b/src/app/core/services/settings.service.ts
@@ -6,8 +6,10 @@ import { Settings } from 'src/electron/shared/Settings'
providedIn: 'root'
})
export class SettingsService {
+ readonly builtinThemes = ['Default', 'Dark']
private settings: Settings
+ private currentThemeLink: HTMLLinkElement
constructor(private electronService: ElectronService) { }
@@ -17,4 +19,47 @@ export class SettingsService {
}
return this.settings
}
+
+ saveSettings() {
+ if (this.settings != undefined) {
+ this.electronService.sendIPC('update-settings', this.settings)
+ }
+ }
+
+ changeTheme(theme: string) {
+ if (this.currentThemeLink != undefined) this.currentThemeLink.remove()
+ if (theme == 'Default') { return }
+
+ const link = document.createElement('link')
+ link.type = 'text/css'
+ link.rel = 'stylesheet'
+ link.href = `assets/themes/${theme}.css`
+ this.currentThemeLink = document.head.appendChild(link)
+ }
+
+ async getCacheSize() {
+ return this.electronService.defaultSession.getCacheSize()
+ }
+
+ async clearCache() {
+ this.saveSettings()
+ return this.electronService.defaultSession.clearCache()
+ }
+
+ get libraryDirectory() {
+ return this.settings == undefined ? '' : this.settings.libraryPath
+ }
+ set libraryDirectory(newValue: string) {
+ this.settings.libraryPath = newValue
+ this.saveSettings()
+ }
+
+ get theme() {
+ return this.settings == undefined ? '' : this.settings.theme
+ }
+ set theme(newValue: string) {
+ this.settings.theme = newValue
+ this.changeTheme(newValue)
+ this.saveSettings()
+ }
}
\ No newline at end of file
diff --git a/src/electron/ipc/InitSettingsHandler.ipc.ts b/src/electron/ipc/InitSettingsHandler.ipc.ts
index 2a0dcee..59b517b 100644
--- a/src/electron/ipc/InitSettingsHandler.ipc.ts
+++ b/src/electron/ipc/InitSettingsHandler.ipc.ts
@@ -1,13 +1,13 @@
-import { exists as _exists, mkdir as _mkdir, readFile as _readFile, writeFile as _writeFile } from 'fs'
+import { exists as _exists, mkdir as _mkdir, readFile as _readFile } from 'fs'
import { dataPath, tempPath, themesPath, settingsPath } from '../shared/Paths'
import { promisify } from 'util'
import { IPCInvokeHandler } from '../shared/IPCHandler'
import { defaultSettings, Settings } from '../shared/Settings'
+import SaveSettingsHandler from './SaveSettingsHandler.ipc'
const exists = promisify(_exists)
const mkdir = promisify(_mkdir)
const readFile = promisify(_readFile)
-const writeFile = promisify(_writeFile)
export default class InitSettingsHandler implements IPCInvokeHandler<'init-settings'> {
event: 'init-settings' = 'init-settings'
@@ -38,8 +38,7 @@ export default class InitSettingsHandler implements IPCInvokeHandler<'init-setti
if (await exists(settingsPath)) {
return JSON.parse(await readFile(settingsPath, 'utf8'))
} else {
- const newSettings = JSON.stringify(defaultSettings, undefined, 2)
- await writeFile(settingsPath, newSettings, 'utf8')
+ await SaveSettingsHandler.saveSettings(defaultSettings)
return defaultSettings
}
} catch (e) {
diff --git a/src/electron/ipc/OpenFolderHandler.ipc.ts b/src/electron/ipc/OpenFolderHandler.ipc.ts
deleted file mode 100644
index ee205e5..0000000
--- a/src/electron/ipc/OpenFolderHandler.ipc.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { IPCEmitHandler } from '../shared/IPCHandler'
-import { shell } from 'electron'
-
-export default class OpenFolderHandler implements IPCEmitHandler<'open-folder'> {
- event: 'open-folder' = 'open-folder'
-
- async handler(filepath: string) {
- shell.showItemInFolder(filepath)
- }
-}
\ No newline at end of file
diff --git a/src/electron/ipc/SaveSettingsHandler.ipc.ts b/src/electron/ipc/SaveSettingsHandler.ipc.ts
new file mode 100644
index 0000000..f09c0af
--- /dev/null
+++ b/src/electron/ipc/SaveSettingsHandler.ipc.ts
@@ -0,0 +1,20 @@
+import { writeFile as _writeFile } from 'fs'
+import { IPCEmitHandler } from '../shared/IPCHandler'
+import { Settings } from '../shared/Settings'
+import { promisify } from 'util'
+import { settingsPath } from '../shared/Paths'
+
+const writeFile = promisify(_writeFile)
+
+export default class SaveSettingsHandler implements IPCEmitHandler<'update-settings'> {
+ event: 'update-settings' = 'update-settings'
+
+ handler(settings: Settings) {
+ SaveSettingsHandler.saveSettings(settings)
+ }
+
+ static async saveSettings(settings: Settings) {
+ const settingsJSON = JSON.stringify(settings, undefined, 2)
+ await writeFile(settingsPath, settingsJSON, 'utf8')
+ }
+}
\ No newline at end of file
diff --git a/src/electron/shared/IPCHandler.ts b/src/electron/shared/IPCHandler.ts
index ccc3965..d55ec7b 100644
--- a/src/electron/shared/IPCHandler.ts
+++ b/src/electron/shared/IPCHandler.ts
@@ -8,7 +8,7 @@ import { DownloadHandler } from '../ipc/download/DownloadHandler'
import { Settings } from './Settings'
import InitSettingsHandler from '../ipc/InitSettingsHandler.ipc'
import BatchSongDetailsHandler from '../ipc/BatchSongDetailsHandler.ipc'
-import OpenFolderHandler from '../ipc/OpenFolderHandler.ipc'
+import SaveSettingsHandler from '../ipc/SaveSettingsHandler.ipc'
/**
* To add a new IPC listener:
@@ -59,14 +59,14 @@ export interface IPCInvokeHandler {
export function getIPCEmitHandlers(): IPCEmitHandler[]{
return [
new DownloadHandler(),
- new OpenFolderHandler()
+ new SaveSettingsHandler()
]
}
export type IPCEmitEvents = {
'download': Download
'download-updated': DownloadProgress
- 'open-folder': string
+ 'update-settings': Settings
}
export interface IPCEmitHandler {