@taruvi/navkit 0.0.47 → 0.0.48-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/App.tsx +16 -4
- package/src/NavkitContext.tsx +21 -8
- package/src/components/MattermostChat/MattermostChat.tsx +56 -93
- package/src/components/Shortucts/Shortcuts.styles.ts +3 -1
- package/src/components/Shortucts/Shortcuts.tsx +4 -20
- package/src/components/Shortucts/ShortcutsMenu.tsx +4 -25
- package/src/types.ts +1 -1
package/package.json
CHANGED
package/src/App.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import AppBar from "@mui/material/AppBar"
|
|
2
2
|
import Toolbar from "@mui/material/Toolbar"
|
|
3
3
|
import IconButton from "@mui/material/IconButton"
|
|
4
|
+
import { SvgIcon } from "@mui/material"
|
|
4
5
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
|
5
6
|
import { useState } from "react"
|
|
6
7
|
import { AppLauncher, Profile, ProfileMenu, Shortcuts, ShortcutsMenu, MattermostChat } from "./components"
|
|
@@ -12,8 +13,14 @@ import taruviLogo from "./assets/logo.svg";
|
|
|
12
13
|
import taruviLogoWhite from "./assets/taruvi-logo-white.png";
|
|
13
14
|
import type { Client } from "@taruvi/sdk"
|
|
14
15
|
|
|
16
|
+
const ChatIcon = ({ color }: { color?: string }) => (
|
|
17
|
+
<SvgIcon viewBox="-2 -2 24 24" sx={{ fontSize: 23, display: 'block', position: 'relative', top: 2 }}>
|
|
18
|
+
<path d="M18 0H2C0.9 0 0 0.9 0 2V20L4 16H18C19.1 16 20 15.1 20 14V2C20 0.9 19.1 0 18 0Z" fill={color || "#9DE5FD"} />
|
|
19
|
+
</SvgIcon>
|
|
20
|
+
)
|
|
21
|
+
|
|
15
22
|
const NavkitContent = () => {
|
|
16
|
-
const { isUserAuthenticated, siteSettings, appSettings, appSettingsLoaded, appName, userData,
|
|
23
|
+
const { isUserAuthenticated, siteSettings, appSettings, appSettingsLoaded, appName, userData, themeMode, navbarColor, iconColor } = useNavigation();
|
|
17
24
|
const resolvedAppName = appName || appSettings?.displayName
|
|
18
25
|
const showTaruviLogo = appSettingsLoaded && !resolvedAppName && !appSettings?.icon
|
|
19
26
|
const styles = getAppStyles(themeMode)
|
|
@@ -52,7 +59,12 @@ const NavkitContent = () => {
|
|
|
52
59
|
) : null}
|
|
53
60
|
</Box>
|
|
54
61
|
<Box component={"div"} sx={styles.rightSection}>
|
|
55
|
-
<Shortcuts
|
|
62
|
+
<Shortcuts onMenuToggle={() => setShowShortcutsMenu(!showShortcutsMenu)} iconColor={iconColor} />
|
|
63
|
+
{isUserAuthenticated && siteSettings['show-chat'] && siteSettings['chat-url'] && (
|
|
64
|
+
<Box onClick={() => setShowChat(true)} sx={{ ...styles.appLauncherContainer, cursor: 'pointer' }}>
|
|
65
|
+
<ChatIcon color={iconColor} />
|
|
66
|
+
</Box>
|
|
67
|
+
)}
|
|
56
68
|
<Box
|
|
57
69
|
onClick={() => {
|
|
58
70
|
if (isUserAuthenticated) {
|
|
@@ -99,7 +111,7 @@ const NavkitContent = () => {
|
|
|
99
111
|
|
|
100
112
|
{showShortcutsMenu && (
|
|
101
113
|
<Box>
|
|
102
|
-
<ShortcutsMenu
|
|
114
|
+
<ShortcutsMenu />
|
|
103
115
|
</Box>
|
|
104
116
|
)}
|
|
105
117
|
|
|
@@ -110,7 +122,7 @@ const NavkitContent = () => {
|
|
|
110
122
|
<FontAwesomeIcon icon={["fas", "external-link-alt"]} style={{ fontSize: "10px" }} />
|
|
111
123
|
</IconButton>
|
|
112
124
|
</Box>
|
|
113
|
-
<MattermostChat
|
|
125
|
+
<MattermostChat mattermostUrl={siteSettings['chat-url']} width="100%" height="100%" />
|
|
114
126
|
</DraggableResizable>
|
|
115
127
|
)}
|
|
116
128
|
</>
|
package/src/NavkitContext.tsx
CHANGED
|
@@ -24,7 +24,7 @@ export const NavkitProvider = ({ children, client, onThemeChange, appName, navba
|
|
|
24
24
|
|
|
25
25
|
const user = useRef<any>(null)
|
|
26
26
|
const settings = useRef<any>(null)
|
|
27
|
-
const siteSettings =
|
|
27
|
+
const [siteSettings, setSiteSettings] = useState<SiteSettings>({
|
|
28
28
|
shortcuts: [],
|
|
29
29
|
logo: '',
|
|
30
30
|
'show-chat': false,
|
|
@@ -38,7 +38,7 @@ export const NavkitProvider = ({ children, client, onThemeChange, appName, navba
|
|
|
38
38
|
const [isDesk, setIsDesk] = useState<boolean>(false)
|
|
39
39
|
const [appsList, setAppsList] = useState<AppData[]>([])
|
|
40
40
|
const [userData, setUserData] = useState<UserData | null>(null)
|
|
41
|
-
const [
|
|
41
|
+
const [sessionToken, setSessionToken] = useState<string>('')
|
|
42
42
|
const [isUserAuthenticated, setIsUserAuthenticated] = useState<boolean>(false)
|
|
43
43
|
const [themeMode, setThemeMode] = useState<ThemeMode>(getInitialTheme)
|
|
44
44
|
|
|
@@ -75,8 +75,8 @@ export const NavkitProvider = ({ children, client, onThemeChange, appName, navba
|
|
|
75
75
|
|
|
76
76
|
// Get JWT token if authenticated from sdk
|
|
77
77
|
if (authenticated) {
|
|
78
|
-
const token =
|
|
79
|
-
|
|
78
|
+
const token = auth.getSessionToken() || ''
|
|
79
|
+
setSessionToken(token)
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -104,18 +104,31 @@ export const NavkitProvider = ({ children, client, onThemeChange, appName, navba
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
const rawSettings = (fetchedSettings?.data ?? fetchedSettings)?.settings ?? {}
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
const newSiteSettings: SiteSettings = {
|
|
108
|
+
shortcuts: [],
|
|
109
|
+
logo: rawSettings['navkit.logo'] ?? '',
|
|
109
110
|
'show-chat': rawSettings['navkit.show-chat'] ?? false,
|
|
110
111
|
'chat-url': rawSettings['navkit.chat-url'] ?? '',
|
|
111
112
|
frontendUrl: rawSettings['navkit.frontend-url'] ?? '',
|
|
112
|
-
logo: rawSettings['navkit.logo'] ?? '',
|
|
113
113
|
enableDarkMode: rawSettings['navkit.enable-dark-mode'] ?? false,
|
|
114
114
|
}
|
|
115
|
+
setSiteSettings(newSiteSettings)
|
|
115
116
|
if (isUserAuthenticated) {
|
|
116
117
|
const userDataResponse = await auth.getCurrentUser()
|
|
117
118
|
setUserData(userDataResponse?.data || null)
|
|
118
119
|
|
|
120
|
+
// Login to Mattermost on init if chat is configured
|
|
121
|
+
const chatUrl = newSiteSettings['chat-url']
|
|
122
|
+
const token = auth.getSessionToken() || ''
|
|
123
|
+
if (chatUrl && token && userDataResponse?.data?.username) {
|
|
124
|
+
fetch(`${chatUrl.replace(/\/$/, '')}/api/v4/users/login`, {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest', 'X-Environment': 'Browser' },
|
|
127
|
+
credentials: 'omit',
|
|
128
|
+
body: JSON.stringify({ username: userDataResponse.data.username, password: token })
|
|
129
|
+
}).catch(() => {})
|
|
130
|
+
}
|
|
131
|
+
|
|
119
132
|
// Fetch user apps using username from userData
|
|
120
133
|
if (userDataResponse?.data?.username) {
|
|
121
134
|
const appsResponse = await user.current.getUserApps?.(userDataResponse.data.username)
|
|
@@ -176,7 +189,7 @@ export const NavkitProvider = ({ children, client, onThemeChange, appName, navba
|
|
|
176
189
|
}, [themeMode, onThemeChange])
|
|
177
190
|
|
|
178
191
|
return (
|
|
179
|
-
<NavigationContext.Provider value={{ navigateToUrl, isDesk, appsList, userData, siteSettings
|
|
192
|
+
<NavigationContext.Provider value={{ navigateToUrl, isDesk, appsList, userData, siteSettings, appSettings, appSettingsLoaded, appName, sessionToken, isUserAuthenticated, client, auth, themeMode, toggleTheme, navbarColor, iconColor }}>
|
|
180
193
|
{children}
|
|
181
194
|
</NavigationContext.Provider>
|
|
182
195
|
)
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import {useEffect, useRef, useState} from 'react'
|
|
2
|
+
import {Box, Badge} from '@mui/material'
|
|
3
3
|
|
|
4
4
|
interface MattermostChatProps {
|
|
5
|
-
|
|
6
|
-
mattermostUrl: string
|
|
7
|
-
loginId: string
|
|
5
|
+
mattermostUrl: string // Mattermost server URL (must be passed from parent app)
|
|
8
6
|
onNotification?: (count: number) => void
|
|
9
7
|
onUrlClick?: (url: string) => void
|
|
10
8
|
width?: string | number
|
|
@@ -22,77 +20,22 @@ interface NotifyMessage {
|
|
|
22
20
|
}
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
const MattermostChat = ({
|
|
26
|
-
jwtToken,
|
|
27
|
-
mattermostUrl,
|
|
28
|
-
loginId,
|
|
29
|
-
onNotification,
|
|
30
|
-
onUrlClick,
|
|
31
|
-
width = '100%',
|
|
32
|
-
height = '100%'
|
|
33
|
-
}: MattermostChatProps) => {
|
|
23
|
+
const MattermostChat = ({ mattermostUrl, onNotification, onUrlClick, width = '100%', height = '100%' }: MattermostChatProps) => {
|
|
34
24
|
const iframeRef = useRef<HTMLIFrameElement>(null)
|
|
35
25
|
const [unreadCount, setUnreadCount] = useState(0)
|
|
36
26
|
const [isWindowFocused, setIsWindowFocused] = useState(true)
|
|
37
|
-
const [authToken, setAuthToken] = useState<string | null>(null)
|
|
38
|
-
const [authError, setAuthError] = useState<string | null>(null)
|
|
39
|
-
const [isLoading, setIsLoading] = useState(true)
|
|
40
|
-
|
|
41
|
-
// Authenticate with Mattermost on mount
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
const login = async () => {
|
|
44
|
-
try {
|
|
45
|
-
const res = await fetch(`${mattermostUrl.replace(/\/$/, '')}/api/v4/users/login`, {
|
|
46
|
-
method: 'POST',
|
|
47
|
-
headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
|
|
48
|
-
credentials: 'omit',
|
|
49
|
-
body: JSON.stringify({ login_id: loginId, password: jwtToken, token: '', deviceId: '' })
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
if (!res.ok) {
|
|
53
|
-
setAuthError(`Login failed (${res.status})`)
|
|
54
|
-
setIsLoading(false)
|
|
55
|
-
return
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const token = res.headers.get('Token')
|
|
59
|
-
if (token) {
|
|
60
|
-
setAuthToken(token)
|
|
61
|
-
} else {
|
|
62
|
-
setAuthError('No token in response')
|
|
63
|
-
}
|
|
64
|
-
} catch (e: any) {
|
|
65
|
-
setAuthError(e.message || 'Login failed')
|
|
66
|
-
}
|
|
67
|
-
setIsLoading(false)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (loginId && jwtToken) login()
|
|
71
|
-
else { setAuthError('Missing credentials'); setIsLoading(false) }
|
|
72
|
-
}, [mattermostUrl, loginId, jwtToken])
|
|
73
|
-
|
|
74
|
-
// Inject auth token into iframe via postMessage once loaded
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
if (!authToken || !iframeRef.current) return
|
|
77
|
-
|
|
78
|
-
const handleIframeLoad = () => {
|
|
79
|
-
// Set the auth cookie/token in the iframe context
|
|
80
|
-
iframeRef.current?.contentWindow?.postMessage(
|
|
81
|
-
{ type: 'token', data: authToken },
|
|
82
|
-
new URL(mattermostUrl).origin
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const iframe = iframeRef.current
|
|
87
|
-
iframe.addEventListener('load', handleIframeLoad)
|
|
88
|
-
return () => iframe.removeEventListener('load', handleIframeLoad)
|
|
89
|
-
}, [authToken, mattermostUrl])
|
|
90
27
|
|
|
91
28
|
useEffect(() => {
|
|
29
|
+
// Handle postMessage events from Mattermost iframe
|
|
92
30
|
const handleMessage = (event: MessageEvent<NotifyMessage>) => {
|
|
31
|
+
// Validate origin for security
|
|
93
32
|
const mattermostOrigin = new URL(mattermostUrl).origin
|
|
94
|
-
if (event.origin !== mattermostOrigin)
|
|
33
|
+
if (event.origin !== mattermostOrigin) {
|
|
34
|
+
console.warn('Received message from untrusted origin:', event.origin)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
95
37
|
|
|
38
|
+
// Handle Notify events
|
|
96
39
|
if (event.data?.event === 'Notify') {
|
|
97
40
|
if (!isWindowFocused) {
|
|
98
41
|
setUnreadCount(prev => {
|
|
@@ -101,16 +44,31 @@ const MattermostChat = ({
|
|
|
101
44
|
return newCount
|
|
102
45
|
})
|
|
103
46
|
}
|
|
104
|
-
|
|
47
|
+
|
|
48
|
+
// Handle URL clicks if provided
|
|
49
|
+
if (event.data.data?.url && onUrlClick) {
|
|
50
|
+
onUrlClick(event.data.data.url)
|
|
51
|
+
}
|
|
105
52
|
}
|
|
106
53
|
}
|
|
107
54
|
|
|
108
|
-
|
|
109
|
-
const
|
|
55
|
+
// Handle window focus/blur to track unread messages
|
|
56
|
+
const handleFocus = () => {
|
|
57
|
+
setIsWindowFocused(true)
|
|
58
|
+
setUnreadCount(0)
|
|
59
|
+
onNotification?.(0)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const handleBlur = () => {
|
|
63
|
+
setIsWindowFocused(false)
|
|
64
|
+
}
|
|
110
65
|
|
|
66
|
+
// Add event listeners
|
|
111
67
|
window.addEventListener('message', handleMessage)
|
|
112
68
|
window.addEventListener('focus', handleFocus)
|
|
113
69
|
window.addEventListener('blur', handleBlur)
|
|
70
|
+
|
|
71
|
+
// Cleanup event listeners on unmount
|
|
114
72
|
return () => {
|
|
115
73
|
window.removeEventListener('message', handleMessage)
|
|
116
74
|
window.removeEventListener('focus', handleFocus)
|
|
@@ -118,34 +76,34 @@ const MattermostChat = ({
|
|
|
118
76
|
}
|
|
119
77
|
}, [mattermostUrl, isWindowFocused, onNotification, onUrlClick])
|
|
120
78
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
<CircularProgress size={32} />
|
|
125
|
-
</Box>
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (authError) {
|
|
130
|
-
return (
|
|
131
|
-
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width, height }}>
|
|
132
|
-
<Typography color="error">{authError}</Typography>
|
|
133
|
-
</Box>
|
|
134
|
-
)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Load Mattermost with the auth token as a cookie header via the access_token param
|
|
138
|
-
const iframeUrl = `${mattermostUrl.replace(/\/$/, '')}?access_token=${authToken}`
|
|
79
|
+
// Construct iframe URL with JWT authentication
|
|
80
|
+
// const iframeUrl = `${mattermostUrl.replace(/\/$/, '')}/login`
|
|
81
|
+
const iframeUrl = mattermostUrl
|
|
139
82
|
|
|
140
83
|
return (
|
|
141
|
-
<Box
|
|
84
|
+
<Box
|
|
85
|
+
sx={{
|
|
86
|
+
position: 'relative',
|
|
87
|
+
width,
|
|
88
|
+
height,
|
|
89
|
+
overflow: 'hidden'
|
|
90
|
+
}}
|
|
91
|
+
>
|
|
142
92
|
{unreadCount > 0 && (
|
|
143
93
|
<Badge
|
|
144
94
|
badgeContent={unreadCount}
|
|
145
95
|
color="error"
|
|
146
96
|
sx={{
|
|
147
|
-
position: 'absolute',
|
|
148
|
-
|
|
97
|
+
position: 'absolute',
|
|
98
|
+
top: 16,
|
|
99
|
+
right: 16,
|
|
100
|
+
zIndex: 1000,
|
|
101
|
+
'& .MuiBadge-badge': {
|
|
102
|
+
fontSize: '1rem',
|
|
103
|
+
height: '28px',
|
|
104
|
+
minWidth: '28px',
|
|
105
|
+
borderRadius: '14px'
|
|
106
|
+
}
|
|
149
107
|
}}
|
|
150
108
|
/>
|
|
151
109
|
)}
|
|
@@ -154,7 +112,12 @@ const MattermostChat = ({
|
|
|
154
112
|
src={iframeUrl}
|
|
155
113
|
title="Mattermost Chat"
|
|
156
114
|
allow="clipboard-read; clipboard-write"
|
|
157
|
-
style={{
|
|
115
|
+
style={{
|
|
116
|
+
width: '100%',
|
|
117
|
+
height: '100%',
|
|
118
|
+
border: 'none',
|
|
119
|
+
display: 'block'
|
|
120
|
+
}}
|
|
158
121
|
/>
|
|
159
122
|
</Box>
|
|
160
123
|
)
|
|
@@ -5,7 +5,6 @@ export const shortcutsStyles = {
|
|
|
5
5
|
display: { xs: 'none', md: 'flex' },
|
|
6
6
|
alignItems: 'center',
|
|
7
7
|
gap: '10px',
|
|
8
|
-
ml: spacing.md,
|
|
9
8
|
},
|
|
10
9
|
mobileTrigger: {
|
|
11
10
|
display: { xs: 'flex', md: 'none' },
|
|
@@ -16,9 +15,12 @@ export const shortcutsStyles = {
|
|
|
16
15
|
display: 'flex',
|
|
17
16
|
flexDirection: 'column' as const,
|
|
18
17
|
alignItems: 'center',
|
|
18
|
+
justifyContent: 'center',
|
|
19
19
|
textDecoration: 'none',
|
|
20
20
|
cursor: 'pointer',
|
|
21
21
|
color: colours.text.secondary,
|
|
22
|
+
minWidth: 40,
|
|
23
|
+
minHeight: 40,
|
|
22
24
|
'&:hover': {
|
|
23
25
|
opacity: 0.7,
|
|
24
26
|
},
|
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
import type { AppData } from '../../types'
|
|
2
|
-
import { Box, Link, IconButton
|
|
2
|
+
import { Box, Link, IconButton } from '@mui/material'
|
|
3
3
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
4
4
|
import { shortcutsStyles } from './Shortcuts.styles'
|
|
5
5
|
import { useNavigation } from '../../NavkitContext'
|
|
6
6
|
|
|
7
|
-
const ChatIcon = ({ color }: { color?: string }) => (
|
|
8
|
-
<SvgIcon viewBox="-2 -2 24 24" sx={{ fontSize: 23, display: 'block', position: 'relative', top: 2 }}>
|
|
9
|
-
<path d="M18 0H2C0.9 0 0 0.9 0 2V20L4 16H18C19.1 16 20 15.1 20 14V2C20 0.9 19.1 0 18 0Z" fill={color || "#9DE5FD"}/>
|
|
10
|
-
</SvgIcon>
|
|
11
|
-
)
|
|
12
|
-
|
|
13
7
|
interface ShortcutsProps {
|
|
14
|
-
showChat?: (showChat: boolean) => void
|
|
15
8
|
onMenuToggle?: () => void
|
|
16
9
|
triggerRef?: React.RefObject<HTMLButtonElement | null>
|
|
17
10
|
iconColor?: string
|
|
@@ -19,8 +12,8 @@ interface ShortcutsProps {
|
|
|
19
12
|
|
|
20
13
|
const Shortcuts = (props: ShortcutsProps) => {
|
|
21
14
|
|
|
22
|
-
const {
|
|
23
|
-
const { navigateToUrl, siteSettings
|
|
15
|
+
const { onMenuToggle, triggerRef, iconColor } = props
|
|
16
|
+
const { navigateToUrl, siteSettings } = useNavigation()
|
|
24
17
|
const shortcuts = siteSettings.shortcuts
|
|
25
18
|
|
|
26
19
|
const handleShortcutClick = (shortcut: AppData) => {
|
|
@@ -45,19 +38,10 @@ const Shortcuts = (props: ShortcutsProps) => {
|
|
|
45
38
|
/>
|
|
46
39
|
</Link>
|
|
47
40
|
))}
|
|
48
|
-
{siteSettings['show-chat'] && siteSettings['chat-url'] && isUserAuthenticated &&
|
|
49
|
-
<Link
|
|
50
|
-
key={"chat"}
|
|
51
|
-
onClick={() => showChat?.(true)}
|
|
52
|
-
sx={shortcutsStyles.shortcut}
|
|
53
|
-
>
|
|
54
|
-
<ChatIcon color={iconColor} />
|
|
55
|
-
</Link>
|
|
56
|
-
}
|
|
57
41
|
</Box>
|
|
58
42
|
|
|
59
43
|
{/* Mobile view - trigger button */}
|
|
60
|
-
{(
|
|
44
|
+
{(shortcuts?.length ?? 0) > 0 && (
|
|
61
45
|
<IconButton
|
|
62
46
|
ref={triggerRef}
|
|
63
47
|
onClick={onMenuToggle}
|
|
@@ -1,22 +1,13 @@
|
|
|
1
|
-
import { Card, MenuItem, Typography
|
|
1
|
+
import { Card, MenuItem, Typography } from '@mui/material'
|
|
2
2
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
3
3
|
import type { AppData } from '../../types'
|
|
4
4
|
import { getShortcutsMenuStyles } from './ShortcutsMenu.styles'
|
|
5
5
|
import { useNavigation } from '../../NavkitContext'
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
<SvgIcon viewBox="-2 -2 24 24" sx={{ fontSize: style.fontSize, color: style.color }}>
|
|
9
|
-
<path d="M18 0H2C0.9 0 0 0.9 0 2V20L4 16H18C19.1 16 20 15.1 20 14V2C20 0.9 19.1 0 18 0Z" fill="currentColor"/>
|
|
10
|
-
</SvgIcon>
|
|
11
|
-
)
|
|
7
|
+
interface ShortcutsMenuProps {}
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const ShortcutsMenu = (props: ShortcutsMenuProps) => {
|
|
18
|
-
const { showChat } = props
|
|
19
|
-
const { navigateToUrl, siteSettings, isUserAuthenticated, themeMode } = useNavigation()
|
|
9
|
+
const ShortcutsMenu = (_props: ShortcutsMenuProps) => {
|
|
10
|
+
const { navigateToUrl, siteSettings, themeMode } = useNavigation()
|
|
20
11
|
const shortcuts = siteSettings.shortcuts
|
|
21
12
|
const themedStyles = getShortcutsMenuStyles(themeMode)
|
|
22
13
|
|
|
@@ -28,18 +19,6 @@ const ShortcutsMenu = (props: ShortcutsMenuProps) => {
|
|
|
28
19
|
|
|
29
20
|
return (
|
|
30
21
|
<Card sx={themedStyles.menu}>
|
|
31
|
-
{siteSettings['show-chat'] && siteSettings['chat-url'] && isUserAuthenticated && (
|
|
32
|
-
<MenuItem
|
|
33
|
-
key="chat"
|
|
34
|
-
onClick={() => showChat(true)}
|
|
35
|
-
sx={themedStyles.menuItem}
|
|
36
|
-
>
|
|
37
|
-
<ChatIcon style={themedStyles.iconStyle} />
|
|
38
|
-
<Typography sx={themedStyles.menuItemText}>
|
|
39
|
-
Chat
|
|
40
|
-
</Typography>
|
|
41
|
-
</MenuItem>
|
|
42
|
-
)}
|
|
43
22
|
{shortcuts?.map((shortcut) => (
|
|
44
23
|
<MenuItem
|
|
45
24
|
key={shortcut.id}
|