Add native titlebar & fix app restart for AppImages
This commit is contained in:
parent
d055ae89e0
commit
a878875f83
12 changed files with 82 additions and 23 deletions
|
@ -183,8 +183,12 @@ const createWindow = async () => {
|
||||||
await installExtensions();
|
await installExtensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nativeFrame = store.get('window_window_bar_style') === 'linux';
|
||||||
|
store.set('window_has_frame', nativeFrame);
|
||||||
|
|
||||||
mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
frame: false,
|
autoHideMenuBar: nativeFrame,
|
||||||
|
frame: nativeFrame,
|
||||||
height: 900,
|
height: 900,
|
||||||
icon: getAssetPath('icon.png'),
|
icon: getAssetPath('icon.png'),
|
||||||
minHeight: 600,
|
minHeight: 600,
|
||||||
|
@ -229,8 +233,18 @@ const createWindow = async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('app-restart', () => {
|
ipcMain.on('app-restart', () => {
|
||||||
app.relaunch();
|
// Fix for .AppImage
|
||||||
app.exit(0);
|
if (process.env.APPIMAGE) {
|
||||||
|
app.exit();
|
||||||
|
app.relaunch({
|
||||||
|
args: process.argv.slice(1).concat(['--appimage-extract-and-run']),
|
||||||
|
execPath: process.env.APPIMAGE,
|
||||||
|
});
|
||||||
|
app.exit(0);
|
||||||
|
} else {
|
||||||
|
app.relaunch();
|
||||||
|
app.exit(0);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('global-media-keys-enable', () => {
|
ipcMain.on('global-media-keys-enable', () => {
|
||||||
|
|
|
@ -34,12 +34,16 @@ const StyledNativeScrollArea = styled.div<{ scrollBarOffset?: string; windowBarS
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
&::-webkit-scrollbar-track {
|
||||||
margin-top: ${(props) =>
|
margin-top: ${(props) =>
|
||||||
props.windowBarStyle !== Platform.WEB ? '0px' : props.scrollBarOffset || '65px'};
|
props.windowBarStyle === Platform.WINDOWS || props.windowBarStyle === Platform.MACOS
|
||||||
|
? '0px'
|
||||||
|
: props.scrollBarOffset || '65px'};
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
&::-webkit-scrollbar-thumb {
|
||||||
margin-top: ${(props) =>
|
margin-top: ${(props) =>
|
||||||
props.windowBarStyle !== Platform.WEB ? '0px' : props.scrollBarOffset || '65px'};
|
props.windowBarStyle === Platform.WINDOWS || props.windowBarStyle === Platform.MACOS
|
||||||
|
? '0px'
|
||||||
|
: props.scrollBarOffset || '65px'};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,10 @@ export const useFixedTableHeader = () => {
|
||||||
const { windowBarStyle } = useWindowSettings();
|
const { windowBarStyle } = useWindowSettings();
|
||||||
|
|
||||||
const isNotPastTableIntersection = useInView(intersectRef, {
|
const isNotPastTableIntersection = useInView(intersectRef, {
|
||||||
margin: windowBarStyle === Platform.WEB ? '-68px 0px 0px 0px' : '-98px 0px 0px 0px',
|
margin:
|
||||||
|
windowBarStyle === Platform.WEB || windowBarStyle === Platform.LINUX
|
||||||
|
? '-68px 0px 0px 0px'
|
||||||
|
: '-98px 0px 0px 0px',
|
||||||
});
|
});
|
||||||
|
|
||||||
const tableInView = useInView(tableContainerRef, {
|
const tableInView = useInView(tableContainerRef, {
|
||||||
|
@ -21,13 +24,13 @@ export const useFixedTableHeader = () => {
|
||||||
const root = document.querySelector('main .ag-root');
|
const root = document.querySelector('main .ag-root');
|
||||||
|
|
||||||
if (isNotPastTableIntersection || !tableInView) {
|
if (isNotPastTableIntersection || !tableInView) {
|
||||||
if (windowBarStyle !== Platform.WEB) {
|
if (windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS) {
|
||||||
header?.classList.remove('window-frame');
|
header?.classList.remove('window-frame');
|
||||||
}
|
}
|
||||||
header?.classList.remove('ag-header-fixed');
|
header?.classList.remove('ag-header-fixed');
|
||||||
root?.classList.remove('ag-header-fixed-margin');
|
root?.classList.remove('ag-header-fixed-margin');
|
||||||
} else {
|
} else {
|
||||||
if (windowBarStyle !== Platform.WEB) {
|
if (windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS) {
|
||||||
header?.classList.add('window-frame');
|
header?.classList.add('window-frame');
|
||||||
}
|
}
|
||||||
header?.classList.add('ag-header-fixed');
|
header?.classList.add('ag-header-fixed');
|
||||||
|
|
|
@ -138,7 +138,9 @@ const HomeRoute = () => {
|
||||||
<Box
|
<Box
|
||||||
ref={cq.ref}
|
ref={cq.ref}
|
||||||
mb="5rem"
|
mb="5rem"
|
||||||
pt={windowBarStyle === Platform.WEB ? '5rem' : '3rem'}
|
pt={
|
||||||
|
windowBarStyle === Platform.WEB || windowBarStyle === Platform.LINUX ? '5rem' : '3rem'
|
||||||
|
}
|
||||||
px="2rem"
|
px="2rem"
|
||||||
>
|
>
|
||||||
<Stack spacing="lg">
|
<Stack spacing="lg">
|
||||||
|
|
|
@ -13,16 +13,17 @@ export const SidebarPlayQueue = () => {
|
||||||
const queueRef = useRef<{ grid: AgGridReactType<Song> } | null>(null);
|
const queueRef = useRef<{ grid: AgGridReactType<Song> } | null>(null);
|
||||||
const { windowBarStyle } = useWindowSettings();
|
const { windowBarStyle } = useWindowSettings();
|
||||||
|
|
||||||
|
const webOrNative = windowBarStyle === Platform.WEB || windowBarStyle === Platform.LINUX;
|
||||||
return (
|
return (
|
||||||
<VirtualGridContainer>
|
<VirtualGridContainer>
|
||||||
{windowBarStyle === Platform.WEB && (
|
{webOrNative && (
|
||||||
<Stack mr={windowBarStyle === Platform.WEB ? '130px' : undefined}>
|
<Stack mr={webOrNative ? '130px' : undefined}>
|
||||||
<PageHeader backgroundColor="var(--titlebar-bg)" />
|
<PageHeader backgroundColor="var(--titlebar-bg)" />
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
<Paper
|
<Paper
|
||||||
display={windowBarStyle !== Platform.WEB ? 'flex' : undefined}
|
display={!webOrNative ? 'flex' : undefined}
|
||||||
h={windowBarStyle !== Platform.WEB ? '65px' : undefined}
|
h={!webOrNative ? '65px' : undefined}
|
||||||
>
|
>
|
||||||
<PlayQueueListControls
|
<PlayQueueListControls
|
||||||
tableRef={queueRef}
|
tableRef={queueRef}
|
||||||
|
|
|
@ -129,7 +129,10 @@ const containerVariants: Variants = {
|
||||||
closed: (custom) => {
|
closed: (custom) => {
|
||||||
const { windowBarStyle } = custom;
|
const { windowBarStyle } = custom;
|
||||||
return {
|
return {
|
||||||
height: windowBarStyle !== Platform.WEB ? 'calc(100vh - 120px)' : 'calc(100vh - 90px)',
|
height:
|
||||||
|
windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS
|
||||||
|
? 'calc(100vh - 120px)'
|
||||||
|
: 'calc(100vh - 90px)',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '100vh',
|
top: '100vh',
|
||||||
transition: {
|
transition: {
|
||||||
|
@ -144,7 +147,10 @@ const containerVariants: Variants = {
|
||||||
const { dynamicBackground, background, windowBarStyle } = custom;
|
const { dynamicBackground, background, windowBarStyle } = custom;
|
||||||
return {
|
return {
|
||||||
background: dynamicBackground ? background : 'var(--main-bg)',
|
background: dynamicBackground ? background : 'var(--main-bg)',
|
||||||
height: windowBarStyle !== Platform.WEB ? 'calc(100vh - 120px)' : 'calc(100vh - 90px)',
|
height:
|
||||||
|
windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS
|
||||||
|
? 'calc(100vh - 120px)'
|
||||||
|
: 'calc(100vh - 90px)',
|
||||||
left: 0,
|
left: 0,
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
|
|
|
@ -5,12 +5,13 @@ import {
|
||||||
SettingsSection,
|
SettingsSection,
|
||||||
SettingOption,
|
SettingOption,
|
||||||
} from '/@/renderer/features/settings/components/settings-section';
|
} from '/@/renderer/features/settings/components/settings-section';
|
||||||
import { Select, Switch } from '/@/renderer/components';
|
import { Select, Switch, toast } from '/@/renderer/components';
|
||||||
|
|
||||||
const WINDOW_BAR_OPTIONS = [
|
const WINDOW_BAR_OPTIONS = [
|
||||||
{ label: 'Web (hidden)', value: Platform.WEB },
|
{ label: 'Web (hidden)', value: Platform.WEB },
|
||||||
{ label: 'Windows', value: Platform.WINDOWS },
|
{ label: 'Windows', value: Platform.WINDOWS },
|
||||||
{ label: 'macOS', value: Platform.MACOS },
|
{ label: 'macOS', value: Platform.MACOS },
|
||||||
|
{ label: 'Native', value: Platform.LINUX },
|
||||||
];
|
];
|
||||||
|
|
||||||
const localSettings = isElectron() ? window.electron.localSettings : null;
|
const localSettings = isElectron() ? window.electron.localSettings : null;
|
||||||
|
@ -28,6 +29,26 @@ export const WindowSettings = () => {
|
||||||
value={settings.windowBarStyle}
|
value={settings.windowBarStyle}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (!e) return;
|
if (!e) return;
|
||||||
|
|
||||||
|
// warn that a restart is required
|
||||||
|
if (
|
||||||
|
(localSettings?.get('window_has_frame') && e !== Platform.LINUX) ||
|
||||||
|
(!localSettings?.get('window_has_frame') && e === Platform.LINUX)
|
||||||
|
) {
|
||||||
|
toast.info({
|
||||||
|
autoClose: false,
|
||||||
|
id: 'restart-toast',
|
||||||
|
message: 'Restart to apply changes... close the notification to restart Feishin',
|
||||||
|
onClose: () => {
|
||||||
|
window.electron.ipc.send('app-restart');
|
||||||
|
},
|
||||||
|
title: 'Restart required',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toast.update({ autoClose: 0, id: 'restart-toast', message: '', onClose: () => {} }); // clean old toasts
|
||||||
|
}
|
||||||
|
|
||||||
|
localSettings?.set('window_window_bar_style', e as Platform);
|
||||||
setSettings({
|
setSettings({
|
||||||
window: {
|
window: {
|
||||||
...settings,
|
...settings,
|
||||||
|
|
|
@ -34,7 +34,7 @@ const SidebarContainer = styled(motion.div)<{ windowBarStyle: Platform }>`
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-height: ${(props) =>
|
max-height: ${(props) =>
|
||||||
props.windowBarStyle === Platform.WEB
|
props.windowBarStyle === Platform.WEB || props.windowBarStyle === Platform.LINUX
|
||||||
? 'calc(100vh - 149px)'
|
? 'calc(100vh - 149px)'
|
||||||
: 'calc(100vh - 119px)'}; // Playerbar (90px), titlebar (65px), windowbar (30px)
|
: 'calc(100vh - 119px)'}; // Playerbar (90px), titlebar (65px), windowbar (30px)
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
|
@ -45,7 +45,7 @@ import { useWindowSettings } from '../../../store/settings.store';
|
||||||
const SidebarContainer = styled.div<{ windowBarStyle: Platform }>`
|
const SidebarContainer = styled.div<{ windowBarStyle: Platform }>`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-height: ${(props) =>
|
max-height: ${(props) =>
|
||||||
props.windowBarStyle === Platform.WEB
|
props.windowBarStyle === Platform.WEB || props.windowBarStyle === Platform.LINUX
|
||||||
? 'calc(100vh - 149px)'
|
? 'calc(100vh - 149px)'
|
||||||
: 'calc(100vh - 179px)'}; // Playerbar (90px), titlebar (65px), windowbar (30px)
|
: 'calc(100vh - 179px)'}; // Playerbar (90px), titlebar (65px), windowbar (30px)
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
|
@ -29,7 +29,7 @@ const Layout = styled.div<{ windowBarStyle: Platform }>`
|
||||||
'main-content'
|
'main-content'
|
||||||
'player';
|
'player';
|
||||||
grid-template-rows: ${(props) =>
|
grid-template-rows: ${(props) =>
|
||||||
props.windowBarStyle !== Platform.WEB
|
props.windowBarStyle === Platform.WINDOWS || props.windowBarStyle === Platform.MACOS
|
||||||
? '30px calc(100vh - 120px) 90px'
|
? '30px calc(100vh - 120px) 90px'
|
||||||
: '0px calc(100vh - 90px) 90px'};
|
: '0px calc(100vh - 90px) 90px'};
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|
|
@ -104,7 +104,10 @@ const QueueDrawerArea = styled(motion.div)`
|
||||||
|
|
||||||
const queueDrawerVariants: Variants = {
|
const queueDrawerVariants: Variants = {
|
||||||
closed: (windowBarStyle) => ({
|
closed: (windowBarStyle) => ({
|
||||||
height: windowBarStyle !== Platform.WEB ? 'calc(100vh - 205px)' : 'calc(100vh - 175px)',
|
height:
|
||||||
|
windowBarStyle === Platform.WINDOWS || Platform.MACOS
|
||||||
|
? 'calc(100vh - 205px)'
|
||||||
|
: 'calc(100vh - 175px)',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
right: 0,
|
right: 0,
|
||||||
top: '75px',
|
top: '75px',
|
||||||
|
@ -117,7 +120,10 @@ const queueDrawerVariants: Variants = {
|
||||||
}),
|
}),
|
||||||
open: (windowBarStyle) => ({
|
open: (windowBarStyle) => ({
|
||||||
boxShadow: '0px 0px 10px 0px rgba(0, 0, 0, 0.8)',
|
boxShadow: '0px 0px 10px 0px rgba(0, 0, 0, 0.8)',
|
||||||
height: windowBarStyle !== Platform.WEB ? 'calc(100vh - 205px)' : 'calc(100vh - 175px)',
|
height:
|
||||||
|
windowBarStyle === Platform.WINDOWS || Platform.MACOS
|
||||||
|
? 'calc(100vh - 205px)'
|
||||||
|
: 'calc(100vh - 175px)',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
right: '20px',
|
right: '20px',
|
||||||
top: '75px',
|
top: '75px',
|
||||||
|
|
|
@ -219,6 +219,7 @@ export const WindowBar = () => {
|
||||||
const statusString = playerStatus === PlayerStatus.PAUSED ? '(Paused) ' : '';
|
const statusString = playerStatus === PlayerStatus.PAUSED ? '(Paused) ' : '';
|
||||||
const queueString = length ? `(${index + 1} / ${length}) ` : '';
|
const queueString = length ? `(${index + 1} / ${length}) ` : '';
|
||||||
const title = length ? `${statusString}${queueString}${currentSong?.name}` : 'Feishin';
|
const title = length ? `${statusString}${queueString}${currentSong?.name}` : 'Feishin';
|
||||||
|
document.title = title;
|
||||||
|
|
||||||
const [max, setMax] = useState(false);
|
const [max, setMax] = useState(false);
|
||||||
|
|
||||||
|
@ -237,12 +238,13 @@ export const WindowBar = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{windowBarStyle === Platform.WINDOWS ? (
|
{windowBarStyle === Platform.WINDOWS && (
|
||||||
<WindowsControls
|
<WindowsControls
|
||||||
controls={{ handleClose, handleMaximize, handleMinimize }}
|
controls={{ handleClose, handleMaximize, handleMinimize }}
|
||||||
title={title}
|
title={title}
|
||||||
/>
|
/>
|
||||||
) : (
|
)}
|
||||||
|
{windowBarStyle === Platform.MACOS && (
|
||||||
<MacOsControls
|
<MacOsControls
|
||||||
controls={{ handleClose, handleMaximize, handleMinimize }}
|
controls={{ handleClose, handleMaximize, handleMinimize }}
|
||||||
title={title}
|
title={title}
|
||||||
|
|
Reference in a new issue