Skip to content

Commit e5643cd

Browse files
committed
update notifications
add skeleton on load
1 parent cdfd7c0 commit e5643cd

File tree

14 files changed

+180
-63
lines changed

14 files changed

+180
-63
lines changed

public/TrayNotificationIcon.svg

Lines changed: 11 additions & 0 deletions
Loading

src/main/classes/controllers/NethLinkController.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,16 @@ export class NethLinkController {
4141
}
4242

4343
init(account: Account) {
44-
this.loadData()
4544
this.show()
46-
this.operatorFetchLoop()
47-
//Avviso la nethWindow che l'utente è cambiato
4845
this.window.emit(IPC_EVENTS.ACCOUNT_CHANGE, account)
46+
new Promise<void>(async (resolve) => {
47+
await this.loadData()
48+
this.operatorFetchLoop()
49+
resolve()
50+
}).then(() => {
51+
this.window.emit(IPC_EVENTS.LOAD_DATA_END)
52+
})
53+
//Avviso la nethWindow che l'utente è cambiato
4954
}
5055

5156
async loadData() {
@@ -62,4 +67,8 @@ export class NethLinkController {
6267
hide() {
6368
this.window.hide()
6469
}
70+
71+
sendUpdateNotification() {
72+
this.window.emit(IPC_EVENTS.UPDATE_APP_NOTIFICATION)
73+
}
6574
}

src/main/lib/ipcEvents.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { LoginController } from '@/classes/controllers/LoginController'
33
import { PhoneIslandController } from '@/classes/controllers/PhoneIslandController'
44
import { IPC_EVENTS, PHONE_ISLAND_EVENTS } from '@shared/constants'
55
import { Account } from '@shared/types'
6-
import { app, ipcMain, shell } from 'electron'
6+
import { Notification, NotificationConstructorOptions, app, ipcMain, shell } from 'electron'
77
import { join } from 'path'
88
import { log } from '@shared/utils/logger'
99
import { cloneDeep } from 'lodash'
@@ -99,6 +99,10 @@ export function registerIpcEvents() {
9999
shell.openExternal(join(account!.host, path))
100100
})
101101

102+
ipcMain.on(IPC_EVENTS.OPEN_EXTERNAL_PAGE, async (_, path) => {
103+
shell.openExternal(join(path))
104+
})
105+
102106
ipcMain.on(IPC_EVENTS.START_CALL, async (_event, phoneNumber) => {
103107
PhoneIslandController.instance.call(phoneNumber)
104108
})
@@ -131,6 +135,38 @@ export function registerIpcEvents() {
131135
NethLinkController.instance.window.emit(IPC_EVENTS.RECEIVE_SEARCH_RESULT, res)
132136
})
133137

138+
ipcMain.on(IPC_EVENTS.SEND_NOTIFICATION, (event, options: NotificationConstructorOptions, openUrl) => {
139+
options.hasReply = false
140+
141+
log('RECEIVED SEND NOTIFICATION', options, openUrl)
142+
if (process.platform !== 'darwin') {
143+
options.icon = "../../../public/TrayNotificationIcon.svg"
144+
}
145+
const notification: Notification = new Notification(options)
146+
147+
// notification.on('click', () => {
148+
// log('RECEIVED CLICK ON NOTIFICATION', options, openUrl)
149+
// if (openUrl) {
150+
// shell.openExternal(openUrl)
151+
// }
152+
// })
153+
notification.once('failed', () => log('failed'))
154+
notification.once('action', () => log('action'))
155+
notification.once('close', () => log('close'))
156+
notification.once('reply', () => log('reply'))
157+
notification.once('show', () => log('show'))
158+
159+
notification.addListener("click", () => {
160+
log('RECEIVED CLICK ON NOTIFICATION', options, openUrl)
161+
if (openUrl) {
162+
shell.openExternal(openUrl)
163+
}
164+
})
165+
166+
notification.show()
167+
168+
})
169+
134170
//SEND BACK ALL PHONE ISLAND EVENTS
135171
Object.keys(PHONE_ISLAND_EVENTS).forEach((ev) => {
136172
ipcMain.on(ev, async (_event, ...args) => {
@@ -165,3 +201,5 @@ export function registerIpcEvents() {
165201
})
166202
})
167203
}
204+
205+

