Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 26 additions & 32 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"dmg-license": "^1.0.11"
},
"dependencies": {
"@nut-tree-fork/nut-js": "^4.2.6"
"@nut-tree-fork/nut-js": "^4.2.6",
"uiohook-napi": "^1.5.4"
}
}
4 changes: 4 additions & 0 deletions public/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,10 @@
"update_available": "Update available for download",
"download": "Download the update"
},
"CommandBar": {
"Placeholder": "Enter a phone number...",
"Call": "Call"
},
"Errors": {
"browser_permissions": "Browser permissions error",
"user_permissions": "Media permissions error",
Expand Down
4 changes: 4 additions & 0 deletions public/locales/it/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,10 @@
"update_available": "Aggiornamento disponibile per il download",
"download": "Scarica l'aggiornamento"
},
"CommandBar": {
"Placeholder": "Inserisci un numero di telefono...",
"Call": "Chiama"
},
"Errors": {
"browser_permissions": "Errore nei permessi del browser",
"user_permissions": "Errore nei permessi dei media",
Expand Down
16 changes: 16 additions & 0 deletions src/main/classes/controllers/AccountController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,22 @@ export class AccountController {
}
}

async updateCommandBarShortcut(commandBarShortcut: any) {
if (store.store) {
const account = store.store.account
if (account) {
account.commandBarShortcut = commandBarShortcut

store.set('account', account, true)
const auth = store.store.auth

auth!.availableAccounts[getAccountUID(account)] = account
store.set('auth', auth, true)
}
store.saveToDisk()
}
}


getAccountPhoneIslandPosition(): { x: number; y: number } | undefined {
return store.store.account?.phoneIslandPosition
Expand Down
80 changes: 80 additions & 0 deletions src/main/classes/controllers/CommandBarController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { CommandBarWindow } from '../windows'
import { IPC_EVENTS } from '@shared/constants'
import { Log } from '@shared/utils/logger'
import { screen } from 'electron'

export class CommandBarController {
static instance: CommandBarController
window: CommandBarWindow
private isVisible: boolean = false

constructor() {
CommandBarController.instance = this
this.window = new CommandBarWindow()
this.setupBlurListener()
}

private setupBlurListener() {
this.window.addOnBuildListener(() => {
const window = this.window.getWindow()
if (window) {
window.on('blur', () => {
this.hide()
})
}
})
}

show() {
try {
const window = this.window.getWindow()
if (window && !this.isVisible) {
const cursorPoint = screen.getCursorScreenPoint()
const currentDisplay = screen.getDisplayNearestPoint(cursorPoint)
const { x, y, width, height } = currentDisplay.workArea
const windowBounds = window.getBounds()

const centerX = x + Math.round((width - windowBounds.width) / 2)
const centerY = y + Math.round(height * 0.3)

window.setBounds({ x: centerX, y: centerY })
window.show()
window.setAlwaysOnTop(true, 'screen-saver')
window.focus()
this.isVisible = true
this.window.emit(IPC_EVENTS.SHOW_COMMAND_BAR)
}
} catch (e) {
Log.warning('error during showing CommandBarWindow:', e)
}
}

hide() {
try {
const window = this.window.getWindow()
if (window && this.isVisible) {
window.hide()
this.isVisible = false
this.window.emit(IPC_EVENTS.HIDE_COMMAND_BAR)
}
} catch (e) {
Log.warning('error during hiding CommandBarWindow:', e)
}
}

toggle() {
if (this.isVisible) {
this.hide()
} else {
this.show()
}
}

isOpen(): boolean {
return this.isVisible
}

async safeQuit() {
await this.window.quit(true)
}
}
1 change: 1 addition & 0 deletions src/main/classes/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './LoginController'
export * from './PhoneIslandController'
export * from './TrayController'
export * from './DevToolsController'
export * from './CommandBarController'
42 changes: 42 additions & 0 deletions src/main/classes/windows/CommandBarWindow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { PAGES } from '@shared/types'
import { BaseWindow } from './BaseWindow'

export class CommandBarWindow extends BaseWindow {
constructor() {
super(PAGES.COMMANDBAR, {
width: 500,
height: 80,
show: false,
fullscreenable: false,
autoHideMenuBar: true,
closable: false,
alwaysOnTop: true,
minimizable: false,
maximizable: false,
movable: false,
resizable: false,
skipTaskbar: true,
roundedCorners: true,
parent: undefined,
transparent: true,
hiddenInMissionControl: true,
hasShadow: true,
center: true,
fullscreen: false,
enableLargerThanScreen: false,
frame: false,
thickFrame: false,
trafficLightPosition: { x: 0, y: 0 },
webPreferences: {
nodeIntegration: true
}
})

this.addOnBuildListener(() => {
const window = this.getWindow()
if (window) {
window.setAlwaysOnTop(true, 'screen-saver')
}
})
}
}
1 change: 1 addition & 0 deletions src/main/classes/windows/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './SplashScreenWindow'
export * from './NethLinkWindow'
export * from './PhoneIslandWindow'
export * from './DevToolsWindow'
export * from './CommandBarWindow'
30 changes: 30 additions & 0 deletions src/main/lib/ipcEvents.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AccountController, DevToolsController } from '@/classes/controllers'
import { LoginController } from '@/classes/controllers/LoginController'
import { PhoneIslandController } from '@/classes/controllers/PhoneIslandController'
import { CommandBarController } from '@/classes/controllers/CommandBarController'
import { IPC_EVENTS } from '@shared/constants'
import { Account, OnDraggingWindow, PAGES } from '@shared/types'
import { BrowserWindow, app, ipcMain, screen, shell, desktopCapturer, globalShortcut, clipboard } from 'electron'
Expand Down Expand Up @@ -462,4 +463,33 @@ export function registerIpcEvents() {
Log.error('URL PARAM error', e)
}
})

