@@ -39,12 +39,6 @@ const initialState: MainState = {
3939 reviews : [ ] ,
4040}
4141
42- const runningDeploymentStatus : DeploymentStatusEnum [ ] = [
43- DeploymentStatusEnum . Waiting ,
44- DeploymentStatusEnum . Created ,
45- DeploymentStatusEnum . Running ,
46- ]
47-
4842export const apiMiddleware : Middleware = ( api : MiddlewareAPI ) => (
4943 next
5044) => ( action ) => {
@@ -81,18 +75,28 @@ export const init = createAsyncThunk<User, void, { state: { main: MainState } }>
8175 }
8276)
8377
78+ /**
79+ * Search all processing deployments that the user can access.
80+ */
8481export const searchDeployments = createAsyncThunk < Deployment [ ] , void , { state : { main : MainState } } > (
8582 "main/searchDeployments" ,
8683 async ( _ , { rejectWithValue } ) => {
8784 try {
88- const deployments = await _searchDeployments ( runningDeploymentStatus , false )
85+ const deployments = await _searchDeployments ( [
86+ DeploymentStatusEnum . Waiting ,
87+ DeploymentStatusEnum . Created ,
88+ DeploymentStatusEnum . Running ,
89+ ] , false )
8990 return deployments
9091 } catch ( e ) {
9192 return rejectWithValue ( e )
9293 }
9394 }
9495)
9596
97+ /**
98+ * Search all reviews has requested.
99+ */
96100export const searchReviews = createAsyncThunk < Review [ ] , void , { state : { main : MainState } } > (
97101 "main/searchReviews" ,
98102 async ( _ , { rejectWithValue } ) => {
@@ -117,6 +121,87 @@ export const fetchLicense = createAsyncThunk<License, void, { state: { main: Mai
117121 }
118122)
119123
124+ const notify = ( title : string , options ?: NotificationOptions ) => {
125+ if ( ! ( "Notification" in window ) ) {
126+ console . log ( "This browser doesn't support the notification." )
127+ return
128+ }
129+
130+ if ( Notification . permission === "default" ) {
131+ Notification . requestPermission ( )
132+ }
133+
134+ new Notification ( title , options )
135+ }
136+
137+ /**
138+ * The browser notifies only the user who triggers the deployment.
139+ */
140+ export const notifyDeploymentEvent = createAsyncThunk < void , Event , { state : { main : MainState } } > (
141+ "main/notifyDeploymentEvent" ,
142+ async ( event , { getState } ) => {
143+ const { user } = getState ( ) . main
144+
145+ if ( event . kind !== EventKindEnum . Deployment ) {
146+ return
147+ }
148+
149+ if ( event . deployment ?. deployer ?. id !== user ?. id ) {
150+ return
151+ }
152+
153+ if ( event . type === EventTypeEnum . Created ) {
154+ notify ( `New Deployment #${ event . deployment ?. number } ` , {
155+ icon : "/logo192.png" ,
156+ body : `Start to deploy ${ event . deployment ?. ref . substring ( 0 , 7 ) } to the ${ event . deployment ?. env } environment of ${ event . deployment ?. repo ?. namespace } /${ event . deployment ?. repo ?. name } .` ,
157+ tag : String ( event . id ) ,
158+ } )
159+ return
160+ }
161+
162+ notify ( `Deployment Updated #${ event . deployment ?. number } ` , {
163+ icon : "/logo192.png" ,
164+ body : `The deployment ${ event . deployment ?. number } of ${ event . deployment ?. repo ?. namespace } /${ event . deployment ?. repo ?. name } is updated ${ event . deployment ?. status } .` ,
165+ tag : String ( event . id ) ,
166+ } )
167+ }
168+ )
169+
170+ /**
171+ * The browser notifies the requester when the review is responded to,
172+ * but it should notify the reviewer when the review is requested.
173+ */
174+ export const notifyReviewmentEvent = createAsyncThunk < void , Event , { state : { main : MainState } } > (
175+ "main/notifyReviewmentEvent" ,
176+ async ( event , { getState } ) => {
177+ const { user } = getState ( ) . main
178+ if ( event . kind !== EventKindEnum . Review ) {
179+ return
180+ }
181+
182+ if ( event . type === EventTypeEnum . Created
183+ && event . review ?. user ?. id === user ?. id ) {
184+ notify ( `Review Requested` , {
185+ icon : "/logo192.png" ,
186+ body : `${ event . review ?. deployment ?. deployer ?. login } requested the review for the deployment ${ event . review ?. deployment ?. number } of ${ event . review ?. deployment ?. repo ?. namespace } /${ event . review ?. deployment ?. repo ?. name } ` ,
187+ tag : String ( event . id ) ,
188+ } )
189+ return
190+ }
191+
192+ if ( event . type === EventTypeEnum . Updated
193+ && event . review ?. deployment ?. deployer ?. id === user ?. id ) {
194+ notify ( `Review Responded` , {
195+ icon : "/logo192.png" ,
196+ body : `${ event . review ?. user ?. login } ${ event . review ?. status } the deployment ${ event . review ?. deployment ?. number } of ${ event . review ?. deployment ?. repo ?. namespace } /${ event . review ?. deployment ?. repo ?. name } ` ,
197+ tag : String ( event . id ) ,
198+ } )
199+ return
200+ }
201+ }
202+ )
203+
204+
120205export const mainSlice = createSlice ( {
121206 name : "main" ,
122207 initialState,
@@ -130,54 +215,48 @@ export const mainSlice = createSlice({
130215 setExpired : ( state , action : PayloadAction < boolean > ) => {
131216 state . expired = action . payload
132217 } ,
218+ /**
219+ * Handle all deployment events that the user can access.
220+ * Note that some deployments are triggered by others.
221+ */
133222 handleDeploymentEvent : ( state , action : PayloadAction < Event > ) => {
134- const user = state . user
135- if ( ! user ) {
136- throw new Error ( "Unauthorized user." )
137- }
138-
139223 const event = action . payload
224+ if ( event . kind !== EventKindEnum . Deployment ) {
225+ return
226+ }
140227
141- // Handling the event when the owner is same.
142- if ( event . deployment ?. deployer ?. id !== user . id ) {
228+ if ( event . type === EventTypeEnum . Created
229+ && event . deployment ) {
230+ state . deployments . unshift ( event . deployment )
143231 return
144232 }
145233
234+ // Update the deployment if it exist.
146235 const idx = state . deployments . findIndex ( ( deployment ) => {
147236 return event . deployment ?. id === deployment . id
148237 } )
149238
150239 if ( idx !== - 1 ) {
151- // Remove from the list when the status is not one of 'waiting', 'created', and 'running'.
152- if ( ! runningDeploymentStatus . includes ( event . deployment . status ) ) {
240+ if ( ! ( event . deployment ?. status === DeploymentStatusEnum . Waiting
241+ || event . deployment ?. status === DeploymentStatusEnum . Created
242+ || event . deployment ?. status === DeploymentStatusEnum . Running ) ) {
153243 state . deployments . splice ( idx , 1 )
154244 return
155245 }
156246
157247 state . deployments [ idx ] = event . deployment
158248 return
159249 }
160-
161- state . deployments . unshift ( event . deployment )
162250 } ,
163251 handleReviewEvent : ( state , action : PayloadAction < Event > ) => {
164252 const event = action . payload
165-
166253 if ( action . payload . kind !== EventKindEnum . Review ) {
167254 return
168- }
255+ }
169256
170- const user = state . user
171- if ( ! user ) {
172- throw new Error ( "Unauthorized user." )
173- }
174-
175- // Handling the event when the user own the event.
176- if ( event . review ?. user ?. id !== user . id ) {
177- return
178- }
179-
180- if ( event . type === EventTypeEnum . Created ) {
257+ if ( event . type === EventTypeEnum . Created
258+ && event . review
259+ && event . review ?. user ?. id === state . user ?. id ) {
181260 state . reviews . unshift ( event . review )
182261 return
183262 }
0 commit comments