1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
1
import React , { useEffect , useRef , useState } from "react"
3
2
import { useEditorContext } from "@/contexts"
4
- import {
5
- ApollonEditor ,
6
- ApollonMode ,
7
- ApollonOptions ,
8
- UMLModel ,
9
- } from "@tumaet/apollon"
3
+ import { ApollonEditor , ApollonMode , ApollonOptions } from "@tumaet/apollon"
10
4
import { useNavigate , useParams , useSearchParams } from "react-router"
11
5
import { toast } from "react-toastify"
12
- import { backendURL , backendWSSUrl } from "@/constants"
13
- import { DiagramView , WebSocketMessage } from "@/types"
14
-
15
- const fetchDiagramData = ( diagramId : string ) : Promise < any > => {
16
- return fetch ( `${ backendURL } /api/${ diagramId } ` , {
17
- method : "GET" ,
18
- headers : {
19
- "Content-Type" : "application/json" ,
20
- } ,
21
- } ) . then ( ( res ) => {
22
- if ( res . ok ) {
23
- return res . json ( )
24
- } else {
25
- throw new Error ( "Failed to fetch diagram data" )
26
- }
27
- } )
28
- }
29
-
30
- const sendPutRequest = async ( diagramId : string , data : UMLModel ) => {
31
- try {
32
- const response = await fetch ( `${ backendURL } /api/${ diagramId } ` , {
33
- method : "PUT" ,
34
- headers : {
35
- "Content-Type" : "application/json" ,
36
- } ,
37
- body : JSON . stringify ( data ) ,
38
- } )
39
- if ( ! response . ok ) {
40
- throw new Error ( "Failed to send PUT request" )
41
- }
42
- } catch ( error ) {
43
- console . error ( "Error in PUT request:" , error )
44
- toast . error ( "Failed to sync diagram data" )
45
- }
46
- }
6
+ import { DiagramView } from "@/types"
7
+ import { WebSocketManager } from "@/services/WebSocketManager"
8
+ import { DiagramAPIManager } from "@/services/DiagramAPIManager"
47
9
48
10
export const ApollonWithConnection : React . FC = ( ) => {
49
11
const { diagramId } = useParams ( )
@@ -52,149 +14,93 @@ export const ApollonWithConnection: React.FC = () => {
52
14
const { setEditor } = useEditorContext ( )
53
15
const [ isLoading , setIsLoading ] = useState ( true )
54
16
const containerRef = useRef < HTMLDivElement | null > ( null )
55
- const websocketRef = useRef < WebSocket | null > ( null )
56
- const intervalRef = useRef < NodeJS . Timeout | null > ( null ) // Ref to store interval ID
17
+ const wsManagerRef = useRef < WebSocketManager | null > ( null )
18
+ const syncIntervalRef = useRef < NodeJS . Timeout | null > ( null )
57
19
const diagramIsUpdated = useRef ( false )
58
20
59
21
useEffect ( ( ) => {
60
22
let instance : ApollonEditor | null = null
61
- if ( containerRef . current && diagramId ) {
62
- const initializeApollon = async ( ) => {
63
- try {
64
- const viewType = searchParams . get ( "view" )
65
- const validViewTypes : string [ ] = [
66
- DiagramView . COLLABORATE ,
67
- DiagramView . GIVE_FEEDBACK ,
68
- DiagramView . SEE_FEEDBACK ,
69
- DiagramView . EDIT ,
70
- ]
71
- const isValidView = viewType && validViewTypes . includes ( viewType )
72
23
73
- if ( ! isValidView ) {
74
- toast . error ( "Invalid view type" )
75
- navigate ( "/" )
76
- return
77
- }
78
-
79
- const diagram = await fetchDiagramData ( diagramId )
24
+ const initialize = async ( ) => {
25
+ if ( ! containerRef . current || ! diagramId ) return
80
26
81
- const editorOptions : ApollonOptions = {
82
- model : diagram ,
83
- }
84
-
85
- if ( viewType === DiagramView . GIVE_FEEDBACK ) {
86
- editorOptions . mode = ApollonMode . Assessment
87
- editorOptions . readonly = false
88
- } else if ( viewType === DiagramView . SEE_FEEDBACK ) {
89
- editorOptions . mode = ApollonMode . Assessment
90
- editorOptions . readonly = true
91
- } else if ( viewType === DiagramView . EDIT ) {
92
- editorOptions . mode = ApollonMode . Modelling
93
- editorOptions . readonly = false
94
- } else {
95
- editorOptions . mode = ApollonMode . Modelling
96
- editorOptions . readonly = false
97
- }
98
-
99
- instance = new ApollonEditor ( containerRef . current ! , editorOptions )
100
- setEditor ( instance )
101
- setIsLoading ( false )
102
-
103
- const makeConnection =
104
- viewType === DiagramView . COLLABORATE ||
105
- viewType === DiagramView . GIVE_FEEDBACK ||
106
- viewType === DiagramView . SEE_FEEDBACK
27
+ try {
28
+ const viewType = searchParams . get ( "view" )
29
+ const validViews = Object . values ( DiagramView )
30
+ if ( ! viewType || ! validViews . includes ( viewType as DiagramView ) ) {
31
+ toast . error ( "Invalid view type" )
32
+ navigate ( "/" )
33
+ return
34
+ }
35
+ console . log ( "Initializing Apollon editor with view type:" , viewType )
107
36
108
- if ( makeConnection ) {
109
- // Set up the WebSocket connection
110
- websocketRef . current = new WebSocket (
111
- `${ backendWSSUrl } ?diagramId=${ diagramId } `
112
- )
37
+ const diagram = await DiagramAPIManager . fetchDiagramData ( diagramId )
38
+ console . log ( "Fetched diagram data:" , diagram )
113
39
114
- // Handle incoming Yjs updates
115
- websocketRef . current . onmessage = ( event : MessageEvent < string > ) => {
116
- const receiveWebSocketMessage = JSON . parse (
117
- event . data
118
- ) as WebSocketMessage
119
- instance ?. receiveBroadcastedMessage (
120
- receiveWebSocketMessage . diagramData
121
- )
122
- }
40
+ const editorOptions : ApollonOptions = {
41
+ model : diagram ,
42
+ }
123
43
124
- // Wait until socket is open before starting sync
125
- websocketRef . current . onopen = ( ) => {
126
- instance ?. sendBroadcastMessage ( ( diagramData ) => {
127
- if ( websocketRef . current ?. readyState === WebSocket . OPEN ) {
128
- const sendData = { diagramData }
129
- websocketRef . current . send ( JSON . stringify ( sendData ) )
130
- } else {
131
- console . warn ( "Tried to send while WebSocket not open" )
132
- }
133
- } )
44
+ if ( viewType === DiagramView . GIVE_FEEDBACK ) {
45
+ editorOptions . mode = ApollonMode . Assessment
46
+ editorOptions . readonly = false
47
+ } else if ( viewType === DiagramView . SEE_FEEDBACK ) {
48
+ editorOptions . mode = ApollonMode . Assessment
49
+ editorOptions . readonly = true
50
+ } else if ( viewType === DiagramView . EDIT ) {
51
+ editorOptions . mode = ApollonMode . Modelling
52
+ editorOptions . readonly = false
53
+ } else {
54
+ editorOptions . mode = ApollonMode . Modelling
55
+ editorOptions . readonly = false
56
+ }
134
57
135
- const initialSyncMessage =
136
- ApollonEditor . generateInitialSyncMessage ( )
137
- const initialMessage = JSON . stringify ( {
138
- diagramData : initialSyncMessage ,
139
- } )
140
- websocketRef . current ?. send ( initialMessage )
141
- }
58
+ instance = new ApollonEditor ( containerRef . current ! , editorOptions )
59
+ setEditor ( instance )
60
+ setIsLoading ( false )
142
61
143
- websocketRef . current . onerror = ( err ) => {
144
- console . error ( "WebSocket error" , err )
145
- toast . error ( "WebSocket connection error" )
146
- }
62
+ if (
63
+ [
64
+ DiagramView . COLLABORATE ,
65
+ DiagramView . GIVE_FEEDBACK ,
66
+ DiagramView . SEE_FEEDBACK ,
67
+ ] . includes ( viewType as DiagramView )
68
+ ) {
69
+ wsManagerRef . current = new WebSocketManager ( diagramId , instance , ( ) =>
70
+ toast . error ( "WebSocket error" )
71
+ )
72
+ wsManagerRef . current . startConnection ( )
73
+ }
147
74
148
- intervalRef . current = setInterval ( ( ) => {
149
- if ( instance && diagramId && diagramIsUpdated . current ) {
150
- const diagramData = instance . model
151
- sendPutRequest ( diagramId , diagramData )
152
- diagramIsUpdated . current = false
153
- }
154
- } , 5000 )
75
+ syncIntervalRef . current = setInterval ( ( ) => {
76
+ if ( diagramIsUpdated . current && diagramId ) {
77
+ DiagramAPIManager . sendDiagramUpdate (
78
+ diagramId ,
79
+ instance ! . model
80
+ ) . catch ( ( ) => toast . error ( "Failed to sync changes" ) )
81
+ diagramIsUpdated . current = false
155
82
}
156
-
157
- instance . subscribeToModelChange ( ( ) => {
158
- diagramIsUpdated . current = true
159
- } )
160
-
161
- // Return cleanup function for Apollon2 and WebSocket
162
- } catch ( error ) {
163
- toast . error ( "Error loading diagram. Please try again." )
164
- navigate ( "/" )
165
- console . error ( "Error setting up Apollon2:" , error )
166
- }
83
+ } , 5000 )
84
+
85
+ instance . subscribeToModelChange ( ( ) => {
86
+ diagramIsUpdated . current = true
87
+ } )
88
+ } catch {
89
+ toast . error ( "Failed to initialize diagram" )
90
+ navigate ( "/" )
167
91
}
168
-
169
- initializeApollon ( )
170
92
}
171
93
172
- return ( ) => {
173
- setEditor ( undefined ) // Clear context
174
-
175
- // Clear interval if it exists
176
- if ( intervalRef . current ) {
177
- clearInterval ( intervalRef . current )
178
- intervalRef . current = null
179
- }
94
+ initialize ( )
180
95
181
- // Clean up WebSocket
182
- if (
183
- websocketRef . current &&
184
- websocketRef . current . readyState === WebSocket . OPEN
185
- ) {
186
- console . log ( "Clearing WebSocket connection" )
187
- websocketRef . current . close ( )
188
- websocketRef . current = null
189
- }
190
-
191
- if ( instance ) {
192
- instance . destroy ( )
193
- instance = null
96
+ return ( ) => {
97
+ setEditor ( undefined )
98
+ wsManagerRef . current ?. cleanup ( )
99
+ if ( syncIntervalRef . current ) {
100
+ clearInterval ( syncIntervalRef . current )
194
101
}
102
+ instance ?. destroy ( )
195
103
}
196
-
197
- // Implicitly return undefined if conditions are not met
198
104
} , [ diagramId , searchParams , setEditor ] )
199
105
200
106
return (
0 commit comments