@taruvi/navkit 0.0.48-beta.2 → 0.0.48-beta.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taruvi/navkit",
3
- "version": "0.0.48-beta.2",
3
+ "version": "0.0.48-beta.4",
4
4
  "main": "src/App.tsx",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/App.tsx CHANGED
@@ -20,7 +20,7 @@ const ChatIcon = ({ color }: { color?: string }) => (
20
20
  )
21
21
 
22
22
  const NavkitContent = () => {
23
- const { isUserAuthenticated, appSettings, appSettingsLoaded, appName, userData, appsList, chatUrl, themeMode, navbarColor, iconColor } = useNavigation();
23
+ const { isUserAuthenticated, appSettings, appSettingsLoaded, appName, userData, appsList, chatUrl, themeMode, navbarColor, iconColor, sessionToken } = useNavigation();
24
24
  const resolvedAppName = appName || appSettings?.displayName
25
25
  const showTaruviLogo = appSettingsLoaded && !resolvedAppName && !appSettings?.icon
26
26
  const styles = getAppStyles(themeMode)
@@ -122,7 +122,13 @@ const NavkitContent = () => {
122
122
  <FontAwesomeIcon icon={["fas", "external-link-alt"]} style={{ fontSize: "10px" }} />
123
123
  </IconButton>
124
124
  </Box>
125
- <MattermostChat mattermostUrl={chatUrl} width="100%" height="100%" />
125
+ <MattermostChat
126
+ mattermostUrl={chatUrl}
127
+ loginId={userData?.username ?? ''}
128
+ sessionToken={sessionToken ?? ''}
129
+ width="100%"
130
+ height="100%"
131
+ />
126
132
  </DraggableResizable>
127
133
  )}
128
134
  </>
