@servicemind.tis/tis-image-and-file-upload-and-view 1.2.24 → 1.2.25
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/fesm2022/servicemind.tis-tis-image-and-file-upload-and-view.mjs +141 -16
- package/fesm2022/servicemind.tis-tis-image-and-file-upload-and-view.mjs.map +1 -1
- package/lib/interfaces/socket-adapter.interface.d.ts +8 -1
- package/lib/services/tis-remote-upload.service.d.ts +12 -3
- package/lib/tis-qr-code-dialog/tis-qr-code-dialog.component.d.ts +15 -0
- package/package.json +1 -1
|
@@ -596,22 +596,27 @@ class TisRemoteUploadService {
|
|
|
596
596
|
// Mobile Communication
|
|
597
597
|
// ===========================================================================
|
|
598
598
|
/**
|
|
599
|
-
* Send message to mobile device
|
|
599
|
+
* Send message to mobile device via channel
|
|
600
600
|
*/
|
|
601
601
|
sendToMobile(type, data) {
|
|
602
602
|
if (!this.socketAdapter?.sendViaSocket) {
|
|
603
603
|
console.warn(`[${TisRemoteUploadService.COMPONENT}] sendViaSocket not available`);
|
|
604
604
|
return;
|
|
605
605
|
}
|
|
606
|
-
|
|
607
|
-
action:
|
|
606
|
+
const message = {
|
|
607
|
+
action: 'send-to-channel',
|
|
608
608
|
data: {
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
609
|
+
channel: this.channelName,
|
|
610
|
+
payload: {
|
|
611
|
+
type,
|
|
612
|
+
...data,
|
|
613
|
+
desktopDeviceId: this.deviceId,
|
|
614
|
+
timestamp: Date.now()
|
|
615
|
+
}
|
|
613
616
|
}
|
|
614
|
-
}
|
|
617
|
+
};
|
|
618
|
+
console.log(`[${TisRemoteUploadService.COMPONENT}] Sending to mobile:`, message);
|
|
619
|
+
this.socketAdapter.sendViaSocket(message);
|
|
615
620
|
}
|
|
616
621
|
/**
|
|
617
622
|
* Accept mobile connection (send SUCCESS response)
|
|
@@ -643,18 +648,61 @@ class TisRemoteUploadService {
|
|
|
643
648
|
this.sendToMobile('connectionState', { state: 'SUCCESS' });
|
|
644
649
|
}
|
|
645
650
|
/**
|
|
646
|
-
* Disconnect from mobile device
|
|
651
|
+
* Disconnect from mobile device - call API and clear local state
|
|
647
652
|
*/
|
|
648
|
-
disconnect() {
|
|
653
|
+
async disconnect() {
|
|
649
654
|
console.log(`[${TisRemoteUploadService.COMPONENT}] Disconnecting from mobile...`);
|
|
650
|
-
|
|
651
|
-
|
|
655
|
+
const mobileDeviceId = this.mobileConnection$.value?.mobileDeviceId;
|
|
656
|
+
// Call disconnect API via socket
|
|
657
|
+
if (this.socketAdapter?.callApiViaSocket && mobileDeviceId) {
|
|
658
|
+
try {
|
|
659
|
+
const callApi = this.socketAdapter.callApiViaSocket.bind(this.socketAdapter);
|
|
660
|
+
const response = await new Promise((resolve, reject) => {
|
|
661
|
+
const timeout = setTimeout(() => reject(new Error('Disconnect API timeout')), 10000);
|
|
662
|
+
callApi('tis-image-mobile-uploader/disconnect-mobile-link', {
|
|
663
|
+
desktopDeviceId: this.deviceId,
|
|
664
|
+
mobileDeviceId: mobileDeviceId,
|
|
665
|
+
initiatedBy: 'desktop'
|
|
666
|
+
}).pipe(take(1)).subscribe({
|
|
667
|
+
next: (res) => {
|
|
668
|
+
clearTimeout(timeout);
|
|
669
|
+
resolve(res);
|
|
670
|
+
},
|
|
671
|
+
error: (err) => {
|
|
672
|
+
clearTimeout(timeout);
|
|
673
|
+
reject(err);
|
|
674
|
+
}
|
|
675
|
+
});
|
|
676
|
+
});
|
|
677
|
+
console.log(`[${TisRemoteUploadService.COMPONENT}] Disconnect API response:`, response);
|
|
678
|
+
}
|
|
679
|
+
catch (error) {
|
|
680
|
+
console.warn(`[${TisRemoteUploadService.COMPONENT}] Disconnect API call failed:`, error);
|
|
681
|
+
// Continue with local cleanup anyway
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
// Notify mobile via channel (backup notification)
|
|
685
|
+
this.sendToMobile('mobile-link-disconnected', {
|
|
686
|
+
desktopDeviceId: this.deviceId,
|
|
687
|
+
initiatedBy: 'desktop'
|
|
688
|
+
});
|
|
652
689
|
// Clear state
|
|
653
690
|
this.mobileConnection$.next(null);
|
|
654
691
|
this.connectionStatus$.next('disconnected');
|
|
655
692
|
this.pairingSession$.next(null);
|
|
656
693
|
this.clearMobileConnection();
|
|
657
694
|
}
|
|
695
|
+
/**
|
|
696
|
+
* Handle disconnect initiated from remote (mobile) side
|
|
697
|
+
*/
|
|
698
|
+
handleRemoteDisconnect(data) {
|
|
699
|
+
console.log(`[${TisRemoteUploadService.COMPONENT}] Mobile disconnected:`, data);
|
|
700
|
+
// Clear state without calling API (mobile already initiated)
|
|
701
|
+
this.mobileConnection$.next(null);
|
|
702
|
+
this.connectionStatus$.next('disconnected');
|
|
703
|
+
this.pairingSession$.next(null);
|
|
704
|
+
this.clearMobileConnection();
|
|
705
|
+
}
|
|
658
706
|
// ===========================================================================
|
|
659
707
|
// Channel Subscription & Message Handling
|
|
660
708
|
// ===========================================================================
|
|
@@ -684,18 +732,23 @@ class TisRemoteUploadService {
|
|
|
684
732
|
*/
|
|
685
733
|
handleChannelMessage(message) {
|
|
686
734
|
console.log(`[${TisRemoteUploadService.COMPONENT}] Received:`, message);
|
|
687
|
-
// Extract message type and data
|
|
688
|
-
const
|
|
689
|
-
const
|
|
735
|
+
// Extract message type and data - handle nested payload structure
|
|
736
|
+
const payload = message.payload || message.data || message;
|
|
737
|
+
const type = payload.type || message.type;
|
|
738
|
+
const data = payload;
|
|
690
739
|
switch (type) {
|
|
691
740
|
case 'connectionState':
|
|
692
741
|
this.handleConnectionState(data);
|
|
693
742
|
break;
|
|
743
|
+
case 'mobile-link-established':
|
|
744
|
+
this.handleMobileLinkEstablished(data);
|
|
745
|
+
break;
|
|
694
746
|
case 'image-uploaded':
|
|
695
747
|
case 'upload_complete':
|
|
696
748
|
this.handleUploadComplete(message);
|
|
697
749
|
break;
|
|
698
750
|
case 'disconnect':
|
|
751
|
+
case 'mobile-link-disconnected':
|
|
699
752
|
this.handleMobileDisconnect(data);
|
|
700
753
|
break;
|
|
701
754
|
default:
|
|
@@ -708,6 +761,50 @@ class TisRemoteUploadService {
|
|
|
708
761
|
}
|
|
709
762
|
}
|
|
710
763
|
}
|
|
764
|
+
/**
|
|
765
|
+
* Handle mobile-link-established message
|
|
766
|
+
* This is sent when mobile successfully connects via the backend
|
|
767
|
+
*/
|
|
768
|
+
handleMobileLinkEstablished(data) {
|
|
769
|
+
const mobileDeviceId = data.mobileDeviceId;
|
|
770
|
+
const mobileConnectionId = data.mobileConnectionId;
|
|
771
|
+
const userId = data.userId;
|
|
772
|
+
console.log(`[${TisRemoteUploadService.COMPONENT}] Mobile link established:`, {
|
|
773
|
+
mobileDeviceId,
|
|
774
|
+
mobileConnectionId,
|
|
775
|
+
userId
|
|
776
|
+
});
|
|
777
|
+
if (mobileDeviceId) {
|
|
778
|
+
// Save mobile connection
|
|
779
|
+
const connectionInfo = {
|
|
780
|
+
mobileDeviceId,
|
|
781
|
+
connectedAt: Date.now(),
|
|
782
|
+
lastActivity: Date.now()
|
|
783
|
+
};
|
|
784
|
+
this.mobileConnection$.next(connectionInfo);
|
|
785
|
+
this.saveMobileConnection(connectionInfo);
|
|
786
|
+
// Update status to connected
|
|
787
|
+
this.connectionStatus$.next('connected');
|
|
788
|
+
// Update session
|
|
789
|
+
const session = this.pairingSession$.value;
|
|
790
|
+
if (session) {
|
|
791
|
+
const updatedSession = {
|
|
792
|
+
...session,
|
|
793
|
+
mobileDeviceId,
|
|
794
|
+
status: 'connected',
|
|
795
|
+
lastActivity: Date.now()
|
|
796
|
+
};
|
|
797
|
+
this.pairingSession$.next(updatedSession);
|
|
798
|
+
}
|
|
799
|
+
// Send SUCCESS acknowledgment to mobile
|
|
800
|
+
this.sendToMobile('connectionState', {
|
|
801
|
+
state: 'SUCCESS',
|
|
802
|
+
desktopDeviceId: this.deviceId,
|
|
803
|
+
mobileConnectionId
|
|
804
|
+
});
|
|
805
|
+
console.log(`[${TisRemoteUploadService.COMPONENT}] Connection established with mobile device:`, mobileDeviceId);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
711
808
|
/**
|
|
712
809
|
* Handle connection state messages from mobile
|
|
713
810
|
*/
|
|
@@ -892,6 +989,10 @@ class TisQrCodeDialogComponent {
|
|
|
892
989
|
this.isConnected = true;
|
|
893
990
|
this.connectionStatus = 'connected';
|
|
894
991
|
this.isLoading = false;
|
|
992
|
+
// Send field info to mobile since already connected
|
|
993
|
+
if (this.data.fieldInfo) {
|
|
994
|
+
this.sendFieldInfoToMobile();
|
|
995
|
+
}
|
|
895
996
|
}
|
|
896
997
|
else {
|
|
897
998
|
// No existing connection, generate QR code
|
|
@@ -920,7 +1021,12 @@ class TisQrCodeDialogComponent {
|
|
|
920
1021
|
.pipe(takeUntil(this.destroy$))
|
|
921
1022
|
.subscribe(status => {
|
|
922
1023
|
this.connectionStatus = status;
|
|
1024
|
+
const wasConnected = this.isConnected;
|
|
923
1025
|
this.isConnected = status === 'connected';
|
|
1026
|
+
// Send field info when first connected
|
|
1027
|
+
if (!wasConnected && this.isConnected && this.data.fieldInfo) {
|
|
1028
|
+
this.sendFieldInfoToMobile();
|
|
1029
|
+
}
|
|
924
1030
|
});
|
|
925
1031
|
// Mobile connection changes
|
|
926
1032
|
this.remoteUploadService.getMobileConnection()
|
|
@@ -944,6 +1050,15 @@ class TisQrCodeDialogComponent {
|
|
|
944
1050
|
console.error('[TisQrCodeDialog] Error:', error);
|
|
945
1051
|
});
|
|
946
1052
|
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Send field configuration to mobile device
|
|
1055
|
+
*/
|
|
1056
|
+
sendFieldInfoToMobile() {
|
|
1057
|
+
if (!this.data.fieldInfo)
|
|
1058
|
+
return;
|
|
1059
|
+
console.log('[TisQrCodeDialog] Sending field info to mobile:', this.data.fieldInfo);
|
|
1060
|
+
this.remoteUploadService.sendToMobile('field-info', this.data.fieldInfo);
|
|
1061
|
+
}
|
|
947
1062
|
startCountdown() {
|
|
948
1063
|
if (this.countdownSubscription) {
|
|
949
1064
|
this.countdownSubscription.unsubscribe();
|
|
@@ -2615,7 +2730,17 @@ class TisImageAndFileUploadAndViewComponent {
|
|
|
2615
2730
|
subtitle: `Scan this QR code to upload ${this.type === 'image' ? 'images' : 'files'} from your mobile device`,
|
|
2616
2731
|
qrSize: 200,
|
|
2617
2732
|
showCountdown: true,
|
|
2618
|
-
autoCloseOnUpload: false
|
|
2733
|
+
autoCloseOnUpload: false,
|
|
2734
|
+
fieldInfo: {
|
|
2735
|
+
label: this.label || `${this.type === 'image' ? 'Images' : 'Files'}`,
|
|
2736
|
+
accept: this.accept || (this.type === 'image' ? 'image/*' : '*'),
|
|
2737
|
+
type: this.type,
|
|
2738
|
+
entityType: this.entityType,
|
|
2739
|
+
entityId: this.entityId,
|
|
2740
|
+
isMultiple: this.config.isMultiple,
|
|
2741
|
+
limit: this.config.limit,
|
|
2742
|
+
isCompressed: this.config.isCompressed
|
|
2743
|
+
}
|
|
2619
2744
|
};
|
|
2620
2745
|
const dialogRef = this.dialog.open(TisQrCodeDialogComponent, {
|
|
2621
2746
|
width: this.isMobile ? '100%' : '420px',
|