Skip to content

Commit 3e2c638

Browse files
committed
feat [wip]: add parking feature
1 parent dcc5ebb commit 3e2c638

21 files changed

+529
-79
lines changed

package-lock.json

Lines changed: 41 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"electron-vite": "^2.0.0",
6868
"eslint": "^8.56.0",
6969
"eslint-plugin-react": "^7.33.2",
70+
"framer-motion": "^11.11.8",
7071
"i18next": "^22.4.9",
7172
"i18next-browser-languagedetector": "^7.0.1",
7273
"i18next-electron-fs-backend": "^3.0.1",

src/main/main.ts

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -209,49 +209,58 @@ function attachOnReadyProcess() {
209209

210210
async function startApp(attempt = 0) {
211211
const data = store.getFromDisk()
212-
store.updateStore(data, 'startApp')
213-
log('START APP, retry:', attempt)
214-
if (!store.store.connection) {
215-
log('NO CONNECTION', attempt, store.store)
216-
if (attempt >= 3)
217-
SplashScreenController.instance.window.emit(IPC_EVENTS.SHOW_NO_CONNECTION)
218-
retryAppStart = setTimeout(() => {
219-
startApp(++attempt)
220-
}, 1000)
221-
} else {
222-
if (retryAppStart) {
223-
clearTimeout(retryAppStart)
224-
}
225-
const auth: AuthAppData | undefined = store.store['auth']
226-
await getPermissions()
227-
if (auth?.isFirstStart !== undefined && !auth?.isFirstStart) {
228-
const isLastUserLogged = await AccountController.instance.autoLogin()
229-
if (isLastUserLogged) {
230-
ipcMain.emit(IPC_EVENTS.LOGIN, undefined, { showNethlink: true })
212+
if (checkData(data)) {
213+
store.updateStore(data, 'startApp')
214+
log('START APP, retry:', attempt)
215+
if (!store.store.connection) {
216+
log('NO CONNECTION', attempt, store.store)
217+
if (attempt >= 3)
218+
SplashScreenController.instance.window.emit(IPC_EVENTS.SHOW_NO_CONNECTION)
219+
retryAppStart = setTimeout(() => {
220+
startApp(++attempt)
221+
}, 1000)
222+
} else {
223+
if (retryAppStart) {
224+
clearTimeout(retryAppStart)
225+
}
226+
const auth: AuthAppData | undefined = store.store['auth']
227+
await getPermissions()
228+
if (auth?.isFirstStart !== undefined && !auth?.isFirstStart) {
229+
const isLastUserLogged = await AccountController.instance.autoLogin()
230+
if (isLastUserLogged) {
231+
ipcMain.emit(IPC_EVENTS.LOGIN, undefined, { showNethlink: true })
232+
} else {
233+
store.updateStore({
234+
auth: {
235+
...store.store['auth']!,
236+
lastUser: undefined,
237+
lastUserCryptPsw: undefined
238+
},
239+
account: undefined,
240+
theme: 'system',
241+
connection: store.store['connection'] || false,
242+
}, 'showLogin')
243+
showLogin()
244+
}
231245
} else {
232-
store.updateStore({
233-
auth: {
234-
...store.store['auth']!,
235-
lastUser: undefined,
236-
lastUserCryptPsw: undefined
237-
},
238-
account: undefined,
239-
theme: 'system',
240-
connection: store.store['connection'] || false,
241-
}, 'showLogin')
246+
await resetApp()
242247
showLogin()
243248
}
244-
} else {
245-
await resetApp()
246-
showLogin()
249+
SplashScreenController.instance.window.quit(true)
250+
//once the loading is complete I enable the ability to click on the icon in the tray
251+
TrayController.instance.updateTray({
252+
enableShowButton: true
253+
})
247254
}
255+
} else {
256+
await resetApp()
257+
showLogin()
248258
SplashScreenController.instance.window.quit(true)
249259
//once the loading is complete I enable the ability to click on the icon in the tray
250260
TrayController.instance.updateTray({
251261
enableShowButton: true
252262
})
253263
}
254-
255264
}
256265

257266
app.on('window-all-closed', () => {
@@ -485,5 +494,13 @@ async function checkForUpdate() {
485494
}
486495
}
487496

497+
function checkData(data: any): boolean {
498+
return data.hasOwnProperty('account') &&
499+
data.hasOwnProperty('auth') &&
500+
data.hasOwnProperty('theme') &&
501+
data.hasOwnProperty('connection')
502+
}
503+
488504
//BEGIN APP
489505
startup()
506+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { IconProp } from "@fortawesome/fontawesome-svg-core"
2+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
3+
4+
5+
export interface EmptyListProps {
6+
icon: IconProp,
7+
text: string
8+
}
9+
export const EmptyList = ({ icon, text }: EmptyListProps) => {
10+
11+
return (
12+
<div className="flex flex-col justify-between items-center gap-5 py-[28px] bg-hoverLight dark:bg-hoverDark min-h-[132px] mt-4 rounded-lg ml-5 mr-3">
13+
<div className="text-emptyIconLight dark:text-emptyIconDark">
14+
<FontAwesomeIcon icon={icon} className="text-[28px] " />
15+
</div>
16+
<span className="text-emptyTextLight dark:text-emptyTextDark text-sm">{text}</span>
17+
</div>
18+
)
19+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import { Account, ParkingType } from "@shared/types";
2+
import {
3+
faSquareParking as ParkedCallIcon,
4+
faPhone as CallIcon
5+
} from '@fortawesome/free-solid-svg-icons'
6+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
7+
import { Button } from "@renderer/components/Nethesis";
8+
import { t } from "i18next";
9+
import { motion } from 'framer-motion'
10+
import { useEffect, useRef, useState } from "react";
11+
import { isEmpty } from "lodash";
12+
import { useAccount } from "@renderer/hooks/useAccount";
13+
import { useStoreState } from "@renderer/store";
14+
import { useLoggedNethVoiceAPI } from "@renderer/hooks/useLoggedNethVoiceAPI";
15+
16+
export interface ParkingCallProps {
17+
parkingDetails: ParkingType,
18+
onPickup: (parkingDetails: ParkingType) => Promise<void>
19+
}
20+
export const ParkedCall = ({ parkingDetails, onPickup }: ParkingCallProps) => {
21+
const [time, setTime] = useState(parkingDetails.parkedCaller?.timeout);
22+
const [cardPressStates, setCardPressStates] = useState<boolean>(false)
23+
const [status, setStatus] = useState('begin');
24+
25+
useEffect(() => {
26+
const interval = setInterval(() => {
27+
setTime((prevTime) => {
28+
if (prevTime === Math.ceil(parkingDetails.timeout / 2)) {
29+
setStatus('middle');
30+
}
31+
if (prevTime === Math.ceil(parkingDetails.timeout / 4)) {
32+
setStatus('end');
33+
}
34+
if (prevTime === 0) {
35+
clearInterval(interval);
36+
}
37+
return prevTime - 1;
38+
});
39+
}, 1000);
40+
41+
return () => clearInterval(interval);
42+
}, [parkingDetails.timeout]);
43+
44+
const formattedTime = new Date(time * 1000).toISOString().slice(14, 19);
45+
const nameText = useRef<null | HTMLDivElement>(null)
46+
const animationControls: any = useRef(null)
47+
48+
49+
50+
51+
const handleButtonDown = (index) => {
52+
if (!cardPressStates) {
53+
setCardPressStates(true)
54+
animationControls.current = setTimeout(() => {
55+
longPressHandler(parkingDetails)
56+
}, 2000)
57+
}
58+
}
59+
60+
const handleButtonUp = (index) => {
61+
setCardPressStates(false)
62+
if (animationControls.current) {
63+
clearTimeout(animationControls.current)
64+
}
65+
resetAnimation()
66+
}
67+
68+
const longPressHandler = (parkingDetails: any) => {
69+
onPickup(parkingDetails)
70+
resetAnimation()
71+
}
72+
73+
const resetAnimation = () => {
74+
animationControls.current = null
75+
}
76+
77+
return (
78+
<div className="relative flex flex-row justify-between items-center min-h-[44px] py-2 text-titleLight dark:text-titleDark">
79+
<div className="flex flex-col gap-0">
80+
<div className="flex flex-row items-center text-sm text-textYellowLight dark:text-textYellowDark gap-2">
81+
<FontAwesomeIcon size="1x" icon={ParkedCallIcon} className="text-[14px]" />
82+
<span>
83+
{t('Parks.Parking')} {parkingDetails.name}
84+
</span>
85+
</div>
86+
<span className='text-sm text-left text-gray-900 dark:text-gray-100 w-44 truncate tooltip-parked-user'>
87+
<div className='scrolling-text-container' ref={nameText}>
88+
{nameText?.current?.clientWidth && nameText?.current?.clientWidth > 180 ? (
89+
<>
90+
<div className='scrolling-text'>{parkingDetails?.parkedCaller?.name}</div>
91+
<div className='scrolling-text'>{parkingDetails?.parkedCaller?.name}</div>
92+
</>
93+
) : (
94+
<>
95+
<div >{parkingDetails?.parkedCaller?.name}</div>
96+
</>
97+
)}
98+
</div>
99+
</span>
100+
</div>
101+
<div>
102+
<span
103+
className={`${status === 'begin'
104+
? 'text-titleLight dark:text-titleDark'
105+
: status === 'middle'
106+
? 'text-amber-700 dark:text-amber-500'
107+
: 'text-red-700 dark:text-red-500'
108+
} w-12 font-mono`}
109+
>
110+
{formattedTime}
111+
</span>
112+
</div>
113+
<div className='flex-grow' />
114+
<div
115+
className='relative w-20 mr-5'
116+
onMouseDown={handleButtonDown}
117+
onMouseUp={handleButtonUp}
118+
onMouseLeave={handleButtonUp}
119+
>
120+
<Button variant='white' className='tooltip-parking-button'>
121+
<FontAwesomeIcon
122+
icon={CallIcon}
123+
className='h-4 w-4 text-gray-500 dark:text-gray-200 mr-2'
124+
/>
125+
<span className='w-14 overflow-hidden whitespace-nowrap'>
126+
{cardPressStates ? `${t('Parks.Hold')}` : `${t('Parks.Pick up')}`}
127+
</span>
128+
</Button>
129+
<motion.div
130+
initial={{ width: 0 }}
131+
animate={
132+
cardPressStates
133+
? { width: '100%', transition: { duration: 2 } }
134+
: { width: 0 }
135+
}
136+
className='absolute top-0 left-0 w-full h-8 bg-emerald-500 rounded-md overflow-hidden'
137+
>
138+
<motion.button
139+
className='w-full h-full bg-transparent text-emerald-500 hover:text-emerald-600 rounded-md focus:outline-none'
140+
onMouseDown={handleButtonDown}
141+
onMouseUp={handleButtonUp}
142+
onMouseLeave={handleButtonUp}
143+
>
144+
<div className='flex items-center pl-2'>
145+
<FontAwesomeIcon
146+
icon={CallIcon}
147+
className='h-4 w-4 text-gray-100 dark:text-gray-200 ml-2 mr-2 '
148+
/>
149+
<span className='text-gray-100 text-base overflow-hidden whitespace-nowrap'>{t('Parks.Hold')}</span>
150+
</div>
151+
</motion.button>
152+
</motion.div>
153+
</div>
154+
</div>
155+
156+
)
157+
}

0 commit comments

Comments
 (0)