mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-17 18:05:42 +02:00
Windows desktop integration fixes. (#9628)
* Windows desktop integration fixes: icons, notification messages, paths, jump list Signed-off-by: Denis Gladkiy <denis.gladkiy@hardcoreeng.com> * Fixed typos and formatting Signed-off-by: Denis Gladkiy <denis.gladkiy@hardcoreeng.com> --------- Signed-off-by: Denis Gladkiy <denis.gladkiy@hardcoreeng.com>
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
!macro customInit
|
||||
StrCpy $INSTDIR "$LOCALAPPDATA\Programs\HulyDesktop"
|
||||
!macroend
|
||||
@@ -64,12 +64,26 @@
|
||||
],
|
||||
"artifactName": "Huly-windows-${version}.${ext}",
|
||||
"verifyUpdateCodeSignature": false,
|
||||
"icon": "./src/AppIcon.png",
|
||||
"icon": "./src/AppIcon.ico",
|
||||
"publish": {
|
||||
"provider": "generic",
|
||||
"url": "https://dist.huly.io",
|
||||
"channel": "latest"
|
||||
}
|
||||
},
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "./dist/ui/public/icons/",
|
||||
"to": "./icons/",
|
||||
"filter": [
|
||||
"**/*.ico"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"include": "installer.nsh",
|
||||
"installerIcon": "./src/AppIcon.ico",
|
||||
"uninstallerIcon": "./src/AppIcon.ico"
|
||||
},
|
||||
"linux": {
|
||||
"artifactName": "Huly-linux-${version}.${ext}",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 403 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
@@ -14,7 +14,7 @@
|
||||
//
|
||||
|
||||
import { app, BrowserWindow } from 'electron'
|
||||
import { MenuBarAction, StandardMenuCommandLogout, StandardMenuCommandSelectWorkspace, StandardMenuCommandOpenSettings } from '../ui/types'
|
||||
import { MenuBarAction, CommandLogout, CommandSelectWorkspace, CommandOpenSettings } from '../ui/types'
|
||||
|
||||
export function dipatchMenuBarAction(mainWindow: BrowserWindow | undefined, action: MenuBarAction) {
|
||||
if (mainWindow == null) {
|
||||
@@ -33,13 +33,13 @@ export function dipatchMenuBarAction(mainWindow: BrowserWindow | undefined, acti
|
||||
|
||||
switch (action) {
|
||||
case 'settings':
|
||||
mainWindow.webContents.send(StandardMenuCommandOpenSettings)
|
||||
mainWindow.webContents.send(CommandOpenSettings)
|
||||
break;
|
||||
case 'select-workspace':
|
||||
mainWindow.webContents.send(StandardMenuCommandSelectWorkspace)
|
||||
mainWindow.webContents.send(CommandSelectWorkspace)
|
||||
break;
|
||||
case 'logout':
|
||||
mainWindow.webContents.send(StandardMenuCommandLogout)
|
||||
mainWindow.webContents.send(CommandLogout)
|
||||
break;
|
||||
case 'exit':
|
||||
app.quit();
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
//
|
||||
|
||||
import { Menu, MenuItemConstructorOptions } from 'electron'
|
||||
import { StandardMenuCommand, StandardMenuCommandOpenSettings, StandardMenuCommandSelectWorkspace, StandardMenuCommandLogout, } from '../ui/types'
|
||||
import { Command, CommandOpenSettings, CommandSelectWorkspace, CommandLogout, } from '../ui/types'
|
||||
|
||||
const isMac = process.platform === 'darwin'
|
||||
const isLinux = process.platform === 'linux'
|
||||
|
||||
export const addMenus = (sendCommand: (cmd: StandardMenuCommand, ...args: any[]) => void): void => {
|
||||
export const addMenus = (sendCommand: (cmd: Command, ...args: any[]) => void): void => {
|
||||
const template: MenuItemConstructorOptions[] = [
|
||||
{
|
||||
label: 'File',
|
||||
@@ -27,15 +27,15 @@ export const addMenus = (sendCommand: (cmd: StandardMenuCommand, ...args: any[])
|
||||
{
|
||||
label: 'Settings',
|
||||
accelerator: isLinux ? 'Ctrl+,' : 'Meta+,',
|
||||
click: () => { sendCommand(StandardMenuCommandOpenSettings) }
|
||||
click: () => { sendCommand(CommandOpenSettings) }
|
||||
},
|
||||
{
|
||||
label: 'Select workspace',
|
||||
click: () => { sendCommand(StandardMenuCommandSelectWorkspace) }
|
||||
click: () => { sendCommand(CommandSelectWorkspace) }
|
||||
},
|
||||
{
|
||||
label: 'Logout',
|
||||
click: () => { sendCommand(StandardMenuCommandLogout) }
|
||||
click: () => { sendCommand(CommandLogout) }
|
||||
},
|
||||
{ role: isMac ? 'close' : 'quit' }
|
||||
]
|
||||
|
||||
+24
-18
@@ -22,7 +22,7 @@ import { ProgressInfo, UpdateInfo } from 'electron-updater'
|
||||
import WinBadge from 'electron-windows-badge'
|
||||
import * as path from 'path'
|
||||
|
||||
import { Config, MenuBarAction, NotificationParams, StandardMenuCommandLogout, StandardMenuCommandSelectWorkspace, StandardMenuCommandOpenSettings } from '../ui/types'
|
||||
import { Config, MenuBarAction, NotificationParams } from '../ui/types'
|
||||
import { getOptions } from './args'
|
||||
import { addMenus } from './standardMenu'
|
||||
import { dipatchMenuBarAction } from './customMenu'
|
||||
@@ -30,6 +30,8 @@ import { addPermissionHandlers } from './permissions'
|
||||
import autoUpdater from './updater'
|
||||
import { generateId } from '@hcengineering/core'
|
||||
import { DownloadItem } from '@hcengineering/desktop-downloads'
|
||||
import { setupWindowsSpecific } from './windowsSpecificSetup'
|
||||
|
||||
|
||||
let mainWindow: BrowserWindow | undefined
|
||||
let winBadge: any
|
||||
@@ -39,7 +41,7 @@ const isWindows = process.platform === 'win32'
|
||||
const isDev = process.env.NODE_ENV === 'development'
|
||||
|
||||
const sessionPartition = !isDev ? 'persist:huly' : 'persist:huly_dev'
|
||||
const iconKey = path.join(app.getAppPath(), 'dist', 'ui', 'public', 'AppIcon.png')
|
||||
const iconKey = path.join(app.getAppPath(), 'dist', 'ui', 'public', isWindows ? 'AppIcon.ico' : 'AppIcon.png')
|
||||
const preloadScriptPath = path.join(app.getAppPath(), 'dist', 'main', 'preload.js')
|
||||
|
||||
const defaultWidth = 1440
|
||||
@@ -200,7 +202,7 @@ function handleAuthRedirects (window: BrowserWindow): void {
|
||||
}
|
||||
|
||||
function handleWillDownload (window: BrowserWindow): void {
|
||||
window.webContents.session.on('will-download', (event, item) => {
|
||||
window.webContents.session.on('will-download', (_event, item) => {
|
||||
const key = generateId()
|
||||
|
||||
const notifyDownloadUpdated = (): void => {
|
||||
@@ -303,10 +305,14 @@ const createWindow = async (): Promise<void> => {
|
||||
}
|
||||
}
|
||||
|
||||
if (false == isWindows) {
|
||||
addMenus((cmd: string, ...args: any[]) => {
|
||||
mainWindow?.webContents.send(cmd, ...args)
|
||||
})
|
||||
function sendCommand(cmd: string, ...args: any[]): void {
|
||||
mainWindow?.webContents.send(cmd, ...args)
|
||||
}
|
||||
|
||||
if (isWindows) {
|
||||
setupWindowsSpecific(sendCommand)
|
||||
} else {
|
||||
addMenus(sendCommand)
|
||||
}
|
||||
|
||||
contextMenu({
|
||||
@@ -315,7 +321,7 @@ contextMenu({
|
||||
showSelectAll: false
|
||||
})
|
||||
|
||||
ipcMain.on('set-badge', (event, badge: number) => {
|
||||
ipcMain.on('set-badge', (_event: any, badge: number) => {
|
||||
app.dock?.setBadge(badge > 0 ? `${badge}` : '')
|
||||
app.badgeCount = badge
|
||||
|
||||
@@ -324,11 +330,11 @@ ipcMain.on('set-badge', (event, badge: number) => {
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('dock-bounce', (event) => {
|
||||
ipcMain.on('dock-bounce', (_event: any) => {
|
||||
app.dock?.bounce('informational')
|
||||
})
|
||||
|
||||
ipcMain.on('send-notification', (event, notificationParams: NotificationParams) => {
|
||||
ipcMain.on('send-notification', (_event: any, notificationParams: NotificationParams) => {
|
||||
if (Notification.isSupported()) {
|
||||
const notification = new Notification(notificationParams)
|
||||
|
||||
@@ -341,13 +347,13 @@ ipcMain.on('send-notification', (event, notificationParams: NotificationParams)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('set-title', (event, title) => {
|
||||
ipcMain.on('set-title', (event: any, title: string) => {
|
||||
const webContents = event.sender
|
||||
const window = BrowserWindow.fromWebContents(webContents)
|
||||
window?.setTitle(title)
|
||||
})
|
||||
|
||||
ipcMain.on('set-combined-config', (event, config: Config) => {
|
||||
ipcMain.on('set-combined-config', (_event: any, config: Config) => {
|
||||
log.info('Config set: ', config)
|
||||
|
||||
setupCookieHandler(config)
|
||||
@@ -363,7 +369,7 @@ ipcMain.on('set-combined-config', (event, config: Config) => {
|
||||
void autoUpdater.checkForUpdatesAndNotify()
|
||||
})
|
||||
|
||||
ipcMain.handle('get-main-config', (event, path) => {
|
||||
ipcMain.handle('get-main-config', (_event: any, _path: any) => {
|
||||
const cfg = {
|
||||
CONFIG_URL: process.env.CONFIG_URL ?? '',
|
||||
FRONT_URL,
|
||||
@@ -373,11 +379,11 @@ ipcMain.handle('get-main-config', (event, path) => {
|
||||
}
|
||||
return cfg
|
||||
})
|
||||
ipcMain.handle('get-host', (event, path) => {
|
||||
ipcMain.handle('get-host', (_event: any, _path: any) => {
|
||||
return new URL(FRONT_URL).host
|
||||
})
|
||||
|
||||
ipcMain.on('set-front-cookie', function (event, host: string, name: string, value: string) {
|
||||
ipcMain.on('set-front-cookie', function (event: any, host: string, name: string, value: string) {
|
||||
const webContents = event.sender
|
||||
const win = BrowserWindow.fromWebContents(webContents)
|
||||
const cv: CookiesSetDetails = {
|
||||
@@ -428,7 +434,7 @@ if (!gotTheLock) {
|
||||
ipcMain.handle('get-screen-access', () => systemPreferences.getMediaAccessStatus('screen') === 'granted')
|
||||
ipcMain.handle('get-screen-sources', () => {
|
||||
return desktopCapturer.getSources({ types: ['window', 'screen'], fetchWindowIcons: true, thumbnailSize: { width: 225, height: 135 } }).then(async sources => {
|
||||
return sources.map(source => {
|
||||
return sources.map((source: any) => {
|
||||
return {
|
||||
...source,
|
||||
appIconURL: source.appIcon?.toDataURL(),
|
||||
@@ -490,7 +496,7 @@ autoUpdater.on('update-available', (info: UpdateInfo) => {
|
||||
defaultId: 0,
|
||||
message: `A new version ${info.version} is available and it is required to continue. It will be downloaded and installed automatically.`
|
||||
})
|
||||
.then(({ response }) => {
|
||||
.then(({ response }: any) => {
|
||||
log.info(`Update dialog exit code: ${response}`) // eslint-disable-line no-console
|
||||
|
||||
if (response !== 0) {
|
||||
@@ -513,7 +519,7 @@ function setDownloadProgress (percent: number): void {
|
||||
mainWindow.webContents.send('handle-update-download-progress', percent)
|
||||
}
|
||||
|
||||
autoUpdater.on('update-downloaded', (info) => {
|
||||
autoUpdater.on('update-downloaded', (_info: any) => {
|
||||
// We have listeners that prevents the app from being exited on mac
|
||||
app.removeAllListeners('window-all-closed')
|
||||
mainWindow?.removeAllListeners('close')
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { app } from 'electron'
|
||||
import { Command, CommandOpenSettings, CommandOpenInbox, CommandOpenOffice, CommandOpenPlanner } from '../ui/types'
|
||||
import * as path from 'path'
|
||||
|
||||
const JUMP_COMMANDS = {
|
||||
INBOX: '--jump-to-inbox',
|
||||
SETTINGS: '--jump-to-settings',
|
||||
PLANNER: '--jump-to-planner',
|
||||
OFFICE: '--jump-to-office'
|
||||
} as const
|
||||
|
||||
type JumpCommand = typeof JUMP_COMMANDS[keyof typeof JUMP_COMMANDS];
|
||||
|
||||
export function setupWindowsSpecific(sendCommand: (cmd: Command, ...args: any[]) => void): void {
|
||||
|
||||
app.setAppUserModelId(app.getName());
|
||||
|
||||
app.on('second-instance', (_event: any, commandLine: any, _workingDirectory: any) => {
|
||||
const jumpCommand = commandLine[1] as JumpCommand;
|
||||
|
||||
let command: Command | undefined;
|
||||
switch (jumpCommand) {
|
||||
case JUMP_COMMANDS.INBOX:
|
||||
command = CommandOpenInbox;
|
||||
break;
|
||||
case JUMP_COMMANDS.OFFICE:
|
||||
command = CommandOpenOffice;
|
||||
break;
|
||||
case JUMP_COMMANDS.PLANNER:
|
||||
command = CommandOpenPlanner;
|
||||
break;
|
||||
case JUMP_COMMANDS.SETTINGS:
|
||||
command = CommandOpenSettings;
|
||||
break;
|
||||
default: {
|
||||
// compile-time check: if jumpCommand is not a known value, this line errors
|
||||
const _exhaustive: never = jumpCommand;
|
||||
return; // or handle error
|
||||
}
|
||||
}
|
||||
|
||||
if (command) {
|
||||
sendCommand(command);
|
||||
}
|
||||
})
|
||||
|
||||
function getIconPath(iconName: string): string {
|
||||
return path.join(process.resourcesPath, 'icons', iconName);
|
||||
}
|
||||
|
||||
app.setUserTasks
|
||||
([
|
||||
{
|
||||
program: process.execPath,
|
||||
arguments: JUMP_COMMANDS.SETTINGS,
|
||||
iconPath: getIconPath('SettingsIcon.ico'),
|
||||
iconIndex: 0,
|
||||
title: 'Settings',
|
||||
description: 'Open Settings Screen'
|
||||
},
|
||||
{
|
||||
program: process.execPath,
|
||||
arguments: JUMP_COMMANDS.INBOX,
|
||||
iconPath: getIconPath('InboxIcon.ico'),
|
||||
iconIndex: 0,
|
||||
title: 'Inbox',
|
||||
description: 'Open Inbox'
|
||||
},
|
||||
{
|
||||
program: process.execPath,
|
||||
arguments: JUMP_COMMANDS.PLANNER,
|
||||
iconPath: getIconPath('PlannerIcon.ico'),
|
||||
iconIndex: 0,
|
||||
title: 'Planner',
|
||||
description: 'Open Planner'
|
||||
},
|
||||
{
|
||||
program: process.execPath,
|
||||
arguments: JUMP_COMMANDS.OFFICE,
|
||||
iconPath: getIconPath('OfficeIcon.ico'),
|
||||
iconIndex: 0,
|
||||
title: 'Office',
|
||||
description: 'Open Office'
|
||||
}
|
||||
])
|
||||
|
||||
}
|
||||
+24
-5
@@ -1,4 +1,7 @@
|
||||
import { loginId } from '@hcengineering/login'
|
||||
import { loveId } from '@hcengineering/love'
|
||||
import { timeId } from '@hcengineering/time'
|
||||
|
||||
import { getEmbeddedLabel, getMetadata } from '@hcengineering/platform'
|
||||
import presentation, { MessageBox, setDownloadProgress } from '@hcengineering/presentation'
|
||||
import settings, { settingId } from '@hcengineering/setting'
|
||||
@@ -21,7 +24,7 @@ import { isOwnerOrMaintainer } from '@hcengineering/core'
|
||||
import { configurePlatform } from './platform'
|
||||
import { setupTitleBarMenu } from './titleBarMenu'
|
||||
import { defineScreenShare, defineGetDisplayMedia } from './screenShare'
|
||||
import { StandardMenuCommandLogout, StandardMenuCommandSelectWorkspace, StandardMenuCommandOpenSettings } from './types'
|
||||
import { CommandLogout, CommandSelectWorkspace, CommandOpenSettings, CommandOpenInbox, CommandOpenPlanner, CommandOpenOffice } from './types'
|
||||
import { ipcMainExposed } from './typesUtils'
|
||||
import { themeStore } from '@hcengineering/theme'
|
||||
|
||||
@@ -61,18 +64,34 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||
createApp(document.body)
|
||||
})
|
||||
|
||||
ipcMain.on(StandardMenuCommandOpenSettings, () => {
|
||||
function openScreen(screenId: any): void {
|
||||
closePopup()
|
||||
closePanel()
|
||||
const loc = getCurrentResolvedLocation()
|
||||
loc.fragment = undefined
|
||||
loc.query = undefined
|
||||
loc.path[2] = settingId
|
||||
loc.path[2] = screenId
|
||||
loc.path.length = 3
|
||||
navigate(loc)
|
||||
}
|
||||
|
||||
ipcMain.on(CommandOpenSettings, () => {
|
||||
openScreen(settingId)
|
||||
})
|
||||
|
||||
ipcMain.on(StandardMenuCommandSelectWorkspace, () => {
|
||||
ipcMain.on(CommandOpenInbox, () => {
|
||||
openScreen(notificationId)
|
||||
})
|
||||
|
||||
ipcMain.on(CommandOpenOffice, () => {
|
||||
openScreen(loveId)
|
||||
})
|
||||
|
||||
ipcMain.on(CommandOpenPlanner, () => {
|
||||
openScreen(timeId)
|
||||
})
|
||||
|
||||
ipcMain.on(CommandSelectWorkspace, () => {
|
||||
closePopup()
|
||||
closePanel()
|
||||
const loc = getCurrentResolvedLocation()
|
||||
@@ -84,7 +103,7 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||
navigate(loc)
|
||||
})
|
||||
|
||||
ipcMain.on(StandardMenuCommandLogout, () => {
|
||||
ipcMain.on(CommandLogout, () => {
|
||||
void logOut().then(() => {
|
||||
navigate({ path: [loginId] })
|
||||
})
|
||||
|
||||
+14
-4
@@ -73,10 +73,20 @@ export interface Branding {
|
||||
|
||||
export type BrandingMap = Record<string, Branding>
|
||||
|
||||
export const StandardMenuCommandOpenSettings = 'open-settings' as const
|
||||
export const StandardMenuCommandSelectWorkspace = 'select-workspace' as const
|
||||
export const StandardMenuCommandLogout = 'logout' as const
|
||||
export type StandardMenuCommand = typeof StandardMenuCommandOpenSettings | typeof StandardMenuCommandSelectWorkspace | typeof StandardMenuCommandLogout
|
||||
export const CommandOpenSettings = 'open-settings' as const
|
||||
export const CommandOpenInbox = 'open-inbox' as const
|
||||
export const CommandOpenOffice = 'open-office' as const
|
||||
export const CommandOpenPlanner = 'open-planner' as const
|
||||
export const CommandSelectWorkspace = 'select-workspace' as const
|
||||
export const CommandLogout = 'logout' as const
|
||||
|
||||
export type Command =
|
||||
typeof CommandOpenSettings |
|
||||
typeof CommandOpenInbox |
|
||||
typeof CommandOpenOffice |
|
||||
typeof CommandOpenPlanner |
|
||||
typeof CommandSelectWorkspace |
|
||||
typeof CommandLogout
|
||||
|
||||
export interface NotificationParams {
|
||||
title: string
|
||||
|
||||
Reference in New Issue
Block a user