diff --git a/.github/workflows/build-app-beta.yaml b/.github/workflows/build-app-beta.yaml index b32ef51bf..22b0d2a2b 100644 --- a/.github/workflows/build-app-beta.yaml +++ b/.github/workflows/build-app-beta.yaml @@ -11,8 +11,8 @@ jobs: strategy: matrix: - # os: [ubuntu-18.04, windows-2016] - os: [macOS-10.15, windows-2022, ubuntu-18.04] + # os: [macOS-10.15, windows-2022, ubuntu-18.04] + os: [macOS-10.15] steps: - name: Context @@ -48,6 +48,8 @@ jobs: GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }} WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }} + CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }} + CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }} - name: Save snap login if: matrix.os == 'ubuntu-18.04' diff --git a/app/package.json b/app/package.json index 77566f39b..4d3146bb0 100644 --- a/app/package.json +++ b/app/package.json @@ -15,8 +15,9 @@ "url": "https://github.com/dbgate/dbgate.git" }, "build": { - "artifactName": "${productName}-${version}-${os}_${arch}.${ext}", + "artifactName": "dbgate-${version}-${os}_${arch}.${ext}", "appId": "org.dbgate", + "productName": "DbGate", "mac": { "category": "database", "icon": "icon512.png", @@ -26,8 +27,7 @@ "target": { "target": "default", "arch": [ - "arm64", - "x64" + "universal" ] } }, diff --git a/app/src/electron.js b/app/src/electron.js index c9fb22cde..e2d7f7013 100644 --- a/app/src/electron.js +++ b/app/src/electron.js @@ -20,6 +20,9 @@ const { settings } = require('cluster'); const configRootPath = path.join(app.getPath('userData'), 'config-root.json'); let initialConfig = {}; +let apiLoaded = false; + +const isMac = () => os.platform() == 'darwin'; try { initialConfig = JSON.parse(fs.readFileSync(configRootPath, { encoding: 'utf-8' })); @@ -41,6 +44,7 @@ try { // be closed automatically when the JavaScript object is garbage collected. let mainWindow; let mainMenu; +let runCommandOnLoad = null; log.transports.file.level = 'debug'; autoUpdater.logger = log; @@ -59,30 +63,62 @@ function formatKeyText(keyText) { return keyText.replace('CtrlOrCommand+', 'Ctrl+'); } -function commandItem(id) { +function commandItem(item) { + const id = item.command; const command = commands[id]; + if (item.skipInApp) { + return { skip: true }; + } return { id, label: command ? command.menuName || command.toolbarName || command.name : id, accelerator: formatKeyText(command ? command.keyText : undefined), enabled: command ? command.enabled : false, click() { - mainWindow.webContents.send('run-command', id); + if (mainWindow) { + mainWindow.webContents.send('run-command', id); + } else { + runCommandOnLoad = id; + createWindow(); + } }, }; } function buildMenu() { - const template = _cloneDeepWith(mainMenuDefinition, item => { + let template = _cloneDeepWith(mainMenuDefinition, item => { if (item.divider) { return { type: 'separator' }; } if (item.command) { - return commandItem(item.command); + return commandItem(item); } }); + template = _cloneDeepWith(template, item => { + if (Array.isArray(item) && item.find(x => x.skip)) { + return item.filter(x => x && !x.skip); + } + }); + + if (isMac()) { + template = [ + { + label: 'DbGate', + submenu: [ + commandItem({ command: 'about.show' }), + { role: 'services' }, + { role: 'hide' }, + { role: 'hideOthers' }, + { role: 'unhide' }, + { role: 'quit' }, + ], + }, + ...template, + ]; + } + return Menu.buildFromTemplate(template); } @@ -105,8 +141,12 @@ ipcMain.on('update-commands', async (event, arg) => { menu.enabled = command.enabled; } }); -ipcMain.on('close-window', async (event, arg) => { - mainWindow.close(); +ipcMain.on('quit-app', async (event, arg) => { + if (isMac()) { + app.quit(); + } else { + mainWindow.close(); + } }); ipcMain.on('set-title', async (event, arg) => { mainWindow.setTitle(arg); @@ -117,6 +157,12 @@ ipcMain.on('open-link', async (event, arg) => { ipcMain.on('open-dev-tools', () => { mainWindow.webContents.openDevTools(); }); +ipcMain.on('app-started', async (event, arg) => { + if (runCommandOnLoad) { + mainWindow.webContents.send('run-command', runCommandOnLoad); + runCommandOnLoad = null; + } +}); ipcMain.on('window-action', async (event, arg) => { switch (arg) { case 'minimize': @@ -252,25 +298,28 @@ function createWindow() { // mainWindow.webContents.toggleDevTools(); } - const apiPackage = path.join( - __dirname, - process.env.DEVMODE ? '../../packages/api/src/index' : '../packages/api/dist/bundle.js' - ); + if (!apiLoaded) { + const apiPackage = path.join( + __dirname, + process.env.DEVMODE ? '../../packages/api/src/index' : '../packages/api/dist/bundle.js' + ); - global.API_PACKAGE = apiPackage; - global.NATIVE_MODULES = path.join(__dirname, 'nativeModules'); + global.API_PACKAGE = apiPackage; + global.NATIVE_MODULES = path.join(__dirname, 'nativeModules'); - // console.log('global.API_PACKAGE', global.API_PACKAGE); - const api = require(apiPackage); - // console.log( - // 'REQUIRED', - // path.resolve( - // path.join(__dirname, process.env.DEVMODE ? '../../packages/api/src/index' : '../packages/api/dist/bundle.js') - // ) - // ); - const main = api.getMainModule(); - main.initializeElectronSender(mainWindow.webContents); - main.useAllControllers(null, electron); + // console.log('global.API_PACKAGE', global.API_PACKAGE); + const api = require(apiPackage); + // console.log( + // 'REQUIRED', + // path.resolve( + // path.join(__dirname, process.env.DEVMODE ? '../../packages/api/src/index' : '../packages/api/dist/bundle.js') + // ) + // ); + const main = api.getMainModule(); + main.initializeElectronSender(mainWindow.webContents); + main.useAllControllers(null, electron); + apiLoaded = true; + } loadMainWindow(); @@ -299,7 +348,7 @@ app.on('ready', onAppReady); app.on('window-all-closed', function () { // On OS X it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q - if (process.platform !== 'darwin') { + if (!isMac()) { app.quit(); } }); diff --git a/app/src/mainMenuDefinition.js b/app/src/mainMenuDefinition.js index 6cd62895a..94bcbd0ea 100644 --- a/app/src/mainMenuDefinition.js +++ b/app/src/mainMenuDefinition.js @@ -17,7 +17,7 @@ module.exports = [ { command: 'group.saveAs', hideDisabled: true }, { divider: true }, { command: 'file.exit', hideDisabled: true }, - { command: 'app.logout', hideDisabled: true }, + { command: 'app.logout', hideDisabled: true, skipInApp: true }, ], }, { diff --git a/package.json b/package.json index f1c69a0a6..27a130fb1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "4.8.4-beta.2", + "version": "4.8.4-beta.6", "name": "dbgate-all", "workspaces": [ "packages/*", diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 8ab6892ff..8edbf3d54 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -17,6 +17,7 @@ import { apiCall } from './utility/api'; import { getConfig, getSettings, getUsedApps } from './utility/metadataLoaders'; import AppTitleProvider from './utility/AppTitleProvider.svelte'; + import getElectron from './utility/getElectron'; let loadedApi = false; let loadedPlugins = false; @@ -63,6 +64,7 @@ if (loadedApi && $loadingPluginStore?.loaded) { setAppLoaded(); loadedPlugins = true; + getElectron()?.send('app-started'); } } diff --git a/packages/web/src/commands/stdCommands.ts b/packages/web/src/commands/stdCommands.ts index 058afaf5a..359b2c5c2 100644 --- a/packages/web/src/commands/stdCommands.ts +++ b/packages/web/src/commands/stdCommands.ts @@ -29,6 +29,7 @@ import { apiCall } from '../utility/api'; import runCommand from './runCommand'; import { openWebLink } from '../utility/exportFileTools'; import { getSettings } from '../utility/metadataLoaders'; +import { isMac } from '../utility/common'; // function themeCommand(theme: ThemeDefinition) { // return { @@ -97,7 +98,7 @@ registerCommand({ toolbarOrder: 2, name: 'Query', toolbarName: 'New query', - keyText: 'CtrlOrCommand+Q', + keyText: 'CtrlOrCommand+T', onClick: () => newQuery(), }); @@ -457,9 +458,10 @@ if (hasPermission('settings/change')) { registerCommand({ id: 'file.exit', category: 'File', - name: 'Exit', + name: isMac() ? 'Quit' : 'Exit', + // keyText: isMac() ? 'Command+Q' : null, testEnabled: () => getElectron() != null, - onClick: () => getElectron().send('close-window'), + onClick: () => getElectron().send('quit-app'), }); registerCommand({ diff --git a/packages/web/src/datagrid/DataGridCore.svelte b/packages/web/src/datagrid/DataGridCore.svelte index 7cd5be1e2..febd606c1 100644 --- a/packages/web/src/datagrid/DataGridCore.svelte +++ b/packages/web/src/datagrid/DataGridCore.svelte @@ -306,6 +306,7 @@ import EditJsonModal from '../modals/EditJsonModal.svelte'; import { apiCall } from '../utility/api'; import getElectron from '../utility/getElectron'; + import { isCtrlOrCommandKey } from '../utility/common'; export let onLoadNextData = undefined; export let grider = undefined; @@ -976,7 +977,7 @@ const oldCurrentCell = currentCell; currentCell = cell; - if (event.ctrlKey) { + if (isCtrlOrCommandKey(event)) { if (isRegularCell(cell)) { if (selectedCells.find(x => x[0] == cell[0] && x[1] == cell[1])) { selectedCells = selectedCells.filter(x => x[0] != cell[0] || x[1] != cell[1]); @@ -1116,6 +1117,7 @@ if ( !event.ctrlKey && !event.altKey && + !event.metaKey && ((event.keyCode >= keycodes.a && event.keyCode <= keycodes.z) || (event.keyCode >= keycodes.n0 && event.keyCode <= keycodes.n9) || (event.keyCode >= keycodes.numPad0 && event.keyCode <= keycodes.numPad9) || @@ -1150,7 +1152,7 @@ function handleCursorMove(event) { if (!isRegularCell(currentCell)) return null; let rowCount = grider.rowCount; - if (event.ctrlKey) { + if (isCtrlOrCommandKey(event)) { switch (event.keyCode) { case keycodes.upArrow: case keycodes.pageUp: diff --git a/packages/web/src/datagrid/InplaceEditor.svelte b/packages/web/src/datagrid/InplaceEditor.svelte index 4b27ebd1b..81d85d389 100644 --- a/packages/web/src/datagrid/InplaceEditor.svelte +++ b/packages/web/src/datagrid/InplaceEditor.svelte @@ -13,6 +13,7 @@ import createRef from '../utility/createRef'; import _ from 'lodash'; import { arrayToHexString, parseCellValue, stringifyCellValue } from 'dbgate-tools'; + import { isCtrlOrCommandKey } from '../utility/common'; export let inplaceEditorState; export let dispatchInsplaceEditor; @@ -43,7 +44,7 @@ dispatchInsplaceEditor({ type: 'close', mode: 'enter' }); break; case keycodes.s: - if (event.ctrlKey) { + if (isCtrlOrCommandKey(event)) { if (isChangedRef.get()) { onSetValue(parseCellValue(domEditor.value)); // grider.setCellValue(rowIndex, uniqueName, editor.value); diff --git a/packages/web/src/designer/DesignerTable.svelte b/packages/web/src/designer/DesignerTable.svelte index ff7d9b9d6..072d00533 100644 --- a/packages/web/src/designer/DesignerTable.svelte +++ b/packages/web/src/designer/DesignerTable.svelte @@ -10,6 +10,7 @@ import { showModal } from '../modals/modalTools'; import { currentThemeDefinition } from '../stores'; import VirtualForeignKeyEditorModal from '../tableeditor/VirtualForeignKeyEditorModal.svelte'; + import { isCtrlOrCommandKey } from '../utility/common'; import contextMenu from '../utility/contextMenu'; import moveDrag from '../utility/moveDrag'; import ColumnLine from './ColumnLine.svelte'; @@ -217,7 +218,7 @@ e.stopPropagation(); onBringToFront(table); if (settings?.canSelectTables && !table?.isSelectedTable) { - onSelectTable(table, e.ctrlKey); + onSelectTable(table, isCtrlOrCommandKey(e)); } } }} diff --git a/packages/web/src/formview/FormView.svelte b/packages/web/src/formview/FormView.svelte index 7e75085e5..06a00b3df 100644 --- a/packages/web/src/formview/FormView.svelte +++ b/packages/web/src/formview/FormView.svelte @@ -177,6 +177,7 @@ import { apiCall } from '../utility/api'; import { copyTextToClipboard, extractRowCopiedValue } from '../utility/clipboard'; + import { isCtrlOrCommandKey } from '../utility/common'; import contextMenu, { getContextMenu, registerMenu } from '../utility/contextMenu'; import createActivator, { getActiveComponent } from '../utility/createActivator'; import createReducer from '../utility/createReducer'; @@ -383,6 +384,7 @@ if ( !event.ctrlKey && + !event.metaKey && !event.altKey && ((event.keyCode >= keycodes.a && event.keyCode <= keycodes.z) || (event.keyCode >= keycodes.n0 && event.keyCode <= keycodes.n9) || @@ -472,7 +474,7 @@ return moveCurrentCell(columnIndex % formDisplay.columns.length, Math.floor(columnIndex / rowCount) * 2); }; - if (event.ctrlKey) { + if (isCtrlOrCommandKey(event)) { switch (event.keyCode) { case keycodes.leftArrow: return moveCurrentCell(currentCell[0], 0); diff --git a/packages/web/src/utility/common.ts b/packages/web/src/utility/common.ts index 2e31ab5a6..423555269 100644 --- a/packages/web/src/utility/common.ts +++ b/packages/web/src/utility/common.ts @@ -64,3 +64,10 @@ export function resolveKeyText(keyText: string): string { } return keyText.replace('CtrlOrCommand+', 'Ctrl+'); } + +export function isCtrlOrCommandKey(event) { + if (isMac()) { + return event.metaKey; + } + return event.ctrlKey; +}