11import TPEN from '../../api/TPEN.js'
2- import User from '../../api/User.js'
32import Project from '../../api/Project.js'
43import { CleanupRegistry } from '../../utilities/CleanupRegistry.js'
4+ import { onUserReady } from '../../utilities/userReady.js'
5+ import { onUserProjectsReady } from '../../utilities/userProjectsReady.js'
56
67/**
78 * UserStats - Displays user profile card with stats and collaborators.
89 * @element user-stats
910 */
1011class UserStats extends HTMLElement {
11- static get observedAttributes ( ) {
12- return [ 'tpen-user-id' ]
13- }
14-
1512 /** @type {CleanupRegistry } Registry for cleanup handlers */
1613 cleanup = new CleanupRegistry ( )
14+ /** @type {Function|null } Unsubscribe function for user ready listener */
15+ _unsubUser = null
16+ /** @type {Function|null } Unsubscribe function for projects ready listener */
17+ _unsubProjects = null
18+ /** @type {Object|null } Cached profile data */
19+ _profile = null
20+ /** @type {Array|null } Cached projects data */
21+ _projects = null
22+ /** @type {boolean } Flag to prevent double rendering */
23+ _isRendering = false
1724
1825 constructor ( ) {
1926 super ( )
@@ -22,35 +29,44 @@ class UserStats extends HTMLElement {
2229
2330 connectedCallback ( ) {
2431 TPEN . attachAuthentication ( this )
25- this . cleanup . onEvent ( TPEN . eventDispatcher , 'tpen-user-loaded' , ev => this . loadAndRender ( ev . detail ) )
32+ this . _unsubUser = onUserReady ( this , ( user ) => {
33+ this . _profile = user
34+ this . renderIfReady ( )
35+ } )
36+ this . _unsubProjects = onUserProjectsReady ( this , ( projects ) => {
37+ this . _projects = projects
38+ this . renderIfReady ( )
39+ } )
2640 }
2741
2842 /**
29- * Loads user projects and renders the stats.
43+ * Renders only when both profile and projects are available.
44+ * Guards against double rendering when both callbacks fire quickly.
45+ */
46+ renderIfReady ( ) {
47+ if ( this . _profile && this . _projects && ! this . _isRendering ) {
48+ this . _isRendering = true
49+ this . loadAndRender ( this . _profile , this . _projects )
50+ . finally ( ( ) => { this . _isRendering = false } )
51+ }
52+ }
53+
54+ /**
55+ * Processes profile and project data, then renders the stats.
3056 * @param {Object } profile - User profile data
57+ * @param {Array } projects - Array of project data
3158 */
32- async loadAndRender ( profile ) {
33- const projects = await TPEN . getUserProjects ( TPEN . getAuthorization ( ) )
59+ async loadAndRender ( profile , projects ) {
3460 await this . processAndRender ( profile , projects )
3561 this . updateProfile ( profile )
3662 }
3763
3864 disconnectedCallback ( ) {
65+ try { this . _unsubUser ?. ( ) } catch { }
66+ try { this . _unsubProjects ?. ( ) } catch { }
3967 this . cleanup . run ( )
4068 }
4169
42- attributeChangedCallback ( name , oldValue , newValue ) {
43- if ( name === 'tpen-user-id' ) {
44- if ( oldValue !== newValue ) {
45- const currVal = this ?. user ?. _id
46- if ( newValue === currVal ) return
47- const loadedUser = new User ( newValue )
48- loadedUser . authentication = TPEN . getAuthorization ( )
49- loadedUser . getProfile ( )
50- }
51- }
52- }
53-
5470 updateProfile ( profile ) {
5571 const publicProfile = this . getPublicProfile ( profile )
5672 if ( publicProfile . imageURL ) {
0 commit comments