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 4, 2020
1 parent dfd5706 commit 13e9bc2
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 5 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
2 changes: 1 addition & 1 deletion src/components/UserSettings/GlobalSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class GlobalSettings extends Component {
},
{
id: Number.MAX_SAFE_INTEGER,
value: msg.sessionDuration(),
value: msg.untilNextPageReload(),
},
]

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
7 changes: 5 additions & 2 deletions src/intl/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ export const messages: { [messageId: string]: MessageType } = {
},
notEditableForPoolsOrPoolVms: 'Not editable for Pools or pool VMs.',
notifications: 'Notifications',
notificationSettingsAffectAllNotifications: 'Notification settings applied here affect all notifications (including VMs and Pools notifications).',
notificationSettingsAffectAllNotifications: 'Notification settings applied here affect all notifications (including all VMs and Pools notifications) and are not persisted (a page reload will clear them).',
noVmAvailable: 'No VM available.',
noVmAvailableForLoggedUser: 'No VM is available for the logged user.',
off: 'Off',
Expand Down Expand Up @@ -557,7 +557,6 @@ export const messages: { [messageId: string]: MessageType } = {
message: 'Your session is about to timeout due to inactivity.',
description: 'Primary message for SessionTimeout modal component',
},
sessionDuration: 'session duration',
shutdown: 'Shutdown',
shutdownStatelessPoolVm: 'This virtual machine belongs to {poolName} and is stateless so any data that is currently attached to the virtual machine will be lost if it is shutdown. The virtual machine will be returned to {poolName} if shutdown.',
shutdownVm: 'Shutdown the VM',
Expand Down Expand Up @@ -637,6 +636,10 @@ export const messages: { [messageId: string]: MessageType } = {
message: 'Dialog contains unsaved changes',
description: 'Title of modal dialog opened when a user tried to navigate off an editor page after changes have been made.',
},
untilNextPageReload: {
message: 'until next page reload',
description: 'Phrase used to describe a time period. Will be used in a dropdown (labeled "Do not disturb for") as one of the possible durations (together with typical durations like 1 hour or 1 day).',
},
updateCloudInit: 'Do you want update Cloud-init hostname on new VM name?',
updateVm: 'Update VM',
upload: 'Upload',
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 13e9bc2

Please sign in to comment.