55 */
66
77import { PageSection } from "@patternfly/react-core" ;
8- import { App } from "antd " ;
9- import type React from "react " ;
8+ import config from "config " ;
9+ import { access } from "fs " ;
1010import { useEffect , useState } from "react" ;
1111import {
12+ genUUID ,
1213 getRoot ,
1314 getRootID ,
15+ getState ,
1416 type ModuleToFunc ,
17+ StateType ,
1518 useReducer ,
1619} from "react-reducer-utils" ;
17- import { useLonk } from "../../api/lonk/index.ts" ;
20+ import { useLocation , useSearchParams } from "react-router-dom" ;
21+ import type { Lonk , LonkMessageData } from "../../api/lonk/types.ts" ;
1822import type { PACSqueryCore } from "../../api/pfdcm/index.ts" ;
1923import * as DoPacs from "../../reducers/pacs" ;
2024import ErrorScreen from "./components/ErrorScreen.tsx" ;
2125import PacsLoadingScreen from "./components/PacsLoadingScreen.tsx" ;
22- import { getSeriesDescription } from "./components/utils.ts" ;
2326import { DEFAULT_PREFERENCES } from "./defaultPreferences.ts" ;
2427import styles from "./PacsApp.module.css" ;
2528import PacsView from "./PacsView.tsx" ;
26- import { type PacsState , SeriesPullState } from "./types.ts" ;
29+ import { type PacsState , QUERY_PROMPT , SearchMode } from "./types.ts" ;
2730import { createFeedWithSeriesInstanceUID , errorCodeIsNot4xx } from "./utils.ts" ;
2831
2932type TDoPacs = ModuleToFunc < typeof DoPacs > ;
@@ -76,22 +79,25 @@ export default () => {
7679 // ========================================
7780 // CLIENTS AND MISC
7881 // ========================================
79- const { message } = App . useApp ( ) ;
80-
81- const [ statePacs , doPacs ] = useReducer < DoPacs . State , TDoPacs > ( DoPacs ) ;
82+ const [ statePacs , doPacs ] = useReducer < DoPacs . State , TDoPacs > (
83+ DoPacs ,
84+ StateType . LOCAL ,
85+ ) ;
86+ const [ searchParams , setSearchParams ] = useSearchParams ( ) ;
87+ const location = useLocation ( ) ;
88+ const [ pacsID , _ ] = useState ( genUUID ( ) ) ;
8289
83- const pacsID = getRootID ( statePacs ) ;
84- const pacs = getRoot ( statePacs ) ?? DoPacs . defaultState ;
90+ const pacs = getState ( statePacs , pacsID ) ?? DoPacs . defaultState ;
8591
8692 const {
8793 expandedStudyUids,
8894 expandedSeries,
8995 studies,
9096 services,
97+ service,
9198 isGetServices,
92- seriesMap,
9399 isLoadingStudies,
94- wsUrl ,
100+ isExpandedAllDone ,
95101 } = pacs ;
96102
97103 // ========================================
@@ -101,7 +107,7 @@ export default () => {
101107 /**
102108 * Indicates a fatal error with the WebSocket.
103109 */
104- const [ wsError , setWsError ] = useState < React . ReactNode | null > ( null ) ;
110+ const [ wsError , setWsError ] = useState ( "" ) ;
105111 // TODO create a settings component for changing preferences
106112 const [ preferences , setPreferences ] = useState ( DEFAULT_PREFERENCES ) ;
107113
@@ -115,78 +121,6 @@ export default () => {
115121 const state : PacsState = { preferences, studies } ;
116122
117123 const error = wsError || pacs . errmsg ;
118- // ========================================
119- // LONK WEBSOCKET
120- // ========================================
121- const lonk = useLonk ( {
122- url : wsUrl ,
123- onDone : async ( pacs_name : string , SeriesInstanceUID : string ) => {
124- doPacs . updateReceiveState ( pacsID , pacs_name , SeriesInstanceUID , {
125- pullState : SeriesPullState . WAITING_OR_COMPLETE ,
126- done : true ,
127- } ) ;
128- doPacs . queryCubeSeriesStateBySeriesUID (
129- pacsID ,
130- pacs_name ,
131- SeriesInstanceUID ,
132- ) ;
133-
134- await createFeedWithSeriesInstanceUID ( SeriesInstanceUID ) ;
135-
136- return ;
137- } ,
138- onProgress : (
139- pacs_name : string ,
140- SeriesInstanceUID : string ,
141- ndicom : number ,
142- ) => {
143- doPacs . updateReceiveState (
144- pacsID ,
145- pacs_name ,
146- SeriesInstanceUID ,
147- { receivedCount : ndicom } ,
148- ( theOrig : number , theNew : number ) => theOrig < theNew ,
149- ) ;
150- } ,
151- onLonkError : (
152- pacs_name : string ,
153- SeriesInstanceUID : string ,
154- error : string ,
155- ) => {
156- doPacs . pushReceiveStateError ( pacsID , pacs_name , SeriesInstanceUID , error ) ;
157-
158- const desc = getSeriesDescription (
159- pacs_name ,
160- SeriesInstanceUID ,
161- seriesMap ,
162- ) ;
163- message . error (
164- < > There was an error while receiving the series "{ desc } "</ > ,
165- ) ;
166- } ,
167- onMessageError : ( data : any , error : string ) => {
168- message . error (
169- < >
170- A < em > LONK</ em > error occurred, please check the console.
171- </ > ,
172- ) ;
173- } ,
174- heartbeat : false ,
175- retryOnError : true ,
176- reconnectAttempts : 3 ,
177- reconnectInterval : 3000 ,
178- shouldReconnect : errorCodeIsNot4xx ,
179- onReconnectStop : ( ) => {
180- console . error ( "PacsApp.lonk: onReconnectStop" ) ;
181- setWsError ( < > The WebSocket is disconnected.</ > ) ;
182- } ,
183- onWebsocketError : ( ) => {
184- console . error ( "PacsApp.lonk: onWebsocketError" ) ;
185- message . error (
186- < > There was an error with the WebSocket. Reconnecting…</ > ,
187- ) ;
188- } ,
189- } ) ;
190124
191125 // ========================================
192126 // CALLBACKS
@@ -216,35 +150,83 @@ export default () => {
216150 // ========================================
217151
218152 // init
219- // biome-ignore lint/correctness/useExhaustiveDependencies: doPacs.init
220153 useEffect ( ( ) => {
154+ doPacs . init ( pacsID ) ;
155+
156+ doPacs . updateServiceQueryBySearchParams ( pacsID , location , searchParams ) ;
157+ } , [
158+ pacsID ,
159+ doPacs . init ,
160+ doPacs . updateServiceQueryBySearchParams ,
161+ location ,
162+ searchParams ,
163+ ] ) ;
164+
165+ useEffect ( ( ) => {
166+ if ( ! pacsID ) {
167+ return ;
168+ }
169+
170+ if ( ! location . pathname . startsWith ( "/pacs" ) ) {
171+ return ;
172+ }
173+
221174 // set document title.
222175 const originalTitle = document . title ;
223176 document . title = "ChRIS PACS" ;
224177
225- // doPacs
226- doPacs . init ( ) ;
178+ doPacs . updateServiceQueryBySearchParams ( pacsID , location , searchParams ) ;
227179
228180 return ( ) => {
229181 document . title = originalTitle ;
230182 } ;
231- } , [ ] ) ;
183+ } , [
184+ pacsID ,
185+ location ,
186+ location . pathname ,
187+ searchParams ,
188+ doPacs . updateServiceQueryBySearchParams ,
189+ ] ) ;
232190
233191 // Subscribe to all expanded series
234192 // biome-ignore lint/correctness/useExhaustiveDependencies: updateReceiveState
235193 useEffect ( ( ) => {
236- for ( const { pacs_name, SeriesInstanceUID } of expandedSeries ) {
237- lonk
238- . subscribe ( pacs_name , SeriesInstanceUID )
239- . then ( ( { pacs_name, SeriesInstanceUID } ) => {
240- doPacs . updateReceiveState ( pacsID , pacs_name , SeriesInstanceUID , {
241- subscribed : true ,
242- } ) ;
243- } ) ;
194+ if ( wsError ) {
195+ return ;
244196 }
197+
198+ if ( isExpandedAllDone ) {
199+ return ;
200+ }
201+
202+ if ( ! expandedSeries . length ) {
203+ return ;
204+ }
205+
206+ const series_uids = expandedSeries
207+ . map ( ( each ) => each . SeriesInstanceUID )
208+ . join ( "," ) ;
209+
210+ const url = `${ config . API_ROOT } /pacs/sse/?pacs_name=${ service } &series_uids=${ series_uids } ` ;
211+ const eventSource = new EventSource ( url ) ;
212+
213+ eventSource . onmessage = ( event ) => {
214+ const data : Lonk < LonkMessageData > = JSON . parse ( event . data ) ;
215+ doPacs . processLonkMsg ( pacsID , data ) ;
216+ } ;
217+
218+ eventSource . onerror = ( err ) => {
219+ console . error ( "PacsApp.eventSource.onerror: err:" , err ) ;
220+ setWsError ( `event error: ${ err } ` ) ;
221+ } ;
222+
223+ return ( ) => {
224+ console . info ( "PacsApp.eventSource: to return" ) ;
225+ eventSource . close ( ) ;
226+ } ;
245227 // Note: we are subscribing to series, but never unsubscribing.
246228 // This is mostly harmless.
247- } , [ expandedSeries ] ) ;
229+ } , [ expandedSeries , wsError , isExpandedAllDone ] ) ;
248230
249231 // ========================================
250232 // RENDER
0 commit comments