Initial settings, download UI, various bugfixes

This commit is contained in:
Geomitron
2020-02-09 21:15:40 -05:00
parent 89948b118b
commit de39ad4f1e
33 changed files with 1034 additions and 110 deletions

View File

@@ -3,6 +3,10 @@ import { VersionResult, AlbumArtResult } from './interfaces/songDetails.interfac
import SearchHandler from '../ipc/SearchHandler.ipc'
import SongDetailsHandler from '../ipc/SongDetailsHandler.ipc'
import AlbumArtHandler from '../ipc/AlbumArtHandler.ipc'
import { Download, NewDownload } from './interfaces/download.interface'
import { AddDownloadHandler } from '../ipc/download/AddDownloadHandler'
import { Settings } from './Settings'
import InitSettingsHandler from '../ipc/InitSettingsHandler.ipc'
/**
* To add a new IPC listener:
@@ -12,30 +16,51 @@ import AlbumArtHandler from '../ipc/AlbumArtHandler.ipc'
* 4.) Add the class to getIPCHandlers
*/
export function getIPCHandlers(): IPCHandler<keyof IPCEvents>[] {
export function getIPCInvokeHandlers(): IPCInvokeHandler<keyof IPCInvokeEvents>[] {
return [
new InitSettingsHandler(),
new SearchHandler(),
new SongDetailsHandler(),
new AlbumArtHandler()
]
}
export type IPCEvents = {
['song-search']: {
export type IPCInvokeEvents = {
'init-settings': {
input: undefined
output: Settings
}
'song-search': {
input: SongSearch
output: SongResult[]
}
['album-art']: {
'album-art': {
input: SongResult['id']
output: AlbumArtResult
}
['song-details']: {
'song-details': {
input: SongResult['id']
output: VersionResult[]
}
}
export interface IPCHandler<E extends keyof IPCEvents> {
export interface IPCInvokeHandler<E extends keyof IPCInvokeEvents> {
event: E
handler(data: IPCEvents[E]['input']): Promise<IPCEvents[E]['output']> | IPCEvents[E]['output']
handler(data: IPCInvokeEvents[E]['input']): Promise<IPCInvokeEvents[E]['output']> | IPCInvokeEvents[E]['output']
}
export function getIPCEmitHandlers(): IPCEmitHandler<keyof IPCEmitEvents>[]{
return [
new AddDownloadHandler()
]
}
export type IPCEmitEvents = {
'add-download': NewDownload
'download-updated': Download
}
export interface IPCEmitHandler<E extends keyof IPCEmitEvents> {
event: E
handler(data: IPCEmitEvents[E]): void
}

View File

@@ -0,0 +1,9 @@
import * as path from 'path'
import { app } from 'electron'
// Data paths
export const dataPath = path.join(app.getPath('userData'), 'bridge_data')
export const libraryPath = path.join(dataPath, 'library.db')
export const settingsPath = path.join(dataPath, 'settings.json')
export const tempPath = path.join(dataPath, 'temp')
export const themesPath = path.join(dataPath, 'themes')

View File

@@ -1,19 +1,11 @@
export class Settings {
export interface Settings {
rateLimitDelay: number // Number of seconds to wait between each file download from Google servers
theme: string // The name of the currently enabled UI theme
libraryPath: string // The path to the user's library
}
// Singleton
private constructor() { }
private static settings: Settings
static async getInstance() {
if (this.settings == undefined) {
this.settings = new Settings()
}
await this.settings.initSettings()
return this.settings
}
songsFolderPath: string
private async initSettings() {
// TODO: load settings from settings file or set defaults
}
export const defaultSettings: Settings = {
rateLimitDelay: 31,
theme: 'Default',
libraryPath: 'C:/Users/bouviejs/Desktop/Bridge Notes/TestLibrary'
}

View File

@@ -1,7 +1,4 @@
import { basename } from 'path'
import { Settings } from './Settings'
const settings = Settings.getInstance()
let sanitize = require('sanitize-filename')
/**
* @param absoluteFilepath The absolute filepath to a folder
@@ -12,4 +9,48 @@ export function getRelativeFilepath(absoluteFilepath: string) {
// loads everything and connects to the database, etc...)
// return basename(scanSettings.songsFolderPath) + absoluteFilepath.substring(scanSettings.songsFolderPath.length)
return absoluteFilepath
}
/**
* @returns A random UUID
*/
export function generateUUID() { // Public Domain/MIT
var d = new Date().getTime()//Timestamp
var d2 = Date.now() // Time in microseconds since page-load or 0 if unsupported
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16//random number between 0 and 16
if (d > 0) {//Use timestamp until depleted
r = (d + r) % 16 | 0
d = Math.floor(d / 16)
} else {//Use microseconds since page-load if supported
r = (d2 + r) % 16 | 0
d2 = Math.floor(d2 / 16)
}
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16)
})
}
/**
* Sanitizes a filename of any characters that cannot be part of a windows filename.
* @param filename The name of the file to sanitize.
*/
export function sanitizeFilename(filename: string): string {
const newName = sanitize(filename, {
replacement: ((invalidChar: string) => {
switch (invalidChar) {
case '/': return '-'
case '\\': return '-'
case '"': return "'"
default: return '_' //TODO: add more cases for replacing invalid characters
}
})
})
return newName
}
/**
* Converts <val> from the range (<fromA>, <fromB>) to the range (<toA>, <toB>).
*/
export function interpolate(val: number, fromA: number, fromB: number, toA: number, toB: number) {
return ((val - fromA) / (fromB - fromA)) * (toB - toA) + toA
}

View File

@@ -0,0 +1,33 @@
/**
* Represents the download of a single chart
*/
export interface Download {
versionID: number
title: string
header: string
description: string
percent: number
//TODO: figure out how to handle user clicking "retry"
}
export interface NewDownload {
versionID: number
avTagName: string
artist: string
charter: string
links: { [type: string]: string }
}
export enum DownloadState {
wait, // Waiting for Google rate limit...
request, // [song.ini] Sending request...
warning, // Warning! [song.ini] has been modified recently and may not match how it was displayed in search results. Download anyway?
download, // [song.ini] Downloading: 25%
extract, // [archive.zip] Extracting: 44%
transfer, // Copying files to library...
complete // Complete
}
// Try again button appears after an error: restarts the stage that failed

View File

@@ -0,0 +1 @@
declare module 'node-7z'