Skip to content

Commit bd91f66

Browse files
committed
fix(presence): online users not showing other org members
- linkUserToOrganization now updates user org_id in database - Added syncUserSessionsOrgId to fix sessions with NULL org_id - Session registration uses organization.id as fallback - Heartbeat callback also uses organization.id fallback - Effect re-runs when organization loads - Added database migration to fix existing sessions
1 parent 9511b59 commit bd91f66

File tree

8 files changed

+160
-39
lines changed

8 files changed

+160
-39
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
All notable changes to BluePLM will be documented in this file.
44

5+
## [2.9.1] - 2025-12-17
6+
7+
### Fixed
8+
- **Online users presence not showing other org members**: Fixed critical bug where sessions were registered with `org_id = NULL` before the organization was fully loaded. Other users in the same org couldn't see each other online. Now properly syncs session org_id with the user's organization and updates user org_id in database when matching org is found.
9+
10+
---
11+
512
## [2.9.0] - 2025-12-17
613

714
### Changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "blue-plm",
3-
"version": "2.9.0",
3+
"version": "2.9.1",
44
"description": "Product Lifecycle Management for everyone who builds",
55
"main": "dist-electron/main.js",
66
"scripts": {

solidworks-addin/BluePLM.SolidWorksService/DocumentManagerAPI.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ public CommandResult GetCustomProperties(string? filePath, string? configuration
510510
object? doc = null;
511511
try
512512
{
513-
doc = OpenDocument(filePath, out var openError);
513+
doc = OpenDocument(filePath!, out var openError);
514514
if (doc == null)
515515
return new CommandResult { Success = false, Error = $"Failed to open file: error code {openError}" };
516516

@@ -618,7 +618,7 @@ public CommandResult SetCustomProperties(string? filePath, Dictionary<string, st
618618
try
619619
{
620620
// Open document for WRITE access (not read-only)
621-
doc = OpenDocumentForWrite(filePath, out var openError);
621+
doc = OpenDocumentForWrite(filePath!, out var openError);
622622
if (doc == null)
623623
return new CommandResult { Success = false, Error = $"Failed to open file for writing: error code {openError}" };
624624

@@ -757,7 +757,7 @@ public CommandResult GetConfigurations(string? filePath)
757757
object? doc = null;
758758
try
759759
{
760-
doc = OpenDocument(filePath, out var openError);
760+
doc = OpenDocument(filePath!, out var openError);
761761
if (doc == null)
762762
return new CommandResult { Success = false, Error = $"Failed to open file: error code {openError}" };
763763

@@ -841,7 +841,7 @@ public CommandResult GetBillOfMaterials(string? filePath, string? configuration
841841
object? doc = null;
842842
try
843843
{
844-
doc = OpenDocument(filePath, out var openError);
844+
doc = OpenDocument(filePath!, out var openError);
845845
if (doc == null)
846846
return new CommandResult { Success = false, Error = $"Failed to open file: error code {openError}" };
847847

@@ -965,7 +965,7 @@ public CommandResult GetExternalReferences(string? filePath)
965965
object? doc = null;
966966
try
967967
{
968-
doc = OpenDocument(filePath, out var openError);
968+
doc = OpenDocument(filePath!, out var openError);
969969
if (doc == null)
970970
return new CommandResult { Success = false, Error = $"Failed to open file: error code {openError}" };
971971

@@ -1051,7 +1051,7 @@ public CommandResult GetPreviewImage(string? filePath, string? configuration = n
10511051
object? doc = null;
10521052
try
10531053
{
1054-
doc = OpenDocument(filePath, out var openError);
1054+
doc = OpenDocument(filePath!, out var openError);
10551055
if (doc == null)
10561056
return new CommandResult { Success = false, Error = $"Failed to open file: error code {openError}" };
10571057

@@ -1196,7 +1196,7 @@ private static string GetPartNumber(Dictionary<string, string> props)
11961196
foreach (var key in partNumberKeys)
11971197
{
11981198
var value = GetDictValue(props, key);
1199-
if (!string.IsNullOrEmpty(value))
1199+
if (value != null && value.Length > 0)
12001200
return value;
12011201
}
12021202

@@ -1225,7 +1225,7 @@ private static string GetRevision(Dictionary<string, string> props)
12251225
foreach (var key in revisionKeys)
12261226
{
12271227
var value = GetDictValue(props, key);
1228-
if (!string.IsNullOrEmpty(value))
1228+
if (value != null && value.Length > 0)
12291229
return value;
12301230
}
12311231

solidworks-addin/BluePLM.SolidWorksService/Program.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ static int Main(string[] args)
7575

7676
// Initialize Document Manager API (for FAST operations - no SW launch!)
7777
Console.Error.WriteLine("=== BluePLM SolidWorks Service Startup ===");
78-
Console.Error.WriteLine($"[Startup] DM License key from command line: {(!string.IsNullOrEmpty(dmLicenseKey) ? $"provided ({dmLicenseKey.Length} chars)" : "not provided")}");
79-
if (!string.IsNullOrEmpty(dmLicenseKey))
78+
Console.Error.WriteLine($"[Startup] DM License key from command line: {(dmLicenseKey == null ? "not provided" : $"provided ({dmLicenseKey.Length} chars)")}");
79+
if (dmLicenseKey != null && dmLicenseKey.Length > 0)
8080
{
8181
Console.Error.WriteLine($"[Startup] License key prefix: {(dmLicenseKey.Length > 30 ? dmLicenseKey.Substring(0, 30) + "..." : dmLicenseKey)}");
8282
}
@@ -96,7 +96,7 @@ static int Main(string[] args)
9696
Console.Error.WriteLine($"[Startup] SolidWorks available: {_swApi.IsSolidWorksAvailable()}");
9797

9898
// Single command mode
99-
if (singleCommand && !string.IsNullOrEmpty(commandJson))
99+
if (singleCommand && commandJson != null && commandJson.Length > 0)
100100
{
101101
var result = ProcessCommand(commandJson);
102102
Console.WriteLine(JsonConvert.SerializeObject(result));
@@ -365,15 +365,13 @@ static CommandResult SetDmLicense(string? licenseKey)
365365
{
366366
Console.Error.WriteLine("[Service] SetDmLicense command received");
367367
Console.Error.WriteLine($"[Service] License key provided: {!string.IsNullOrEmpty(licenseKey)}");
368-
if (!string.IsNullOrEmpty(licenseKey))
369-
{
370-
Console.Error.WriteLine($"[Service] License key length: {licenseKey.Length}");
371-
Console.Error.WriteLine($"[Service] License key prefix: {(licenseKey.Length > 30 ? licenseKey.Substring(0, 30) + "..." : licenseKey)}");
372-
}
373368

374-
if (string.IsNullOrEmpty(licenseKey))
369+
if (licenseKey == null || licenseKey.Length == 0)
375370
return new CommandResult { Success = false, Error = "Missing 'licenseKey'" };
376371

372+
Console.Error.WriteLine($"[Service] License key length: {licenseKey.Length}");
373+
Console.Error.WriteLine($"[Service] License key prefix: {(licenseKey.Length > 30 ? licenseKey.Substring(0, 30) + "..." : licenseKey)}");
374+
377375
if (_dmApi == null)
378376
{
379377
Console.Error.WriteLine("[Service] Creating new DocumentManagerAPI instance");

solidworks-addin/BluePLM.SolidWorksService/SolidWorksAPI.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ public CommandResult GetBillOfMaterials(string? filePath, bool includeChildren =
180180
ModelDoc2? doc = null;
181181
try
182182
{
183-
doc = OpenDocument(filePath, out var errors, out var warnings);
183+
doc = OpenDocument(filePath!, out var errors, out var warnings);
184184
if (doc == null)
185185
return new CommandResult { Success = false, Error = $"Failed to open file: errors={errors}" };
186186

@@ -290,7 +290,7 @@ public CommandResult GetExternalReferences(string? filePath)
290290
ModelDoc2? doc = null;
291291
try
292292
{
293-
doc = OpenDocument(filePath, out var errors, out var warnings);
293+
doc = OpenDocument(filePath!, out var errors, out var warnings);
294294
if (doc == null)
295295
return new CommandResult { Success = false, Error = $"Failed to open file: errors={errors}" };
296296

@@ -310,7 +310,7 @@ public CommandResult GetExternalReferences(string? filePath)
310310
path = refPath,
311311
fileName = Path.GetFileName(refPath),
312312
exists = File.Exists(refPath),
313-
fileType = GetFileType(refPath)
313+
fileType = GetFileType(refPath!)
314314
});
315315
}
316316
}
@@ -359,7 +359,7 @@ public CommandResult GetCustomProperties(string? filePath, string? configuration
359359
ModelDoc2? doc = null;
360360
try
361361
{
362-
doc = OpenDocument(filePath, out var errors, out var warnings);
362+
doc = OpenDocument(filePath!, out var errors, out var warnings);
363363
if (doc == null)
364364
return new CommandResult { Success = false, Error = $"Failed to open file: errors={errors}" };
365365

@@ -420,7 +420,7 @@ public CommandResult SetCustomProperties(string? filePath, Dictionary<string, st
420420
ModelDoc2? doc = null;
421421
try
422422
{
423-
doc = OpenDocument(filePath, out var errors, out var warnings, readOnly: false);
423+
doc = OpenDocument(filePath!, out var errors, out var warnings, readOnly: false);
424424
if (doc == null)
425425
return new CommandResult { Success = false, Error = $"Failed to open file: errors={errors}" };
426426

@@ -527,7 +527,7 @@ public CommandResult GetConfigurations(string? filePath)
527527
ModelDoc2? doc = null;
528528
try
529529
{
530-
doc = OpenDocument(filePath, out var errors, out var warnings);
530+
doc = OpenDocument(filePath!, out var errors, out var warnings);
531531
if (doc == null)
532532
return new CommandResult { Success = false, Error = $"Failed to open file: errors={errors}" };
533533

@@ -594,7 +594,7 @@ public CommandResult GetMassProperties(string? filePath, string? configuration =
594594
ModelDoc2? doc = null;
595595
try
596596
{
597-
doc = OpenDocument(filePath, out var errors, out var warnings);
597+
doc = OpenDocument(filePath!, out var errors, out var warnings);
598598
if (doc == null)
599599
return new CommandResult { Success = false, Error = $"Failed to open file: errors={errors}" };
600600

@@ -854,7 +854,7 @@ public CommandResult ExportToIges(string? filePath, string? outputPath)
854854
ModelDoc2? doc = null;
855855
try
856856
{
857-
doc = OpenDocument(filePath, out var errors, out var warnings);
857+
doc = OpenDocument(filePath!, out var errors, out var warnings);
858858
if (doc == null)
859859
return new CommandResult { Success = false, Error = $"Failed to open file: errors={errors}" };
860860

@@ -912,7 +912,7 @@ public CommandResult ExportToDxf(string? filePath, string? outputPath)
912912
ModelDoc2? doc = null;
913913
try
914914
{
915-
doc = OpenDocument(filePath, out var errors, out var warnings);
915+
doc = OpenDocument(filePath!, out var errors, out var warnings);
916916
if (doc == null)
917917
return new CommandResult { Success = false, Error = $"Failed to open file: errors={errors}" };
918918

@@ -970,7 +970,7 @@ public CommandResult ExportToImage(string? filePath, string? outputPath, int wid
970970
ModelDoc2? doc = null;
971971
try
972972
{
973-
doc = OpenDocument(filePath, out var errors, out var warnings);
973+
doc = OpenDocument(filePath!, out var errors, out var warnings);
974974
if (doc == null)
975975
return new CommandResult { Success = false, Error = $"Failed to open file: errors={errors}" };
976976

@@ -1039,7 +1039,7 @@ public CommandResult ReplaceComponent(string? assemblyPath, string? oldComponent
10391039
ModelDoc2? doc = null;
10401040
try
10411041
{
1042-
doc = OpenDocument(assemblyPath, out var errors, out var warnings, readOnly: false);
1042+
doc = OpenDocument(assemblyPath!, out var errors, out var warnings, readOnly: false);
10431043
if (doc == null)
10441044
return new CommandResult { Success = false, Error = $"Failed to open assembly: errors={errors}" };
10451045

@@ -1236,7 +1236,7 @@ private static string GetPartNumber(Dictionary<string, string> props)
12361236
foreach (var key in partNumberKeys)
12371237
{
12381238
var value = GetDictValue(props, key);
1239-
if (!string.IsNullOrEmpty(value))
1239+
if (value != null && value.Length > 0)
12401240
return value;
12411241
}
12421242

@@ -1270,7 +1270,7 @@ private static string GetRevision(Dictionary<string, string> props)
12701270
foreach (var key in revisionKeys)
12711271
{
12721272
var value = GetDictValue(props, key);
1273-
if (!string.IsNullOrEmpty(value))
1273+
if (value != null && value.Length > 0)
12741274
return value;
12751275
}
12761276

@@ -1624,7 +1624,7 @@ public CommandResult GetDocumentInfo(string? filePath)
16241624
isOpen = true,
16251625
isReadOnly = doc.IsOpenedReadOnly(),
16261626
isDirty = doc.GetSaveFlag(),
1627-
fileType = GetFileType(filePath),
1627+
fileType = GetFileType(filePath!),
16281628
activeConfiguration = doc.ConfigurationManager?.ActiveConfiguration?.Name ?? "",
16291629
properties = props
16301630
}

src/App.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { registerModule, unregisterModule } from '@/lib/telemetry'
33
import { usePDMStore } from './stores/pdmStore'
44
import { SettingsContent } from './components/SettingsContent'
55
import type { SettingsTab } from './types/settings'
6-
import { supabase, getCurrentSession, isSupabaseConfigured, getFilesLightweight, getCheckedOutUsers, linkUserToOrganization, getUserProfile, setCurrentAccessToken, registerDeviceSession, startSessionHeartbeat, stopSessionHeartbeat, signOut } from './lib/supabase'
6+
import { supabase, getCurrentSession, isSupabaseConfigured, getFilesLightweight, getCheckedOutUsers, linkUserToOrganization, getUserProfile, setCurrentAccessToken, registerDeviceSession, startSessionHeartbeat, stopSessionHeartbeat, signOut, syncUserSessionsOrgId } from './lib/supabase'
77
import { subscribeToFiles, subscribeToActivity, subscribeToOrganization, unsubscribeAll } from './lib/realtime'
88
import { getBackupStatus, isThisDesignatedMachine, updateHeartbeat } from './lib/backup'
99
import { MenuBar } from './components/MenuBar'
@@ -355,6 +355,9 @@ function App() {
355355
console.log('[Auth] Updating user org_id in store:', (org as any).id)
356356
setUser({ ...userData, org_id: (org as any).id })
357357
}
358+
359+
// Sync all user sessions to have the correct org_id (fixes sessions created before org was linked)
360+
syncUserSessionsOrgId(session.user.id, (org as any).id)
358361
} else if (error) {
359362
console.log('[Auth] No organization found:', error)
360363
}
@@ -426,6 +429,9 @@ function App() {
426429
console.log('[Auth] Updating user org_id in store:', (org as any).id)
427430
setUser({ ...currentUser, org_id: (org as any).id })
428431
}
432+
433+
// Sync all user sessions to have the correct org_id (fixes sessions created before org was linked)
434+
syncUserSessionsOrgId(session.user.id, (org as any).id)
429435
} else {
430436
console.log('[Auth] No organization found:', orgError)
431437
setIsConnecting(false)
@@ -1958,7 +1964,9 @@ function App() {
19581964
}
19591965

19601966
// Register this device's session
1961-
registerDeviceSession(user.id, user.org_id || null)
1967+
// Use user.org_id first, fall back to organization.id if not set
1968+
const orgIdForSession = user.org_id || usePDMStore.getState().organization?.id || null
1969+
registerDeviceSession(user.id, orgIdForSession)
19621970
.then(result => {
19631971
if (result.success) {
19641972
console.log('[Session] Device session registered')
@@ -1975,7 +1983,8 @@ function App() {
19751983
clearOrg(null)
19761984
},
19771985
// Get current org_id from store (handles org changes during session)
1978-
() => usePDMStore.getState().user?.org_id
1986+
// Fall back to organization.id if user.org_id is not set
1987+
() => usePDMStore.getState().user?.org_id || usePDMStore.getState().organization?.id
19791988
)
19801989
} else {
19811990
console.error('[Session] Failed to register session:', result.error)
@@ -1988,7 +1997,7 @@ function App() {
19881997
return () => {
19891998
stopSessionHeartbeat()
19901999
}
1991-
}, [user?.id, user?.org_id])
2000+
}, [user?.id, user?.org_id, organization?.id])
19922001

19932002
// Backup machine heartbeat - keeps designated_machine_last_seen updated
19942003
// This runs at App level so it doesn't require BackupPanel to be open

0 commit comments

Comments
 (0)