Skip to content

Commit

Permalink
Allow blocking toast notifications ('do not disturb')
Browse files Browse the repository at this point in the history
Allow user to block system notifications via Account Settings:
1. each change to the snooze duration resets the snooze timer
2. notification settings are not persisted - page reload clears them.
3. only displaying is blocked - logs continue to be accumulated in the
   store (userMessages -> records)
  • Loading branch information
rszwajko committed Dec 2, 2020
1 parent 31552e4 commit 098dba8
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 2 deletions.
15 changes: 15 additions & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import {
SET_WEBSOCKET,
SHOW_TOKEN_EXPIRED_MSG,
START_SCHEDULER_FIXED_DELAY,
START_SCHEDULER_FOR_RESUMING_NOTIFICATIONS,
STOP_SCHEDULER_FIXED_DELAY,
STOP_SCHEDULER_FOR_RESUMING_NOTIFICATIONS,
UPDATE_PAGING_DATA,
} from '_/constants'

Expand Down Expand Up @@ -69,6 +71,19 @@ export function stopSchedulerFixedDelay () {
return { type: STOP_SCHEDULER_FIXED_DELAY }
}

export function startSchedulerForResumingNotifications (delayInSeconds) {
return {
type: START_SCHEDULER_FOR_RESUMING_NOTIFICATIONS,
payload: {
delayInSeconds,
},
}
}

export function stopSchedulerForResumingNotifications () {
return { type: STOP_SCHEDULER_FOR_RESUMING_NOTIFICATIONS }
}

