From 27987f5078764bc2f68ab41d686efeacc047d158 Mon Sep 17 00:00:00 2001 From: therockerline Date: Tue, 10 Dec 2024 16:38:19 +0100 Subject: [PATCH 01/21] [wip] add click2call --- README.md | 10 + package-lock.json | 83 +++++- package.json | 9 +- public/locales/en/translations.json | 8 +- public/locales/it/translations.json | 8 +- .../controllers/PhoneIslandController.ts | 14 +- src/main/lib/ipcEvents.ts | 17 +- src/main/main.ts | 3 - .../public/locales/en/translations.json | 8 +- .../public/locales/it/translations.json | 8 +- .../Modules/NethVoice/BaseModule/Navbar.tsx | 240 ++---------------- .../NethVoice/BaseModule/ProfileDialog.tsx | 125 +++++++++ .../DeviceSettings/DeviceBox.tsx | 73 ++++++ .../BaseModule/ProfileDialog/Line.tsx | 3 + .../BaseModule/ProfileDialog/MenuAction.tsx | 43 ++++ .../BaseModule/ProfileDialog/MenuPage.tsx | 44 ++++ .../ProfileDialog/NethVoiceLink.tsx | 21 ++ .../ProfileDialog/OptionElement.tsx | 52 ++++ .../PresenceSettings}/PresenceBadge.tsx | 0 .../PresenceSettings}/PresenceBox.tsx | 141 +++++----- .../PresenceSettings}/PresenceItem.tsx | 0 .../BaseModule/ProfileDialog/ProfileData.tsx | 28 ++ .../ProfileDialog/ThemeSettings/ThemeBox.tsx | 58 +++++ .../phoneIsland/phoneIslandContainer.tsx | 16 +- .../src/hooks/usePhoneIslandEventListeners.ts | 11 +- src/renderer/src/pages/PhoneIslandPage.tsx | 13 +- src/renderer/src/store.ts | 1 + src/shared/constants.ts | 5 +- src/shared/types.ts | 2 + src/shared/useNethVoiceAPI.ts | 12 +- 30 files changed, 729 insertions(+), 327 deletions(-) create mode 100644 src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx create mode 100644 src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx create mode 100644 src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/Line.tsx create mode 100644 src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuAction.tsx create mode 100644 src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuPage.tsx create mode 100644 src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/NethVoiceLink.tsx create mode 100644 src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/OptionElement.tsx rename src/renderer/src/components/Modules/NethVoice/{Presence => BaseModule/ProfileDialog/PresenceSettings}/PresenceBadge.tsx (100%) rename src/renderer/src/components/Modules/NethVoice/{Presence => BaseModule/ProfileDialog/PresenceSettings}/PresenceBox.tsx (59%) rename src/renderer/src/components/Modules/NethVoice/{Presence => BaseModule/ProfileDialog/PresenceSettings}/PresenceItem.tsx (100%) create mode 100644 src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/ProfileData.tsx create mode 100644 src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/ThemeSettings/ThemeBox.tsx diff --git a/README.md b/README.md index b39d0e4e..59ba7b9b 100644 --- a/README.md +++ b/README.md @@ -53,3 +53,13 @@ $ npm run publish:patch > `INSTANCE=`\ > enables multiple instances of the process. Use this function with extreme caution, it can cause many problems - for testing purposes only. When this variable is set, a new `user_data_.json` is created and the instance only changes its related file. + +### User data folders + +- Windows: `%APPDATA%/nethlink/` +- Linux: `~/.config/nethlink/` +- macOS: `~/Library/Application Support/nethlink/` + + +We then have two files: user_data.json and available_users.json the first file contains the data of the currently logged in user, the other contains the data of all available users in that device who have logged in at least once + diff --git a/package-lock.json b/package-lock.json index 5582d707..e61e945a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,10 @@ "@fortawesome/react-fontawesome": "^0.2.0", "@headlessui/react": "^1.7.18", "@hookform/resolvers": "^3.3.4", - "@nethesis/phone-island": "^0.8.28", + "@nethesis/nethesis-brands-svg-icons": "github:nethesis/Font-Awesome#ns-brands", + "@nethesis/nethesis-light-svg-icons": "github:nethesis/Font-Awesome#ns-light", + "@nethesis/nethesis-solid-svg-icons": "github:nethesis/Font-Awesome#ns-solid", + "@nethesis/phone-island": "^0.8.27", "@tailwindcss/forms": "^0.5.7", "@types/lodash": "^4.14.202", "@types/node": "^18.19.9", @@ -52,6 +55,7 @@ "i18next-http-backend": "^2.1.1", "lodash": "^4.17.21", "moment": "^2.30.1", + "motion": "^11.13.1", "path-browserify": "^1.0.1", "postcss": "^8.4.35", "prettier": "^3.2.4", @@ -4963,6 +4967,30 @@ "tslib": "^2.3.1" } }, + "node_modules/@nethesis/nethesis-brands-svg-icons": { + "version": "6.2.1", + "resolved": "git+ssh://git@github.com/nethesis/Font-Awesome.git#dbcf431acf0e504145486c9ac70f14946279e2e7", + "dev": true, + "hasInstallScript": true, + "license": "UNLICENSED", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.2.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@nethesis/nethesis-brands-svg-icons/node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.2.1.tgz", + "integrity": "sha512-Sz07mnQrTekFWLz5BMjOzHl/+NooTdW8F8kDQxjWwbpOJcnoSg4vUDng8d/WR1wOxM0O+CY9Zw0nR054riNYtQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/@nethesis/nethesis-light-svg-icons": { "version": "6.2.1", "resolved": "git+ssh://git@github.com/nethesis/Font-Awesome.git#3e110fb0ae63495fb8b7ca9811e5f07eed1ac962", @@ -5012,9 +5040,9 @@ } }, "node_modules/@nethesis/phone-island": { - "version": "0.8.28", - "resolved": "https://registry.npmjs.org/@nethesis/phone-island/-/phone-island-0.8.28.tgz", - "integrity": "sha512-4t9cf3AwvKa8G+AVgA+6y5JT0rZJ0q2qF7XNLl3Xrwgd4aICpVOVbd2D6lLJPlanG8dFFwENCwnej4Zq6KMIPA==", + "version": "0.8.27", + "resolved": "https://registry.npmjs.org/@nethesis/phone-island/-/phone-island-0.8.27.tgz", + "integrity": "sha512-Ui00Thsxus/oSa5SPNKh5uh/ATt06WOVq9t2ar47mt9QpeT1eDULzRWhk584swv8Nk1IK8eL5Ug8g2yaq3rBKg==", "dev": true, "license": "GPL-3.0-or-later", "dependencies": { @@ -14195,12 +14223,14 @@ } }, "node_modules/framer-motion": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.12.0.tgz", - "integrity": "sha512-gZaZeqFM6pX9kMVti60hYAa75jGpSsGYWAHbBfIkuHN7DkVHVkxSxeNYnrGmHuM0zPkWTzQx10ZT+fDjn7N4SA==", + "version": "11.13.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.13.1.tgz", + "integrity": "sha512-F40tpGTHByhn9h3zdBQPcEro+pSLtzARcocbNqAyfBI+u9S+KZuHH/7O9+z+GEkoF3eqFxfvVw0eBDytohwqmQ==", "dev": true, "license": "MIT", "dependencies": { + "motion-dom": "^11.13.0", + "motion-utils": "^11.13.0", "tslib": "^2.4.0" }, "peerDependencies": { @@ -19415,6 +19445,45 @@ "node": "*" } }, + "node_modules/motion": { + "version": "11.13.1", + "resolved": "https://registry.npmjs.org/motion/-/motion-11.13.1.tgz", + "integrity": "sha512-64+QpZQv8WJJFn+tEEzX04il9s6ReA6lhKRZaxzD6SunGqoaq5g+AFVfcKWme8N83eytUOpGp7mpfJ9cyZlhAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "framer-motion": "^11.13.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/motion-dom": { + "version": "11.13.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.13.0.tgz", + "integrity": "sha512-Oc1MLGJQ6nrvXccXA89lXtOqFyBmvHtaDcTRGT66o8Czl7nuA8BeHAd9MQV1pQKX0d2RHFBFaw5g3k23hQJt0w==", + "dev": true + }, + "node_modules/motion-utils": { + "version": "11.13.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.13.0.tgz", + "integrity": "sha512-lq6TzXkH5c/ysJQBxgLXgM01qwBH1b4goTPh57VvZWJbVJZF/0SB31UWEn4EIqbVPf3au88n2rvK17SpDTja1A==", + "dev": true + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", diff --git a/package.json b/package.json index fc76f125..35aac7bb 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,10 @@ "@fortawesome/react-fontawesome": "^0.2.0", "@headlessui/react": "^1.7.18", "@hookform/resolvers": "^3.3.4", - "@nethesis/phone-island": "^0.8.28", + "@nethesis/nethesis-light-svg-icons": "github:nethesis/Font-Awesome#ns-light", + "@nethesis/nethesis-brands-svg-icons": "github:nethesis/Font-Awesome#ns-brands", + "@nethesis/nethesis-solid-svg-icons": "github:nethesis/Font-Awesome#ns-solid", + "@nethesis/phone-island": "^0.8.27", "@tailwindcss/forms": "^0.5.7", "@types/lodash": "^4.14.202", "@types/node": "^18.19.9", @@ -76,6 +79,7 @@ "i18next-http-backend": "^2.1.1", "lodash": "^4.17.21", "moment": "^2.30.1", + "motion": "^11.13.1", "path-browserify": "^1.0.1", "postcss": "^8.4.35", "prettier": "^3.2.4", @@ -100,5 +104,6 @@ }, "optionalDependencies": { "dmg-license": "^1.0.11" - } + }, + "dependencies": {} } diff --git a/public/locales/en/translations.json b/public/locales/en/translations.json index 4930fac7..5d040822 100644 --- a/public/locales/en/translations.json +++ b/public/locales/en/translations.json @@ -322,7 +322,10 @@ "For the following states": "For the following states", "Callforward": "Forward", "Do not disturb": "Do not disturb", - "Available presence": "Available presence" + "Available presence": "Available presence", + "IP Phone": "IP Phone", + "Web Phone": "Web Phone", + "Desktop Phone": "Desktop Phone" }, "TopBar": { "Signed in as": "Signed in as", @@ -344,7 +347,8 @@ "Mobile": "Mobile", "Cellphone": "Cellphone", "Activate voicemail": "Activate voicemail", - "Go to NethVoice CTI": "Go to NethVoice CTI" + "Go to NethVoice CTI": "Go to NethVoice CTI", + "Pair device": "Pair device" }, "OperatorDrawer": { "Book": "Book", diff --git a/public/locales/it/translations.json b/public/locales/it/translations.json index 94db954f..ce0f7ebd 100644 --- a/public/locales/it/translations.json +++ b/public/locales/it/translations.json @@ -322,7 +322,10 @@ "For the following states": "Per i seguenti stati", "Callforward": "Inoltro", "Do not disturb": "Non disturbare", - "Available presence": "Presence disponibili" + "Available presence": "Presence disponibili", + "IP Phone": "Telefono IP", + "Web Phone": "Web Phone", + "Desktop Phone": "Desktop Phone" }, "TopBar": { "Signed in as": "Accesso effettuato come", @@ -344,7 +347,8 @@ "Mobile": "Cellulare", "Cellphone": "Cellulare", "Activate voicemail": "Attiva voicemail", - "Go to NethVoice CTI": "Vai a NethVoice CTI" + "Go to NethVoice CTI": "Vai a NethVoice CTI", + "Pair device": "Abbina dispositivo" }, "OperatorDrawer": { "Book": "Prenota", diff --git a/src/main/classes/controllers/PhoneIslandController.ts b/src/main/classes/controllers/PhoneIslandController.ts index 07bf3ea1..dec46e8c 100644 --- a/src/main/classes/controllers/PhoneIslandController.ts +++ b/src/main/classes/controllers/PhoneIslandController.ts @@ -7,7 +7,7 @@ import { once } from '@/lib/ipcEvents' import { useNethVoiceAPI } from '@shared/useNethVoiceAPI' import { store } from '@/lib/mainStore' import { screen } from 'electron' -import { Size } from '@shared/types' +import { Extension, Size } from '@shared/types' export class PhoneIslandController { static instance: PhoneIslandController @@ -120,6 +120,18 @@ export class PhoneIslandController { this.window.emit(IPC_EVENTS.TRANSFER_CALL, to) } + updateDefaultDevice(ext: Extension, force: boolean) { + try { + + //const { NethVoiceAPI } = useNethVoiceAPI(store.store.account) + //NethVoiceAPI.User.me().then((me) => { + this.window.emit(IPC_EVENTS.CHANGE_DEFAULT_DEVICE, ext, force) + //}) + } catch (e) { + Log.warning('error during emitting updateDefaultDevice event to the PhoneIslandWindow:', e) + } + } + reconnect() { try { Log.info('PHONE ISLAND RECONNECT') diff --git a/src/main/lib/ipcEvents.ts b/src/main/lib/ipcEvents.ts index 252f9f95..e0eba91f 100644 --- a/src/main/lib/ipcEvents.ts +++ b/src/main/lib/ipcEvents.ts @@ -13,7 +13,7 @@ import { debouncer, getAccountUID, getPageFromQuery } from '@shared/utils/utils' import { NetworkController } from '@/classes/controllers/NetworkController' import { useLogin } from '@shared/useLogin' import { PhoneIslandWindow } from '@/classes/windows' - +import http from 'http' function onSyncEmitter( channel: IPC_EVENTS, @@ -51,6 +51,16 @@ export function registerIpcEvents() { return app.getSystemLocale() }) + ipcMain.on(IPC_EVENTS.EMIT_START_CALL, async (_event, phoneNumber) => { + PhoneIslandController.instance.call(phoneNumber) + }) + + ipcMain.on(IPC_EVENTS.START_CALL_BY_URL, async (_event, url) => { + http.get(url, (res) => { + Log.info('START_CALL_BY_URL', res) + }) + }) + ipcMain.on(IPC_EVENTS.UPDATE_SHARED_STATE, (_, newState, page, selector) => { const windows = BrowserWindow.getAllWindows(); store.updateStore(newState, `${page}[${selector}]`) @@ -180,6 +190,11 @@ export function registerIpcEvents() { ipcMain.on(IPC_EVENTS.LOGIN_WINDOW_RESIZE, (_, h) => { LoginController.instance.resize(h) }) + + ipcMain.on(IPC_EVENTS.CHANGE_DEFAULT_DEVICE, (_, ext, force) => { + PhoneIslandController.instance.updateDefaultDevice(ext, force ?? false) + }) + ipcMain.on(IPC_EVENTS.DELETE_ACCOUNT, (_, account: Account) => { Log.info('DELETE ACCOUNT', account) const accountUID = getAccountUID(account) diff --git a/src/main/main.ts b/src/main/main.ts index ab4a5511..6bee2a8c 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -65,9 +65,6 @@ function startup() { }) } - ipcMain.on(IPC_EVENTS.EMIT_START_CALL, async (_event, phoneNumber) => { - PhoneIslandController.instance.call(phoneNumber) - }) ipcMain.on(IPC_EVENTS.LOGIN, async (e, props?: { account?: Account, password?: string, showNethlink: boolean, }) => { const { password, showNethlink, account } = props || { showNethlink: true } if (LoginController.instance && LoginController.instance.window.isOpen() && password && account) { diff --git a/src/renderer/public/locales/en/translations.json b/src/renderer/public/locales/en/translations.json index 4930fac7..5d040822 100644 --- a/src/renderer/public/locales/en/translations.json +++ b/src/renderer/public/locales/en/translations.json @@ -322,7 +322,10 @@ "For the following states": "For the following states", "Callforward": "Forward", "Do not disturb": "Do not disturb", - "Available presence": "Available presence" + "Available presence": "Available presence", + "IP Phone": "IP Phone", + "Web Phone": "Web Phone", + "Desktop Phone": "Desktop Phone" }, "TopBar": { "Signed in as": "Signed in as", @@ -344,7 +347,8 @@ "Mobile": "Mobile", "Cellphone": "Cellphone", "Activate voicemail": "Activate voicemail", - "Go to NethVoice CTI": "Go to NethVoice CTI" + "Go to NethVoice CTI": "Go to NethVoice CTI", + "Pair device": "Pair device" }, "OperatorDrawer": { "Book": "Book", diff --git a/src/renderer/public/locales/it/translations.json b/src/renderer/public/locales/it/translations.json index 94db954f..ce0f7ebd 100644 --- a/src/renderer/public/locales/it/translations.json +++ b/src/renderer/public/locales/it/translations.json @@ -322,7 +322,10 @@ "For the following states": "Per i seguenti stati", "Callforward": "Inoltro", "Do not disturb": "Non disturbare", - "Available presence": "Presence disponibili" + "Available presence": "Presence disponibili", + "IP Phone": "Telefono IP", + "Web Phone": "Web Phone", + "Desktop Phone": "Desktop Phone" }, "TopBar": { "Signed in as": "Accesso effettuato come", @@ -344,7 +347,8 @@ "Mobile": "Cellulare", "Cellphone": "Cellulare", "Activate voicemail": "Attiva voicemail", - "Go to NethVoice CTI": "Vai a NethVoice CTI" + "Go to NethVoice CTI": "Vai a NethVoice CTI", + "Pair device": "Abbina dispositivo" }, "OperatorDrawer": { "Book": "Prenota", diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/Navbar.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/Navbar.tsx index 3f9670ce..441f5f36 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/Navbar.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/Navbar.tsx @@ -19,63 +19,25 @@ import { debouncer, getAccountUID, isDev } from '@shared/utils/utils' import { useSharedState } from '@renderer/store' import { createRef, useState } from 'react' import { useTheme } from '@renderer/theme/Context' -import { PresenceBox } from '../Presence/PresenceBox' +import { PresenceBox } from './ProfileDialog/PresenceSettings/PresenceBox' import classNames from 'classnames' import { truncate } from 'lodash' -import { PresenceBadge } from '../Presence/PresenceBadge' +import { PresenceBadge } from './ProfileDialog/PresenceSettings/PresenceBadge' import { SearchBox } from '../SearchResults/SearchBox' +import { ThemeBox } from './ProfileDialog/ThemeSettings/ThemeBox' +import { ProfileDialog } from './ProfileDialog' export interface NavbarProps { onClickAccount: () => void } -const themeOptions = [ - { id: 1, name: 'system', icon: SystemIcon }, - { id: 2, name: 'light', icon: LightIcon }, - { id: 3, name: 'dark', icon: DarkIcon } -] - export function Navbar({ onClickAccount }: NavbarProps): JSX.Element { - const { theme: nethTheme } = useTheme() + const { status } = useAccount() - const [account, setAccount] = useSharedState('account') - const [, setAuth] = useSharedState('auth') + const [account] = useSharedState('account') const [operators] = useSharedState('operators') - const [, setTheme] = useSharedState('theme') - const [isPresenceDialogVisible, setIsPresenceDialogVisible] = useState(false) - - const btnRef = createRef() - - function handleSetTheme(theme) { - setTheme(() => theme) - const updatedAccount = { ...account!, theme: theme } - setAccount(() => updatedAccount) - setAuth((p) => ({ - ...p, - isFirstStart: p?.isFirstStart ?? true, - availableAccounts: { - ...p?.availableAccounts, - [getAccountUID(updatedAccount as Account)]: updatedAccount - } - })) - } - function handleGoToNethVoicePage() { - window.api.openHostPage('/') - } - - function handleExitNethLink() { - window.api.exitNethLink() - } - - function handleLogout() { - window.api.logout() - } - - function showPresenceDialog(e) { - e.preventDefault() - setIsPresenceDialogVisible(true) - } + const [isProfileDialogOpen, setIsProfileDialogOpen] = useState(false) if (!account) return <> @@ -87,180 +49,28 @@ export function Navbar({ onClickAccount }: NavbarProps): JSX.Element { mainPresence={account?.data?.mainPresence} className={classNames()} /> -
- - - - - { - const el: HTMLButtonElement = e.target.parentElement! - .children[0]! as HTMLButtonElement - el.addEventListener( - 'focus', - (e) => { - ; (e.target! as HTMLButtonElement).blur() - }, - { - once: true - } - ) - }} - className={`dark:bg-bgDark bg-bgLight border dark:border-borderDark border-borderLight rounded-lg mt-2 fixed min-w-[225px] min-h-[145px] z-[200] translate-x-[calc(-100%+36px)]`} - > -

