Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.

Commit 710dd74

Browse files
author
Noah Lee
authored
Provide the browser notification for events (#268)
* Fix to handle all deployment event * Add the browser notification for the event.
1 parent 650a464 commit 710dd74

File tree

2 files changed

+113
-32
lines changed

2 files changed

+113
-32
lines changed

Diff for: ui/src/redux/main.ts

+110-31
Original file line numberDiff line numberDiff line change
@@ -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-
4842
export 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+
*/
8481
export 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+
*/
96100
export 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+
120205
export 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
}

Diff for: ui/src/views/Main.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { SettingFilled } from "@ant-design/icons"
55
import { Helmet } from "react-helmet"
66

77
import { useAppSelector, useAppDispatch } from "../redux/hooks"
8-
import { init, searchDeployments, searchReviews, fetchLicense, mainSlice as slice } from "../redux/main"
8+
import { init, searchDeployments, searchReviews, fetchLicense, notifyDeploymentEvent, notifyReviewmentEvent, mainSlice as slice } from "../redux/main"
99
import { subscribeEvents } from "../apis"
1010

1111
import RecentActivities from "../components/RecentActivities"
@@ -37,6 +37,8 @@ export default function Main(props: any) {
3737
const sub = subscribeEvents((event) => {
3838
dispatch(slice.actions.handleDeploymentEvent(event))
3939
dispatch(slice.actions.handleReviewEvent(event))
40+
dispatch(notifyDeploymentEvent(event))
41+
dispatch(notifyReviewmentEvent(event))
4042
})
4143

4244
return () => {

0 commit comments

Comments
 (0)