export function setUserFilterPermission (filter) {
return {
type: SET_USER_FILTER_PERMISSION,
Expand Down
10 changes: 10 additions & 0 deletions src/actions/userMessages.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ADD_USER_MESSAGE,
AUTO_ACKNOWLEDGE,
CLEAR_USER_MSGS,
DISMISS_EVENT,
DISMISS_USER_MSG,
Expand All @@ -23,6 +24,15 @@ export function clearUserMessages () {
return { type: CLEAR_USER_MSGS }
}

export function setAutoAcknowledge (autoAcknowledge) {
return {
type: AUTO_ACKNOWLEDGE,
payload: {
autoAcknowledge,
},
}
}

export function setNotificationNotified ({ eventId }) {
return {
type: SET_USERMSG_NOTIFIED,
Expand Down
3 changes: 3 additions & 0 deletions src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const ADD_SNAPSHOT_RESTORE_PENDING_TASK = 'ADD_SNAPSHOT_RESTORE_PENDING_T
export const ADD_VM_NIC = 'ADD_VM_NIC'
export const ADD_USER_MESSAGE = 'ADD_USER_MESSAGE'
export const APP_CONFIGURED = 'APP_CONFIGURED'
export const AUTO_ACKNOWLEDGE = 'AUTO_ACKNOWLEDGE'
export const CHANGE_PAGE = 'CHANGE_PAGE'
export const CHANGE_VM_CDROM = 'CHANGE_VM_CDROM'
export const CHECK_CONSOLE_IN_USE = 'CHECK_CONSOLE_IN_USE'
Expand Down Expand Up @@ -125,8 +126,10 @@ export const NAVIGATE_TO_VM_DETAILS = 'NAVIGATE_TO_VM_DETAILS'
export const SHUTDOWN_VM = 'SHUTDOWN_VM'
export const START_POOL = 'START_POOL'
export const START_SCHEDULER_FIXED_DELAY = 'START_SCHEDULER_FIXED_DELAY'
export const START_SCHEDULER_FOR_RESUMING_NOTIFICATIONS = 'START_SCHEDULER_FOR_RESUMING_NOTIFICATIONS'
export const START_VM = 'START_VM'
export const STOP_SCHEDULER_FIXED_DELAY = 'STOP_SCHEDULER_FIXED_DELAY'
export const STOP_SCHEDULER_FOR_RESUMING_NOTIFICATIONS = 'STOP_SCHEDULER_FOR_RESUMING_NOTIFICATIONS'
export const SUSPEND_VM = 'SUSPEND_VM'
export const UPDATE_ICONS = 'UPDATE_ICONS'
export const UPDATE_PAGING_DATA = 'UPDATE_PAGING_DATA'
Expand Down
9 changes: 8 additions & 1 deletion src/reducers/userMessages.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Immutable from 'immutable'
import {
ADD_USER_MESSAGE,
AUTO_ACKNOWLEDGE,
DISMISS_USER_MSG,
FAILED_EXTERNAL_ACTION,
LOGIN_FAILED,
Expand All @@ -23,13 +24,14 @@ function addLogEntry ({ state, message, type = 'ERROR', failedAction }) {
type,
failedAction,
time: Date.now(),
notified: false,
notified: state.get('autoAcknowledge'),
source: 'local',
})))
}

const initialState = Immutable.fromJS({
records: [],
autoAcknowledge: false,
})

const userMessages = actionReducer(initialState, {
Expand Down Expand Up @@ -75,6 +77,11 @@ const userMessages = actionReducer(initialState, {
[DISMISS_USER_MSG] (state, { payload: { eventId } }) {
return state.update('records', records => records.delete(state.get('records').findIndex(r => r.get('id') === eventId)))
},

[AUTO_ACKNOWLEDGE] (state, { payload: { autoAcknowledge = false } }) {
return state.set('autoAcknowledge', autoAcknowledge)
},

})

export default userMessages
16 changes: 16 additions & 0 deletions src/sagas/background-refresh.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
resumeNotifications,
loadUserOptions,
} from './options'

Expand Down Expand Up @@ -263,6 +264,20 @@ function* schedulerWithFixedDelay ({
console.log(`⏰ schedulerWithFixedDelay[${myId}] 🡒 running after delay of: ${delayInSeconds}`)
}
}
let _SchedulerForNotificationsCount = 0
function* scheduleResumingNotifications ({ payload: { delayInSeconds } }) {
yield put(Actions.stopSchedulerForResumingNotifications())
const myId = _SchedulerForNotificationsCount++
console.log(`notification timer [${myId}] - delay [${delayInSeconds}] sec`)
const { stopped } = yield call(schedulerWaitFor, delayInSeconds, C.STOP_SCHEDULER_FOR_RESUMING_NOTIFICATIONS)
if (stopped) {
console.log(`notification timer [${myId}] - stopped`)
} else {
console.log(`notification timer [${myId}] - resume notifications`)
yield call(resumeNotifications)
}
}

/**
* When ovirt-web-ui is installed to ovirt-engine, a logout should push the user to the
* base ovirt welcome page. But when running in dev mode or via container, the logout
Expand All @@ -280,4 +295,5 @@ export default [
throttle(5000, C.REFRESH_DATA, refreshData),
takeLatest(C.CHANGE_PAGE, changePage),
takeEvery(C.LOGOUT, logoutAndCancelScheduler),
takeEvery(C.START_SCHEDULER_FOR_RESUMING_NOTIFICATIONS, scheduleResumingNotifications),
]
39 changes: 38 additions & 1 deletion src/sagas/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,16 @@ function* saveGlobalOptions ({ payload: { sshKey, showNotifications, notificatio
}

if (showNotifications !== undefined || notificationSnoozeDuration !== undefined) {

yield call(
updateNotifications,
{
current: yield select((state) => state.options.getIn(['global', 'showNotifications'])),
next: showNotifications,
},
{
current: yield select((state) => state.options.getIn(['global', 'notificationSnoozeDuration'])),
next: notificationSnoozeDuration,
})
}

yield put(
Expand All @@ -105,6 +114,34 @@ function* saveGlobalOptions ({ payload: { sshKey, showNotifications, notificatio
)
}

function* updateNotifications (show: {current: boolean, next?: boolean}, snooze: {current: number, next?: number}): any {
const snoozeDuration = snooze.next || snooze.current
const showNotifications = show.next === undefined ? show.current : show.next
const snoozeUntilPageReload = snoozeDuration === Number.MAX_SAFE_INTEGER

yield put(A.setOption({ key: ['global', 'showNotifications'], value: showNotifications }))
yield put(A.setOption({ key: ['global', 'notificationSnoozeDuration'], value: snoozeDuration }))
yield put(A.setAutoAcknowledge(!showNotifications))
if (showNotifications || snoozeUntilPageReload) {
yield put(A.stopSchedulerForResumingNotifications())
} else {
// minutes -> seconds
yield put(A.startSchedulerForResumingNotifications(snoozeDuration * 60))
}
}

export function* resumeNotifications (): any {
yield call(
updateNotifications,
{
current: yield select((state) => state.options.getIn(['global', 'showNotifications'])),
next: true,
},
{
current: yield select((state) => state.options.getIn(['global', 'notificationSnoozeDuration'])),
})
}

export function* loadUserOptions (): any {
const userId = yield select(state => state.config.getIn(['user', 'id']))
yield put(A.getSSHKey({ userId }))
Expand Down

0 comments on commit 098dba8

Please sign in to comment.