Skip to content

Commit

Permalink
Switch to PF4 toast notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
rszwajko committed Dec 6, 2021
1 parent 6e3cdb4 commit 4e19d2a
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 22 deletions.
6 changes: 4 additions & 2 deletions src/actions/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function extractErrorText (exception: Object): string {
: (exception.statusText || 'UNKNOWN')
}

export function failedExternalAction ({ message, messageDescriptor, exception, failedAction }: FailedExternalActionInputType): FailedExternalActionType {
export function failedExternalAction ({ message, titleDescriptor, messageDescriptor, exception, failedAction }: FailedExternalActionInputType): FailedExternalActionType {
if (exception) {
message = message || extractErrorText(exception)
message = customizeErrorMessage(message)
Expand All @@ -25,6 +25,7 @@ export function failedExternalAction ({ message, messageDescriptor, exception, f
payload: {
message,
messageDescriptor,
titleDescriptor,
type,
failedAction,
},
Expand All @@ -34,8 +35,9 @@ export function failedExternalAction ({ message, messageDescriptor, exception, f
return {
type: FAILED_EXTERNAL_ACTION,
payload: {
messageDescriptor,
message,
messageDescriptor,
titleDescriptor,
failedAction,
},
}
Expand Down
2 changes: 2 additions & 0 deletions src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type MessageDescriptorType = {
export type FailedExternalActionInputType = {
message: string,
messageDescriptor: ?MessageDescriptorType,
titleDescriptor: ?MessageDescriptorType,
exception?: Object,
failedAction?: Object
}
Expand All @@ -48,6 +49,7 @@ export type FailedExternalActionType = {
message: string,
failedAction?: Object,
messageDescriptor: ?MessageDescriptorType,
titleDescriptor: ?MessageDescriptorType,
type?: number | 'ERROR'
}
}
62 changes: 47 additions & 15 deletions src/components/ToastNotifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,68 @@ import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'

import { TimedToastNotification, ToastNotificationList } from 'patternfly-react'
import { Alert, AlertGroup, AlertActionCloseButton } from '@patternfly/react-core'
import { setNotificationNotified } from '_/actions'
import { withMsg } from '_/intl'
import { buildMessageFromRecord } from '_/helpers'
import { buildMessageFromRecord, translate } from '_/helpers'

import style from './sharedStyle.css'

function normalizeType (theType) {
theType = String(theType).toLowerCase()
const isExpected = ['error', 'warning', 'success', 'info', 'danger'].includes(theType)
return isExpected ? theType : 'warning'
// PF4 statuses
if (['default', 'warning', 'success', 'info', 'danger'].includes(theType)) {
return theType
}

// 'error' (used in PF3) was replaced by 'danger'
return theType === 'error' ? 'danger' : 'warning'
}

function buildTitle ({ id } = {}, msg, type) {
if (!id) {
// no title provide - generate one based on type
return mapTypeToTitle(msg, type)
}
return translate({ id, msg })
}

function mapTypeToTitle (msg, type) {
switch (type) {
case 'warning':
return msg.warning()
case 'danger':
return msg.error()
case 'success':
return msg.success()
case 'info':
default:
return msg.info()
}
}

const ToastNotifications = ({ userMessages, onDismissNotification, msg }) => {
return (
<ToastNotificationList>
{ userMessages.get('records').filter(r => !r.get('notified')).map(r => (
<TimedToastNotification
<AlertGroup isToast isLiveRegion>
{ userMessages.get('records').toJS().filter(({ notified }) => !notified).map(r => (
<Alert
variant={normalizeType(r.type)}
className={style['toast-margin-top']}
type={normalizeType(r.get('type'))}
onDismiss={() => onDismissNotification(r.get('id'))}
key={r.get('time')}
title={buildTitle(r.titleDescriptor, msg, normalizeType(r.type))}
timeout={true}
onTimeout={() => onDismissNotification(r.id)}
actionClose={(
<AlertActionCloseButton
onClose={() => onDismissNotification(r.id)}
/>
)}
key={r.time}
>
<span>
{buildMessageFromRecord(r.toJS(), msg)}
</span>
</TimedToastNotification>
{buildMessageFromRecord(r, msg)}
</Alert>
)
)}
</ToastNotificationList>
</AlertGroup>
)
}

Expand Down
4 changes: 4 additions & 0 deletions src/intl/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ export const messages: { [messageId: string]: MessageType } = {
message: 'Not responding',
description: 'VM is not responding. One of states of a virtual machine. Other are e.g. Up, Down, Powering-Up',
},
error: 'Error',
errorWhileCreatingNewDisk: 'Error while creating new disk:',
every30Seconds: 'Every 30 seconds',
everyMinute: 'Every minute',
Expand Down Expand Up @@ -405,6 +406,7 @@ export const messages: { [messageId: string]: MessageType } = {
htmlUnsupportedOvirtVersionFoundButVersionAtLeastRequired: '<strong>Unsupported {version} {productName} version</strong> found, but at least version {requiredVersion} is required.',
icon: 'Icon',
ifVmIsRunningClickToAccessItsGraphicsConsole: 'If the virtual machine is running, click the protocol name to access its Graphical Console.',
info: 'Information',
inPreview: 'In Preview',
ieNotSupported: 'Internet Explorer is not a supported browser.',
ipAddress: { message: 'IP Address', description: 'Label for IP addresses reported by VM guest agent' },
Expand Down Expand Up @@ -649,6 +651,7 @@ export const messages: { [messageId: string]: MessageType } = {
message: '({size} {unit} free)',
description: 'Show the amount of free space a storage domain has when rendered in a select list of storage domains',
},
success: 'Success',
suspend: 'Suspend',
suspendVm: 'Suspend the VM',
suspendVmQuestion: 'Are you sure you want to Suspend the VM?',
Expand Down Expand Up @@ -783,6 +786,7 @@ export const messages: { [messageId: string]: MessageType } = {
vncOptions: 'VNC Options',
vnicProfile: 'VNIC Profile',
vnicProfileEmpty: '<Empty>',
warning: 'Warning',
yes: 'Yes',
youHaveNoAllowedVnicProfiles: 'You cannot create or edit NICs because you do not have permission to use any vNIC Profiles in the VM\'s Data Center.',
}
Expand Down
6 changes: 4 additions & 2 deletions src/reducers/userMessages.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ import uniqueId from 'lodash/uniqueId'

import type { FailedExternalActionType } from '_/actions/types'

function addLogEntry ({ state, message, type = 'ERROR', failedAction, messageDescriptor }: any): any {
function addLogEntry ({ state, message, type = 'ERROR', failedAction, messageDescriptor, titleDescriptor }: any): any {
// TODO: use seq
return state
.update('records', records => records.unshift(Immutable.fromJS({
id: uniqueId(),
message,
messageDescriptor,
titleDescriptor,
type,
failedAction,
time: Date.now(),
Expand All @@ -42,11 +43,12 @@ const initialState = Immutable.fromJS({

const userMessages: any = actionReducer(initialState, {
// Log external action failures (i.e. AJAX calls) as user messages
[FAILED_EXTERNAL_ACTION] (state: any, { payload: { message, messageDescriptor, type, failedAction } }: FailedExternalActionType): any {
[FAILED_EXTERNAL_ACTION] (state: any, { payload: { message, messageDescriptor, titleDescriptor, type, failedAction } }: FailedExternalActionType): any {
return addLogEntry({
state,
message,
messageDescriptor,
titleDescriptor,
type,
failedAction,
})
Expand Down
6 changes: 3 additions & 3 deletions src/sagas/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,15 @@ export function* callExternalAction (method, action = {}, canBeMissing = false)
yield put(checkTokenExpired())
}

let messageDescriptor = shortErrorMessage({ action })
let titleDescriptor = shortErrorMessage({ action })
if (e.status === 0 && e.statusText === 'error') { // special case, mixing https and http
messageDescriptor = { id: 'apiConnectionFailed' }
titleDescriptor = { id: 'apiConnectionFailed' }
e.statusText = 'Unable to connect to oVirt REST API. Please check URL and protocol (https).'
}

yield put(failedExternalAction({
exception: e,
messageDescriptor,
titleDescriptor,
failedAction: action,
}))
}
Expand Down

0 comments on commit 4e19d2a

Please sign in to comment.