src/main/main.ts

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Notification, app, nativeTheme, protocol, shell, systemPreferences } from 'electron'
1+
import { app, nativeTheme, protocol, shell, systemPreferences } from 'electron'
22
import { registerIpcEvents } from '@/lib/ipcEvents'
33
import { AccountController, DevToolsController } from './classes/controllers'
44
import { PhoneIslandController } from './classes/controllers/PhoneIslandController'
@@ -13,13 +13,13 @@ import { debouncer, delay, isDev } from '@shared/utils/utils'
1313
import { IPC_EVENTS } from '@shared/constants'
1414
import { NetworkController } from './classes/controllers/NetworkController'
1515
import { AppController } from './classes/controllers/AppController'
16-
import { t } from 'i18next'
1716
new AppController(app)
1817
new NetworkController()
1918
new AccountController(app)
2019

2120
//registro tutti gli eventi che la parte frontend emette verso il backend
2221
registerIpcEvents()
22+
let isFirstStart = true
2323

2424
//imposto che l'app si debba aprire all'avvio del sistema operativo
2525
app.setLoginItemSettings({
@@ -64,31 +64,14 @@ app.whenReady().then(async () => {
6464
NethLinkController.instance.window.addOnBuildListener(updateBuildedWindows)
6565
LoginController.instance.window.addOnBuildListener(updateBuildedWindows)
6666

67-
//aspetto che tutte le finestre siano pronte o un max di 2,5 secondi
67+
//aspetto che tutte le finestre siano pronte o un max di 25 secondi
6868
let time = 0
6969
while (windowsLoaded <= 2 && time < 25) {
7070
await delay(100)
7171
time++
7272
//log(time, windowsLoaded)
7373
}
7474
await getPermissions()
75-
const latestVersionData = await NetworkController.instance.get(`https://api.github.com/repos/nethesis/nethlink/releases/latest`)
76-
log(app.getVersion())
77-
if (latestVersionData.name !== app.getVersion()) {
78-
// const updateLink = `https://github.com/nethesis/nethlink/releases/tag/v${latestVersionData.name}`
79-
const updateLink = 'https://nethesis.github.io/nethlink/'
80-
const notification = new Notification({
81-
title: t('Notification.application_update_title') || 'Aggiornamento disponibile',
82-
body: t('Notification.application_update_body') || 'Clicca quí per scaricare'
83-
})
84-
85-
86-
notification.on('click', (e) => {
87-
// log(e)
88-
shell.openExternal(updateLink)
89-
})
90-
notification.show()
91-
}
9275
//log('call addOnBuildListener ')
9376
nativeTheme.on('updated', () => {
9477
const updatedSystemTheme: AvailableThemes = nativeTheme.shouldUseDarkColors
@@ -136,6 +119,12 @@ const onAccountLogin = (account: Account) => {
136119
//inizializzo la pagina di nethLink e avvio i fetch di history, speeddials e l'interval sugli operatori
137120
NethLinkController.instance.init(account)
138121
//quando l'utente cambia devo riloggarlo sulla phone island
122+
123+
//controllo se ci sono aggiornamenti
124+
if (isFirstStart) {
125+
isFirstStart = false
126+
checkForUpdate()
127+
}
139128
} catch (e) {
140129
console.error(e)
141130
}
@@ -163,6 +152,14 @@ const onLoginFromLoginPage = (account: Account) => {
163152
AccountController.instance.removeEventListener('LOGIN', onLoginFromLoginPage)
164153
}
165154

155+
const checkForUpdate = async () => {
156+
const latestVersionData = await NetworkController.instance.get(`https://api.github.com/repos/nethesis/nethlink/releases/latest`)
157+
log(app.getVersion())
158+
if (latestVersionData.name !== app.getVersion()) {
159+
NethLinkController.instance.sendUpdateNotification()
160+
}
161+
}
162+
166163
app.on('window-all-closed', () => {
167164
app.dock?.hide()
168165
//i18nextBackend.clearMainBindings(ipcMain);

src/preload/index.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { contextBridge, ipcRenderer } from 'electron'
1+
import { NotificationConstructorOptions, contextBridge, ipcRenderer } from 'electron'
22
import { electronAPI } from '@electron-toolkit/preload'
33
import { IPC_EVENTS, PHONE_ISLAND_EVENTS } from '@shared/constants'
44
import {
@@ -16,7 +16,6 @@ import { preloadBindings } from 'i18next-electron-fs-backend'
1616
import { log } from '@shared/utils/logger'
1717

1818
export interface IElectronAPI {
19-
2019
env: NodeJS.ProcessEnv
2120

2221
// Use `contextBridge` APIs to expose Electron APIs to
@@ -51,9 +50,12 @@ export interface IElectronAPI {
5150
onThemeChange(callback: (theme: AvailableThemes) => void): void
5251
onOperatorsChange(callback: (updateOperators: OperatorData) => void): void
5352
onQueueLoaded(onQueueUpdate: (queues: { [queueId: string]: any }) => void): void
53+
onUpdateAppNotification(showUpdateAppNotification: () => void): void
54+
onLoadDataEnd(callback: () => void): void
5455

5556
//EMITTER - only emit, no response
5657
openDevTool(hash: string): unknown
58+
sendNotification(notificationoption: NotificationConstructorOptions, openUrl: string | undefined): void
5759
logout: () => void
5860
startCall(phoneNumber: string): void
5961
changeTheme(theme: AvailableThemes): void
@@ -63,7 +65,8 @@ export interface IElectronAPI {
6365
resizePhoneIsland(offsetWidth: number, offsetHeight: number): void
6466
sendInitializationCompleted(id: string): void
6567
addPhoneIslandListener: (event: PHONE_ISLAND_EVENTS, callback: (...args: any[]) => void) => void
66-
openHostPage(path: string): unknown
68+
openHostPage(path: string): void
69+
openExternalPage(url: string): void
6770
hideNethLink: () => void
6871
exitNethLink: () => void
6972
showPhoneIsland: () => void
@@ -120,6 +123,7 @@ const api: IElectronAPI = {
120123
deviceDefaultChange: setEmitterSync<void>(IPC_EVENTS.DEVICE_DEFAULT_CHANGE),
121124

122125
//EMITTER - only emit, no response
126+
sendNotification: setEmitter(IPC_EVENTS.SEND_NOTIFICATION),
123127
openDevTool: setEmitter(IPC_EVENTS.OPEN_DEV_TOOLS),
124128
hideLoginWindow: setEmitter(IPC_EVENTS.HIDE_LOGIN_WINDOW),
125129
logout: setEmitter(IPC_EVENTS.LOGOUT),
@@ -130,6 +134,7 @@ const api: IElectronAPI = {
130134
changeTheme: setEmitter(IPC_EVENTS.CHANGE_THEME),
131135
sendSearchText: setEmitter(IPC_EVENTS.SEARCH_TEXT),
132136
openHostPage: setEmitter(IPC_EVENTS.OPEN_HOST_PAGE),
137+
openExternalPage: setEmitter(IPC_EVENTS.OPEN_EXTERNAL_PAGE),
133138
hideNethLink: setEmitter(IPC_EVENTS.HIDE_NETH_LINK),
134139
exitNethLink: setEmitter(IPC_EVENTS.CLOSE_NETH_LINK),
135140
showPhoneIsland: setEmitter(IPC_EVENTS.SHOW_PHONE_ISLAND),
@@ -162,6 +167,8 @@ const api: IElectronAPI = {
162167
onThemeChange: addListener(IPC_EVENTS.ON_CHANGE_THEME),
163168
onOperatorsChange: addListener(IPC_EVENTS.OPERATORS_CHANGE),
164169
onQueueLoaded: addListener(IPC_EVENTS.QUEUE_LOADED),
170+
onUpdateAppNotification: addListener(IPC_EVENTS.UPDATE_APP_NOTIFICATION),
171+
onLoadDataEnd: addListener(IPC_EVENTS.LOAD_DATA_END),
165172

166173
addPhoneIslandListener: (event, callback) => {
167174
const evName = `on-${event}`

src/renderer/src/components/MissedCallsBox.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { MissedCall } from './MissedCall'
44
import { CallData } from '@shared/types'
55
import { Button } from './Nethesis/Button'
66
import { t } from 'i18next'
7+
import { SkeletonRow } from './SkeletonRow'
8+
import { useSubscriber } from '@renderer/hooks/useSubscriber'
79

810
export interface MissedCallsBoxProps {
911
missedCalls: CallData[]
@@ -16,6 +18,8 @@ export function MissedCallsBox({
1618
viewAllMissedCalls,
1719
handleSelectedMissedCall
1820
}: MissedCallsBoxProps): JSX.Element {
21+
22+
const isDataLoaded = useSubscriber<boolean>('loadDataEnded')
1923
/* Oltre al fatto che sono le chiamate in entrate esse non devono aver avuto risposta */
2024
/* TODO modificare richiesta al server */
2125
const missedCallsIn = missedCalls?.filter(
@@ -45,7 +49,7 @@ export function MissedCallsBox({
4549
</Button>
4650
</div>
4751
<div className="flex flex-col max-h-[240px] overflow-y-auto">
48-
{missedCallsIn.map((call, idx) => {
52+
{isDataLoaded ? (missedCallsIn.map((call, idx) => {
4953
return (
5054
<div
5155
className={`${idx === missedCallsIn.length - 1 ? `` : `border-b dark:border-borderDark border-borderLight`}`}
@@ -58,6 +62,14 @@ export function MissedCallsBox({
5862
/>
5963
</div>
6064
)
65+
})) : Array(3).fill('').map((_, idx) => {
66+
return <div
67+
className={`${idx === 2 ? `` : `border-b dark:border-borderDark border-borderLight`}`}
68+
key={idx}
69+
>
70+
<SkeletonRow />
71+
72+
</div>
6173
})}
6274
</div>
6375
</div>

src/renderer/src/components/Nethesis/Button.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ export interface ButtonProps
2222
children: ReactNode;
2323
size?: "small" | "base" | "large";
2424
variant?:
25-
| "primary"
26-
| "secondary"
27-
| "white"
28-
| "ghost"
29-
| "danger"
30-
| "dashboard";
25+
| "primary"
26+
| "secondary"
27+
| "white"
28+
| "ghost"
29+
| "danger"
30+
| "dashboard";
3131
fullWidth?: boolean;
3232
fullHeight?: boolean;
3333
disabled?: boolean;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export const SkeletonRow = () => {
2+
return <div
3+
className={`relative flex flex-row justify-between items-center min-h-[44px] p-2 px-5`}
4+
>
5+
<div className="flex gap-6 items-center">
6+
<div className='animate-pulse rounded-full h-12 w-12 bg-gray-300 dark:bg-gray-600'></div>
7+
<div className="flex flex-col gap-1">
8+
<div className='animate-pulse rounded-full h-4 w-[230px] bg-gray-300 dark:bg-gray-600'></div>
9+
</div>
10+
</div>
11+
</div>
12+
}

src/renderer/src/components/SpeedDialsBox.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { SpeedDialNumber } from './SpeedDialNumber'
44
import { ContactType } from '@shared/types'
55
import { Button } from './Nethesis/Button'
66
import { t } from 'i18next'
7+
import { useSubscriber } from '@renderer/hooks/useSubscriber'
8+
import { SkeletonRow } from './SkeletonRow'
79

810
export interface SpeedDialsBoxProps {
911
speeddials: ContactType[] | undefined
@@ -20,6 +22,8 @@ export function SpeedDialsBox({
2022
handleSelectedSpeedDial,
2123
handleDeleteSpeedDial
2224
}: SpeedDialsBoxProps): JSX.Element {
25+
26+
const isDataLoaded = useSubscriber<boolean>('loadDataEnded')
2327
return (
2428
<div className="flex flex-col h-full">
2529
<div className="flex justify-between items-center pb-4 border border-t-0 border-r-0 border-l-0 dark:border-borderDark border-borderLight max-h-[28px] px-5 mt-3">
@@ -41,7 +45,7 @@ export function SpeedDialsBox({
4145
</Button>
4246
</div>
4347
<div className="flex flex-col min-h-[120px] max-h-[240px] overflow-y-auto">
44-
{speeddials && speeddials.length > 0 ? (
48+
{isDataLoaded ? (speeddials && speeddials.length > 0 ? (
4549
speeddials?.map((e, idx) => {
4650
return (
4751
<div
@@ -63,7 +67,14 @@ export function SpeedDialsBox({
6367
<div className="dark:text-titleDark text-titleLight dark:bg-bgDark bg-bgLight px-5 py-2">
6468
{t('SpeedDial.No speed dials')}
6569
</div>
66-
)}
70+
)) : Array(3).fill('').map((_, idx) => {
71+
return <div
72+
className={`${idx === 2 ? `` : `border-b dark:border-borderDark border-borderLight`}`}
73+
key={idx}
74+
>
75+
<SkeletonRow />
76+
</div>
77+
})}
6778
</div>
6879
</div>
6980
)

0 commit comments

Comments
 (0)