-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a tray icon to the application #1676
Conversation
9b07f0b
to
518e270
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Was waiting for this feature, thank you 👏
main.js
Outdated
{id: 'quit', label: locale.messages.quit.message, click: () => {app.quit();}} | ||
]); | ||
|
||
// NOTE: a function equivalent to the following will become part of the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe open a "remind this" Issue here to remove it when electron time comes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, an issue to track this, linked in the code, would be great. That issue can link to the places we can go to see if it's available yet.
So this still needs to be hooked up to settings? |
Yes, I thought it was safer to have it tested by different devs on different platforms first, if it works and there are no other problems, adding a couple switches to the settings later should be straightforward (and maybe add other features, like a different icon when there are unread messages). I think keeping a command line switch may be useful, it could be used to launch the app with a specific behaviour regardless of the settings (e.g. when launching the program from some autostart script). I would find it useful, but I guess this may be a matter of taste. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quite a lot of work you put into this, thanks! And I really appreciate the test strategy you've come up with here, enabling it for advance testing first.
I've left a number of comments and requests for changes. But a higher level thing to consider is how much of his can be extracted into its own file under /app
. main.js
is getting really big at this point. The way our top-level menus are generated might be a good example.
main.js
Outdated
@@ -167,7 +176,7 @@ function createWindow () { | |||
|
|||
// Emitted when the window is about to be closed. | |||
mainWindow.on('close', function (e) { | |||
if (process.platform === 'darwin' && !windowState.shouldQuit() && config.environment !== 'test') { | |||
if ((usingTrayIcon || process.platform === 'darwin') && !windowState.shouldQuit() && config.environment !== 'test') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably break this up into explanatory variables, now that it's gotten so long. Can you do that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I broke it in two meaningful if statements, hope it looks better now.
main.js
Outdated
function createTrayIcon() { | ||
logger.info('Creating tray icon'); | ||
|
||
// TODO: better to not use a hardcoded icon file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you recommend here instead of a hardcoded path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I should have detailed this better. Having multiple icon styles (light, dark, colour) for the tray and allowing the user to choose in the options would be great for theme integration. But probably this is not the most urgent issue.
main.js
Outdated
|
||
trayContextMenu = Menu.buildFromTemplate([ | ||
{id: 'show', label: locale.messages.show.message, visible: !mainWindow.isVisible(), click: () => {showWindow();}}, | ||
{id: 'hide', label: locale.messages.hide.message, visible: mainWindow.isVisible(), click: () => {hideWindow();}}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm seeing a lot of () => { something(); }
syntax in this PR, but that can be simplfied to something
, no surrounding function required.
And in cases where it's something like app.quit()
inside that function, we can change that to app.quit.bind(app)
. That's a common pattern in this codebase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was really silly of me, I moved the body of the lambdas to named functions, but I left the lambdas there...
main.js
Outdated
{id: 'quit', label: locale.messages.quit.message, click: () => {app.quit();}} | ||
]); | ||
|
||
// NOTE: a function equivalent to the following will become part of the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, an issue to track this, linked in the code, would be great. That issue can link to the places we can go to see if it's available yet.
main.js
Outdated
// https://github.com/electron/electron/releases/tag/v1.8.1 | ||
// https://github.com/electron/electron/pull/10618 | ||
trayContextMenu.getMenuItemById = function (id) { | ||
return this.items.find(item => item.id === id) || null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The || null
here seems unecessary. Below, where this is used, there's no check before setting its visible
property, so it will crash. Maybe the right thing to do here is to throw if we can't find any menu item with the provided id?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think throwing an exception there would be the cleanest way. But the method implemented in electron does not do that, it just returns null, so if we want to be able to replace this function with it we may need to do null checks (that I added where needed, thank you for pointing it out).
Otherwise we can do differently and not rely on the (future) electron's API for it (after all, this is a very short piece of code and it is called just twice).
This is the method in electron 1.8.1: https://github.com/electron/electron/blob/ba754cf5c3f36d66a3b23f409613a348bfa6b308/lib/browser/api/menu.js#L84
main.js
Outdated
|
||
tray.setContextMenu(trayContextMenu); | ||
|
||
tray.on('clicked', () => {toggleWindowVisibility();}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the difference between the 'clicked' and 'click' events?
Also, another situation where we can dispense with the surrounding function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops. 'click' is the event. 'clicked' was an experiment I made, and somehow I missed to remove that line later.
main.js
Outdated
tray.on('clicked', () => {toggleWindowVisibility();}); | ||
tray.on('click', () => {toggleWindowVisibility();}); | ||
|
||
tray.setToolTip('Signal Desktop'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be localized. Let's pull this string from locale
.
main.js
Outdated
if (mainWindow) { | ||
if (mainWindow.isVisible()) { | ||
hideWindow(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A minor issue, but we generally use } else {
brace style in this project.
main.js
Outdated
function showWindow() { | ||
if (mainWindow) { | ||
mainWindow.show(); | ||
|
||
// trick to bring above other windows and get focus |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tell me more about this trick. What platforms has it been tested on (we've had particular difficulty with window placement/focus on windows) and where did it come from?
Also, note the 'show-window' IPC handler just a few lines above this. The right thing here might be to extract that out into a focusWindow
method which shows the window if it isn't already showing. Then the tray handlers can use that instead of the simple showWindow
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved this in a specific method for the tray.
Sorry, I should definitely have given more detail here. I was aware that on some versions of GNOME there were problems when using this API, and the window may not be restored on top if it was not focused when it was hidden (cfr. also this issue on an app that uses the same API: https://github.com/Enrico204/Whatsapp-Desktop/issues/29 ), so I included it to be on the safe side (tested on Linux, KDE Plasma 5.11 and GNOME 3.26), even if I suspect it may be unnecessary.
Thanks @scottnonnenberg for the review. I am sorry for so much sloppiness in my code, I had to implement this in very little time and probably I should not have submitted it so early. I moved the tray related code in a new module in the app folder and followed your suggestions for the other issues. I am still not satisfied with the |
Note: I just moved this to target the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the changes. We're almost there!
app/tray_icon.js
Outdated
|
||
function createTrayIcon(mainWindow, messages) { | ||
|
||
// TODO: for better desktop theme integration, it would be good to provide |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to put a TODO in the code, please create an issue and link to it directly in the code next to the TODO.
main.js
Outdated
@@ -273,6 +298,11 @@ app.on('ready', function() { | |||
|
|||
createWindow(); | |||
|
|||
if (usingTrayIcon) { | |||
const createTrayIcon = require("./app/tray_icon"); | |||
tray = createTrayIcon(mainWindow, locale.messages); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like how everything has been pulled into a different file! However, there's one thing to consider: there can be multiple values of mainWindow
over the lifetime of the application. I think, instead of passing mainWindow
directly here, a function getMainWindow()
should be passed instead.
app/tray_icon.js
Outdated
|
||
// TODO: for better desktop theme integration, it would be good to provide | ||
// multiple tray icons (light, dark, colour) and allow selection from the options | ||
tray = new Tray(path.join(__dirname, '../images/icon_48.png')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about path.join(__dirname, '..', 'images', 'icon_48.png')
instead? Keep those pesky linux-y /
characters out of a cross-platform codebase? :0)
I apologise for being so slow to reply, unfortunately I can work on this only during the weekend. |
@m-pilia We're looking good on these changes (nice simplification!), but it looks like there's still a merge conflict to deal with. I think we're ready to merge as soon as that is addressed. |
Right, I didn't look at it. Now it should be ok I think! |
@m-pilia That is one huge commit, that merge from the |
bf9708b
to
9d7cab1
Compare
@scottnonnenberg sorry, this was embarassing... I'm familiar with rebase, but apparently I'm definitely not familiar with GitHub. I tried to use the web interface to solve the conflict, but I didn't realise it was also going to merge the entire development branch in my feature branch... |
Okay, I just tested this - looking good! Fix that merge conflict and I'll merge this PR. If we can get this done in the next couple hours, it will go out in the next beta build. :0) |
This commit adds a tray icon to the application, shown in the system tray bar, that can be used to minimise the application window. This is a common feature on most desktop messaging apps (e.g. Telegram Desktop or Slack) and allows to save space in the system task bar. The tray icon provides a context menu that contains a button to show/hide the application window, and a button to quit the application. When the tray icon is clicked, the visibility of the window is toggled. When the close (x) button of the window is pressed, the application is not terminated but minimised to the tray icon instead (it can be terminated by using the "Quit" entry in the File menu or in the context menu of the tray icon). The tray icon is disabled by default, and two command line arguments are available to enable it: --use-tray-icon: enables the tray icon --start-in-tray: enables the tray icon and the application starts minimised in the tray bar Resolves: signalapp#1480
Thanks so much!! <3 |
Add proxy support based on environment variables (#1855) Fix issue where window would not show new message alerts on some Linux systesm - thanks @cornerman (#1820) Add a tray icon to the application behind command-line argument - thanks @m-pilia (#1676) Emoji: - Fix issue where clicks in emoji panel wouldn't do anything (#1849) - Add support for Emoji 5 - thanks @liliakai (#1797) - Eliminate unused emoji images from production package (#1849) Deployment: - aptly.sh: support for current and previous ubuntu versions (#1856) - deployment: update electron-publisher-s3 to match builder update (17f0bb4) Dev: - Update development branch to include everything up to v1.0.40 (f013eed and 3ac29a4) - Update readme.md to use signal.org URLs - thanks @scienmind (#1814)
Any timeframe on when the next build will be released? As in not one that needs compiled manually. |
The tray is in current beta builds. You can install the beta like this:
|
Some feedback on this, since I just got a chance to test it. 1. The start in tray works great, the icon shows there just fine.
2. The minimize still goes to taskbar but closing it will keep the icon in the tray, great, I can live with that to some extent.
3. Minimizing to tray feature is great but current implementation loses great usefulness
again, thank you for doing this, it is a great start and hope we can improve on it. |
@GuardianMajor I think it's time to take some of this behavior to other issues. This pull request is long-dead. In particular, your item 3 is definitely a bug. Notifications are supposed to happen when the window is minimized. We'll need more information: platform details, log, etc. as usual. I'm locking this discussion. |
This commit adds a tray icon to the application, shown in the system
tray bar, that can be used to minimise the application window. This
is a common feature on most desktop messaging apps (e.g. Telegram
Desktop or Slack) and allows to save space in the system task bar.
The tray icon provides a context menu that contains a button to
show/hide the application window, and a button to quit the
application. When the tray icon is clicked, the visibility of the
window is toggled. When the close (x) button of the window is
pressed, the application is not terminated but minimised to the tray
icon instead (it can be terminated by using the "Quit" entry in the
File menu or in the context menu of the tray icon).
The tray icon is disabled by default, and two command line arguments
are available to enable it:
Resolves: #1480
First time contributor checklist
Contributor checklist
Description