Browse Source

A couple of QOL Improvements (#398)

* Add frequent "quick save" system

* Implement system for saving window dimensions

Adds a toggle for retaining window dimensions. Saves dimensions when exiting application

* Add user-defined application dimensions

User can now choose a default width and height, which are used when the Retain Window Size setting is disabled.

The minimum window size is 600 x 600

* Update App.test.tsx

* Add window position saving

* Update main.dev.js

Fix misplaced template string

* Update PlayerConfig.tsx

Updated description to reflect new functionality

* Update LookAndFeelConfig.tsx

Fixes a silly little mistake

* Fix conflicts

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
master
fishnxt 2 years ago
committed by GitHub
parent
commit
b298fe4788
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      src/__tests__/App.test.tsx
  2. 7
      src/components/player/Player.tsx
  3. 80
      src/components/settings/ConfigPanels/LookAndFeelConfig.tsx
  4. 9
      src/components/settings/ConfigPanels/PlayerConfig.tsx
  5. 30
      src/components/shared/setDefaultSettings.ts
  6. 42
      src/main.dev.js
  7. 31
      src/redux/miscSlice.ts
  8. 5
      src/shared/mockSettings.ts

5
src/__tests__/App.test.tsx

@ -102,6 +102,11 @@ const miscState: General = {
modalPages: [],
isProcessingPlaylist: [],
dynamicBackground: false,
retainWindowSize: false,
savedWindowSize: [1024, 728],
savedWindowPos: [50, 50],
defaultWindowWidth: 1024,
defaultWindowHeight: 728,
highlightOnRowHover: true,
imageCachePath: '',
songCachePath: '',

7
src/components/player/Player.tsx

@ -643,6 +643,13 @@ const Player = ({ currentEntryList, muted, children }: any, ref: any) => {
ipcRenderer.send('current-song', playQueue.current);
setMetadata(playQueue.current);
// Save the queue 2.5 seconds after fade length
if (settings.get('resume')) {
setTimeout(() => {
ipcRenderer.send('quicksave');
}, playQueue.fadeDuration * 1000 + 2500);
}
if (config.player.systemNotifications && currentSong) {
// eslint-disable-next-line no-new
new Notification(currentSong.title, {

80
src/components/settings/ConfigPanels/LookAndFeelConfig.tsx

@ -24,7 +24,14 @@ import ListViewConfig from './ListViewConfig';
import Fonts from '../Fonts';
import { ALBUM_SORT_TYPES } from '../../library/AlbumList';
import { useAppDispatch, useAppSelector } from '../../../redux/hooks';
import { setTheme, setDynamicBackground, setMiscSetting } from '../../../redux/miscSlice';
import {
setTheme,
setDynamicBackground,
setMiscSetting,
setRetainWindowSize,
setDefaultWindowWidth,
setDefaultWindowHeight,
} from '../../../redux/miscSlice';
import {
songColumnPicker,
songColumnListAuto,
@ -290,6 +297,15 @@ export const ThemeConfigPanel = ({ bordered }: any) => {
const [dynamicBackgroundChk, setDynamicBackgroundChk] = useState(
Boolean(settings.get('dynamicBackground'))
);
const [retainWindowSizeChk, setRetainWindowSizeChk] = useState(
Boolean(settings.get('retainWindowSize'))
);
const [defaultWindowHeight, setDefaultWindowHeightValue] = useState(
Number(settings.get('defaultWindowHeight'))
);
const [defaultWindowWidth, setDefaultWindowWidthValue] = useState(
Number(settings.get('defaultWindowWidth'))
);
const [selectedTheme, setSelectedTheme] = useState(String(settings.get('theme')));
const languagePickerContainerRef = useRef(null);
@ -482,6 +498,68 @@ export const ThemeConfigPanel = ({ bordered }: any) => {
}
/>
<ConfigOption
name={t('Retain Window Size')}
description={t(
'Retains the size and position of the application window. Size is only saved when the program is exited properly!'
)}
option={
<StyledToggle
defaultChecked={retainWindowSizeChk}
checked={retainWindowSizeChk}
onChange={(e: boolean) => {
settings.set('retainWindowSize', e);
dispatch(setRetainWindowSize(e));
setRetainWindowSizeChk(e);
}}
/>
}
/>
<ConfigOption
name={t('Default Window Width')}
description={t(
'The default width to use when Retain Window Size is disabled. Default: 1024'
)}
option={
<StyledInputNumber
defaultValue={defaultWindowWidth}
value={defaultWindowWidth}
step={1}
min={768}
max={7680}
width={125}
onChange={(e: number) => {
settings.set('defaultWindowWidth', Number(e));
dispatch(setDefaultWindowWidth(Number(e)));
setDefaultWindowWidthValue(Number(e));
}}
/>
}
/>
<ConfigOption
name={t('Default Window Height')}
description={t(
'The default height to use when Retain Window Size is disabled. Default: 728'
)}
option={
<StyledInputNumber
defaultValue={defaultWindowHeight}
value={defaultWindowHeight}
step={1}
min={600}
max={7680}
width={125}
onChange={(e: number) => {
settings.set('defaultWindowHeight', Number(e));
dispatch(setDefaultWindowHeight(Number(e)));
setDefaultWindowHeightValue(Number(e));
}}
/>
}
/>
<Divider />
<ConfigOption

9
src/components/settings/ConfigPanels/PlayerConfig.tsx

@ -165,14 +165,7 @@ const PlayerConfig = ({ bordered }: any) => {
/>
<ConfigOption
name={t('Resume Playback')}
description={
<Trans>
Remember play queue on startup. The current Now Playing queue will be saved on exiting,
and will be restored when you reopen Sonixd. Be warned that you should manually close
Sonixd for the queue to be saved. An improper shutdown (such as the app closing during a
shutdown or force quitting) may result in history not being saved.
</Trans>
}
description={t('Resumes the player queue on startup.')}
option={
<StyledToggle
defaultChecked={resume}

30
src/components/shared/setDefaultSettings.ts

@ -125,6 +125,11 @@ interface Settings {
themes: any[];
themesDefault: any[];
infoMode?: boolean;
retainWindowSize: boolean;
defaultWindowWidth: number;
defaultWindowHeight: number;
savedWindowSize: number[];
savedWindowPos: number[];
}
const DEFAULT_SETTINGS: Settings = {
@ -241,6 +246,11 @@ const DEFAULT_SETTINGS: Settings = {
miniListRowHeight: '40',
genreListFontSize: '14',
genreListRowHeight: '50',
retainWindowSize: false,
defaultWindowWidth: 1280,
defaultWindowHeight: 720,
savedWindowSize: [1280, 720],
savedWindowPos: [50, 50],
themes: [],
themesDefault: [
{
@ -1722,6 +1732,26 @@ export const setDefaultSettings = (force: boolean) => {
settings.set('titleBarStyle', isMacOS() ? 'mac' : 'windows');
}
if (force || !settings.has('retainWindowSize')) {
settings.set('retainWindowSize', false);
}
if (force || !settings.has('defaultWindowWidth')) {
settings.set('defaultWindowWidth', 1280);
}
if (force || !settings.has('defaultWindowHeight')) {
settings.set('defaultWindowHeight', 720);
}
if (force || !settings.has('savedWindowSize')) {
settings.set('savedWindowSize', [1280, 720]);
}
if (force || !settings.has('savedWindowPos')) {
settings.set('savedWindowPos', [50, 50]);
}
if (force || !settings.has('musicListColumns')) {
settings.set('musicListColumns', [
{

42
src/main.dev.js

@ -100,6 +100,10 @@ const previousTrack = () => {
mainWindow.webContents.send('player-prev-track');
};
const quickSave = () => {
mainWindow.webContents.send('save-queue-state', app.getPath('userData'));
};
if (isLinux()) {
const mprisPlayer = Player({
name: 'Sonixd',
@ -279,7 +283,7 @@ if (isWindows() && isWindows10()) {
const Controls = windowsMediaPlayback.BackgroundMediaPlayer.current.systemMediaTransportControls;
if (settings.getSync('systemMediaTransportControls')) {
if (settings.get('systemMediaTransportControls')) {
Controls.isEnabled = true;
} else {
Controls.isEnabled = false;
@ -411,10 +415,26 @@ const createWindow = async () => {
await installExtensions();
}
let windowDimensions = [];
let windowPos = [];
let isCentered = true;
// If retained window size is enabled, use saved dimensions and position. Otherwise, use defined defaults
if (settings.get('retainWindowSize')) {
windowDimensions = settings.get('savedWindowSize');
windowPos = settings.get('savedWindowPos');
isCentered = false;
} else {
windowDimensions = [settings.get('defaultWindowWidth'), settings.get('defaultWindowHeight')];
}
mainWindow = new BrowserWindow({
show: false,
width: 1024,
height: 728,
width: windowDimensions[0],
height: windowDimensions[1],
center: isCentered,
x: windowPos[0],
y: windowPos[1],
icon: getAssetPath('icon.png'),
webPreferences: {
nodeIntegration: true,
@ -461,6 +481,10 @@ const createWindow = async () => {
});
}
ipcMain.on('quicksave', () => {
quickSave();
});
ipcMain.on('enableGlobalHotkeys', () => {
electronLocalshortcut.unregisterAll(mainWindow);
@ -532,6 +556,12 @@ const createWindow = async () => {
}
});
mainWindow.on('moved', () => {
if (settings.get('retainWindowSize')) {
settings.set('savedWindowPos', mainWindow.getPosition());
}
});
mainWindow.on('close', (event) => {
if (!exitFromTray && !forceQuit && store.getState().config.window.exitToTray) {
exitFromTray = true;
@ -539,6 +569,12 @@ const createWindow = async () => {
mainWindow.hide();
}
// If retain window size is enabled, save the dimensions
if (settings.get('retainWindowSize')) {
const curSize = mainWindow.getSize();
settings.set('savedWindowSize', [curSize[0], curSize[1]]);
}
// If we have enabled saving the queue, we need to defer closing the main window until it has finished saving.
if (!saved && settings.get('resume')) {
event.preventDefault();

31
src/redux/miscSlice.ts

@ -52,6 +52,11 @@ export interface General {
isProcessingPlaylist: string[];
contextMenu: ContextMenu;
dynamicBackground: boolean;
retainWindowSize: boolean;
savedWindowSize: number[];
savedWindowPos: number[];
defaultWindowWidth: number;
defaultWindowHeight: number;
highlightOnRowHover: boolean;
imageCachePath: string;
songCachePath: string;
@ -75,6 +80,11 @@ const initialState: General = {
show: false,
},
dynamicBackground: Boolean(parsedSettings.dynamicBackground),
retainWindowSize: Boolean(parsedSettings.retainWindowSize),
savedWindowSize: Array(parsedSettings.savedWindowSize),
savedWindowPos: Array(parsedSettings.savedWindowPos),
defaultWindowWidth: Number(parsedSettings.defaultWindowWidth),
defaultWindowHeight: Number(parsedSettings.defaultWindowHeight),
highlightOnRowHover: Boolean(parsedSettings.highlightOnRowHover),
imageCachePath: getImageCachePath(),
songCachePath: getSongCachePath(),
@ -90,6 +100,22 @@ const miscSlice = createSlice({
state.dynamicBackground = action.payload;
},
setRetainWindowSize: (state, action: PayloadAction<boolean>) => {
state.retainWindowSize = action.payload;
},
savedWindowSize: (state, action: PayloadAction<Array<number>>) => {
state.savedWindowSize = action.payload;
},
savedWindowPos: (state, action: PayloadAction<Array<number>>) => {
state.savedWindowPos = action.payload;
},
setDefaultWindowWidth: (state, action: PayloadAction<number>) => {
state.defaultWindowWidth = action.payload;
},
setDefaultWindowHeight: (state, action: PayloadAction<number>) => {
state.defaultWindowHeight = action.payload;
},
setMiscSetting: (state, action: PayloadAction<{ setting: string; value: any }>) => {
switch (action.payload.setting) {
case 'imageCachePath':
@ -196,7 +222,12 @@ export const {
removeProcessingPlaylist,
setContextMenu,
setDynamicBackground,
savedWindowSize,
savedWindowPos,
setDefaultWindowWidth,
setDefaultWindowHeight,
setMiscSetting,
setImgModal,
setRetainWindowSize,
} = miscSlice.actions;
export default miscSlice.reducer;

5
src/shared/mockSettings.ts

@ -332,6 +332,11 @@ export const mockSettings = {
server: 'http://192.168.14.11:4040',
serverBase64: 'aHR0cDovLzE5Mi4xNjguMTQuMTE6NDA0MA==',
dynamicBackground: false,
retainWindowSize: false,
savedWindowSize: [1024, 728],
savedWindowPos: [50, 50],
defaultWindowWidth: 1024,
defaultWindowHeight: 728,
minimizeToTray: true,
exitToTray: true,
windowPosition: { x: 0, y: 0, width: 960, height: 1560 },

Loading…
Cancel
Save