ipcMain.on(IPC_EVENTS.TOGGLE_COMMAND_BAR, () => {
try {
CommandBarController.instance?.toggle()
} catch (e) {
Log.error('TOGGLE_COMMAND_BAR error', e)
}
})

ipcMain.on(IPC_EVENTS.SHOW_COMMAND_BAR, () => {
try {
CommandBarController.instance?.show()
} catch (e) {
Log.error('SHOW_COMMAND_BAR error', e)
}
})

ipcMain.on(IPC_EVENTS.HIDE_COMMAND_BAR, () => {
try {
CommandBarController.instance?.hide()
} catch (e) {
Log.error('HIDE_COMMAND_BAR error', e)
}
})

ipcMain.on(IPC_EVENTS.CHANGE_COMMAND_BAR_SHORTCUT, async (_, combo) => {
AccountController.instance.updateCommandBarShortcut(combo)
Log.info('Command Bar shortcut changed to:', combo)
})
}
56 changes: 56 additions & 0 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { app, ipcMain, nativeTheme, powerMonitor, protocol, systemPreferences, d
import { registerIpcEvents, isCallActive } from '@/lib/ipcEvents'
import { AccountController } from './classes/controllers'
import { PhoneIslandController } from './classes/controllers/PhoneIslandController'
import { CommandBarController } from './classes/controllers/CommandBarController'
import { Account, AuthAppData, AvailableThemes } from '@shared/types'
import { TrayController } from './classes/controllers/TrayController'
import { LoginController } from './classes/controllers/LoginController'
Expand Down Expand Up @@ -374,6 +375,22 @@ function attachOnReadyProcess() {
Log.info("Unregister all shortcuts")
await globalShortcut.unregisterAll()

// Stop uiohook for command bar
if (uiohookStarted) {
try {
const { uIOhook } = require('uiohook-napi')
uIOhook.stop()
Log.info('uIOhook stopped')
} catch (e) {
Log.warning('Failed to stop uIOhook:', e)
}
}

// Quit command bar
if (CommandBarController.instance) {
await CommandBarController.instance.safeQuit()
}

Log.info('APP QUIT CORRECTLY')
app.exit();
})
Expand Down Expand Up @@ -705,6 +722,8 @@ async function createNethLink(show: boolean = true) {
NethLinkController.instance.show()
await delay(1000)
new PhoneIslandController()
new CommandBarController()
initCommandBarShortcut()
checkForUpdate()
const account = store.get('account') as Account
if (account) {
Expand All @@ -716,6 +735,43 @@ async function createNethLink(show: boolean = true) {
}
}

let uiohookStarted = false
let lastModifierPress = 0
const DOUBLE_TAP_THRESHOLD = 400

function initCommandBarShortcut() {
if (uiohookStarted) return

try {
const { uIOhook, UiohookKey } = require('uiohook-napi')

uIOhook.on('keydown', (e: any) => {
const isMac = process.platform === 'darwin'
const isModifierKey = isMac
? e.keycode === UiohookKey.Meta || e.keycode === UiohookKey.MetaRight
: e.keycode === UiohookKey.Ctrl || e.keycode === UiohookKey.CtrlRight

if (isModifierKey) {
const now = Date.now()
if (now - lastModifierPress < DOUBLE_TAP_THRESHOLD) {
if (CommandBarController.instance) {
CommandBarController.instance.toggle()
}
lastModifierPress = 0
} else {
lastModifierPress = now
}
}
})

uIOhook.start()
uiohookStarted = true
Log.info('Command Bar shortcut initialized (double-tap Cmd/Ctrl)')
} catch (e) {
Log.warning('Failed to initialize Command Bar shortcut:', e)
}
}

async function checkForUpdate() {
Log.info('Current app version:', app.getVersion(), 'check for updates...')
const latestVersionData = await NetworkController.instance.get(GIT_RELEASES_URL)
Expand Down
Loading