@@ -129,17 +129,6 @@ export const NavkitProvider = ({ children, client, onThemeChange, appName, navba
129
129
  const userDataResponse = await auth.getCurrentUser()
130
130
  setUserData(userDataResponse?.data || null)
131
131
 
132
- // Login to Mattermost on init if chat is configured
133
- const token = auth.getSessionToken() || ''
134
- if (chatUrl && token && userDataResponse?.data?.username) {
135
- fetch(`${chatUrl.replace(/\/$/, '')}/api/v4/users/login`, {
136
- method: 'POST',
137
- headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest', 'X-Environment': 'Browser' },
138
- credentials: 'omit',
139
- body: JSON.stringify({ username: userDataResponse.data.username, password: token, auth_type: "session" })
140
- }).catch(() => {})
141
- }
142
-
143
132
  // Fetch user apps using username from userData
144
133
  if (userDataResponse?.data?.username) {
145
134
  const appsResponse = await user.current.getUserApps?.(userDataResponse.data.username)
@@ -3,39 +3,34 @@ import {Box, Badge} from '@mui/material'
3
3
 
4
4
  interface MattermostChatProps {
5
5
  mattermostUrl: string // Mattermost server URL (must be passed from parent app)
6
+ loginId: string // Mattermost login_id (username)
7
+ sessionToken: string // Taruvi session token used as password for auth_type=session
6
8
  onNotification?: (count: number) => void
7
9
  onUrlClick?: (url: string) => void
8
10
  width?: string | number
9
11
  height?: string | number
10
12
  }
11
13
 
12
- interface NotifyMessage {
13
- event: string
14
- data?: {
15
- title?: string
16
- body?: string
17
- channel?: string
18
- teamId?: string
19
- url?: string
20
- }
21
- }
22
-
23
- const MattermostChat = ({ mattermostUrl, onNotification, onUrlClick, width = '100%', height = '100%' }: MattermostChatProps) => {
14
+ const MattermostChat = ({ mattermostUrl, loginId, sessionToken, onNotification, onUrlClick, width = '100%', height = '100%' }: MattermostChatProps) => {
24
15
  const iframeRef = useRef<HTMLIFrameElement>(null)
25
16
  const [unreadCount, setUnreadCount] = useState(0)
26
17
  const [isWindowFocused, setIsWindowFocused] = useState(true)
18
+ const mattermostOrigin = new URL(mattermostUrl).origin
27
19
 
28
20
  useEffect(() => {
29
- // Handle postMessage events from Mattermost iframe
30
- const handleMessage = (event: MessageEvent<NotifyMessage>) => {
31
- // Validate origin for security
32
- const mattermostOrigin = new URL(mattermostUrl).origin
33
- if (event.origin !== mattermostOrigin) {
34
- console.warn('Received message from untrusted origin:', event.origin)
21
+ const handleMessage = (event: MessageEvent) => {
22
+ if (event.origin !== mattermostOrigin) return
23
+
24
+ // Bridge ready send login credentials
25
+ if (event.data?.type === 'mm-bridge-ready') {
26
+ iframeRef.current?.contentWindow?.postMessage(
27
+ { type: 'mm-login', loginId, token: sessionToken },
28
+ mattermostOrigin
29
+ )
35
30
  return
36
31
  }
37
32
 
38
- // Handle Notify events
33
+ // Mattermost Notify events (unread badge, URL clicks)
39
34
  if (event.data?.event === 'Notify') {
40
35
  if (!isWindowFocused) {
41
36
  setUnreadCount(prev => {
@@ -44,41 +39,34 @@ const MattermostChat = ({ mattermostUrl, onNotification, onUrlClick, width = '10
44
39
  return newCount
45
40
  })
46
41
  }
47
-
48
- // Handle URL clicks if provided
49
42
  if (event.data.data?.url && onUrlClick) {
50
43
  onUrlClick(event.data.data.url)
51
44
  }
52
45
  }
53
46
  }
54
47
 
55
- // Handle window focus/blur to track unread messages
56
48
  const handleFocus = () => {
57
49
  setIsWindowFocused(true)
58
50
  setUnreadCount(0)
59
51
  onNotification?.(0)
60
52
  }
61
53
 
62
- const handleBlur = () => {
63
- setIsWindowFocused(false)
64
- }
54
+ const handleBlur = () => setIsWindowFocused(false)
65
55
 
66
- // Add event listeners
67
56
  window.addEventListener('message', handleMessage)
68
57
  window.addEventListener('focus', handleFocus)
69
58
  window.addEventListener('blur', handleBlur)
70
59
 
71
- // Cleanup event listeners on unmount
72
60
  return () => {
73
61
  window.removeEventListener('message', handleMessage)
74
62
  window.removeEventListener('focus', handleFocus)
75
63
  window.removeEventListener('blur', handleBlur)
76
64
  }
77
- }, [mattermostUrl, isWindowFocused, onNotification, onUrlClick])
65
+ }, [mattermostOrigin, loginId, sessionToken, isWindowFocused, onNotification, onUrlClick])
78
66
 
79
- // Construct iframe URL with JWT authentication
80
- // const iframeUrl = `${mattermostUrl.replace(/\/$/, '')}/login`
81
- const iframeUrl = mattermostUrl
67
+ // Always load the autologin bridge first — it handles storage access + login,
68
+ // then redirects the iframe to Mattermost
69
+ const bridgeUrl = `${mattermostOrigin}/static/autologin.html`
82
70
 
83
71
  return (
84
72
  <Box
@@ -109,9 +97,9 @@ const MattermostChat = ({ mattermostUrl, onNotification, onUrlClick, width = '10
109
97
  )}
110
98
  <iframe
111
99
  ref={iframeRef}
112
- src={iframeUrl}
100
+ src={bridgeUrl}
113
101
  title="Mattermost Chat"
114
- allow="clipboard-read; clipboard-write"
102
+ allow="clipboard-read; clipboard-write; storage-access"
115
103
  style={{
116
104
  width: '100%',
117
105
  height: '100%',