Skip to content

Commit

Permalink
✨ start minimized option
Browse files Browse the repository at this point in the history
Signed-off-by: Marc Nuri <[email protected]>
  • Loading branch information
manusa committed Mar 8, 2024
1 parent c7aee11 commit c21c1b0
Show file tree
Hide file tree
Showing 16 changed files with 131 additions and 50 deletions.
2 changes: 2 additions & 0 deletions src/__tests__/electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const mockBrowserWindowInstance = () => {
setBounds: jest.fn(),
setBrowserView: jest.fn(),
setFullScreen: jest.fn(),
show: jest.fn(),
showInactive: jest.fn(),
webContents: {
loadedUrl: '',
browserWindowInstance: () => instance,
Expand Down
12 changes: 6 additions & 6 deletions src/chrome-tabs/__tests__/chrome-tabs.browser.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('ChromeTabs in Browser test suite', () => {
{id: 13373, favicon: 'https://13373.png', url: 'https://13373.com'}
];
});
test('addTabs, should set tabs', async () => {
test('addTabs, should set tabs without restoring the main window / activating any tab', async () => {
// When
mockIpcRenderer.events.addTabs({}, tabs);
// Then
Expand All @@ -62,7 +62,7 @@ describe('ChromeTabs in Browser test suite', () => {
expect($addedTabs[1].hasAttribute('active')).toBe(false);
expect($addedTabs[2].querySelector('.chrome-tab-favicon').style.backgroundImage)
.toBe('url(https://13373.png)');
expect(mockIpcRenderer.send).toHaveBeenCalledWith('activateTab', {id: 1337});
expect(mockIpcRenderer.send).toHaveBeenCalledWith('activateTab', {id: 1337, restoreWindow: false});
});
test('activateTabInContainer, should change active tab', async () => {
// Given
Expand Down Expand Up @@ -136,7 +136,7 @@ describe('ChromeTabs in Browser test suite', () => {
// When
fireEvent.click($chromeTabs.querySelector('.chrome-tab[data-tab-id="313373"]'));
// Then
expect(mockIpcRenderer.send).toHaveBeenNthCalledWith(3, 'activateTab', {id: 313373});
expect(mockIpcRenderer.send).toHaveBeenNthCalledWith(3, 'activateTab', {id: 313373, restoreWindow: true});
});
test('click, on active tab, should do nothing', () => {
// When
Expand All @@ -153,9 +153,9 @@ describe('ChromeTabs in Browser test suite', () => {
// Then
expect(event.preventDefault).toHaveBeenCalledTimes(1);
});
test('dragStart, should activate tab and set initial drag values', () => {
test('dragStart, on inactive tab, should activate tab and set initial drag values', () => {
// Given
const $tab = $chromeTabs.querySelector('.chrome-tab[data-tab-id="1337"]');
const $tab = $chromeTabs.querySelector('.chrome-tab[data-tab-id="313373"]');
const event = new MouseEvent('dragstart', {
clientX: 100, clientY: 0});
Object.defineProperty(event, 'dataTransfer', {value: {
Expand All @@ -164,7 +164,7 @@ describe('ChromeTabs in Browser test suite', () => {
// When
fireEvent($tab, event);
// Then
expect(mockIpcRenderer.send).toHaveBeenCalledWith('activateTab', {id: 1337});
expect(mockIpcRenderer.send).toHaveBeenCalledWith('activateTab', {id: 313373, restoreWindow: true});
expect(event.dataTransfer.setDragImage).toHaveBeenCalledTimes(1);
});
test('drag, same position, should keep positions moving current tab left', async () => {
Expand Down
2 changes: 1 addition & 1 deletion src/chrome-tabs/chrome-tabs.browser.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const BackgroundSvg = () => html`
const Tab = ({dispatch, numberOfTabs, idx, id, active, offsetX = 0, title, url, width, ...rest}) => {
const tabClick = () => {
if (active !== true) {
sendActivateTab(id);
sendActivateTab({id, restoreWindow: true});
activateTab({dispatch})(null, {tabId: id});
}
};
Expand Down
6 changes: 4 additions & 2 deletions src/chrome-tabs/chrome-tabs.reducer.browser.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
*/
/* eslint-disable no-undef */
import {APP_EVENTS} from '../components/index.mjs';
export const sendActivateTab = id => ipcRenderer.send(APP_EVENTS.activateTab, {id});
export const sendActivateTab = ({id, restoreWindow = true}) => {
ipcRenderer.send(APP_EVENTS.activateTab, {id, restoreWindow});
};
const sendReorderTabs = tabs =>
ipcRenderer.send(APP_EVENTS.tabReorder, {tabIds: tabs.map(({id}) => id)});

Expand Down Expand Up @@ -79,7 +81,7 @@ export const addTabs = ({dispatch}) => (_event, tabs) => {
dispatch({type: ACTIONS.SET_TABS, payload: tabs});
const activeTabMeta = tabs.find(({active}) => active === true);
if (tabs.length > 0 && activeTabMeta) {
sendActivateTab(activeTabMeta.id);
sendActivateTab({id: activeTabMeta.id, restoreWindow: false});
}
};
export const moveTab = ({dispatch}) => ({id, idx, offsetX}) =>
Expand Down
1 change: 1 addition & 0 deletions src/components/icon.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Icon.inbox = '\ue156';
Icon.info = '\ue88e';
Icon.lock = '\ue88d';
Icon.lockOpen = '\ue898';
Icon.minimize = '\ue931';
Icon.more = '\ue619';
Icon.moreVert = '\ue5d4';
Icon.notifications = '\ue7f4';
Expand Down
42 changes: 42 additions & 0 deletions src/main/__tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,46 @@ describe('Main module test suite', () => {
}));
});
});
describe('startMinimized', () => {
test('Main window should always start not shown', () => {
// When
main.init();
// Then
expect(electron.BrowserWindow).toHaveBeenCalledWith(expect.objectContaining({
show: false, paintWhenInitiallyHidden: false
}));
});
describe('=true', () => {
beforeEach(() => {
mockSettings.startMinimized = true;
main.init();
});
test('should call showInactive', () => {
expect(mockBrowserWindow.showInactive).toHaveBeenCalledTimes(1);
});
test('should call minimize after showInactive', () => {
expect(mockBrowserWindow.minimize).toHaveBeenCalledAfter(mockBrowserWindow.showInactive);
});
test('should not call show', () => {
expect(mockBrowserWindow.show).not.toHaveBeenCalled();
});
});
describe('=false', () => {
beforeEach(() => {
mockSettings.startMinimized = false;
main.init();
});
test('should call show', () => {
expect(mockBrowserWindow.show).toHaveBeenCalledTimes(1);
});
test('should not call showInactive', () => {
expect(mockBrowserWindow.showInactive).not.toHaveBeenCalled();
});
test('should not call minimize', () => {
expect(mockBrowserWindow.minimize).not.toHaveBeenCalled();
});
});
});
test('fixUserDataLocation, should set a location in lower-case (Electron <14 compatible)', () => {
// Given
electron.app.getPath.mockImplementation(() => 'ImMixed-Case/WithSome\\Separator$');
Expand Down Expand Up @@ -320,6 +360,7 @@ describe('Main module test suite', () => {
});
test('notificationClick, should restore window and activate tab', () => {
// Given
mockSettings.startMinimized = true;
mockBrowserWindow.restore = jest.fn();
mockBrowserWindow.show = jest.fn();
jest.spyOn(tabManagerModule, 'getTab').mockImplementation();
Expand All @@ -330,6 +371,7 @@ describe('Main module test suite', () => {
expect(mockBrowserView.webContents.send).toHaveBeenCalledWith('activateTabInContainer', {tabId: 'validId'});
expect(mockBrowserWindow.restore).toHaveBeenCalledTimes(1);
expect(mockBrowserWindow.show).toHaveBeenCalledTimes(1);
expect(mockBrowserWindow.show).toHaveBeenCalledAfter(mockBrowserWindow.restore);
expect(tabManagerModule.getTab).toHaveBeenCalledWith('validId');
});
test('handleReload', () => {
Expand Down
27 changes: 19 additions & 8 deletions src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const resetMainWindow = () => {
}
};

const activateTab = tabId => {
const activateTab = ({tabId, restoreWindow = true}) => {
const activeTab = tabManager.getTab(tabId);
if (activeTab) {
const {width, height} = mainWindow.getContentBounds();
Expand All @@ -64,7 +64,9 @@ const activateTab = tabId => {
tabContainer.setBounds({x: 0, y: 0, width, height: TABS_CONTAINER_HEIGHT});
activeTab.setBounds({x: 0, y: TABS_CONTAINER_HEIGHT, width, height: height - TABS_CONTAINER_HEIGHT});
tabManager.setActiveTab(tabId);
activeTab.webContents.focus();
if (restoreWindow) {
activeTab.webContents.focus();
}
}
};

Expand Down Expand Up @@ -99,7 +101,7 @@ const handleTabTraverse = getTabIdFunction => () => {
}
const tabId = getTabIdFunction();
tabContainer.webContents.send(APP_EVENTS.activateTabInContainer, {tabId});
activateTab(tabId);
activateTab({tabId});
};

const handleTabSwitchToPosition = tabPosition => {
Expand Down Expand Up @@ -145,14 +147,16 @@ const initTabListener = () => {
eventBus.emit(APP_EVENTS.settingsOpenDialog);
}
});
eventBus.on(APP_EVENTS.activateTab, (_event, data) => activateTab(data.id));
eventBus.on(APP_EVENTS.activateTab, (_event, data) => {
activateTab({tabId: data.id, restoreWindow: data.restoreWindow});
});
eventBus.on(APP_EVENTS.canNotify, (event, tabId) => {
event.returnValue = tabManager.canNotify(tabId);
});
eventBus.on(APP_EVENTS.notificationClick, (_event, {tabId}) => {
tabContainer.webContents.send(APP_EVENTS.activateTabInContainer, {tabId});
eventBus.emit(APP_EVENTS.restore);
activateTab(tabId);
activateTab({tabId});
});
eventBus.on(APP_EVENTS.reload, handleTabReload);
eventBus.on(APP_EVENTS.tabReorder, handleTabReorder);
Expand All @@ -176,7 +180,7 @@ const appMenuClose = () => {
return;
}
mainWindow.removeBrowserView(appMenu);
activateTab(tabManager.getActiveTab());
activateTab({tabId: tabManager.getActiveTab()});
};

const fullscreenToggle = () => {
Expand All @@ -188,7 +192,7 @@ const closeDialog = () => {
return;
}
const dialogView = mainWindow.getBrowserView();
activateTab(tabManager.getActiveTab());
activateTab({tabId: tabManager.getActiveTab()});
dialogView.webContents.destroy();
};

Expand Down Expand Up @@ -253,13 +257,20 @@ const browserVersionsReady = () => {
const init = () => {
fixUserDataLocation();
loadDictionaries();
const {width = 800, height = 600, theme} = loadSettings();
const {width = 800, height = 600, startMinimized, theme} = loadSettings();
nativeTheme.themeSource = theme;
mainWindow = new BrowserWindow({
width, height, resizable: true, maximizable: true,
icon: path.resolve(__dirname, '..', 'assets', getPlatform() === 'linux' ? 'icon.png' : 'icon.ico'),
show: false, paintWhenInitiallyHidden: false,
webPreferences
});
if (startMinimized) {
mainWindow.showInactive();
mainWindow.minimize();
} else {
mainWindow.show();
}
mainWindow.removeMenu();
['resize', 'maximize']
.forEach(event => mainWindow.on(event, handleMainWindowResize));
Expand Down
6 changes: 5 additions & 1 deletion src/settings/__tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ describe('Settings module test suite', () => {
'{\n "tabs": [],\n' +
' "useNativeSpellChecker": false,\n' +
' "enabledDictionaries": [\n "en"\n ],\n' +
' "theme": "system",\n "trayEnabled": false,\n' +
' "theme": "system",\n' +
' "trayEnabled": false,\n' +
' "startMinimized": false,\n' +
' "closeButtonBehavior": "quit"\n' +
'}');
});
Expand All @@ -140,6 +142,7 @@ describe('Settings module test suite', () => {
' "enabledDictionaries": [\n "en"\n ],\n' +
' "theme": "system",\n' +
' "trayEnabled": false,\n' +
' "startMinimized": false,\n' +
' "closeButtonBehavior": "quit",\n' +
' "activeTab": 1337,\n' +
' "otherSetting": "1337"\n' +
Expand All @@ -157,6 +160,7 @@ describe('Settings module test suite', () => {
' "enabledDictionaries": [\n "en"\n ],\n' +
' "theme": "system",\n' +
' "trayEnabled": false,\n' +
' "startMinimized": false,\n' +
' "closeButtonBehavior": "quit",\n' +
' "activeTab": 1337\n}');
});
Expand Down
3 changes: 2 additions & 1 deletion src/settings/__tests__/settings.browser.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export const ipcRenderer = () => {
{id: '2', url: 'https://initial-tab-2.com', disabled: true, disableNotifications: true}
],
theme: 'dark',
trayEnabled: true
trayEnabled: true,
startMinimized: false
};
return {
mockDictionariesAvailableNative,
Expand Down
3 changes: 2 additions & 1 deletion src/settings/__tests__/settings.browser.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ describe('Settings in Browser test suite', () => {
enabledDictionaries: ['en'],
disableNotificationsGlobally: false,
theme: 'dark',
trayEnabled: true
trayEnabled: true,
startMinimized: false
});
});
test('Cancel should send close dialog event', () => {
Expand Down
24 changes: 24 additions & 0 deletions src/settings/__tests__/settings.other.browser.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,30 @@ describe('Settings (Other) in Browser test suite', () => {
await waitFor(() => expect($traySwitch.checked).toBe(true));
});
});
describe('Toggle Start Minimized click', () => {
let $startMinimizedSwitch;
beforeEach(() => {
// In tests, start minimized is disabled by default
$startMinimizedSwitch = document.querySelector('.settings__start-minimized .switch input');
});
test('when start minimized enabled, should uncheck input', async () => {
// Given
fireEvent.click($startMinimizedSwitch);
await waitFor(() => expect($startMinimizedSwitch.checked).toBe(true));
// When
fireEvent.click($startMinimizedSwitch);
// Then
await waitFor(() => expect($startMinimizedSwitch.checked).toBe(false));
});
test('when start minimized disabled, should check input', async () => {
// Given
expect($startMinimizedSwitch.checked).toBe(false);
// When
fireEvent.click($startMinimizedSwitch);
// Then
await waitFor(() => expect($startMinimizedSwitch.checked).toBe(true));
});
});
test('ElectronIM version is visible', async () => {
const $electronimVersion = await findByTestId(document, 'settings-electronim-version');
expect($electronimVersion.textContent).toBe('ElectronIM version 0.0.0');
Expand Down
1 change: 1 addition & 0 deletions src/settings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const DEFAULT_SETTINGS = {
enabledDictionaries: ['en'],
theme: 'system',
trayEnabled: false,
startMinimized: false,
closeButtonBehavior: CLOSE_BUTTON_BEHAVIORS.quit
};

Expand Down
2 changes: 2 additions & 0 deletions src/settings/settings.browser.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const Settings = ({initialState}) => {
disableNotificationsGlobally: state.disableNotificationsGlobally,
theme: state.theme,
trayEnabled: state.trayEnabled,
startMinimized: state.startMinimized,
closeButtonBehavior: state.closeButtonBehavior
});
const cancel = () => ipcRenderer.send(APP_EVENTS.closeDialog);
Expand Down Expand Up @@ -96,6 +97,7 @@ Promise.all([
disableNotificationsGlobally: currentSettings.disableNotificationsGlobally,
theme: currentSettings.theme,
trayEnabled: currentSettings.trayEnabled,
startMinimized: currentSettings.startMinimized,
closeButtonBehavior: currentSettings.closeButtonBehavior
};
render(html`<${Settings} initialState=${initialState} />`, settingsRoot());
Expand Down
14 changes: 10 additions & 4 deletions src/settings/settings.other.browser.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ import {
closeButtonBehavior,
setProperty,
theme,
toggleNotifications,
toggleTray
toggleProperty
} from './settings.reducer.browser.mjs';
import {SettingsOption, SettingsRow} from './settings.common.browser.mjs';

Expand All @@ -47,7 +46,7 @@ export const OtherPane = ({dispatch, state}) => {
label='Disable notifications globally'
icon=${state.disableNotificationsGlobally ? Icon.notificationsOff : Icon.notifications}
checked=${state.disableNotificationsGlobally}
onClick=${toggleNotifications({dispatch})}
onClick=${toggleProperty({dispatch, property: 'disableNotificationsGlobally'})}
/>
<${Card.Divider} />
<${SettingsRow}>
Expand All @@ -65,7 +64,14 @@ export const OtherPane = ({dispatch, state}) => {
label='Show ElectronIM in System Tray'
icon=${Icon.inbox}
checked=${state.trayEnabled}
onClick=${toggleTray({dispatch})}
onClick=${toggleProperty({dispatch, property: 'trayEnabled'})}
/>
<${SettingsOption}
className='settings__start-minimized'
label='Start ElectronIM minimized'
icon=${Icon.minimize}
checked=${state.startMinimized}
onClick=${toggleProperty({dispatch, property: 'startMinimized'})}
/>
<${Card.Divider} />
<div data-testid='settings-electronim-version'>
Expand Down
Loading

0 comments on commit c21c1b0

Please sign in to comment.