- {t('Settings.Theme')} -

- {themeOptions.map((availableTheme) => ( - -
handleSetTheme(availableTheme.name)} - > - {account.theme === availableTheme.name && ( - - )} -
- -

- {availableTheme.name === 'system' - ? t('Settings.System') - : availableTheme.name === 'light' - ? t('Settings.Light') - : t('Settings.Dark')} -

-
-
-
- ))} -
-
-
- - { - debouncer('reload_me', onClickAccount, 1000) - }} - > - - - { - const el: HTMLButtonElement = e.target.parentElement! - .children[0]! as HTMLButtonElement - el.addEventListener( - 'focus', - (e) => { - ; (e.target! as HTMLButtonElement).blur() - }, - { - once: true - } - ) - }} - static={isPresenceDialogVisible} - className={`dark:bg-bgDark bg-bgLight border dark:border-borderDark border-borderLight mt-2 pb-2 fixed rounded-lg min-w-[225px] min-h-[125px] z-[200] translate-x-[calc(-100%+36px)]`} - > - -
-

{t('TopBar.Signed in as')}

-
-

- {truncate(account.data?.name, { length: 20 })} -

-

- {account.data?.endpoints.mainextension[0].id} -

- {isDev() && ( -

- [{account.data?.default_device.type}] -

- )} -
-
-
- -
- -

{t('TopBar.Go to NethVoice CTI')}

-
-
- -
- -

{t('TopBar.Presence')}

-
-
- -
- -

{t('TopBar.Logout')}

-
-
- -
- -

{t('Common.Quit')}

-
-
-
-
+
{ + setIsProfileDialogOpen((p) => !p) + debouncer('reload_me', onClickAccount, 1000) + }} + > + +
- setIsPresenceDialogVisible(false)} + setIsProfileDialogOpen(false)} /> - + ) } diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx new file mode 100644 index 00000000..ef3c7d40 --- /dev/null +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx @@ -0,0 +1,125 @@ +import { + faXmarkCircle as ExitIcon, + faArrowRightFromBracket as LogoutIcon, +} from '@fortawesome/free-solid-svg-icons' +import { motion } from 'motion/react' +import { ProfileData } from './ProfileDialog/ProfileData' +import { MenuAction } from './ProfileDialog/MenuAction' +import { t } from 'i18next' +import { useEffect, useState } from 'react' +import classNames from 'classnames' +import { StatusDot } from '@renderer/components/Nethesis' +import { useAccount } from '@renderer/hooks/useAccount' +import { useSharedState } from '@renderer/store' +import { IconProp } from '@fortawesome/fontawesome-svg-core' +import { MenuPage } from './ProfileDialog/MenuPage' +import { Line } from './ProfileDialog/Line' +import { PresenceBox } from './ProfileDialog/PresenceSettings/PresenceBox' +import { ThemeBox, ThemeIcons } from './ProfileDialog/ThemeSettings/ThemeBox' +import { DeviceBox, DeviceIcons } from './ProfileDialog/DeviceSettings/DeviceBox' + +enum MenuItem { + device = 1, + presence, + theme, + +} +export const ProfileDialog = ({ + isOpen, + onClose, +}: { + isOpen: boolean, + onClose: () => void +}) => { + const { status, } = useAccount() + const [selectedMenu, setSelectedMenu] = useState(undefined) + const [account] = useSharedState('account') + const [device] = useSharedState('device') + const [x, setX] = useState(0) + const [dialogPageTitle, setDialogPageTitle] = useState('') + const [themeIcon, setThemeIcon] = useState(ThemeIcons['system']) + const [deviceIcon, setDeviceIcon] = useState(DeviceIcons['nethlink']) + function handleExitNethLink() { + window.api.exitNethLink() + } + + useEffect(() => { + if (selectedMenu) { + setX(250) + switch (selectedMenu) { + case MenuItem.device: setDialogPageTitle(() => t("TopBar.Device")); break; + case MenuItem.theme: setDialogPageTitle(() => t("TopBar.Theme")); break; + case MenuItem.presence: setDialogPageTitle(() => t("TopBar.Presence")); break; + } + } else { + setDialogPageTitle(() => '') + setX(0) + } + }, [selectedMenu]) + + function handleLogout() { + window.api.logout() + } + + useEffect(() => { + if (account) { + setDeviceIcon(() => DeviceIcons[device || 'nethlink']) + } + + }, [device]) + + useEffect(() => { + if (account) { + setThemeIcon(() => ThemeIcons[account?.theme || 'system']) + } + }, [account]) + + return ( +
+
+
+ + +
+ + setSelectedMenu(() => MenuItem.presence)} > + + +

{t('TopBar.Presence')}

+
+ setSelectedMenu(() => MenuItem.device)} icon={deviceIcon} label={t("TopBar.Pair device")} + /> + setSelectedMenu(() => MenuItem.theme)} icon={themeIcon} label={t('Settings.Theme')} /> +
+ + + + +
+ + setSelectedMenu(() => undefined)} + title={dialogPageTitle} + > + {selectedMenu === MenuItem.presence && } + {selectedMenu === MenuItem.device && } + {selectedMenu === MenuItem.theme && } + + + +
+
+ ) +} diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx new file mode 100644 index 00000000..22069117 --- /dev/null +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx @@ -0,0 +1,73 @@ +import { IconProp } from "@fortawesome/fontawesome-svg-core" +import { + faAdjust as WebDevice, + faAnchorLock as DesktopDevice, + faAngleDoubleLeft as PhysicalDevice, +} from '@fortawesome/free-solid-svg-icons' +import { useSharedState } from "@renderer/store" +import { AvailableDevices, Extension } from "@shared/types" +import { Log } from "@shared/utils/logger" +import { t } from "i18next" +import { OptionElement } from "../OptionElement" +import { useLoggedNethVoiceAPI } from "@renderer/hooks/useLoggedNethVoiceAPI" +import { debouncer } from "@shared/utils/utils" +import { IPC_EVENTS } from "@shared/constants" + +export const DeviceIcons = { + nethlink: DesktopDevice, + webrtc: WebDevice, + physical: PhysicalDevice +} +type DeviceType = { name: AvailableDevices, label: string, icon: IconProp } +type AvailableDeviceOption = { + id: string, + ext: Extension +} & DeviceType +export const DeviceBox = () => { + const [account] = useSharedState('account') + const [device, setDevice] = useSharedState('device') + async function handleSetDevice(newDevice: AvailableDeviceOption) { + Log.info('change device to', newDevice) + setDevice(() => newDevice.name) + debouncer('update-account-default-device', () => { + //await NethVoiceAPI.User.default_device(newDevice.ext) + window.electron.send(IPC_EVENTS.CHANGE_DEFAULT_DEVICE, newDevice.ext, true) + //TODO: lanciare anche l'eventDispatch su phoneIsland + }, 250) + } + + const themeOptions: DeviceType[] = [ + { name: 'nethlink', icon: DeviceIcons.nethlink, label: t('Settings.Desktop Phone') }, + { name: 'physical', icon: DeviceIcons.physical, label: t('Settings.IP Phone') }, + { name: 'webrtc', icon: DeviceIcons.webrtc, label: t('Settings.Web Phone') } + ] + + if (!account) return <> + const accountDevices: AvailableDeviceOption[] = account.data?.endpoints.extension.reduce((p, d) => { + const option = themeOptions.find((o) => o.name === d.type) + Log.info(d) + if (option) { + p.push({ + ...option, + label: d.description || option.label, + id: d.id, + ext: d + } as AvailableDeviceOption) + } + return p + }, []) || [] + + return ( +
+ {accountDevices.map((availableDevices) => ( + handleSetDevice(availableDevices)} + /> + ))} +
+ ) +} diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/Line.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/Line.tsx new file mode 100644 index 00000000..6822fc3c --- /dev/null +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/Line.tsx @@ -0,0 +1,3 @@ +export const Line = () => { + return
+} diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuAction.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuAction.tsx new file mode 100644 index 00000000..c6c6d9d5 --- /dev/null +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuAction.tsx @@ -0,0 +1,43 @@ +import { IconProp } from "@fortawesome/fontawesome-svg-core" +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" +import classNames from "classnames" +import { ReactElement } from "react" + +type BaseMenuActionProps = { + className?: string, + onClick: () => void, +} + +type MenuActionProps = { + label: string, + icon: IconProp +} & BaseMenuActionProps + +type MenuActionWrapProps = { + children: ReactElement | ReactElement[] +} & BaseMenuActionProps + +function MenuAction({ className, children, onClick }: MenuActionWrapProps) { + + return ( +
+
+
+ {children} +
+
+
+ ) +} + +MenuAction.item = (props: MenuActionProps) => + +

{props.label}

+
+MenuAction.itemWrap = (props: MenuActionWrapProps) => {props.children} + +export { + MenuAction +} diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuPage.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuPage.tsx new file mode 100644 index 00000000..e5aefd3d --- /dev/null +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuPage.tsx @@ -0,0 +1,44 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" +import { + faArrowLeft as ArrowIcon, +} from '@fortawesome/free-solid-svg-icons' +import { Button } from "@renderer/components/Nethesis" +import { t } from "i18next" +import { Line } from "./Line" +import classNames from "classnames" +import { Scrollable } from "@renderer/components/Scrollable" + +export const MenuPage = ({ goBack, title, children }) => { + return ( +
+
+
+ +

+ {t('Login.Back')} +

+
+
+ +
+

+ {title} +

+ + {children} + +
+
+ ) +} diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/NethVoiceLink.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/NethVoiceLink.tsx new file mode 100644 index 00000000..1c16ca32 --- /dev/null +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/NethVoiceLink.tsx @@ -0,0 +1,21 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" +import { + faArrowUpRightFromSquare as GoToNethVoiceIcon +} from '@fortawesome/free-solid-svg-icons' +import { t } from "i18next" +export const NethVoiceLink = () => { + + function handleGoToNethVoicePage() { + window.api.openHostPage('/') + } + + return ( +
+ +

{t('TopBar.Go to NethVoice CTI')}

+
+ ) +} diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/OptionElement.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/OptionElement.tsx new file mode 100644 index 00000000..9e581b03 --- /dev/null +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/OptionElement.tsx @@ -0,0 +1,52 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" +import { + faCheck as ChooseThemeMenuIcon, +} from '@fortawesome/free-solid-svg-icons' +import { IconProp } from "@fortawesome/fontawesome-svg-core" +import classNames from "classnames" + +type SettingsOptionElementProps = { + icon: IconProp, + label: string, + isSelected: boolean + onClick: () => void, +} +export const OptionElement = ({ + icon, + label, + isSelected, + onClick, +}: SettingsOptionElementProps) => { + return ( +
onClick()} + > +
+ +

+ {label} +

