1- import { useEffect , useRef , useState , useMemo , useCallback } from "react"
1+ import { useEffect , useState , useRef } from "react"
22import axios from "axios"
33import type { ProviderSettings , ZgsmUserInfo } from "@roo-code/types"
44import { TelemetryEventName } from "@roo-code/types"
@@ -20,7 +20,7 @@ function parseJwt(token: string) {
2020 throw new Error ( "Invalid JWT" )
2121 }
2222 const payload = parts [ 1 ]
23- const decoded = atob ( payload . replace ( / - / g, "+" ) . replace ( / _ / g, "/" ) ) // base64url → base64 → decode
23+ const decoded = atob ( payload . replace ( / - / g, "+" ) . replace ( / _ / g, "/" ) )
2424 return JSON . parse ( decoded )
2525}
2626
@@ -42,7 +42,7 @@ async function hashToken(token: string): Promise<string> {
4242export async function imageUrlToBase64 ( url : string ) : Promise < string | null > {
4343 try {
4444 const response = await axios . get ( url , {
45- responseType : "blob" , // Key! Ensure axios returns Blob
45+ responseType : "blob" ,
4646 } )
4747
4848 const blob = response . data as Blob
@@ -51,114 +51,104 @@ export async function imageUrlToBase64(url: string): Promise<string | null> {
5151 const reader = new FileReader ( )
5252 reader . onloadend = ( ) => resolve ( reader . result as string )
5353 reader . onerror = ( ) => reject ( "Failed to convert blob to base64" )
54- reader . readAsDataURL ( blob ) // Automatically adds data:image/png;base64,...
54+ reader . readAsDataURL ( blob )
5555 } )
5656 } catch ( error ) {
5757 console . error ( "Failed to convert image to base64" , error )
5858 return null
5959 }
6060}
6161
62- /**
63- * 用于管理ZGSM用户信息的自定义Hook
64- * 提供用户信息解析、头像处理、token哈希等功能
65- * 优化版本:使用 useMemo 缓存昂贵的计算操作,避免滚动时重复计算
66- */
6762export function useZgsmUserInfo ( tokenOrConfig ?: string | ProviderSettings ) : ZgsmUserData {
68- const [ logoPic , setLogoPic ] = useState ( "" )
69- const [ hash , setHash ] = useState ( "" )
70- const wasAuthenticatedRef = useRef ( false )
71-
72- // 提取实际的 token 值
73- const token = useMemo ( ( ) => {
74- if ( typeof tokenOrConfig === "string" ) {
75- return tokenOrConfig
76- }
77- return tokenOrConfig ?. zgsmAccessToken
78- } , [ tokenOrConfig ] )
63+ const [ data , setData ] = useState < ZgsmUserData > ( {
64+ userInfo : null ,
65+ logoPic : "" ,
66+ hash : "" ,
67+ isAuthenticated : false ,
68+ } )
69+
70+ const cacheRef = useRef < {
71+ token ?: string
72+ result ?: ZgsmUserData
73+ isProcessing : boolean
74+ } > ( { isProcessing : false } )
7975
80- // 使用 useMemo 缓存 JWT 解析结果,只有当 token 真正变化时才重新解析
81- const parsedJwt = useMemo ( ( ) => {
82- if ( ! token ) return null
83-
84- try {
85- return parseJwt ( token )
86- } catch ( error ) {
87- console . error ( "Failed to parse JWT token:" , error )
88- return null
89- }
90- } , [ token ] )
91-
92- // 使用 useMemo 缓存用户基本信息,只有当解析结果变化时才重新计算
93- const userInfo = useMemo ( ( ) : ZgsmUserInfo | null => {
94- if ( ! parsedJwt ) return null
95-
96- return {
97- id : parsedJwt . id ,
98- name : parsedJwt ?. properties ?. oauth_GitHub_username || parsedJwt . id ,
99- picture : undefined ,
100- email : parsedJwt . email ,
101- phone : parsedJwt . phone ,
102- organizationName : parsedJwt . organizationName ,
103- organizationImageUrl : parsedJwt . organizationImageUrl ,
104- }
105- } , [ parsedJwt ] )
106-
107- // 使用 useCallback 缓存头像处理函数
108- const processAvatar = useCallback ( async ( avatarUrl : string ) => {
109- try {
110- const base64 = await imageUrlToBase64 ( avatarUrl )
111- if ( base64 ) {
112- setLogoPic ( base64 )
113- }
114- } catch ( error ) {
115- console . error ( "Failed to process avatar:" , error )
116- setLogoPic ( "" )
76+ useEffect ( ( ) => {
77+ const token = typeof tokenOrConfig === "string" ? tokenOrConfig : tokenOrConfig ?. zgsmAccessToken
78+
79+ if ( ! token ) {
80+ setData ( ( prevData ) => {
81+ if ( prevData . isAuthenticated ) {
82+ telemetryClient . capture ( TelemetryEventName . ACCOUNT_LOGOUT_SUCCESS )
83+ }
84+
85+ return {
86+ userInfo : null ,
87+ logoPic : "" ,
88+ hash : "" ,
89+ isAuthenticated : false ,
90+ }
91+ } )
92+
93+ cacheRef . current = { isProcessing : false }
94+ return
11795 }
118- } , [ ] )
119-
120- // 使用 useCallback 缓存哈希计算函数
121- const processTokenHash = useCallback ( async ( tokenValue : string ) => {
122- try {
123- const result = await hashToken ( tokenValue )
124- setHash ( result )
125- } catch ( error ) {
126- console . error ( "Failed to hash token:" , error )
127- setHash ( "" )
96+
97+ if ( cacheRef . current . token === token && cacheRef . current . result ) {
98+ setData ( cacheRef . current . result )
99+ return
128100 }
129- } , [ ] )
130101
131- // 处理副作用:头像和哈希计算
132- useEffect ( ( ) => {
133- if ( token && parsedJwt ) {
134- wasAuthenticatedRef . current = true
135-
136- // 处理头像
137- if ( parsedJwt . avatar ) {
138- processAvatar ( parsedJwt . avatar )
139- } else {
140- setLogoPic ( "" )
102+ if ( cacheRef . current . isProcessing ) return
103+ cacheRef . current . isProcessing = true
104+
105+ const processToken = async ( ) => {
106+ try {
107+ const parsedJwt = parseJwt ( token )
108+
109+ const userInfo : ZgsmUserInfo = {
110+ id : parsedJwt . id ,
111+ name : parsedJwt ?. properties ?. oauth_GitHub_username || parsedJwt . id ,
112+ picture : undefined ,
113+ email : parsedJwt . email ,
114+ phone : parsedJwt . phone ,
115+ organizationName : parsedJwt . organizationName ,
116+ organizationImageUrl : parsedJwt . organizationImageUrl ,
117+ }
118+
119+ const [ logoPic , hash ] = await Promise . all ( [
120+ parsedJwt . avatar ? imageUrlToBase64 ( parsedJwt . avatar ) : Promise . resolve ( "" ) ,
121+ hashToken ( token ) ,
122+ ] )
123+
124+ const result : ZgsmUserData = {
125+ userInfo,
126+ logoPic : logoPic || "" ,
127+ hash,
128+ isAuthenticated : true ,
129+ }
130+
131+ cacheRef . current = {
132+ token,
133+ result,
134+ isProcessing : false ,
135+ }
136+ setData ( result )
137+ } catch ( error ) {
138+ console . error ( "Failed to parse JWT token:" , error )
139+ const errorResult : ZgsmUserData = {
140+ userInfo : null ,
141+ logoPic : "" ,
142+ hash : "" ,
143+ isAuthenticated : true ,
144+ }
145+ cacheRef . current . isProcessing = false
146+ setData ( errorResult )
141147 }
142-
143- // 计算token哈希
144- processTokenHash ( token )
145- } else if ( wasAuthenticatedRef . current && ! token ) {
146- // 检测到登出
147- telemetryClient . capture ( TelemetryEventName . ACCOUNT_LOGOUT_SUCCESS )
148- wasAuthenticatedRef . current = false
149- setLogoPic ( "" )
150- setHash ( "" )
151- } else if ( ! token ) {
152- // 确保在没有token时清空状态
153- setLogoPic ( "" )
154- setHash ( "" )
155148 }
156- } , [ token , parsedJwt , processAvatar , processTokenHash ] )
157149
158- return {
159- userInfo,
160- logoPic,
161- hash,
162- isAuthenticated : ! ! token ,
163- }
150+ processToken ( )
151+ } , [ tokenOrConfig ] )
152+
153+ return data
164154}
0 commit comments