+
+ {isSelected && ( + + )} +
+ ) +} diff --git a/src/renderer/src/components/Modules/NethVoice/Presence/PresenceBadge.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceBadge.tsx similarity index 100% rename from src/renderer/src/components/Modules/NethVoice/Presence/PresenceBadge.tsx rename to src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceBadge.tsx diff --git a/src/renderer/src/components/Modules/NethVoice/Presence/PresenceBox.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceBox.tsx similarity index 59% rename from src/renderer/src/components/Modules/NethVoice/Presence/PresenceBox.tsx rename to src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceBox.tsx index e82d654f..73435d65 100644 --- a/src/renderer/src/components/Modules/NethVoice/Presence/PresenceBox.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceBox.tsx @@ -6,7 +6,7 @@ import { faMobile as CallForwardMobileIcon, faVoicemail as VoiceMailIcon } from '@fortawesome/free-solid-svg-icons' -import { Button, TextInput } from '../../../Nethesis' +import { Button, TextInput } from '../../../../../Nethesis' import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' @@ -21,11 +21,7 @@ import classNames from 'classnames' import { useAccount } from '@renderer/hooks/useAccount' import { PERMISSION } from '@shared/constants' -export interface PresenceBoxProps { - isOpen: boolean - onClose: () => void -} -export function PresenceBox({ isOpen, onClose: onClosePresenceDialog }: PresenceBoxProps) { +export function PresenceBox() { const { saveOperators } = usePhoneIslandEventHandler() const [isForwardDialogOpen, setIsForwardDialogOpen] = useState(false) const [account, setAccount] = useSharedState('account') @@ -75,7 +71,6 @@ export function PresenceBox({ isOpen, onClose: onClosePresenceDialog }: Presence Log.warning('error during the presence change', e) } finally { setIsForwardDialogOpen(false) - onClosePresenceDialog() } } } @@ -150,81 +145,71 @@ export function PresenceBox({ isOpen, onClose: onClosePresenceDialog }: Presence } catch (e) { Log.error('ON PRESENCE CHANGE', e) } finally { - onClosePresenceDialog() } } return ( - isOpen && ( - <> -
- - - {/* check callforward permission */} - {hasPermission(PERMISSION.CALL_FORWARD) && ( - setIsForwardDialogOpen(true)} - status="callforward" - presenceName={t('TopBar.Call forward')} - presenceDescription={t('TopBar.Forward incoming calls to another phone number')} - icon={CallForwardIcon} - > - )} - {!isEmpty(account?.data?.endpoints.cellphone) && ( - - onSelectPresence('callforward', account!.data!.endpoints.cellphone[0]!.id) - } - status="callforward" - presenceName={t('TopBar.Mobile')} - presenceDescription={t('TopBar.Do not receive any calls')} - icon={CallForwardMobileIcon} - > - )} - {!isEmpty(account?.data?.endpoints.voicemail) && ( - onSelectPresence('voicemail')} - status="voicemail" - presenceName={t('TopBar.Voicemail')} - presenceDescription={t('TopBar.Activate voicemail')} - icon={VoiceMailIcon} - > - )} - {/* check dnd permission */} - {hasPermission(PERMISSION.DND) && ( - - )} - +
+ + {/* check callforward permission */} + {hasPermission(PERMISSION.CALL_FORWARD) && ( + setIsForwardDialogOpen(true)} + status="callforward" + presenceName={t('TopBar.Call forward')} + presenceDescription={t('TopBar.Forward incoming calls to another phone number')} + icon={CallForwardIcon} + > + )} + {!isEmpty(account?.data?.endpoints.cellphone) && ( + + onSelectPresence('callforward', account!.data!.endpoints.cellphone[0]!.id) + } + status="callforward" + presenceName={t('TopBar.Mobile')} + presenceDescription={t('TopBar.Do not receive any calls')} + icon={CallForwardMobileIcon} + > + )} + {!isEmpty(account?.data?.endpoints.voicemail) && ( + onSelectPresence('voicemail')} + status="voicemail" + presenceName={t('TopBar.Voicemail')} + presenceDescription={t('TopBar.Activate voicemail')} + icon={VoiceMailIcon} + > + )} + {/* check dnd permission */} + {hasPermission(PERMISSION.DND) && ( + + )} + + {isForwardDialogOpen && ( +
+ { + onSelectPresence('callforward', data.to) + }} + /> + setIsForwardDialogOpen(false)} + className={'bg-white opacity-[0.25] z-[204]'} + />
- - {isForwardDialogOpen && ( - <> - { - onSelectPresence('callforward', data.to) - }} - /> - setIsForwardDialogOpen(false)} - className={'bg-white opacity-[0.25] z-[204]'} - /> - - )} - - ) + )} +
) } diff --git a/src/renderer/src/components/Modules/NethVoice/Presence/PresenceItem.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceItem.tsx similarity index 100% rename from src/renderer/src/components/Modules/NethVoice/Presence/PresenceItem.tsx rename to src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceItem.tsx diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/ProfileData.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/ProfileData.tsx new file mode 100644 index 00000000..6939a7f2 --- /dev/null +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/ProfileData.tsx @@ -0,0 +1,28 @@ +import { useSharedState } from "@renderer/store" +import { isDev } from "@shared/utils/utils" +import { t } from "i18next" +import { truncate } from "lodash" + +export const ProfileData = () => { + const [account] = useSharedState('account') + + if (!account) return <>No account + return ( +
+

{t('TopBar.Signed in as')}

+
+

+ {truncate(account.data?.name, { length: 20 })} +

+

+ {account.data?.endpoints.mainextension[0].id} +

+ {isDev() && ( +

+ [{account.data?.default_device.type}] +

+ )} +
+
+ ) +} diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/ThemeSettings/ThemeBox.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/ThemeSettings/ThemeBox.tsx new file mode 100644 index 00000000..8e54dc9a --- /dev/null +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/ThemeSettings/ThemeBox.tsx @@ -0,0 +1,58 @@ +import { + faPalette as SystemIcon, + faSun as LightIcon, + faMoon as DarkIcon, +} from '@fortawesome/free-solid-svg-icons' +import { useSharedState } from "@renderer/store" +import { Account } from "@shared/types" +import { getAccountUID } from "@shared/utils/utils" +import { t } from "i18next" +import { OptionElement } from "../OptionElement" +import { Log } from "@shared/utils/logger" + +export const ThemeIcons = { + system: SystemIcon, + light: LightIcon, + dark: DarkIcon +} +export const ThemeBox = () => { + const [account, setAccount] = useSharedState('account') + const [, setAuth] = useSharedState('auth') + const [, setTheme] = useSharedState('theme') + function handleSetTheme(theme) { + Log.info('change theme to', theme) + setTheme(() => theme) + const updatedAccount = { ...account!, theme: theme } + setAccount(() => updatedAccount) + setAuth((p) => ({ + ...p, + isFirstStart: p?.isFirstStart ?? true, + availableAccounts: { + ...p?.availableAccounts, + [getAccountUID(updatedAccount as Account)]: updatedAccount + } + })) + } + + const themeOptions = [ + { id: 1, name: 'system', icon: ThemeIcons.system, label: t('Settings.System') }, + { id: 2, name: 'light', icon: ThemeIcons.light, label: t('Settings.Light') }, + { id: 3, name: 'dark', icon: ThemeIcons.dark, label: t('Settings.Dark') } + ] + + + if (!account) return <> + return ( +
+ {themeOptions.map((availableTheme) => ( + handleSetTheme(availableTheme.name)} + /> + ))} +
+ ) +} diff --git a/src/renderer/src/components/pageComponents/phoneIsland/phoneIslandContainer.tsx b/src/renderer/src/components/pageComponents/phoneIsland/phoneIslandContainer.tsx index 5ec88939..853deacf 100644 --- a/src/renderer/src/components/pageComponents/phoneIsland/phoneIslandContainer.tsx +++ b/src/renderer/src/components/pageComponents/phoneIsland/phoneIslandContainer.tsx @@ -2,7 +2,7 @@ import { PhoneIsland } from "@nethesis/phone-island" import { eventDispatch } from "@renderer/hooks/eventDispatch" import { useLoggedNethVoiceAPI } from "@renderer/hooks/useLoggedNethVoiceAPI" import { useSharedState } from "@renderer/store" -import { PHONE_ISLAND_EVENTS } from "@shared/constants" +import { IPC_EVENTS, PHONE_ISLAND_EVENTS } from "@shared/constants" import { Log } from "@shared/utils/logger" import { useEffect, useMemo } from "react" @@ -15,13 +15,13 @@ export const PhoneIslandContainer = ({ dataConfig, deviceInformationObject, isDa }, [dataConfig]) const updateAccountInfo = async () => { - if (account!.data!.default_device.type !== 'nethlink' && deviceInformationObject) { - try { - await NethVoiceAPI.User.default_device(deviceInformationObject) - eventDispatch(PHONE_ISLAND_EVENTS['phone-island-default-device-change'], { deviceInformationObject }) - } catch (err) { - Log.warning('error during NethVoiceAPI.User.default_device:', err) - } + if (deviceInformationObject) { + Log.info('FORCE DEFAULT DEVICE TO NETHLINK') + //TODO: controlla + window.electron.send(IPC_EVENTS.CHANGE_DEFAULT_DEVICE, deviceInformationObject) + // await NethVoiceAPI.User.default_device(deviceInformationObject) + // eventDispatch(PHONE_ISLAND_EVENTS['phone-island-default-device-change'], { deviceInformationObject }) + } } diff --git a/src/renderer/src/hooks/usePhoneIslandEventListeners.ts b/src/renderer/src/hooks/usePhoneIslandEventListeners.ts index 620567a4..69eb2119 100644 --- a/src/renderer/src/hooks/usePhoneIslandEventListeners.ts +++ b/src/renderer/src/hooks/usePhoneIslandEventListeners.ts @@ -5,6 +5,8 @@ import { useEffect, useState } from "react" import { t } from "i18next" import { sendNotification } from "@renderer/utils" import { useSharedState } from "@renderer/store" +import { useNetwork } from "@shared/useNetwork" +import http from 'http' const defaultCall = { @@ -16,6 +18,7 @@ const defaultCall = { export const usePhoneIslandEventListener = () => { const [account] = useSharedState('account') const [connected, setConnected] = useSharedState('connection') + const { GET } = useNetwork() const [phoneIslandData, setPhoneIslandData] = useState({ activeAlerts: {}, @@ -34,7 +37,7 @@ export const usePhoneIslandEventListener = () => { [event]: (...data) => { const customEvent = data[0] const detail = customEvent['detail'] - Log.info('PHONE ISLAND', event, data) + Log.info('PHONE ISLAND', event, data, detail) callback?.(detail) } }) @@ -50,6 +53,11 @@ export const usePhoneIslandEventListener = () => { phoneIsalndSizes, events: { //CALLS + ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-action-physical"], async (data) => { + //const res = await GET(data.urlCallObject.url) + Log.info('phone-island-action-physical', data.urlCallObject.url) + window.electron.send(IPC_EVENTS.START_CALL_BY_URL, data.urlCallObject.url) + }), ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-call-ringing"], () => { setPhoneIslandData((p) => ({ ...p, currentCall: { @@ -111,6 +119,7 @@ export const usePhoneIslandEventListener = () => { }, view: null })) + //generate lost calls window.electron.send(IPC_EVENTS.EMIT_CALL_END) }), diff --git a/src/renderer/src/pages/PhoneIslandPage.tsx b/src/renderer/src/pages/PhoneIslandPage.tsx index 715ff1a3..0fea66fc 100644 --- a/src/renderer/src/pages/PhoneIslandPage.tsx +++ b/src/renderer/src/pages/PhoneIslandPage.tsx @@ -11,11 +11,13 @@ import { usePhoneIsland } from '@renderer/hooks/usePhoneIsland' import { PhoneIslandContainer } from '@renderer/components/pageComponents/phoneIsland/phoneIslandContainer' import { usePhoneIslandEventListener } from '@renderer/hooks/usePhoneIslandEventListeners' import { useInitialize } from '@renderer/hooks/useInitialize' +import { useNethVoiceAPI } from '@shared/useNethVoiceAPI' export function PhoneIslandPage() { const [account] = useSharedState('account') const [dataConfig, setDataConfig] = useState(undefined) const { state, phoneIsalndSizes, events } = usePhoneIslandEventListener() const { createDataConfig, dispatchAndWait } = usePhoneIsland() + const { NethVoiceAPI } = useNethVoiceAPI() const deviceInformationObject = useRef(undefined) const isDataConfigCreated = useRef(false) @@ -40,6 +42,7 @@ export function PhoneIslandPage() { window.electron.receive(IPC_EVENTS.LOGOUT, logout) window.electron.receive(IPC_EVENTS.START_CALL, (number: string) => { + //controllare se sono physical eventDispatch(PHONE_ISLAND_EVENTS['phone-island-call-start'], { number }) @@ -54,6 +57,14 @@ export function PhoneIslandPage() { window.electron.receive(IPC_EVENTS.RECONNECT_PHONE_ISLAND, () => { logout() }) + + window.electron.receive(IPC_EVENTS.CHANGE_DEFAULT_DEVICE, async (deviceInformationObject, force) => { + Log.info('CHANGE_DEFAULT_DEVICE', { force, deviceInformationObject, }) + const changed = await NethVoiceAPI.User.default_device(deviceInformationObject, force) + if (changed) { + eventDispatch(PHONE_ISLAND_EVENTS['phone-island-default-device-change'], { deviceInformationObject }) + } + }) }) const resize = (size: Size, state: PhoneIslandData) => { @@ -108,7 +119,7 @@ export function PhoneIslandPage() { await dispatchAndWait(PHONE_ISLAND_EVENTS['phone-island-call-end'], PHONE_ISLAND_EVENTS['phone-island-call-ended']) await dispatchAndWait(PHONE_ISLAND_EVENTS['phone-island-detach'], PHONE_ISLAND_EVENTS['phone-island-detached'], { data: { - deviceInformationObject: deviceInformationObject.current + deviceInformationObject: deviceInformationObject.current //nethlink extension } }) } diff --git a/src/renderer/src/store.ts b/src/renderer/src/store.ts index c80bb922..6baef1ed 100644 --- a/src/renderer/src/store.ts +++ b/src/renderer/src/store.ts @@ -95,6 +95,7 @@ export const { useRegisterStoreHook } = createGlobalStateHook({ account: undefined, + device: undefined, auth: undefined, connection: undefined, lastCalls: undefined, diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 3dd71e55..fe14049a 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -78,7 +78,9 @@ export enum IPC_EVENTS { EMIT_MAIN_PRESENCE_UPDATE = "EMIT_MAIN_PRESENCE_CHANGE", EMIT_QUEUE_UPDATE = "EMIT_QUEUE_UPDATE", EMIT_PARKING_UPDATE = "EMIT_PARKING_UPDATE", - TRANSFER_CALL = "TRANSFER_CALL" + TRANSFER_CALL = "TRANSFER_CALL", + CHANGE_DEFAULT_DEVICE = "CHANGE_DEFAULT_DEVICE", + START_CALL_BY_URL = "START_CALL_BY_URL" } //PHONE ISLAND EVENTS @@ -90,6 +92,7 @@ export enum PHONE_ISLAND_EVENTS { 'phone-island-audio-input-change' = 'phone-island-audio-input-change', 'phone-island-audio-output-change' = 'phone-island-audio-output-change', 'phone-island-theme-change' = 'phone-island-theme-change', + 'phone-island-action-physical' = 'phone-island-action-physical', // Dispatch Phone-Island Events: phone-island* 'phone-island-webrtc-registered' = 'phone-island-webrtc-registered', 'phone-island-attached' = 'phone-island-attached', diff --git a/src/shared/types.ts b/src/shared/types.ts index bd4c7906..64e6bceb 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -1,6 +1,7 @@ import { FilterTypes, MENU_ELEMENT, NEW_ACCOUNT } from "./constants" export type AvailableThemes = 'system' | 'light' | 'dark' +export type AvailableDevices = 'nethlink' | 'physical' | 'webrtc' export enum PAGES { SPLASHSCREEN = "splashscreenpage", @@ -368,6 +369,7 @@ export type Size = { w: number; h: number } export type LocalStorageData = { account?: Account, auth?: AuthAppData, + device?: AvailableDevices, page?: PageType, theme?: AvailableThemes, connection?: boolean, diff --git a/src/shared/useNethVoiceAPI.ts b/src/shared/useNethVoiceAPI.ts index a90be055..ba5a9b07 100644 --- a/src/shared/useNethVoiceAPI.ts +++ b/src/shared/useNethVoiceAPI.ts @@ -296,7 +296,17 @@ export const useNethVoiceAPI = (loggedAccount: Account | undefined = undefined) all_avatars: async () => await _GET('/webrest/user/all_avatars'), all_endpoints: async () => await _GET('/webrest/user/endpoints/all'), heartbeat: async (extension: string, username: string) => await _POST('/webrest/user/nethlink', { extension, username }), - default_device: async (deviceIdInformation: Extension) => await _POST('/webrest/user/default_device', { id: deviceIdInformation.id }), + default_device: async (deviceIdInformation: Extension, force = false): Promise => { + try { + if (account?.data?.default_device.type !== 'physical' || force) { + await _POST('/webrest/user/default_device', { id: deviceIdInformation.id }) + return true + } + } catch (e) { + Log.error(e) + } + return false; + }, setPresence: async (status: StatusTypes, to?: string) => await _POST('/webrest/user/presence', { status, ...(to ? { to } : {}) }) } From 73d6030bdfafc824ee54bdfe4b09be458abba619 Mon Sep 17 00:00:00 2001 From: therockerline Date: Tue, 10 Dec 2024 18:42:26 +0100 Subject: [PATCH 02/21] fix: favourite on search are now visible --- .../Modules/NethVoice/SearchResults/SearchNumber.tsx | 2 +- .../Modules/NethVoice/Speeddials/hook/useFavouriteModule.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/components/Modules/NethVoice/SearchResults/SearchNumber.tsx b/src/renderer/src/components/Modules/NethVoice/SearchResults/SearchNumber.tsx index 8ceb1dee..b0477f76 100644 --- a/src/renderer/src/components/Modules/NethVoice/SearchResults/SearchNumber.tsx +++ b/src/renderer/src/components/Modules/NethVoice/SearchResults/SearchNumber.tsx @@ -82,7 +82,7 @@ export function SearchNumber({ user, className }: SearchNumberProps) { displayedNumber={highlightedNumber} isHighlight={true} username={username} - isFavourite={isSearchAlsoAFavourite(user) || false} + isFavourite={false} isSearchData={true} /> - -
-
- - ) - } - - function Backdrop({ onBackdropClick, className }) { - return ( -
{ - e.preventDefault() - e.stopPropagation() - onBackdropClick() - }} - >
- ) - } - - async function onSelectPresence(status, to: string | undefined = undefined) { - try { - await NethVoiceAPI.User.setPresence(status, to) - const me = await NethVoiceAPI.User.me() - if (to) { - const operators = await NethVoiceAPI.fetchOperators() - saveOperators(operators) - } - setAccount({ - ...account!, - data: { - ...account!.data!, - ...me - } - }) - } catch (e) { - Log.error('ON PRESENCE CHANGE', e) - } finally { - } - } + const { onSelectPresence } = usePresenceService() return (
@@ -155,7 +27,7 @@ export function PresenceBox() { status="online" presenceName={t('TopBar.Online')} presenceDescription={t('TopBar.Make and receive phone calls')} - > + /> {/* check callforward permission */} {hasPermission(PERMISSION.CALL_FORWARD) && ( + /> )} {!isEmpty(account?.data?.endpoints.cellphone) && ( + /> )} {!isEmpty(account?.data?.endpoints.voicemail) && ( + /> )} {/* check dnd permission */} {hasPermission(PERMISSION.DND) && ( @@ -194,21 +66,7 @@ export function PresenceBox() { presenceName={t('TopBar.Do not disturb')} presenceDescription={t('TopBar.Do not receive any calls')} hasTopBar={true} - > - )} - - {isForwardDialogOpen && ( -
- { - onSelectPresence('callforward', data.to) - }} - /> - setIsForwardDialogOpen(false)} - className={'bg-white opacity-[0.25] z-[204]'} - /> -
+ /> )}
) diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceForwardDialog.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceForwardDialog.tsx new file mode 100644 index 00000000..d4a53fa1 --- /dev/null +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceForwardDialog.tsx @@ -0,0 +1,103 @@ +import { zodResolver } from "@hookform/resolvers/zod" +import { Button, TextInput } from "@renderer/components/Nethesis" +import { getIsPhoneNumber } from "@renderer/lib/utils" +import { useNethlinkData } from "@renderer/store" +import { Log } from "@shared/utils/logger" +import { t } from "i18next" +import { useEffect } from "react" +import { useForm } from "react-hook-form" +import { z } from "zod" +import { usePresenceService } from "./usePresenceService" + +export function PresenceForwardDialog() { + + const [, setIsForwardDialogOpen] = useNethlinkData('isForwardDialogOpen') + const { onSelectPresence } = usePresenceService() + useEffect(() => { + setFocus('to') + }, []) + + const onSubmit = (data) => { + onSelectPresence('callforward', data.to) + } + + const schema: z.ZodType<{ to: string }> = z.object({ + to: z + .string() + .trim() + .min(1, `${t('Common.This field is required')}`) + }) + + const { + register, + handleSubmit, + watch, + setError, + setFocus, + formState: { errors } + } = useForm({ + defaultValues: { + to: '' + }, + resolver: zodResolver(schema) + }) + function handleCancel(e) { + e.preventDefault() + e.stopPropagation() + setIsForwardDialogOpen(false) + } + + async function submit(data) { + if (!getIsPhoneNumber(data.to)) { + setError('to', { + message: t('Common.This is not a phone number') as string + }) + } else { + try { + await onSubmit(data) + } catch (e) { + Log.warning('error during the presence change', e) + } finally { + setIsForwardDialogOpen(false) + } + } + } + + const to = watch('to') + + return ( +
+
+
+
{t('TopBar.Enter phone number for call forward')}
+
+
+ + +
+
+
+ + +
+
+
+ ) +} diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/usePresenceService.ts b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/usePresenceService.ts new file mode 100644 index 00000000..99767ecb --- /dev/null +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/usePresenceService.ts @@ -0,0 +1,35 @@ +import { useLoggedNethVoiceAPI } from "@renderer/hooks/useLoggedNethVoiceAPI" +import { usePhoneIslandEventHandler } from "@renderer/hooks/usePhoneIslandEventHandler" +import { useSharedState } from "@renderer/store" +import { Log } from "@shared/utils/logger" + +export const usePresenceService = () => { + const { saveOperators } = usePhoneIslandEventHandler() + const [account, setAccount] = useSharedState('account') + const { NethVoiceAPI } = useLoggedNethVoiceAPI() + + async function onSelectPresence(status, to: string | undefined = undefined) { + try { + await NethVoiceAPI.User.setPresence(status, to) + const me = await NethVoiceAPI.User.me() + if (to) { + const operators = await NethVoiceAPI.fetchOperators() + saveOperators(operators) + } + setAccount({ + ...account!, + data: { + ...account!.data!, + ...me + } + }) + } catch (e) { + Log.error('ON PRESENCE CHANGE', e) + } finally { + } + } + + return { + onSelectPresence + } +} diff --git a/src/renderer/src/store.ts b/src/renderer/src/store.ts index 6baef1ed..edc8b535 100644 --- a/src/renderer/src/store.ts +++ b/src/renderer/src/store.ts @@ -112,6 +112,7 @@ export const { export const useNethlinkData = createGlobalStateHook({ selectedSidebarMenu: MENU_ELEMENT.FAVOURITES, + isForwardDialogOpen: false, phonebookModule: { selectedContact: undefined }, diff --git a/src/shared/types.ts b/src/shared/types.ts index 64e6bceb..9436ee0c 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -410,6 +410,7 @@ export type AuthAppData = { export type NethLinkPageData = { selectedSidebarMenu: MENU_ELEMENT, showPhonebookSearchModule?: boolean, + isForwardDialogOpen?: boolean, showAddContactModule?: boolean, speeddialsModule?: SpeedDialModuleData phonebookSearchModule?: PhonebookSearchModuleData From 2cf7926434ee52e6df139c9329e191585cb874d5 Mon Sep 17 00:00:00 2001 From: therockerline Date: Tue, 10 Dec 2024 20:01:09 +0100 Subject: [PATCH 04/21] fix: persist theme change --- src/main/lib/ipcEvents.ts | 6 +++--- .../BaseModule/ProfileDialog/ThemeSettings/ThemeBox.tsx | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/lib/ipcEvents.ts b/src/main/lib/ipcEvents.ts index e0eba91f..68bac83e 100644 --- a/src/main/lib/ipcEvents.ts +++ b/src/main/lib/ipcEvents.ts @@ -210,10 +210,10 @@ export function registerIpcEvents() { ipcMain.on(IPC_EVENTS.CHANGE_THEME, (_, theme) => { AccountController.instance.updateTheme(theme) - PhoneIslandController.instance.window.emit(IPC_EVENTS.ON_CHANGE_THEME, theme) - LoginController.instance.window.emit(IPC_EVENTS.ON_CHANGE_THEME, theme) + //PhoneIslandController.instance?.window?.emit(IPC_EVENTS.ON_CHANGE_THEME, theme) + //LoginController.instance?.window?.emit(IPC_EVENTS.ON_CHANGE_THEME, theme) DevToolsController.instance?.window?.emit(IPC_EVENTS.ON_CHANGE_THEME, theme) - NethLinkController.instance.window.emit(IPC_EVENTS.ON_CHANGE_THEME, theme) + NethLinkController.instance?.window?.emit(IPC_EVENTS.ON_CHANGE_THEME, theme) }) ipcMain.on(IPC_EVENTS.GET_NETHVOICE_CONFIG, async (e, account) => { diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/ThemeSettings/ThemeBox.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/ThemeSettings/ThemeBox.tsx index 8e54dc9a..715bb7c0 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/ThemeSettings/ThemeBox.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/ThemeSettings/ThemeBox.tsx @@ -9,6 +9,7 @@ import { getAccountUID } from "@shared/utils/utils" import { t } from "i18next" import { OptionElement } from "../OptionElement" import { Log } from "@shared/utils/logger" +import { IPC_EVENTS } from '@shared/constants' export const ThemeIcons = { system: SystemIcon, @@ -32,6 +33,7 @@ export const ThemeBox = () => { [getAccountUID(updatedAccount as Account)]: updatedAccount } })) + window.electron.send(IPC_EVENTS.CHANGE_THEME, theme) } const themeOptions = [ From 4f79c47899ba2bc42d7f447d3a9f6822d4b1a690 Mon Sep 17 00:00:00 2001 From: therockerline Date: Wed, 11 Dec 2024 19:17:30 +0100 Subject: [PATCH 05/21] fix: updated presence icons --- .../NethVoice/BaseModule/ProfileDialog.tsx | 15 +++++- .../DeviceSettings/DeviceBox.tsx | 48 +++++++++++-------- .../BaseModule/ProfileDialog/MenuAction.tsx | 8 +++- .../ProfileDialog/OptionElement.tsx | 12 +++-- src/renderer/src/icons/NethLingLogoIcon.tsx | 20 ++++++++ src/renderer/src/icons/index.ts | 1 + 6 files changed, 76 insertions(+), 28 deletions(-) create mode 100644 src/renderer/src/icons/NethLingLogoIcon.tsx diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx index ceb8fcce..922e0df3 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx @@ -20,6 +20,7 @@ import { DeviceBox, DeviceIcons } from './ProfileDialog/DeviceSettings/DeviceBox import { PresenceForwardDialog } from './ProfileDialog/PresenceSettings/PresenceForwardDialog' import { Backdrop } from './Backdrop' import { Log } from '@shared/utils/logger' +import { IconDefinition } from '@nethesis/nethesis-solid-svg-icons' enum MenuItem { device = 1, @@ -41,7 +42,7 @@ export const ProfileDialog = ({ const [x, setX] = useState(0) const [dialogPageTitle, setDialogPageTitle] = useState('') const [themeIcon, setThemeIcon] = useState(ThemeIcons['system']) - const [deviceIcon, setDeviceIcon] = useState(DeviceIcons['nethlink']) + const [deviceIcon, setDeviceIcon] = useState(DeviceIcons['nethlink']) const [isForwardDialogOpen, setIsForwardDialogOpen] = useNethlinkData('isForwardDialogOpen') function handleExitNethLink() { window.api.exitNethLink() @@ -99,7 +100,17 @@ export const ProfileDialog = ({

{t('TopBar.Presence')}

- setSelectedMenu(() => MenuItem.device)} icon={deviceIcon} label={t("TopBar.Pair device")} + setSelectedMenu(() => MenuItem.device)} + label={t("TopBar.Pair device")} + {...( + deviceIcon.hasOwnProperty('Icon') ? { + iconElem: (deviceIcon as { Icon: JSX.Element }).Icon + } + : { + icon: deviceIcon as IconProp + } + )} /> setSelectedMenu(() => MenuItem.theme)} icon={themeIcon} label={t('Settings.Theme')} /> diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx index 22069117..8ede3358 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx @@ -1,24 +1,26 @@ import { IconProp } from "@fortawesome/fontawesome-svg-core" import { - faAdjust as WebDevice, - faAnchorLock as DesktopDevice, - faAngleDoubleLeft as PhysicalDevice, -} from '@fortawesome/free-solid-svg-icons' + NethLinkLogoIcon as DesktopDevice, +} from '@renderer/icons' +import { + IconDefinition, + faOfficePhone as PhysicalDevice +} from '@nethesis/nethesis-solid-svg-icons' import { useSharedState } from "@renderer/store" import { AvailableDevices, Extension } from "@shared/types" import { Log } from "@shared/utils/logger" import { t } from "i18next" import { OptionElement } from "../OptionElement" -import { useLoggedNethVoiceAPI } from "@renderer/hooks/useLoggedNethVoiceAPI" import { debouncer } from "@shared/utils/utils" import { IPC_EVENTS } from "@shared/constants" export const DeviceIcons = { - nethlink: DesktopDevice, - webrtc: WebDevice, + nethlink: { + Icon: + }, physical: PhysicalDevice } -type DeviceType = { name: AvailableDevices, label: string, icon: IconProp } +type DeviceType = { name: AvailableDevices, label: string, icon?: IconProp | IconDefinition, iconElem?: JSX.Element } type AvailableDeviceOption = { id: string, ext: Extension @@ -36,33 +38,39 @@ export const DeviceBox = () => { }, 250) } - const themeOptions: DeviceType[] = [ - { name: 'nethlink', icon: DeviceIcons.nethlink, label: t('Settings.Desktop Phone') }, - { name: 'physical', icon: DeviceIcons.physical, label: t('Settings.IP Phone') }, - { name: 'webrtc', icon: DeviceIcons.webrtc, label: t('Settings.Web Phone') } - ] + const themeOptions = { + nethlink: { iconElem: DeviceIcons.nethlink.Icon, label: t('Settings.Desktop Phone') }, + physical: { icon: DeviceIcons.physical, label: t('Settings.IP Phone') }, + } if (!account) return <> + const nethlink = account.data!.endpoints.extension.find((e) => e.type === 'nethlink')! const accountDevices: AvailableDeviceOption[] = account.data?.endpoints.extension.reduce((p, d) => { - const option = themeOptions.find((o) => o.name === d.type) - Log.info(d) - if (option) { + Log.info('devices', d) + if (d.type === 'physical') { p.push({ - ...option, - label: d.description || option.label, + ...themeOptions.physical, + name: 'physical', + label: d.description || themeOptions.physical.label, id: d.id, ext: d } as AvailableDeviceOption) } return p - }, []) || [] + }, [{ + ...themeOptions.nethlink, + name: 'nethlink', + id: nethlink?.id, + ext: nethlink + }]) || [] return (
{accountDevices.map((availableDevices) => ( handleSetDevice(availableDevices)} diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuAction.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuAction.tsx index c6c6d9d5..6f447bd0 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuAction.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuAction.tsx @@ -10,7 +10,8 @@ type BaseMenuActionProps = { type MenuActionProps = { label: string, - icon: IconProp + icon?: IconProp + iconElem?: JSX.Element } & BaseMenuActionProps type MenuActionWrapProps = { @@ -33,7 +34,10 @@ function MenuAction({ className, children, onClick }: MenuActionWrapProps) { } MenuAction.item = (props: MenuActionProps) => - + {props.icon + ? + : props.iconElem || <> + }

{props.label}

MenuAction.itemWrap = (props: MenuActionWrapProps) => {props.children} diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/OptionElement.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/OptionElement.tsx index 9e581b03..b5c92ef3 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/OptionElement.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/OptionElement.tsx @@ -4,19 +4,23 @@ import { } from '@fortawesome/free-solid-svg-icons' import { IconProp } from "@fortawesome/fontawesome-svg-core" import classNames from "classnames" +import { IconDefinition } from "@nethesis/nethesis-solid-svg-icons" type SettingsOptionElementProps = { - icon: IconProp, + icon?: IconProp | IconDefinition, + iconElem?: JSX.Element, label: string, isSelected: boolean onClick: () => void, } export const OptionElement = ({ icon, + iconElem, label, isSelected, onClick, }: SettingsOptionElementProps) => { + return (
- -

+ {iconElem ? iconElem : icon ? : <>} +

{label}

diff --git a/src/renderer/src/icons/NethLingLogoIcon.tsx b/src/renderer/src/icons/NethLingLogoIcon.tsx new file mode 100644 index 00000000..a18910b8 --- /dev/null +++ b/src/renderer/src/icons/NethLingLogoIcon.tsx @@ -0,0 +1,20 @@ +import { useSharedState } from '@renderer/store' +import { parseThemeToClassName } from '@renderer/utils' + +export function NethLinkLogoIcon() { + const [theme, _] = useSharedState('theme') + return ( + + + + + ) +} diff --git a/src/renderer/src/icons/index.ts b/src/renderer/src/icons/index.ts index 16763991..4ced97f4 100644 --- a/src/renderer/src/icons/index.ts +++ b/src/renderer/src/icons/index.ts @@ -2,3 +2,4 @@ export * from './OutCallAnsweredIcon' export * from './InCallNotAnsweredIcon' export * from './InCallAnsweredIcon' export * from './OutCallNotAnsweredIcon' +export * from './NethLingLogoIcon' From a1f3040e8cb84503949ceea2eec7705946975f59 Mon Sep 17 00:00:00 2001 From: therockerline Date: Thu, 12 Dec 2024 19:02:56 +0100 Subject: [PATCH 06/21] fixed loss of status data after hiding the nethlink window fixed data sharing removed unused data from other windows from shared data --- .vscode/launch.json | 23 ------- public/locales/en/translations.json | 3 +- public/locales/it/translations.json | 3 +- .../classes/controllers/AccountController.ts | 4 +- src/main/lib/mainStore.ts | 2 +- src/main/main.ts | 5 +- .../public/locales/en/translations.json | 3 +- .../public/locales/it/translations.json | 3 +- src/renderer/src/App.tsx | 14 ++-- .../BaseModule/ContactNameAndAction.tsx | 4 +- .../Modules/NethVoice/BaseModule/Navbar.tsx | 11 ++-- .../NethVoice/BaseModule/ProfileDialog.tsx | 3 +- .../DeviceSettings/DeviceBox.tsx | 3 +- .../PresenceSettings/PresenceBadge.tsx | 13 ++-- .../PresenceSettings/PresenceItem.tsx | 31 +++++++-- .../PresenceSettings/usePresenceService.ts | 6 +- .../Modules/NethVoice/BaseModule/Sidebar.tsx | 2 +- .../Modules/NethVoice/LastCalls/LastCall.tsx | 6 +- .../NethVoice/LastCalls/LastCallsBox.tsx | 8 +-- .../Parking/hook/useParkingModule.ts | 4 +- .../NethVoice/SearchResults/SearchNumber.tsx | 4 +- .../SearchResults/SearchNumberBox.tsx | 4 +- .../Speeddials/hook/useFavouriteModule.ts | 7 +- .../Speeddials/hook/useSpeedDialsModule.ts | 2 +- .../Speeddials/shared/ContactNumber.tsx | 6 +- .../src/hooks/usePhoneIslandEventHandler.ts | 52 ++++++++++----- src/renderer/src/pages/NethLinkPage.tsx | 6 ++ src/renderer/src/pages/PhoneIslandPage.tsx | 5 +- src/renderer/src/store.ts | 65 ++++++++++++------- src/shared/types.ts | 12 ++-- src/shared/useNethVoiceAPI.ts | 5 +- 31 files changed, 175 insertions(+), 144 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index d462b880..ad08126a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -85,29 +85,6 @@ "presentation": { "hidden": false } - }, - { - "name": "Debug Renderer Process", - "port": 9222, - "request": "attach", - "type": "chrome", - "webRoot": "${workspaceFolder}/src/renderer", - "timeout": 15000, - "presentation": { - "hidden": true - } - } - ], - "compounds": [ - { - "name": "Debug main and renderer processes", - "configurations": [ - "Debug Main Process", - "Debug Renderer Process" - ], - "presentation": { - "order": 1 - } } ] } \ No newline at end of file diff --git a/public/locales/en/translations.json b/public/locales/en/translations.json index 5d040822..365863a0 100644 --- a/public/locales/en/translations.json +++ b/public/locales/en/translations.json @@ -324,8 +324,7 @@ "Do not disturb": "Do not disturb", "Available presence": "Available presence", "IP Phone": "IP Phone", - "Web Phone": "Web Phone", - "Desktop Phone": "Desktop Phone" + "Only nethlink": "Only Nethlink" }, "TopBar": { "Signed in as": "Signed in as", diff --git a/public/locales/it/translations.json b/public/locales/it/translations.json index ce0f7ebd..d2747920 100644 --- a/public/locales/it/translations.json +++ b/public/locales/it/translations.json @@ -324,8 +324,7 @@ "Do not disturb": "Non disturbare", "Available presence": "Presence disponibili", "IP Phone": "Telefono IP", - "Web Phone": "Web Phone", - "Desktop Phone": "Desktop Phone" + "Only nethlink": "Solo Nethlink" }, "TopBar": { "Signed in as": "Accesso effettuato come", diff --git a/src/main/classes/controllers/AccountController.ts b/src/main/classes/controllers/AccountController.ts index ed66252f..ff022848 100644 --- a/src/main/classes/controllers/AccountController.ts +++ b/src/main/classes/controllers/AccountController.ts @@ -1,4 +1,4 @@ -import { Account, AuthAppData, ConfigFile } from '@shared/types' +import { Account, AuthAppData, AvailableDevices, ConfigFile } from '@shared/types' import { Log } from '@shared/utils/logger' import { safeStorage } from 'electron' import { store } from '@/lib/mainStore' @@ -111,6 +111,7 @@ export class AccountController { lastUser: accountUID, lastUserCryptPsw: cryptString }, + device: account.data?.default_device.type as AvailableDevices, connection: store.store.connection || false }, 'saveLoggedAccount') store.saveToDisk() @@ -170,6 +171,7 @@ export class AccountController { setAccountNethLinkBounds(nethlinkBounds: Electron.Rectangle | undefined): void { const account = store.store.account + Log.info('MAIN PRESENCE BACK', account?.data?.mainPresence) const auth = store.store.auth if (account) { account!.nethlinkBounds = nethlinkBounds diff --git a/src/main/lib/mainStore.ts b/src/main/lib/mainStore.ts index ed488ddd..cb2625df 100644 --- a/src/main/lib/mainStore.ts +++ b/src/main/lib/mainStore.ts @@ -59,7 +59,7 @@ class Store { updateStore(newState: T, from: string) { const diff = difference(Object.values(newState as any || {}), Object.values(this.store as any || {})) - Log.info('STORE update shared store from', from, diff.length) + Log.info('STORE update shared store from', from, Object.keys(newState as any || {})) if (diff.length > 0 || this.store === undefined) { this.store = Object.assign({}, newState) } diff --git a/src/main/main.ts b/src/main/main.ts index 6bee2a8c..e30437ca 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -30,9 +30,10 @@ for (const arg of params) { const kv: any[] = arg.split('=') if (['DEV', 'DEVTOOLS'].includes(kv[0])) { kv[1] = kv[1] === 'true' - } else { - kv[1] = undefined } + // } else { + // kv[1] = undefined + // } if (kv[1]) process.env[kv[0]] = kv[1] } diff --git a/src/renderer/public/locales/en/translations.json b/src/renderer/public/locales/en/translations.json index 5d040822..365863a0 100644 --- a/src/renderer/public/locales/en/translations.json +++ b/src/renderer/public/locales/en/translations.json @@ -324,8 +324,7 @@ "Do not disturb": "Do not disturb", "Available presence": "Available presence", "IP Phone": "IP Phone", - "Web Phone": "Web Phone", - "Desktop Phone": "Desktop Phone" + "Only nethlink": "Only Nethlink" }, "TopBar": { "Signed in as": "Signed in as", diff --git a/src/renderer/public/locales/it/translations.json b/src/renderer/public/locales/it/translations.json index ce0f7ebd..d2747920 100644 --- a/src/renderer/public/locales/it/translations.json +++ b/src/renderer/public/locales/it/translations.json @@ -324,8 +324,7 @@ "Do not disturb": "Non disturbare", "Available presence": "Presence disponibili", "IP Phone": "Telefono IP", - "Web Phone": "Web Phone", - "Desktop Phone": "Desktop Phone" + "Only nethlink": "Solo Nethlink" }, "TopBar": { "Signed in as": "Accesso effettuato come", diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index a738e0ad..f289fafc 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -13,7 +13,6 @@ import { useRegisterStoreHook, useSharedState } from "@renderer/store"; import { PageContext, usePageCtx } from './contexts/pageContext' import { GIT_RELEASES_URL, IPC_EVENTS } from '@shared/constants' import { useNetwork } from '@shared/useNetwork' -import { useRefState } from './hooks/useRefState' const RequestStateComponent = () => { @@ -21,7 +20,7 @@ const RequestStateComponent = () => { useRegisterStoreHook() const [theme,] = useSharedState('theme') const [account,] = useSharedState('account') - const [connection,] = useRefState(useSharedState('connection')) + const [connection,] = useSharedState('connection') const [hasWindowConfig, setHasWindowConfig] = useState(false) const { GET } = useNetwork() @@ -33,8 +32,8 @@ const RequestStateComponent = () => { resolve(false) }) }) - Log.info('check connection', { connected, connection: connection.current }) - if (connected !== connection.current) { + Log.info('check connection', { connected, connection: connection }) + if (connected !== connection) { window.electron.send(IPC_EVENTS.UPDATE_CONNECTION_STATE, connected); } } @@ -43,7 +42,7 @@ const RequestStateComponent = () => { if (account) { if (!window['CONFIG']) { // @ts-ignore (define in dts) - window.CONFIG = { + window['CONFIG'] = { PRODUCT_NAME: 'NethLink', COMPANY_NAME: 'Nethesis', COMPANY_SUBNAME: 'CTI', @@ -57,14 +56,14 @@ const RequestStateComponent = () => { TIMEZONE: account.timezone, VOICE_ENDPOINT: account.voiceEndpoint } - Log.info(window['CONFIG']) + Log.info('WINDOW CONFIG', pageData?.page, window['CONFIG']) setHasWindowConfig(true) } } else { window['CONFIG'] = undefined setHasWindowConfig(false) } - }, [account, pageData?.page]) + }, [account?.username, pageData?.page]) const loader = async () => { Log.info('check i18n initialization') @@ -146,7 +145,6 @@ export default function App() { Log.info('initialize i18n') loadI18n() }) - return ( diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ContactNameAndAction.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ContactNameAndAction.tsx index 9f666905..cc21b9e8 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ContactNameAndAction.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ContactNameAndAction.tsx @@ -4,7 +4,7 @@ import { isDev } from "@shared/utils/utils" import { FavouriteStar } from "./FavouritesStar" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { useAccount } from "@renderer/hooks/useAccount" -import { useSharedState } from "@renderer/store" +import { useNethlinkData } from "@renderer/store" import { usePhoneIslandEventHandler } from "@renderer/hooks/usePhoneIslandEventHandler" import { Avatar } from "../../../Nethesis" import { @@ -24,7 +24,7 @@ export const ContactNameAndActions = ({ contact, number, isHighlight, displayedN isSearchData: boolean }) => { const { isCallsEnabled } = useAccount() - const [operators] = useSharedState('operators') + const [operators] = useNethlinkData('operators') const { callNumber } = usePhoneIslandEventHandler() const avatarSrc = username && operators?.avatars?.[username] diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/Navbar.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/Navbar.tsx index c800dcd1..ade9cbd7 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/Navbar.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/Navbar.tsx @@ -15,11 +15,9 @@ import { Account } from '@shared/types' import { t } from 'i18next' import { StatusDot } from '../../../Nethesis' import { useAccount } from '@renderer/hooks/useAccount' -import { debouncer, getAccountUID, isDev } from '@shared/utils/utils' -import { useSharedState } from '@renderer/store' -import { createRef, useState } from 'react' -import { useTheme } from '@renderer/theme/Context' -import { PresenceBox } from './ProfileDialog/PresenceSettings/PresenceBox' +import { debouncer } from '@shared/utils/utils' +import { useNethlinkData, useSharedState } from '@renderer/store' +import { useState } from 'react' import classNames from 'classnames' import { truncate } from 'lodash' import { PresenceBadge } from './ProfileDialog/PresenceSettings/PresenceBadge' @@ -35,7 +33,7 @@ export function Navbar({ onClickAccount }: NavbarProps): JSX.Element { const { status } = useAccount() const [account] = useSharedState('account') - const [operators] = useSharedState('operators') + const [operators] = useNethlinkData('operators') const [isProfileDialogOpen, setIsProfileDialogOpen] = useState(false) @@ -47,7 +45,6 @@ export function Navbar({ onClickAccount }: NavbarProps): JSX.Element {
diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx index 922e0df3..0afb76ef 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx @@ -52,7 +52,7 @@ export const ProfileDialog = ({ if (selectedMenu) { setX(250) switch (selectedMenu) { - case MenuItem.device: setDialogPageTitle(() => t("TopBar.Device")); break; + case MenuItem.device: setDialogPageTitle(() => t("TopBar.Pair device")); break; case MenuItem.theme: setDialogPageTitle(() => t("TopBar.Theme")); break; case MenuItem.presence: setDialogPageTitle(() => t("TopBar.Presence")); break; } @@ -145,7 +145,6 @@ export const ProfileDialog = ({ ) } onBackdropClick={() => { - Log.warning('CLICK') if (isForwardDialogOpen) { setIsForwardDialogOpen(() => false) } else { diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx index 8ede3358..993f422b 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx @@ -39,14 +39,13 @@ export const DeviceBox = () => { } const themeOptions = { - nethlink: { iconElem: DeviceIcons.nethlink.Icon, label: t('Settings.Desktop Phone') }, + nethlink: { iconElem: DeviceIcons.nethlink.Icon, label: t('Settings.Only nethlink') }, physical: { icon: DeviceIcons.physical, label: t('Settings.IP Phone') }, } if (!account) return <> const nethlink = account.data!.endpoints.extension.find((e) => e.type === 'nethlink')! const accountDevices: AvailableDeviceOption[] = account.data?.endpoints.extension.reduce((p, d) => { - Log.info('devices', d) if (d.type === 'physical') { p.push({ ...themeOptions.physical, diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceBadge.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceBadge.tsx index a0d9c11a..45dd4b39 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceBadge.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceBadge.tsx @@ -1,23 +1,28 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faArrowRight, faMobile, faVoicemail } from '@fortawesome/free-solid-svg-icons' import { Badge } from '@renderer/components/Nethesis/Badge' -import { useSharedState } from '@renderer/store' +import { useNethlinkData, useSharedState } from '@renderer/store' import { StatusTypes } from '@shared/types' import { t } from 'i18next' import { useTheme } from '@renderer/theme/Context' import classNames from 'classnames' import { Tooltip } from 'react-tooltip' +import { useEffect, useState } from 'react' export interface PresenceBadgeProps { - mainPresence: StatusTypes | undefined className?: string } export const PresenceBadgeVisibility = ['callforward', 'voicemail', 'cellphone'] -export const PresenceBadge = ({ mainPresence, className }: PresenceBadgeProps) => { +export const PresenceBadge = ({ className }: PresenceBadgeProps) => { const [account] = useSharedState('account') - const [operators] = useSharedState('operators') + const [operators] = useNethlinkData('operators') const { badge: theme, status: statuses } = useTheme().theme + const [mainPresence, setMainPresence] = useState(account?.data?.mainPresence) + + useEffect(() => { + setMainPresence(() => account?.data?.mainPresence) + }, [account]) if (PresenceBadgeVisibility.includes(mainPresence as string)) { const isCallforward = ['callforward', 'voicemail'].includes(mainPresence as string) diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceItem.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceItem.tsx index c785c183..5a1fd1e3 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceItem.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/PresenceItem.tsx @@ -1,7 +1,12 @@ import { StatusTypes } from "@shared/types"; -import { IconDefinition } from "@fortawesome/free-solid-svg-icons"; +import { + IconDefinition, + faCheck as ChooseThemeMenuIcon, +} from "@fortawesome/free-solid-svg-icons"; import { StatusDot } from "@renderer/components/Nethesis"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useAccount } from "@renderer/hooks/useAccount"; +import classNames from "classnames"; export interface PresenceItemProps { presenceName: string, presenceDescription: string, @@ -20,6 +25,7 @@ export function PresenceItem({ onClick }: PresenceItemProps) { + const { status: currentStatus } = useAccount() const handleClick = (e) => { e.preventDefault() @@ -30,15 +36,26 @@ export function PresenceItem({ return ( <> {hasTopBar ?
: null} -
-
- +
+
- {presenceName} - {icon && } + +
+ {presenceName} + {icon && } +
+
{presenceDescription}
-
{presenceDescription}
+ {currentStatus === status && ( + + )}
) diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/usePresenceService.ts b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/usePresenceService.ts index 99767ecb..ff899a3a 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/usePresenceService.ts +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/PresenceSettings/usePresenceService.ts @@ -1,6 +1,7 @@ import { useLoggedNethVoiceAPI } from "@renderer/hooks/useLoggedNethVoiceAPI" import { usePhoneIslandEventHandler } from "@renderer/hooks/usePhoneIslandEventHandler" import { useSharedState } from "@renderer/store" +import { StatusTypes } from "@shared/types" import { Log } from "@shared/utils/logger" export const usePresenceService = () => { @@ -8,13 +9,12 @@ export const usePresenceService = () => { const [account, setAccount] = useSharedState('account') const { NethVoiceAPI } = useLoggedNethVoiceAPI() - async function onSelectPresence(status, to: string | undefined = undefined) { + async function onSelectPresence(status: StatusTypes, to: string | undefined = undefined) { try { await NethVoiceAPI.User.setPresence(status, to) const me = await NethVoiceAPI.User.me() if (to) { - const operators = await NethVoiceAPI.fetchOperators() - saveOperators(operators) + NethVoiceAPI.fetchOperators().then(saveOperators) } setAccount({ ...account!, diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/Sidebar.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/Sidebar.tsx index 297a943b..aed7a8b9 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/Sidebar.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/Sidebar.tsx @@ -23,7 +23,7 @@ export function Sidebar({ onChangeMenu }: SidebarProps): JSX.Element { const [selectedSidebarMenu, setSelectedSidebarMenu] = useNethlinkData('selectedSidebarMenu') const [, setShowPhonebookSearchModule] = useNethlinkData('showPhonebookSearchModule') const [, setPhonebookSearchModule] = useNethlinkData('phonebookSearchModule') - const [missedCalls] = useSharedState('missedCalls') + const [missedCalls] = useNethlinkData('missedCalls') const [notifications] = useSharedState('notifications') const [lastMenu, setLastMenu] = useState(MENU_ELEMENT.FAVOURITES) const [isAboutVisited, setIsAboutVisited] = useState(false) diff --git a/src/renderer/src/components/Modules/NethVoice/LastCalls/LastCall.tsx b/src/renderer/src/components/Modules/NethVoice/LastCalls/LastCall.tsx index 4ff54a83..e90ea23c 100644 --- a/src/renderer/src/components/Modules/NethVoice/LastCalls/LastCall.tsx +++ b/src/renderer/src/components/Modules/NethVoice/LastCalls/LastCall.tsx @@ -16,7 +16,7 @@ import { truncate } from '@renderer/utils' import { Tooltip } from 'react-tooltip' import { Badge } from '../../../Nethesis/Badge' import { useAccount } from '@renderer/hooks/useAccount' -import { useSharedState } from '@renderer/store' +import { useNethlinkData } from '@renderer/store' import { usePhonebookModule } from '../PhonebookModule/hook/usePhonebookModule' import { OutCallAnsweredIcon, @@ -41,8 +41,8 @@ export function LastCall({ }: LastCallProps): JSX.Element { const phonebookModule = usePhonebookModule() const [, setSelectedContact] = phonebookModule.selectedContact - const [queues] = useSharedState('queues') - const [operators] = useSharedState('operators') + const [queues] = useNethlinkData('queues') + const [operators] = useNethlinkData('operators') const { isCallsEnabled } = useAccount() const [showCreateButton, setShowCreateButton] = useState(false) const [isQueueLoading, setIsQueueLoading] = useState(true) diff --git a/src/renderer/src/components/Modules/NethVoice/LastCalls/LastCallsBox.tsx b/src/renderer/src/components/Modules/NethVoice/LastCalls/LastCallsBox.tsx index 22268556..4af4f0fb 100644 --- a/src/renderer/src/components/Modules/NethVoice/LastCalls/LastCallsBox.tsx +++ b/src/renderer/src/components/Modules/NethVoice/LastCalls/LastCallsBox.tsx @@ -5,7 +5,7 @@ import { import { LastCall } from './LastCall' import { CallData, LastCallData } from '@shared/types' import { t } from 'i18next' -import { useSharedState } from '@renderer/store' +import { useNethlinkData, useSharedState } from '@renderer/store' import { SkeletonRow } from '@renderer/components/SkeletonRow' import { useEffect, useState } from 'react' import { Scrollable } from '@renderer/components/Scrollable' @@ -14,9 +14,9 @@ import { EmptyList } from '@renderer/components/EmptyList' export function LastCallsBox({ showContactForm }): JSX.Element { - const [lastCalls] = useSharedState('lastCalls') - const [operators] = useSharedState('operators') - const [missedCalls, setMissedCalls] = useSharedState('missedCalls') + const [lastCalls] = useNethlinkData('lastCalls') + const [operators] = useNethlinkData('operators') + const [missedCalls, setMissedCalls] = useNethlinkData('missedCalls') const [preparedCalls, setPreparedCalls] = useState([]) const title = `${t('LastCalls.Calls', { count: lastCalls?.length })} (${lastCalls?.length || 0})` diff --git a/src/renderer/src/components/Modules/NethVoice/Parking/hook/useParkingModule.ts b/src/renderer/src/components/Modules/NethVoice/Parking/hook/useParkingModule.ts index c6093e62..25b61cb0 100644 --- a/src/renderer/src/components/Modules/NethVoice/Parking/hook/useParkingModule.ts +++ b/src/renderer/src/components/Modules/NethVoice/Parking/hook/useParkingModule.ts @@ -1,10 +1,10 @@ -import { useSharedState } from "@renderer/store" +import { useNethlinkData } from "@renderer/store" import { ParkingType } from "@shared/types" import { useEffect, useState } from "react" export const useParkingModule = () => { - const [parkedCalls] = useSharedState('parkings') + const [parkedCalls] = useNethlinkData('parkings') const [validParkedCalls, setValidParkedCalls] = useState(undefined) useEffect(() => { parkedCalls && extractValidParkedCalls(parkedCalls) diff --git a/src/renderer/src/components/Modules/NethVoice/SearchResults/SearchNumber.tsx b/src/renderer/src/components/Modules/NethVoice/SearchResults/SearchNumber.tsx index b0477f76..13ffd4d7 100644 --- a/src/renderer/src/components/Modules/NethVoice/SearchResults/SearchNumber.tsx +++ b/src/renderer/src/components/Modules/NethVoice/SearchResults/SearchNumber.tsx @@ -2,7 +2,7 @@ import { ReactNode } from 'react' import { t } from 'i18next' import { SearchData } from '@shared/types' import { useAccount } from '@renderer/hooks/useAccount' -import { useSharedState } from '@renderer/store' +import { useNethlinkData, useSharedState } from '@renderer/store' import { Button } from '@renderer/components/Nethesis' import { usePhonebookSearchModule } from './hook/usePhoneBookSearchModule' import { usePhoneIslandEventHandler } from '@renderer/hooks/usePhoneIslandEventHandler' @@ -18,7 +18,7 @@ export function SearchNumber({ user, className }: SearchNumberProps) { const phoneBookModule = usePhonebookSearchModule() const { callNumber } = usePhoneIslandEventHandler() const [searchText] = phoneBookModule.searchTextState - const [operators] = useSharedState('operators') + const [operators] = useNethlinkData('operators') const { isCallsEnabled } = useAccount() const { isSearchAlsoAFavourite } = useFavouriteModule() diff --git a/src/renderer/src/components/Modules/NethVoice/SearchResults/SearchNumberBox.tsx b/src/renderer/src/components/Modules/NethVoice/SearchResults/SearchNumberBox.tsx index 1e30abf5..85773a10 100644 --- a/src/renderer/src/components/Modules/NethVoice/SearchResults/SearchNumberBox.tsx +++ b/src/renderer/src/components/Modules/NethVoice/SearchResults/SearchNumberBox.tsx @@ -11,7 +11,7 @@ import { t } from 'i18next' import { useAccount } from '@renderer/hooks/useAccount' import { cloneDeep } from 'lodash' import { cleanRegex, getIsPhoneNumber, sortByProperty } from '@renderer/lib/utils' -import { useSharedState } from '@renderer/store' +import { useNethlinkData, useSharedState } from '@renderer/store' import { usePhonebookSearchModule } from './hook/usePhoneBookSearchModule' import { usePhoneIslandEventHandler } from '@renderer/hooks/usePhoneIslandEventHandler' import { Scrollable } from '@renderer/components/Scrollable' @@ -25,7 +25,7 @@ export function SearchNumberBox({ searchResult, showContactForm }: SearchNumberB const { callNumber } = usePhoneIslandEventHandler() const phoneBookModule = usePhonebookSearchModule() const [searchText] = phoneBookModule.searchTextState - const [operators] = useSharedState('operators') + const [operators] = useNethlinkData('operators') const [filteredPhoneNumbers, setFilteredPhoneNumbers] = useState([]) const [canAddToPhonebook, setCanAddToPhonebook] = useState(false) const { isCallsEnabled } = useAccount() diff --git a/src/renderer/src/components/Modules/NethVoice/Speeddials/hook/useFavouriteModule.ts b/src/renderer/src/components/Modules/NethVoice/Speeddials/hook/useFavouriteModule.ts index c8fd6700..9cdf89ef 100644 --- a/src/renderer/src/components/Modules/NethVoice/Speeddials/hook/useFavouriteModule.ts +++ b/src/renderer/src/components/Modules/NethVoice/Speeddials/hook/useFavouriteModule.ts @@ -1,4 +1,4 @@ -import { useNethlinkData, useSharedState } from "@renderer/store" +import { useNethlinkData } from "@renderer/store" import { ContactType, SearchData } from "@shared/types" import { useEffect, useState } from "react" import { debouncer } from "@shared/utils/utils" @@ -7,8 +7,8 @@ import { FilterTypes, SpeeddialTypes } from "@shared/constants" import { Log } from "@shared/utils/logger" export const useFavouriteModule = () => { const [speeddialsModule] = useNethlinkData('speeddialsModule') - const [rawSpeedDials, setRawSpeedDials] = useSharedState('speeddials') - const [operators] = useSharedState('operators') + const [rawSpeedDials, setRawSpeedDials] = useNethlinkData('speeddials') + const [operators] = useNethlinkData('operators') const [favourites, setFavourites] = useState(undefined) const { NethVoiceAPI } = useLoggedNethVoiceAPI() useEffect(() => { @@ -37,7 +37,6 @@ export const useFavouriteModule = () => { } const isFavourite = (contact: ContactType) => { - Log.warning(contact) return contact.notes?.includes(SpeeddialTypes.FAVOURITES) } diff --git a/src/renderer/src/components/Modules/NethVoice/Speeddials/hook/useSpeedDialsModule.ts b/src/renderer/src/components/Modules/NethVoice/Speeddials/hook/useSpeedDialsModule.ts index ea7bd6f7..ad6919db 100644 --- a/src/renderer/src/components/Modules/NethVoice/Speeddials/hook/useSpeedDialsModule.ts +++ b/src/renderer/src/components/Modules/NethVoice/Speeddials/hook/useSpeedDialsModule.ts @@ -12,7 +12,7 @@ export const useSpeedDialsModule = (): { upsertSpeedDial(data: ContactType): Promise } => { const [speeddialsModule, setSpeeddialsModule] = useNethlinkData('speeddialsModule') - const [rawSpeedDials, setRawSpeedDials] = useSharedState('speeddials') + const [rawSpeedDials, setRawSpeedDials] = useNethlinkData('speeddials') const [speedDials, setSpeedDials] = useState(undefined) const { NethVoiceAPI } = useLoggedNethVoiceAPI() const update = (selector: keyof SpeedDialModuleData) => (value: T | undefined) => { diff --git a/src/renderer/src/components/Modules/NethVoice/Speeddials/shared/ContactNumber.tsx b/src/renderer/src/components/Modules/NethVoice/Speeddials/shared/ContactNumber.tsx index 5e52c71f..4a647812 100644 --- a/src/renderer/src/components/Modules/NethVoice/Speeddials/shared/ContactNumber.tsx +++ b/src/renderer/src/components/Modules/NethVoice/Speeddials/shared/ContactNumber.tsx @@ -5,9 +5,9 @@ import { faTrash as DeleteIcon, } from '@fortawesome/free-solid-svg-icons' import { Menu } from '@headlessui/react' -import { ContactType, OperatorData } from '@shared/types' +import { ContactType } from '@shared/types' import { t } from 'i18next' -import { useSharedState } from '@renderer/store' +import { useNethlinkData } from '@renderer/store' import { truncate } from '@renderer/utils' import { useTheme } from '@renderer/theme/Context' import { ContactNameAndActions } from '@renderer/components/Modules/NethVoice/BaseModule/ContactNameAndAction' @@ -31,7 +31,7 @@ export function ContactNumber({ isLastItem, }: SpeedDialNumberProps): JSX.Element { const { theme: nethTheme } = useTheme() - const [operators] = useSharedState('operators') + const [operators] = useNethlinkData('operators') return (
{ const { isCallsEnabled, hasPermission } = useAccount() const { NethVoiceAPI } = useLoggedNethVoiceAPI() const [account, setAccount] = useRefState(useSharedState('account')) - const [operators, setOperators] = useRefState(useSharedState('operators')) - const [, setSpeeddials] = useRefState(useSharedState('speeddials')) - const [, setQueues] = useRefState(useSharedState('queues')) - const [, setParkings] = useRefState(useSharedState('parkings')) - const [lastCalls, setLastCalls] = useRefState(useSharedState('lastCalls')) - const [missedCalls, setMissedCalls] = useRefState(useSharedState('missedCalls')) + const [operators, setOperators] = useRefState(useNethlinkData('operators')) + const [, setSpeeddials] = useRefState(useNethlinkData('speeddials')) + const [, setQueues] = useRefState(useNethlinkData('queues')) + const [, setParkings] = useRefState(useNethlinkData('parkings')) + const [lastCalls, setLastCalls] = useRefState(useNethlinkData('lastCalls')) + const [missedCalls, setMissedCalls] = useRefState(useNethlinkData('missedCalls')) function callNumber(number: string) { @@ -45,17 +45,31 @@ export const usePhoneIslandEventHandler = () => { extensions: operators.current?.extensions || {}, } for (const [username, operator] of Object.entries(op)) { - updatedOperators.operators[username] = { - ...(updatedOperators.operators[username] || operator), - ...operator - } - if (account.current && username === account.current.username) { - account.current.data!.mainPresence = operator.mainPresence - setAccount(() => account.current) + if (username !== account.current!.username) { + if (updatedOperators.operators[username]) { + updatedOperators.operators[username].mainPresence = operator.mainPresence + } else { + updatedOperators.operators[username] = { + ...operator + } + } + } else { + setAccount((p) => { + if (p && p.data) { + return { + ...p, + data: { + ...p.data, + mainPresence: operator.mainPresence + } + } + } + return undefined + }) } } setOperators(() => updatedOperators) - }, [account.current, operators.current]) + }, [account.current, operators]) function onQueueUpdate(queues: { [queueId: string]: any }) { setQueues((p) => ({ @@ -121,7 +135,9 @@ export const usePhoneIslandEventHandler = () => { return missed }) } - setLastCalls(newLastCalls.rows) + setLastCalls(() => [ + ...newLastCalls.rows + ]) } const updateLastCalls = async () => { diff --git a/src/renderer/src/pages/NethLinkPage.tsx b/src/renderer/src/pages/NethLinkPage.tsx index 275a3798..c13b6a19 100644 --- a/src/renderer/src/pages/NethLinkPage.tsx +++ b/src/renderer/src/pages/NethLinkPage.tsx @@ -12,6 +12,7 @@ import { ConnectionErrorDialog } from '@renderer/components' import { debouncer } from '@shared/utils/utils' import { useAccount } from '@renderer/hooks/useAccount' import { Sidebar } from '@renderer/components/Modules/NethVoice/BaseModule/Sidebar' +import { AvailableDevices } from '@shared/types' export interface NethLinkPageProps { @@ -20,6 +21,7 @@ export interface NethLinkPageProps { export function NethLinkPage({ handleRefreshConnection }: NethLinkPageProps) { const [account, setAccount] = useSharedState('account') + const [, setDevice] = useSharedState('device') const [, setNotifications] = useSharedState('notifications') const [connection] = useSharedState('connection') const { hasPermission } = useAccount() @@ -68,6 +70,7 @@ export function NethLinkPage({ handleRefreshConnection }: NethLinkPageProps) { } function initialize() { + Log.info('INITIALIZE NETHLINK FRONTEND') Notification.requestPermission() .then(() => { Log.info('requested notification permission') @@ -103,10 +106,13 @@ export function NethLinkPage({ handleRefreshConnection }: NethLinkPageProps) { ...me } })) + const device = me.default_device.type as AvailableDevices + setDevice(() => device) }) } async function loadData() { + Log.info('update account') NethVoiceAPI.fetchOperators().then((op) => { saveOperators(op) me() diff --git a/src/renderer/src/pages/PhoneIslandPage.tsx b/src/renderer/src/pages/PhoneIslandPage.tsx index 0fea66fc..83d25a02 100644 --- a/src/renderer/src/pages/PhoneIslandPage.tsx +++ b/src/renderer/src/pages/PhoneIslandPage.tsx @@ -11,13 +11,13 @@ import { usePhoneIsland } from '@renderer/hooks/usePhoneIsland' import { PhoneIslandContainer } from '@renderer/components/pageComponents/phoneIsland/phoneIslandContainer' import { usePhoneIslandEventListener } from '@renderer/hooks/usePhoneIslandEventListeners' import { useInitialize } from '@renderer/hooks/useInitialize' -import { useNethVoiceAPI } from '@shared/useNethVoiceAPI' +import { useLoggedNethVoiceAPI } from '@renderer/hooks/useLoggedNethVoiceAPI' export function PhoneIslandPage() { const [account] = useSharedState('account') const [dataConfig, setDataConfig] = useState(undefined) const { state, phoneIsalndSizes, events } = usePhoneIslandEventListener() const { createDataConfig, dispatchAndWait } = usePhoneIsland() - const { NethVoiceAPI } = useNethVoiceAPI() + const { NethVoiceAPI } = useLoggedNethVoiceAPI() const deviceInformationObject = useRef(undefined) const isDataConfigCreated = useRef(false) @@ -61,6 +61,7 @@ export function PhoneIslandPage() { window.electron.receive(IPC_EVENTS.CHANGE_DEFAULT_DEVICE, async (deviceInformationObject, force) => { Log.info('CHANGE_DEFAULT_DEVICE', { force, deviceInformationObject, }) const changed = await NethVoiceAPI.User.default_device(deviceInformationObject, force) + Log.info('CHANGE_DEFAULT_DEVICE', { changed }) if (changed) { eventDispatch(PHONE_ISLAND_EVENTS['phone-island-default-device-change'], { deviceInformationObject }) } diff --git a/src/renderer/src/store.ts b/src/renderer/src/store.ts index edc8b535..7cbb7514 100644 --- a/src/renderer/src/store.ts +++ b/src/renderer/src/store.ts @@ -13,8 +13,9 @@ import { devtools } from 'zustand/middleware' import { usePageCtx } from './contexts/pageContext'; type SharedState = { - [Key in keyof T]: T[Key]; -} & { + data: { + [Key in keyof T]: T[Key]; + }, setData: (key: keyof T, value: any) => T; }; @@ -27,35 +28,51 @@ export function createGlobalStateHook(globalStateDefaultObject: T, sharedWith // @ts-ignore const useSharedStore = create>(devtools((set) => ({ - ...globalStateDefaultObject, - setData: (key: keyof T, value: any) => set((state: any) => ({ ...state, [key]: typeof value === 'function' ? value(state[key]) : value })) + data: { + ...globalStateDefaultObject, + }, + setData: (key: keyof T, value: any) => { + let newState + set((state: SharedState) => { + let res: T | undefined = typeof value === 'function' + ? value(state.data[key]) + : value + newState = { + setData: state.setData, + data: { + ...state.data, + [key]: res + } + } + return newState + }) + return newState.data + } }))) function useGlobalState( key: Key ): [T[Key], (setter: ((previous: T[Key]) => T[Key]) | T[Key]) => void] { - const value = useSharedStore((state: SharedState) => state[key]); + //const global = useSharedStore((state: SharedState) => state.data); + const value = useSharedStore((state: SharedState) => state.data[key]); const setData = useSharedStore((state: SharedState) => state.setData); const pageData = usePageCtx() const setValue = (arg0: ((prev: T[Key]) => T[Key]) | T[Key]) => { - let state - let updatedValue + let global: T if (typeof arg0 === 'function') { - state = setData(key, (prevValue: T[Key]) => { - updatedValue = (arg0 as (prev: T[Key]) => T[Key])(prevValue); - return updatedValue + global = setData(key, (prevValue: T[Key]) => { + return (arg0 as (prev: T[Key]) => T[Key])(prevValue); }); } else { - updatedValue = arg0 - state = setData(key, arg0); + global = setData(key, arg0); } - if (sharedWithBackend) { - const sharedStateCopy = Object.assign({}, state) + if (pageData?.page && sharedWithBackend) { + const sharedStateCopy = Object.assign({}, global) Log.info('STORE share state from', pageData?.page, { key: key }) - window.electron.send(IPC_EVENTS.UPDATE_SHARED_STATE, sharedStateCopy, pageData?.page, key); + window.electron.send(IPC_EVENTS.UPDATE_SHARED_STATE, sharedStateCopy, pageData.page, key); } }; @@ -65,6 +82,7 @@ export function createGlobalStateHook(globalStateDefaultObject: T, sharedWith const useRegisterStoreHook = () => { const pageData = usePageCtx() const setData = useSharedStore((state: SharedState) => state.setData); + //const global = useSharedStore((state: SharedState) => state.data); const isRegistered = useRef(false) useEffect(() => { @@ -76,6 +94,7 @@ export function createGlobalStateHook(globalStateDefaultObject: T, sharedWith Log.info('shared state received from', fromPage) Object.keys(newStore as object).forEach((k: any) => { setData(k, newStore[k]) + //global[k] = newStore[k] }) } }) @@ -98,20 +117,20 @@ export const { device: undefined, auth: undefined, connection: undefined, - lastCalls: undefined, lostCallNotifications: undefined, - missedCalls: undefined, notifications: undefined, - operators: undefined, page: undefined, - parkings: undefined, - queues: undefined, - speeddials: undefined, theme: undefined } as LocalStorageData, true) export const useNethlinkData = createGlobalStateHook({ selectedSidebarMenu: MENU_ELEMENT.FAVOURITES, + lastCalls: undefined, + speeddials: undefined, + missedCalls: undefined, + operators: undefined, + queues: undefined, + parkings: undefined, isForwardDialogOpen: false, phonebookModule: { selectedContact: undefined @@ -127,12 +146,12 @@ export const useNethlinkData = createGlobalStateHook({ }, showAddContactModule: false, showPhonebookSearchModule: false -} as NethLinkPageData, true).useGlobalState +} as NethLinkPageData).useGlobalState export const useLoginPageData = createGlobalStateHook({ isLoading: false, selectedAccount: undefined, windowHeight: LoginPageSize.h -} as LoginPageData, true).useGlobalState +} as LoginPageData).useGlobalState diff --git a/src/shared/types.ts b/src/shared/types.ts index 9436ee0c..8d217fd3 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -373,12 +373,6 @@ export type LocalStorageData = { page?: PageType, theme?: AvailableThemes, connection?: boolean, - operators?: OperatorData, - queues?: QueuesType, - parkings?: ParkingType[], - lastCalls?: CallData[], - speeddials?: ContactType[], - missedCalls?: CallData[], notifications?: NotificationData, lostCallNotifications?: CallData[], } @@ -409,6 +403,12 @@ export type AuthAppData = { } export type NethLinkPageData = { selectedSidebarMenu: MENU_ELEMENT, + operators?: OperatorData, + queues?: QueuesType, + parkings?: ParkingType[], + lastCalls?: CallData[], + speeddials?: ContactType[], + missedCalls?: CallData[], showPhonebookSearchModule?: boolean, isForwardDialogOpen?: boolean, showAddContactModule?: boolean, diff --git a/src/shared/useNethVoiceAPI.ts b/src/shared/useNethVoiceAPI.ts index ba5a9b07..f344f739 100644 --- a/src/shared/useNethVoiceAPI.ts +++ b/src/shared/useNethVoiceAPI.ts @@ -101,7 +101,6 @@ export const useNethVoiceAPI = (loggedAccount: Account | undefined = undefined) if (!nethlinkExtension) reject(new Error("Questo utente non è abilitato all'uso del NethLink")) else { - resolve(account) } } @@ -121,7 +120,7 @@ export const useNethVoiceAPI = (loggedAccount: Account | undefined = undefined) try { await _POST('/webrest/authentication/logout', {}) } catch (e) { - Log.warning(" error during logout:", e) + Log.warning("error during logout:", e) } finally { resolve() } @@ -288,7 +287,7 @@ export const useNethVoiceAPI = (loggedAccount: Account | undefined = undefined) if (ext && !loggedAccount && isFirstHeartbeat) { isFirstHeartbeat = false const response = await User.heartbeat(ext.id, data.username) - Log.info('Send HEARTBEAT', { response }) + Log.info('Sent HEARTBEAT', { response }) } return data }, From af337bdd8111c2789f0c9600b66aad6f10dd9c90 Mon Sep 17 00:00:00 2001 From: therockerline Date: Thu, 12 Dec 2024 19:50:22 +0100 Subject: [PATCH 07/21] fix connection dialog --- .../src/hooks/usePhoneIslandEventListeners.ts | 24 +++++++++++++------ src/shared/constants.ts | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/renderer/src/hooks/usePhoneIslandEventListeners.ts b/src/renderer/src/hooks/usePhoneIslandEventListeners.ts index 69eb2119..d7b9279d 100644 --- a/src/renderer/src/hooks/usePhoneIslandEventListeners.ts +++ b/src/renderer/src/hooks/usePhoneIslandEventListeners.ts @@ -201,7 +201,10 @@ export const usePhoneIslandEventListener = () => { ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-call-transfer-successfully"]), ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-call-transfer-successfully-popup-close"]), ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-call-transfer-successfully-popup-open"], () => { - sendNotification(t('Notification.call_transferred_title'), t('Notification.call_transferred_body')) + sendNotification( + t('Notification.call_transferred_title'), + t('Notification.call_transferred_body') + ) }), ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-call-transfer-switch"]), ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-call-transfer-switched"]), @@ -305,27 +308,34 @@ export const usePhoneIslandEventListener = () => { }), ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-socket-connected"], () => { + setConnected(true) setPhoneIslandData((p) => ({ ...p, - activeAlerts: { - ...p.activeAlerts, - ['socket-disconnected']: false - }, + activeAlerts: {}, currentCall: { ...defaultCall }, + view: null })) }), ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-socket-disconnected"], () => { if (account && connected) { setConnected(false) } - + setPhoneIslandData((p) => ({ + ...p, + currentCall: { + ...defaultCall + } + })) }), ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-socket-disconnected-popup-close"], () => { setPhoneIslandData((p) => ({ ...p, - activeAlerts: {}, + activeAlerts: { + ...p.activeAlerts, + ['socket-disconnected']: false + }, })) }), ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-socket-disconnected-popup-open"], () => { diff --git a/src/shared/constants.ts b/src/shared/constants.ts index fe14049a..12bee5fb 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -315,7 +315,7 @@ const phoneIslandSizes = { // Alerts Section alerts: { width: 418, - height: 88, + height: 92, }, }, } From cc5c6ee839a3a9919c55e04ec9263cb9b7de42d8 Mon Sep 17 00:00:00 2001 From: therockerline Date: Fri, 13 Dec 2024 14:47:38 +0100 Subject: [PATCH 08/21] fix: save theme changes on disk --- src/main/classes/controllers/AccountController.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/classes/controllers/AccountController.ts b/src/main/classes/controllers/AccountController.ts index ff022848..5abb00f4 100644 --- a/src/main/classes/controllers/AccountController.ts +++ b/src/main/classes/controllers/AccountController.ts @@ -138,6 +138,7 @@ export class AccountController { auth!.availableAccounts[getAccountUID(account)] = account store.set('auth', auth) } + store.saveToDisk() } } From eaec26ffaf5e90c01171b90f1d9b35c69c45beb8 Mon Sep 17 00:00:00 2001 From: therockerline Date: Fri, 13 Dec 2024 15:21:30 +0100 Subject: [PATCH 09/21] fix: add back button hover on profile dialog --- .../Modules/NethVoice/BaseModule/ProfileDialog.tsx | 5 ++--- .../NethVoice/BaseModule/ProfileDialog/MenuPage.tsx | 7 ++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx index 0afb76ef..ab488dff 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx @@ -86,7 +86,7 @@ export const ProfileDialog = ({ 'w-[252px] h-[297px]', 'bg-bgInput dark:bg-bgInputDark', 'rounded-lg border dark:border-borderDark border-borderLight', - 'fixed z-[200] right-[56px] top-[54px]' + 'fixed z-[200] right-[58px] top-[54px]' ) } >
@@ -94,7 +94,6 @@ export const ProfileDialog = ({
- setSelectedMenu(() => MenuItem.presence)} > @@ -120,7 +119,7 @@ export const ProfileDialog = ({
diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuPage.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuPage.tsx index e5aefd3d..7d0d55e5 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuPage.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/MenuPage.tsx @@ -17,8 +17,9 @@ export const MenuPage = ({ goBack, title, children }) => { )} >
-
{

{t('Login.Back')}

-
+
From e1315f81cb82a7dba3b8a4129bccd4d82e5f895e Mon Sep 17 00:00:00 2001 From: therockerline Date: Fri, 13 Dec 2024 15:58:56 +0100 Subject: [PATCH 10/21] fix: get account theme from disk after autologin --- src/main/classes/controllers/AccountController.ts | 1 + src/renderer/src/hooks/usePhoneIslandEventHandler.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/classes/controllers/AccountController.ts b/src/main/classes/controllers/AccountController.ts index 5abb00f4..8f2c894c 100644 --- a/src/main/classes/controllers/AccountController.ts +++ b/src/main/classes/controllers/AccountController.ts @@ -77,6 +77,7 @@ export class AccountController { let loggedAccount: Account = { ...lastLoggedAccount, ...tempLoggedAccount, + theme: lastLoggedAccount.theme || tempLoggedAccount.theme } const { parseConfig } = useLogin() diff --git a/src/renderer/src/hooks/usePhoneIslandEventHandler.ts b/src/renderer/src/hooks/usePhoneIslandEventHandler.ts index 298b9cee..1b7c4e56 100644 --- a/src/renderer/src/hooks/usePhoneIslandEventHandler.ts +++ b/src/renderer/src/hooks/usePhoneIslandEventHandler.ts @@ -80,7 +80,7 @@ export const usePhoneIslandEventHandler = () => { function onParkingsUpdate(parkings: ParkingsType) { const parkedCalls: ParkingType[] = Object.values(parkings) - setParkings(() => parkedCalls || []) + setParkings(() => [...parkedCalls] || []) } function saveSpeeddials(speeddialsResponse: ContactType[] | undefined) { From abe30f592f3dabb4d296ee8a786667717927bc82 Mon Sep 17 00:00:00 2001 From: Edoardo Spadoni Date: Sun, 15 Dec 2024 16:54:53 +0100 Subject: [PATCH 11/21] Update usePhoneIslandEventHandler.ts --- src/renderer/src/hooks/usePhoneIslandEventHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/hooks/usePhoneIslandEventHandler.ts b/src/renderer/src/hooks/usePhoneIslandEventHandler.ts index 1b7c4e56..398547a2 100644 --- a/src/renderer/src/hooks/usePhoneIslandEventHandler.ts +++ b/src/renderer/src/hooks/usePhoneIslandEventHandler.ts @@ -80,7 +80,7 @@ export const usePhoneIslandEventHandler = () => { function onParkingsUpdate(parkings: ParkingsType) { const parkedCalls: ParkingType[] = Object.values(parkings) - setParkings(() => [...parkedCalls] || []) + setParkings(() => [...parkedCalls]) } function saveSpeeddials(speeddialsResponse: ContactType[] | undefined) { From 9a8a0e167c46d1073044609cc4dea3bc173f1794 Mon Sep 17 00:00:00 2001 From: Edoardo Spadoni Date: Sun, 15 Dec 2024 17:14:37 +0100 Subject: [PATCH 12/21] Update translations.json --- public/locales/en/translations.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/en/translations.json b/public/locales/en/translations.json index 365863a0..572202dc 100644 --- a/public/locales/en/translations.json +++ b/public/locales/en/translations.json @@ -347,7 +347,8 @@ "Cellphone": "Cellphone", "Activate voicemail": "Activate voicemail", "Go to NethVoice CTI": "Go to NethVoice CTI", - "Pair device": "Pair device" + "Pair device": "Pair device", + "Theme": "Theme" }, "OperatorDrawer": { "Book": "Book", From ac85c14f27ff18b1072081fa974e69df81aede47 Mon Sep 17 00:00:00 2001 From: Edoardo Spadoni Date: Sun, 15 Dec 2024 17:14:57 +0100 Subject: [PATCH 13/21] Update translations.json --- public/locales/it/translations.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/it/translations.json b/public/locales/it/translations.json index d2747920..c99ddd43 100644 --- a/public/locales/it/translations.json +++ b/public/locales/it/translations.json @@ -347,7 +347,8 @@ "Cellphone": "Cellulare", "Activate voicemail": "Attiva voicemail", "Go to NethVoice CTI": "Vai a NethVoice CTI", - "Pair device": "Abbina dispositivo" + "Pair device": "Abbina dispositivo", + "Theme": "Tema" }, "OperatorDrawer": { "Book": "Prenota", From db376d242a868e36b9b09d4e8959c9843ce32f04 Mon Sep 17 00:00:00 2001 From: Edoardo Spadoni Date: Sun, 15 Dec 2024 17:15:20 +0100 Subject: [PATCH 14/21] Update translations.json --- src/renderer/public/locales/en/translations.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/renderer/public/locales/en/translations.json b/src/renderer/public/locales/en/translations.json index 365863a0..572202dc 100644 --- a/src/renderer/public/locales/en/translations.json +++ b/src/renderer/public/locales/en/translations.json @@ -347,7 +347,8 @@ "Cellphone": "Cellphone", "Activate voicemail": "Activate voicemail", "Go to NethVoice CTI": "Go to NethVoice CTI", - "Pair device": "Pair device" + "Pair device": "Pair device", + "Theme": "Theme" }, "OperatorDrawer": { "Book": "Book", From e73afe1cb2a958f38f888c68bb098e2f7653fc7d Mon Sep 17 00:00:00 2001 From: Edoardo Spadoni Date: Sun, 15 Dec 2024 17:15:53 +0100 Subject: [PATCH 15/21] Update translations.json --- src/renderer/public/locales/it/translations.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/renderer/public/locales/it/translations.json b/src/renderer/public/locales/it/translations.json index d2747920..c99ddd43 100644 --- a/src/renderer/public/locales/it/translations.json +++ b/src/renderer/public/locales/it/translations.json @@ -347,7 +347,8 @@ "Cellphone": "Cellulare", "Activate voicemail": "Attiva voicemail", "Go to NethVoice CTI": "Vai a NethVoice CTI", - "Pair device": "Abbina dispositivo" + "Pair device": "Abbina dispositivo", + "Theme": "Tema" }, "OperatorDrawer": { "Book": "Prenota", From e62d87a49d7c41cf99ee510c1dfadbb27dc76332 Mon Sep 17 00:00:00 2001 From: Edoardo Spadoni Date: Tue, 17 Dec 2024 08:58:40 +0100 Subject: [PATCH 16/21] chore(dep): update phone-island --- package-lock.json | 8 ++++---- package.json | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index e61e945a..be498266 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@nethesis/nethesis-brands-svg-icons": "github:nethesis/Font-Awesome#ns-brands", "@nethesis/nethesis-light-svg-icons": "github:nethesis/Font-Awesome#ns-light", "@nethesis/nethesis-solid-svg-icons": "github:nethesis/Font-Awesome#ns-solid", - "@nethesis/phone-island": "^0.8.27", + "@nethesis/phone-island": "^0.8.29", "@tailwindcss/forms": "^0.5.7", "@types/lodash": "^4.14.202", "@types/node": "^18.19.9", @@ -5040,9 +5040,9 @@ } }, "node_modules/@nethesis/phone-island": { - "version": "0.8.27", - "resolved": "https://registry.npmjs.org/@nethesis/phone-island/-/phone-island-0.8.27.tgz", - "integrity": "sha512-Ui00Thsxus/oSa5SPNKh5uh/ATt06WOVq9t2ar47mt9QpeT1eDULzRWhk584swv8Nk1IK8eL5Ug8g2yaq3rBKg==", + "version": "0.8.29", + "resolved": "https://registry.npmjs.org/@nethesis/phone-island/-/phone-island-0.8.29.tgz", + "integrity": "sha512-w/PIX2ScZe6H/Nr6n1PG4nK3/qj1mwahOo41qM+s4/K1+qYSe19B36KqBp6VA3vMMNZMZlO50dUAx8l9dbFckQ==", "dev": true, "license": "GPL-3.0-or-later", "dependencies": { diff --git a/package.json b/package.json index 35aac7bb..ab67132f 100644 --- a/package.json +++ b/package.json @@ -46,10 +46,10 @@ "@fortawesome/react-fontawesome": "^0.2.0", "@headlessui/react": "^1.7.18", "@hookform/resolvers": "^3.3.4", - "@nethesis/nethesis-light-svg-icons": "github:nethesis/Font-Awesome#ns-light", "@nethesis/nethesis-brands-svg-icons": "github:nethesis/Font-Awesome#ns-brands", + "@nethesis/nethesis-light-svg-icons": "github:nethesis/Font-Awesome#ns-light", "@nethesis/nethesis-solid-svg-icons": "github:nethesis/Font-Awesome#ns-solid", - "@nethesis/phone-island": "^0.8.27", + "@nethesis/phone-island": "^0.8.29", "@tailwindcss/forms": "^0.5.7", "@types/lodash": "^4.14.202", "@types/node": "^18.19.9", @@ -104,6 +104,5 @@ }, "optionalDependencies": { "dmg-license": "^1.0.11" - }, - "dependencies": {} + } } From 83e88b80ad40a6d44c563a83faf1e141fff0b659 Mon Sep 17 00:00:00 2001 From: therockerline Date: Tue, 17 Dec 2024 13:00:37 +0100 Subject: [PATCH 17/21] fix: minor handle device selection --- package-lock.json | 8 +-- package.json | 2 +- .../classes/controllers/AccountController.ts | 5 +- src/main/lib/ipcEvents.ts | 4 ++ src/main/main.ts | 5 -- .../NethVoice/BaseModule/ProfileDialog.tsx | 2 +- .../DeviceSettings/DeviceBox.tsx | 30 ++++++++--- src/renderer/src/hooks/useAccount.ts | 52 ++++++++++++++++--- .../src/hooks/usePhoneIslandEventListeners.ts | 17 +++--- src/renderer/src/pages/NethLinkPage.tsx | 36 +++---------- src/shared/constants.ts | 4 +- src/shared/types.ts | 33 +++++++----- src/shared/useNethVoiceAPI.ts | 5 +- 13 files changed, 127 insertions(+), 76 deletions(-) diff --git a/package-lock.json b/package-lock.json index be498266..5ff30d7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@nethesis/nethesis-brands-svg-icons": "github:nethesis/Font-Awesome#ns-brands", "@nethesis/nethesis-light-svg-icons": "github:nethesis/Font-Awesome#ns-light", "@nethesis/nethesis-solid-svg-icons": "github:nethesis/Font-Awesome#ns-solid", - "@nethesis/phone-island": "^0.8.29", + "@nethesis/phone-island": "^0.8.30", "@tailwindcss/forms": "^0.5.7", "@types/lodash": "^4.14.202", "@types/node": "^18.19.9", @@ -5040,9 +5040,9 @@ } }, "node_modules/@nethesis/phone-island": { - "version": "0.8.29", - "resolved": "https://registry.npmjs.org/@nethesis/phone-island/-/phone-island-0.8.29.tgz", - "integrity": "sha512-w/PIX2ScZe6H/Nr6n1PG4nK3/qj1mwahOo41qM+s4/K1+qYSe19B36KqBp6VA3vMMNZMZlO50dUAx8l9dbFckQ==", + "version": "0.8.30", + "resolved": "https://registry.npmjs.org/@nethesis/phone-island/-/phone-island-0.8.30.tgz", + "integrity": "sha512-wol7QCf2dDpR0pjtcwC5BiYLOomoboZi61ngNh43DxJDgigayzIOR20hOVWGXJRIEDjVehM8zW5BvLLutv8DRw==", "dev": true, "license": "GPL-3.0-or-later", "dependencies": { diff --git a/package.json b/package.json index ab67132f..8ad1316e 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@nethesis/nethesis-brands-svg-icons": "github:nethesis/Font-Awesome#ns-brands", "@nethesis/nethesis-light-svg-icons": "github:nethesis/Font-Awesome#ns-light", "@nethesis/nethesis-solid-svg-icons": "github:nethesis/Font-Awesome#ns-solid", - "@nethesis/phone-island": "^0.8.29", + "@nethesis/phone-island": "^0.8.30", "@tailwindcss/forms": "^0.5.7", "@types/lodash": "^4.14.202", "@types/node": "^18.19.9", diff --git a/src/main/classes/controllers/AccountController.ts b/src/main/classes/controllers/AccountController.ts index 8f2c894c..64cedef7 100644 --- a/src/main/classes/controllers/AccountController.ts +++ b/src/main/classes/controllers/AccountController.ts @@ -112,7 +112,10 @@ export class AccountController { lastUser: accountUID, lastUserCryptPsw: cryptString }, - device: account.data?.default_device.type as AvailableDevices, + device: account.data?.default_device ? { + type: account.data.default_device.type as AvailableDevices, + id: account.data.default_device.id, + } : undefined, connection: store.store.connection || false }, 'saveLoggedAccount') store.saveToDisk() diff --git a/src/main/lib/ipcEvents.ts b/src/main/lib/ipcEvents.ts index 68bac83e..5b2074f5 100644 --- a/src/main/lib/ipcEvents.ts +++ b/src/main/lib/ipcEvents.ts @@ -240,6 +240,10 @@ export function registerIpcEvents() { NethLinkController.instance.window.emit(IPC_EVENTS.EMIT_PARKING_UPDATE) }) + ipcMain.on(IPC_EVENTS.UPDATE_ACCOUNT, (_) => { + NethLinkController.instance.window.emit(IPC_EVENTS.UPDATE_ACCOUNT) + }) + } diff --git a/src/main/main.ts b/src/main/main.ts index e30437ca..63489c7b 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -258,10 +258,6 @@ function attachOnReadyProcess() { } }) - //TODO:da me.endpoints.extensions cerco il tipo e prendo il primo diverso da nethlink tra [webrtc, physical] (con questa priorità) - //post del defaultDevice - - async function startApp(attempt = 0) { let data = store.store || store.getFromDisk() if (!checkData(data)) { @@ -482,7 +478,6 @@ async function attachProtocolListeners() { Log.info('HandleProtocol Nethlink:', url) const data = new URL("nethlink://" + url) - //TODO: define actions const action = data.host try { switch (action) { diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx index ab488dff..b72fb77c 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog.tsx @@ -68,7 +68,7 @@ export const ProfileDialog = ({ useEffect(() => { if (account) { - setDeviceIcon(() => DeviceIcons[device || 'nethlink']) + setDeviceIcon(() => DeviceIcons[device?.type || 'nethlink']) } }, [device]) diff --git a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx index 993f422b..e87a8e4a 100644 --- a/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx +++ b/src/renderer/src/components/Modules/NethVoice/BaseModule/ProfileDialog/DeviceSettings/DeviceBox.tsx @@ -7,12 +7,14 @@ import { faOfficePhone as PhysicalDevice } from '@nethesis/nethesis-solid-svg-icons' import { useSharedState } from "@renderer/store" -import { AvailableDevices, Extension } from "@shared/types" +import { AvailableDevices, Extension, ExtensionsType, StatusTypes } from "@shared/types" import { Log } from "@shared/utils/logger" import { t } from "i18next" import { OptionElement } from "../OptionElement" import { debouncer } from "@shared/utils/utils" import { IPC_EVENTS } from "@shared/constants" +import { useEffect, useState } from "react" +import { useLoggedNethVoiceAPI } from "@renderer/hooks/useLoggedNethVoiceAPI" export const DeviceIcons = { nethlink: { @@ -23,18 +25,30 @@ export const DeviceIcons = { type DeviceType = { name: AvailableDevices, label: string, icon?: IconProp | IconDefinition, iconElem?: JSX.Element } type AvailableDeviceOption = { id: string, + status: StatusTypes ext: Extension } & DeviceType export const DeviceBox = () => { const [account] = useSharedState('account') const [device, setDevice] = useSharedState('device') + const [devicesStatus, setDevicesStatus] = useState({}) + const { NethVoiceAPI } = useLoggedNethVoiceAPI() + useEffect(() => { + NethVoiceAPI.AstProxy.extensions().then((devices: ExtensionsType) => { + setDevicesStatus(() => ({ ...devices })) + }) + }, []) + + async function handleSetDevice(newDevice: AvailableDeviceOption) { Log.info('change device to', newDevice) - setDevice(() => newDevice.name) + setDevice(() => ({ + id: newDevice.id, + type: newDevice.name, + status: newDevice.status + })) debouncer('update-account-default-device', () => { - //await NethVoiceAPI.User.default_device(newDevice.ext) window.electron.send(IPC_EVENTS.CHANGE_DEFAULT_DEVICE, newDevice.ext, true) - //TODO: lanciare anche l'eventDispatch su phoneIsland }, 250) } @@ -47,10 +61,13 @@ export const DeviceBox = () => { const nethlink = account.data!.endpoints.extension.find((e) => e.type === 'nethlink')! const accountDevices: AvailableDeviceOption[] = account.data?.endpoints.extension.reduce((p, d) => { if (d.type === 'physical') { + const status = devicesStatus[d.id]?.status + const isOffline = status !== 'online' p.push({ ...themeOptions.physical, name: 'physical', - label: d.description || themeOptions.physical.label, + label: `${d.description || themeOptions.physical.label} ${isOffline ? '(offline)' : ''}`, + status, id: d.id, ext: d } as AvailableDeviceOption) @@ -58,6 +75,7 @@ export const DeviceBox = () => { return p }, [{ ...themeOptions.nethlink, + status: 'online', name: 'nethlink', id: nethlink?.id, ext: nethlink @@ -71,7 +89,7 @@ export const DeviceBox = () => { icon={availableDevices.icon} iconElem={availableDevices.iconElem} label={availableDevices.label} - isSelected={device === availableDevices.name} + isSelected={device?.id === availableDevices.id} onClick={() => handleSetDevice(availableDevices)} /> ))} diff --git a/src/renderer/src/hooks/useAccount.ts b/src/renderer/src/hooks/useAccount.ts index c0536d16..38c34613 100644 --- a/src/renderer/src/hooks/useAccount.ts +++ b/src/renderer/src/hooks/useAccount.ts @@ -1,38 +1,74 @@ -import { StatusTypes } from "@shared/types" -import { useEffect, useState } from "react" +import { AvailableDevices, Device, Extension, ExtensionsType, StatusTypes } from "@shared/types" +import { useEffect, useRef, useState } from "react" import { useSharedState } from "@renderer/store" import { PERMISSION } from "@shared/constants" +import { Log } from "@shared/utils/logger" +import { useLoggedNethVoiceAPI } from "./useLoggedNethVoiceAPI" export const useAccount = () => { - const [account] = useSharedState('account') + const [account, setAccount] = useSharedState('account') + const [device, setDevice] = useSharedState('device') const [status, setStatus] = useState('offline') const [isCallsEnabled, setIsCallsEnabled] = useState(false) + const lastDevice = useRef() + const { NethVoiceAPI } = useLoggedNethVoiceAPI() useEffect(() => { - if (account) { + updateStatus() + }, [account?.data, device]) + - const _status: StatusTypes = account.data?.mainPresence || status + const updateStatus = async () => { + if (account) { + let _status: StatusTypes = account.data?.mainPresence || status + if (lastDevice.current?.id !== device?.id) { + lastDevice.current = device + if (device?.type === 'physical') { + const devices = await NethVoiceAPI.AstProxy.extensions() + _status = devices[device.id].status || 'offline' + Log.info('update device', device?.id || 'ND', device?.type || 'ND', devices[device?.id || ''].status || 'ND', _status) + } + } setStatus(() => _status) + Log.info('update device status', _status, device?.id, device?.type) setIsCallsEnabled(() => !(_status === 'busy' || _status === 'ringing' || _status === 'offline')) } else { setStatus('offline') setIsCallsEnabled(false) } - - }, [account?.data]) + } const hasPermission = (permission: PERMISSION) => { return account?.data?.profile?.macro_permissions?.settings?.permissions?.[permission]?.value } + const updateAccountData = async () => { + const me = await NethVoiceAPI.User.me() + Log.info('phone-island-default-device-updated', me.default_device) + const device = { + type: me.default_device.type as AvailableDevices, + id: me.default_device.id + } + setDevice(() => device) + setAccount((p) => ({ + ...p!, + data: { + ...p?.data, + ...me + } + })) + + } + return { status, isCallsEnabled, - hasPermission + hasPermission, + updateAccountData } } diff --git a/src/renderer/src/hooks/usePhoneIslandEventListeners.ts b/src/renderer/src/hooks/usePhoneIslandEventListeners.ts index d7b9279d..18c49184 100644 --- a/src/renderer/src/hooks/usePhoneIslandEventListeners.ts +++ b/src/renderer/src/hooks/usePhoneIslandEventListeners.ts @@ -5,8 +5,6 @@ import { useEffect, useState } from "react" import { t } from "i18next" import { sendNotification } from "@renderer/utils" import { useSharedState } from "@renderer/store" -import { useNetwork } from "@shared/useNetwork" -import http from 'http' const defaultCall = { @@ -18,7 +16,6 @@ const defaultCall = { export const usePhoneIslandEventListener = () => { const [account] = useSharedState('account') const [connected, setConnected] = useSharedState('connection') - const { GET } = useNetwork() const [phoneIslandData, setPhoneIslandData] = useState({ activeAlerts: {}, @@ -55,14 +52,16 @@ export const usePhoneIslandEventListener = () => { //CALLS ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-action-physical"], async (data) => { //const res = await GET(data.urlCallObject.url) - Log.info('phone-island-action-physical', data.urlCallObject.url) window.electron.send(IPC_EVENTS.START_CALL_BY_URL, data.urlCallObject.url) + Log.info('phone-island-action-physical', data.urlCallObject.url) }), ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-call-ringing"], () => { setPhoneIslandData((p) => ({ ...p, currentCall: { ...p.currentCall, - incoming: true + accepted: false, + incoming: true, + outgoing: false }, view: PhoneIslandView.CALL })) @@ -105,7 +104,9 @@ export const usePhoneIslandEventListener = () => { ...p, currentCall: { ...p.currentCall, - outgoing: true + outgoing: true, + accepted: false, + incoming: false }, view: PhoneIslandView.CALL })) @@ -245,6 +246,10 @@ export const usePhoneIslandEventListener = () => { ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-default-device-change"]), ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-default-device-changed"]), + ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-default-device-updated"], (e) => { + Log.info('"phone-island-default-device-updated', e) + window.electron.send(IPC_EVENTS.UPDATE_ACCOUNT) + }), //update the status of device from server ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-detach"]), ...eventHandler(PHONE_ISLAND_EVENTS["phone-island-detached"]), diff --git a/src/renderer/src/pages/NethLinkPage.tsx b/src/renderer/src/pages/NethLinkPage.tsx index c13b6a19..12e0b793 100644 --- a/src/renderer/src/pages/NethLinkPage.tsx +++ b/src/renderer/src/pages/NethLinkPage.tsx @@ -20,11 +20,10 @@ export interface NethLinkPageProps { } export function NethLinkPage({ handleRefreshConnection }: NethLinkPageProps) { - const [account, setAccount] = useSharedState('account') - const [, setDevice] = useSharedState('device') + const [account] = useSharedState('account') const [, setNotifications] = useSharedState('notifications') const [connection] = useSharedState('connection') - const { hasPermission } = useAccount() + const { hasPermission, updateAccountData } = useAccount() const isFetching = useRef(false) const { saveOperators, onQueueUpdate, onParkingsUpdate, saveLastCalls, saveSpeeddials, onMainPresence, updateLastCalls, updateParkings } = @@ -42,16 +41,10 @@ export function NethLinkPage({ handleRefreshConnection }: NethLinkPageProps) { if (account) { if (!operatorFetchLoopInterval.current) { loadData() - operatorFetchLoopInterval.current = setInterval( - () => { - loadData() - }, + operatorFetchLoopInterval.current = setInterval(loadData, 1000 * 60 * 60 * 24 ) - accountMeInterval.current = setInterval( - () => { - me() - }, + accountMeInterval.current = setInterval(updateAccountData, 1000 * 60 * 45 ) } @@ -83,6 +76,7 @@ export function NethLinkPage({ handleRefreshConnection }: NethLinkPageProps) { window.electron.receive(IPC_EVENTS.EMIT_MAIN_PRESENCE_UPDATE, onMainPresence) window.electron.receive(IPC_EVENTS.EMIT_PARKING_UPDATE, updateParkings) window.electron.receive(IPC_EVENTS.EMIT_QUEUE_UPDATE, onQueueUpdate) + window.electron.receive(IPC_EVENTS.UPDATE_ACCOUNT, updateAccountData) } const showUpdateAppNotification = () => { @@ -97,25 +91,11 @@ export function NethLinkPage({ handleRefreshConnection }: NethLinkPageProps) { })) } - function me() { - NethVoiceAPI.User.me().then((me) => { - setAccount((p) => ({ - ...p!, - data: { - ...p?.data, - ...me - } - })) - const device = me.default_device.type as AvailableDevices - setDevice(() => device) - }) - } - async function loadData() { Log.info('update account') NethVoiceAPI.fetchOperators().then((op) => { saveOperators(op) - me() + updateAccountData() }) NethVoiceAPI.HistoryCall.interval().then(saveLastCalls) @@ -130,7 +110,7 @@ export function NethLinkPage({ handleRefreshConnection }: NethLinkPageProps) { if (!isFetching.current) { isFetching.current = true NethVoiceAPI.Phonebook.getSpeeddials().then(saveSpeeddials) - me() + updateAccountData() } debouncer('speeddial-fetch', () => { isFetching.current = false @@ -157,7 +137,7 @@ export function NethLinkPage({ handleRefreshConnection }: NethLinkPageProps) {
- me()} /> + updateAccountData()} />
reloadData()} /> diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 12bee5fb..5deeb8fa 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -80,7 +80,8 @@ export enum IPC_EVENTS { EMIT_PARKING_UPDATE = "EMIT_PARKING_UPDATE", TRANSFER_CALL = "TRANSFER_CALL", CHANGE_DEFAULT_DEVICE = "CHANGE_DEFAULT_DEVICE", - START_CALL_BY_URL = "START_CALL_BY_URL" + START_CALL_BY_URL = "START_CALL_BY_URL", + UPDATE_ACCOUNT = "UPDATE_ACCOUNT" } //PHONE ISLAND EVENTS @@ -102,6 +103,7 @@ export enum PHONE_ISLAND_EVENTS { 'phone-island-theme-changed' = 'phone-island-theme-changed', 'phone-island-default-device-change' = 'phone-island-default-device-change', 'phone-island-default-device-changed' = 'phone-island-default-device-changed', + 'phone-island-default-device-updated' = 'phone-island-default-device-updated', // Listen Call Events: phone-island-call* 'phone-island-call-start' = 'phone-island-call-start', 'phone-island-call-answer' = 'phone-island-call-answer', diff --git a/src/shared/types.ts b/src/shared/types.ts index 8d217fd3..11f5227f 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -297,26 +297,27 @@ export type GroupsType = { export type ExtensionsType = { [phoneNumber: string]: { + ip: string cf: string - cfVm: string + mac: string cfb: string - cfbVm: string cfu: string + dnd: boolean + cfVm: string + port: string + name: string + cfbVm: string cfuVm: string - chanType: string + exten: string codecs: string[] + status: StatusTypes context: string - conversations: {} - dnd: false - exten: string - ip: string - mac: string - name: string - port: string - sipuseragent: string - status: string + chanType: string username: string + sipuseragent: string + conversations: object, } + } export type AvatarType = { @@ -366,10 +367,16 @@ export type PageType = { export type Size = { w: number; h: number } +export type Device = { + type: AvailableDevices, + id: string, + status?: StatusTypes +} + export type LocalStorageData = { account?: Account, auth?: AuthAppData, - device?: AvailableDevices, + device?: Device, page?: PageType, theme?: AvailableThemes, connection?: boolean, diff --git a/src/shared/useNethVoiceAPI.ts b/src/shared/useNethVoiceAPI.ts index f344f739..d4cc16ca 100644 --- a/src/shared/useNethVoiceAPI.ts +++ b/src/shared/useNethVoiceAPI.ts @@ -11,7 +11,8 @@ import { StatusTypes, OperatorsType, AccountData, - BaseAccountData + BaseAccountData, + ExtensionsType } from '@shared/types' import { Log } from '@shared/utils/logger' import { useNetwork } from './useNetwork' @@ -64,7 +65,7 @@ export const useNethVoiceAPI = (loggedAccount: Account | undefined = undefined) const AstProxy = { groups: async () => await _GET('/webrest/astproxy/opgroups'), - extensions: async () => await _GET('/webrest/astproxy/extensions'), + extensions: async (): Promise => await _GET('/webrest/astproxy/extensions'), getQueues: async () => await _GET('/webrest/astproxy/queues'), getParkings: async () => await _GET('/webrest/astproxy/parkings'), pickupParking: async (parkInformation: any) => await _POST('/webrest/astproxy/pickup_parking', parkInformation) From c8ffb9449b260966017cb08545efea31a19e9145 Mon Sep 17 00:00:00 2001 From: Edoardo Spadoni Date: Tue, 17 Dec 2024 14:59:34 +0100 Subject: [PATCH 18/21] chore(dep): update phone-island --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5ff30d7f..2c055f75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@nethesis/nethesis-brands-svg-icons": "github:nethesis/Font-Awesome#ns-brands", "@nethesis/nethesis-light-svg-icons": "github:nethesis/Font-Awesome#ns-light", "@nethesis/nethesis-solid-svg-icons": "github:nethesis/Font-Awesome#ns-solid", - "@nethesis/phone-island": "^0.8.30", + "@nethesis/phone-island": "^0.8.31", "@tailwindcss/forms": "^0.5.7", "@types/lodash": "^4.14.202", "@types/node": "^18.19.9", @@ -5040,9 +5040,9 @@ } }, "node_modules/@nethesis/phone-island": { - "version": "0.8.30", - "resolved": "https://registry.npmjs.org/@nethesis/phone-island/-/phone-island-0.8.30.tgz", - "integrity": "sha512-wol7QCf2dDpR0pjtcwC5BiYLOomoboZi61ngNh43DxJDgigayzIOR20hOVWGXJRIEDjVehM8zW5BvLLutv8DRw==", + "version": "0.8.31", + "resolved": "https://registry.npmjs.org/@nethesis/phone-island/-/phone-island-0.8.31.tgz", + "integrity": "sha512-c1v/GOTQ9CFMoW2AZbZpQHG1Qe2w4OlW6CvO5jvJLSN7HxUqUhSy1hkbQQ6yOP1bCEHsxueSGguATfNb7YO7uw==", "dev": true, "license": "GPL-3.0-or-later", "dependencies": { diff --git a/package.json b/package.json index 8ad1316e..0b307709 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@nethesis/nethesis-brands-svg-icons": "github:nethesis/Font-Awesome#ns-brands", "@nethesis/nethesis-light-svg-icons": "github:nethesis/Font-Awesome#ns-light", "@nethesis/nethesis-solid-svg-icons": "github:nethesis/Font-Awesome#ns-solid", - "@nethesis/phone-island": "^0.8.30", + "@nethesis/phone-island": "^0.8.31", "@tailwindcss/forms": "^0.5.7", "@types/lodash": "^4.14.202", "@types/node": "^18.19.9", From 59f40e26bee32d29051ba5a2c02acc12ce193cfe Mon Sep 17 00:00:00 2001 From: tonyco97 Date: Tue, 17 Dec 2024 15:15:48 +0100 Subject: [PATCH 19/21] fix: fixed wrong translations --- public/locales/en/translations.json | 29 ++++++++++--------- public/locales/it/translations.json | 29 ++++++++++--------- .../public/locales/en/translations.json | 29 ++++++++++--------- .../public/locales/it/translations.json | 29 ++++++++++--------- 4 files changed, 60 insertions(+), 56 deletions(-) diff --git a/public/locales/en/translations.json b/public/locales/en/translations.json index 572202dc..83192899 100644 --- a/public/locales/en/translations.json +++ b/public/locales/en/translations.json @@ -897,19 +897,20 @@ "download": "Download the update" }, "Errors": { - "browser_permissions": "Errore di autorizzazione", - "user_permissions": "Errore di autorizzazione", - "unknown_media_permissions": "Errore di autorizzazione", - "webrtc_down": "WebRTC non disponibile", - "socket_down": "Socket non disponibile", - "busy_camera": "Camera occupata", - "call_transfered": "Chiamata trasferita con successo", - "The browser doesn't have permission to access camera or microphone.": "Il browser non ha il permesso di accedere alla fotocamera o al microfono.", - "You must accept audio and video permissions.": "Devi accettare i permessi audio e video.", - "Web Phone can't access audio or camera on this device.": "Il Web Phone non può accedere all'audio o alla fotocamera su questo dispositivo.", - "Web Phone connection is down.": "La connessione del Web Phone è interrotta.", - "Server connection is down.": "La connessione al server è interrotta.", - "Camera is used by another application.": "La fotocamera è utilizzata da un'altra applicazione.", - "Call transferred successfully.": "Chiamata trasferita con successo." + "browser_permissions": "Browser Permissions Error", + "user_permissions": "User Permissions Error", + "unknown_media_permissions": "Unknown Media Permissions Error", + "webrtc_down": "WebRTC Down", + "socket_down": "Socket Down", + "busy_camera": "Busy Camera", + "call_transfered": "Call Transferred Successfully", + "The browser doesn't have permission to access camera or microphone.": "The browser doesn't have permission to access camera or microphone.", + "You must accept audio and video permissions.": "You must accept audio and video permissions.", + "Web Phone can't access audio or camera on this device.": "Web Phone can't access audio or camera on this device.", + "Web Phone connection is down.": "Web Phone connection is down.", + "NethLink connection is down.": "NethLink connection is down", + "Server connection is down.": "Server connection is down.", + "Camera is used by another application.": "Camera is used by another application.", + "Call transferred successfully.": "Call transferred successfully." } } diff --git a/public/locales/it/translations.json b/public/locales/it/translations.json index c99ddd43..04aa01d4 100644 --- a/public/locales/it/translations.json +++ b/public/locales/it/translations.json @@ -897,19 +897,20 @@ "download": "Scarica l'aggiornamento" }, "Errors": { - "browser_permissions": "Browser Permissions Error", - "user_permissions": "User Permissions Error", - "unknown_media_permissions": "Unknown Media Permissions Error", - "webrtc_down": "WebRTC Down", - "socket_down": "Socket Down", - "busy_camera": "Busy Camera", - "call_transfered": "Call Transferred Successfully", - "The browser doesn't have permission to access camera or microphone.": "The browser doesn't have permission to access camera or microphone.", - "You must accept audio and video permissions.": "You must accept audio and video permissions.", - "Web Phone can't access audio or camera on this device.": "Web Phone can't access audio or camera on this device.", - "Web Phone connection is down.": "Web Phone connection is down.", - "Server connection is down.": "Server connection is down.", - "Camera is used by another application.": "Camera is used by another application.", - "Call transferred successfully.": "Call transferred successfully." + "browser_permissions": "Errore di autorizzazione", + "user_permissions": "Errore di autorizzazione", + "unknown_media_permissions": "Errore di autorizzazione", + "webrtc_down": "WebRTC non disponibile", + "socket_down": "Socket non disponibile", + "busy_camera": "Camera occupata", + "call_transfered": "Chiamata trasferita con successo", + "The browser doesn't have permission to access camera or microphone.": "Il browser non ha il permesso di accedere alla fotocamera o al microfono.", + "You must accept audio and video permissions.": "Devi accettare i permessi audio e video.", + "Web Phone can't access audio or camera on this device.": "Il Web Phone non può accedere all'audio o alla fotocamera su questo dispositivo.", + "Web Phone connection is down.": "La connessione del Web Phone è interrotta.", + "NethLink connection is down.": "Connessione NethLink non disponibile", + "Server connection is down.": "La connessione al server è interrotta.", + "Camera is used by another application.": "La fotocamera è utilizzata da un'altra applicazione.", + "Call transferred successfully.": "Chiamata trasferita con successo." } } diff --git a/src/renderer/public/locales/en/translations.json b/src/renderer/public/locales/en/translations.json index 572202dc..83192899 100644 --- a/src/renderer/public/locales/en/translations.json +++ b/src/renderer/public/locales/en/translations.json @@ -897,19 +897,20 @@ "download": "Download the update" }, "Errors": { - "browser_permissions": "Errore di autorizzazione", - "user_permissions": "Errore di autorizzazione", - "unknown_media_permissions": "Errore di autorizzazione", - "webrtc_down": "WebRTC non disponibile", - "socket_down": "Socket non disponibile", - "busy_camera": "Camera occupata", - "call_transfered": "Chiamata trasferita con successo", - "The browser doesn't have permission to access camera or microphone.": "Il browser non ha il permesso di accedere alla fotocamera o al microfono.", - "You must accept audio and video permissions.": "Devi accettare i permessi audio e video.", - "Web Phone can't access audio or camera on this device.": "Il Web Phone non può accedere all'audio o alla fotocamera su questo dispositivo.", - "Web Phone connection is down.": "La connessione del Web Phone è interrotta.", - "Server connection is down.": "La connessione al server è interrotta.", - "Camera is used by another application.": "La fotocamera è utilizzata da un'altra applicazione.", - "Call transferred successfully.": "Chiamata trasferita con successo." + "browser_permissions": "Browser Permissions Error", + "user_permissions": "User Permissions Error", + "unknown_media_permissions": "Unknown Media Permissions Error", + "webrtc_down": "WebRTC Down", + "socket_down": "Socket Down", + "busy_camera": "Busy Camera", + "call_transfered": "Call Transferred Successfully", + "The browser doesn't have permission to access camera or microphone.": "The browser doesn't have permission to access camera or microphone.", + "You must accept audio and video permissions.": "You must accept audio and video permissions.", + "Web Phone can't access audio or camera on this device.": "Web Phone can't access audio or camera on this device.", + "Web Phone connection is down.": "Web Phone connection is down.", + "NethLink connection is down.": "NethLink connection is down", + "Server connection is down.": "Server connection is down.", + "Camera is used by another application.": "Camera is used by another application.", + "Call transferred successfully.": "Call transferred successfully." } } diff --git a/src/renderer/public/locales/it/translations.json b/src/renderer/public/locales/it/translations.json index c99ddd43..04aa01d4 100644 --- a/src/renderer/public/locales/it/translations.json +++ b/src/renderer/public/locales/it/translations.json @@ -897,19 +897,20 @@ "download": "Scarica l'aggiornamento" }, "Errors": { - "browser_permissions": "Browser Permissions Error", - "user_permissions": "User Permissions Error", - "unknown_media_permissions": "Unknown Media Permissions Error", - "webrtc_down": "WebRTC Down", - "socket_down": "Socket Down", - "busy_camera": "Busy Camera", - "call_transfered": "Call Transferred Successfully", - "The browser doesn't have permission to access camera or microphone.": "The browser doesn't have permission to access camera or microphone.", - "You must accept audio and video permissions.": "You must accept audio and video permissions.", - "Web Phone can't access audio or camera on this device.": "Web Phone can't access audio or camera on this device.", - "Web Phone connection is down.": "Web Phone connection is down.", - "Server connection is down.": "Server connection is down.", - "Camera is used by another application.": "Camera is used by another application.", - "Call transferred successfully.": "Call transferred successfully." + "browser_permissions": "Errore di autorizzazione", + "user_permissions": "Errore di autorizzazione", + "unknown_media_permissions": "Errore di autorizzazione", + "webrtc_down": "WebRTC non disponibile", + "socket_down": "Socket non disponibile", + "busy_camera": "Camera occupata", + "call_transfered": "Chiamata trasferita con successo", + "The browser doesn't have permission to access camera or microphone.": "Il browser non ha il permesso di accedere alla fotocamera o al microfono.", + "You must accept audio and video permissions.": "Devi accettare i permessi audio e video.", + "Web Phone can't access audio or camera on this device.": "Il Web Phone non può accedere all'audio o alla fotocamera su questo dispositivo.", + "Web Phone connection is down.": "La connessione del Web Phone è interrotta.", + "NethLink connection is down.": "Connessione NethLink non disponibile", + "Server connection is down.": "La connessione al server è interrotta.", + "Camera is used by another application.": "La fotocamera è utilizzata da un'altra applicazione.", + "Call transferred successfully.": "Chiamata trasferita con successo." } } From 7a4ad1ff290de29917c7bb188be15ad0744e6194 Mon Sep 17 00:00:00 2001 From: therockerline Date: Wed, 18 Dec 2024 16:12:17 +0100 Subject: [PATCH 20/21] fix: error handling on unreachable phone --- public/locales/en/translations.json | 1 + public/locales/it/translations.json | 1 + src/main/lib/ipcEvents.ts | 31 ++++++++++-- src/main/main.ts | 48 +++++++++++++------ .../public/locales/en/translations.json | 1 + .../public/locales/it/translations.json | 1 + src/renderer/src/pages/NethLinkPage.tsx | 9 ++++ src/renderer/src/pages/PhoneIslandPage.tsx | 5 ++ src/shared/constants.ts | 4 +- 9 files changed, 82 insertions(+), 19 deletions(-) diff --git a/public/locales/en/translations.json b/public/locales/en/translations.json index 83192899..e398fcfb 100644 --- a/public/locales/en/translations.json +++ b/public/locales/en/translations.json @@ -127,6 +127,7 @@ "application_update_body": "Click here to open the page where you can download the new release", "lost_call_title": "Missed call from {{user}}", "lost_call_body": "You received a call from {{number}} at {{datetime}}", + "physical_phone_error": "the phone {{phone}} is not reachable. Make sure it is connected", "call_transferred_title": "Call tranferred", "call_transferred_body": "The call was succesfully tranferred" }, diff --git a/public/locales/it/translations.json b/public/locales/it/translations.json index 04aa01d4..4c900f36 100644 --- a/public/locales/it/translations.json +++ b/public/locales/it/translations.json @@ -127,6 +127,7 @@ "application_update_body": "Clicca quì per aprire la pagina dove potrai scaricare la nuova release", "lost_call_title": "Chiamata persa da {{user}}", "lost_call_body": "Hai ricevuto una chiamata da {{number}} alle {{datetime}}", + "physical_phone_error": "Il telefono {{phone}} non è raggiungibile. Assicurarsi che sia collegato", "call_transferred_title": "Traferimento di chiamata", "call_transferred_body": "La chiamata è stata trasferita con successo" }, diff --git a/src/main/lib/ipcEvents.ts b/src/main/lib/ipcEvents.ts index 5b2074f5..825a2077 100644 --- a/src/main/lib/ipcEvents.ts +++ b/src/main/lib/ipcEvents.ts @@ -1,7 +1,7 @@ import { AccountController, DevToolsController } from '@/classes/controllers' import { LoginController } from '@/classes/controllers/LoginController' import { PhoneIslandController } from '@/classes/controllers/PhoneIslandController' -import { IPC_EVENTS } from '@shared/constants' +import { IPC_EVENTS, PHONE_ISLAND_EVENTS } from '@shared/constants' import { Account, OnDraggingWindow, PAGES } from '@shared/types' import { BrowserWindow, app, ipcMain, screen, shell } from 'electron' import { join } from 'path' @@ -56,9 +56,32 @@ export function registerIpcEvents() { }) ipcMain.on(IPC_EVENTS.START_CALL_BY_URL, async (_event, url) => { - http.get(url, (res) => { - Log.info('START_CALL_BY_URL', res) - }) + + function triggerError(e, request: http.ClientRequest | undefined = undefined) { + Log.error(e) + PhoneIslandController.instance.window.emit(IPC_EVENTS.END_CALL) + NethLinkController.instance.window.emit(IPC_EVENTS.RESPONSE_START_CALL_BY_URL, false) + request && request.destroy() + } + PhoneIslandController.instance.window.hide() + try { + const request = http.get(url, { + timeout: 3000 + }, (res) => { + if (res.statusCode !== 200) { + triggerError(new Error('status error'), request) + } + NethLinkController.instance.window.emit(IPC_EVENTS.RESPONSE_START_CALL_BY_URL, true) + PhoneIslandController.instance.window.show() + Log.info('START_CALL_BY_URL', url, res.statusCode) + }) + + request.on('error', (e) => { + triggerError(e, request) + }) + } catch (e) { + triggerError(e) + } }) ipcMain.on(IPC_EVENTS.UPDATE_SHARED_STATE, (_, newState, page, selector) => { diff --git a/src/main/main.ts b/src/main/main.ts index 63489c7b..f2db3dcf 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -22,6 +22,7 @@ import { uniq } from 'lodash' import { Registry } from 'rage-edit'; import { useNethVoiceAPI } from '@shared/useNethVoiceAPI' import { URL } from 'url' +import { platform } from 'os' //get app parameter const params = process.argv @@ -546,22 +547,41 @@ function attachPowerMonitor() { } } +function changeNethlinkTheme() { + let updatedSystemTheme: AvailableThemes = nativeTheme.shouldUseDarkColors + ? 'dark' + : 'light' + + //set nethlink pages theme + if (store.store) { + if (store.store.account?.theme === 'dark' || store.store.account?.theme === 'light') { + store.set('theme', store.store.account?.theme) + } else { + store.set('theme', updatedSystemTheme) + } + } + + //se tray icon theme based on system settings + + if (process.platform === 'win32') { + Registry.get(`HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize`, 'SystemUsesLightTheme').then((system) => { + Log.info('THEME CHANGE SYSTEM', system) + const theme = system === 1 ? 'light' : 'dark' + TrayController.instance?.changeIconByTheme(theme) + }).catch((e) => { + Log.error(e) + TrayController.instance?.changeIconByTheme('dark') + }); + } else { + TrayController.instance?.changeIconByTheme(updatedSystemTheme) + } +} + function attachThemeChangeListener() { + + changeNethlinkTheme() nativeTheme.on('updated', () => { - if (store.store) { - const theme = store.store.theme - const updatedSystemTheme: AvailableThemes = nativeTheme.shouldUseDarkColors - ? 'dark' - : 'light' - - if (store.store.account?.theme === 'dark' || store.store.account?.theme === 'light') { - store.set('theme', store.store.account?.theme) - } else { - store.set('theme', updatedSystemTheme) - } - //update theme state on the store - TrayController.instance?.changeIconByTheme(updatedSystemTheme) - } + changeNethlinkTheme() }) } /** diff --git a/src/renderer/public/locales/en/translations.json b/src/renderer/public/locales/en/translations.json index 83192899..e398fcfb 100644 --- a/src/renderer/public/locales/en/translations.json +++ b/src/renderer/public/locales/en/translations.json @@ -127,6 +127,7 @@ "application_update_body": "Click here to open the page where you can download the new release", "lost_call_title": "Missed call from {{user}}", "lost_call_body": "You received a call from {{number}} at {{datetime}}", + "physical_phone_error": "the phone {{phone}} is not reachable. Make sure it is connected", "call_transferred_title": "Call tranferred", "call_transferred_body": "The call was succesfully tranferred" }, diff --git a/src/renderer/public/locales/it/translations.json b/src/renderer/public/locales/it/translations.json index 04aa01d4..4c900f36 100644 --- a/src/renderer/public/locales/it/translations.json +++ b/src/renderer/public/locales/it/translations.json @@ -127,6 +127,7 @@ "application_update_body": "Clicca quì per aprire la pagina dove potrai scaricare la nuova release", "lost_call_title": "Chiamata persa da {{user}}", "lost_call_body": "Hai ricevuto una chiamata da {{number}} alle {{datetime}}", + "physical_phone_error": "Il telefono {{phone}} non è raggiungibile. Assicurarsi che sia collegato", "call_transferred_title": "Traferimento di chiamata", "call_transferred_body": "La chiamata è stata trasferita con successo" }, diff --git a/src/renderer/src/pages/NethLinkPage.tsx b/src/renderer/src/pages/NethLinkPage.tsx index 12e0b793..fc7508d0 100644 --- a/src/renderer/src/pages/NethLinkPage.tsx +++ b/src/renderer/src/pages/NethLinkPage.tsx @@ -13,6 +13,7 @@ import { debouncer } from '@shared/utils/utils' import { useAccount } from '@renderer/hooks/useAccount' import { Sidebar } from '@renderer/components/Modules/NethVoice/BaseModule/Sidebar' import { AvailableDevices } from '@shared/types' +import { sendNotification } from '@renderer/utils' export interface NethLinkPageProps { @@ -62,6 +63,13 @@ export function NethLinkPage({ handleRefreshConnection }: NethLinkPageProps) { } } + function handleStartCallByUrlResponse(isValid: boolean) { + if (!isValid) { + const phone = account?.data?.default_device.description || t('Settings.IP Phone') + sendNotification(t('Common.Warning'), t('Notification.physical_phone_error', { phone })) + } + } + function initialize() { Log.info('INITIALIZE NETHLINK FRONTEND') Notification.requestPermission() @@ -77,6 +85,7 @@ export function NethLinkPage({ handleRefreshConnection }: NethLinkPageProps) { window.electron.receive(IPC_EVENTS.EMIT_PARKING_UPDATE, updateParkings) window.electron.receive(IPC_EVENTS.EMIT_QUEUE_UPDATE, onQueueUpdate) window.electron.receive(IPC_EVENTS.UPDATE_ACCOUNT, updateAccountData) + window.electron.receive(IPC_EVENTS.RESPONSE_START_CALL_BY_URL, handleStartCallByUrlResponse) } const showUpdateAppNotification = () => { diff --git a/src/renderer/src/pages/PhoneIslandPage.tsx b/src/renderer/src/pages/PhoneIslandPage.tsx index 83d25a02..fe5ef8ca 100644 --- a/src/renderer/src/pages/PhoneIslandPage.tsx +++ b/src/renderer/src/pages/PhoneIslandPage.tsx @@ -48,6 +48,11 @@ export function PhoneIslandPage() { }) }) + window.electron.receive(IPC_EVENTS.END_CALL, () => { + //controllare se sono physical + eventDispatch(PHONE_ISLAND_EVENTS['phone-island-call-end']) + }) + window.electron.receive(IPC_EVENTS.TRANSFER_CALL, (to: string) => { eventDispatch(PHONE_ISLAND_EVENTS['phone-island-call-transfer'], { to diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 5deeb8fa..d1bbb798 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -81,7 +81,9 @@ export enum IPC_EVENTS { TRANSFER_CALL = "TRANSFER_CALL", CHANGE_DEFAULT_DEVICE = "CHANGE_DEFAULT_DEVICE", START_CALL_BY_URL = "START_CALL_BY_URL", - UPDATE_ACCOUNT = "UPDATE_ACCOUNT" + UPDATE_ACCOUNT = "UPDATE_ACCOUNT", + RESPONSE_START_CALL_BY_URL = "RESPONSE_START_CALL_BY_URL", + END_CALL = "END_CALL" } //PHONE ISLAND EVENTS From d062fd1061b4c26d71ee5836b689fcec49b163c1 Mon Sep 17 00:00:00 2001 From: therockerline Date: Wed, 18 Dec 2024 16:24:44 +0100 Subject: [PATCH 21/21] [wip] check reconnect after token expiration --- src/main/main.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/main.ts b/src/main/main.ts index f2db3dcf..e9748fc1 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -535,8 +535,12 @@ function attachPowerMonitor() { if (store.store.account && NethLinkController.instance) { const isOpen = NethLinkController.instance.window.isOpen() showNethlink = isOpen ?? true - await PhoneIslandController.instance.logout() - NethLinkController.instance.logout() + try { + await PhoneIslandController.instance.logout() + NethLinkController.instance.logout() + } catch (e) { + Log.error('POWER RESUME ERROR on logout', e) + } const autoLoginResult = await AccountController.instance.autoLogin() if (autoLoginResult) { ipcMain.emit(IPC_EVENTS.LOGIN, undefined, { showNethlink })