KEYCLOAK-11539: Rewrite Device Activity page

This commit is contained in:
Stan Silvert 2019-10-18 14:19:24 -04:00
parent 9a93e5028c
commit 787386fc21
2 changed files with 20 additions and 116 deletions

View file

@ -26,8 +26,6 @@ import {
DataListItem, DataListItem,
DataListItemRow, DataListItemRow,
DataListCell, DataListCell,
DataListToggle,
DataListContent,
DataListItemCells, DataListItemCells,
Grid, Grid,
GridItem, GridItem,
@ -44,11 +42,8 @@ import {
FirefoxIcon, FirefoxIcon,
GlobeIcon, GlobeIcon,
InternetExplorerIcon, InternetExplorerIcon,
LaptopIcon,
MobileAltIcon,
OperaIcon, OperaIcon,
SafariIcon, SafariIcon,
TabletAltIcon,
YandexInternationalIcon, YandexInternationalIcon,
} from '@patternfly/react-icons'; } from '@patternfly/react-icons';
@ -64,7 +59,6 @@ export interface DeviceActivityPageProps {
} }
export interface DeviceActivityPageState { export interface DeviceActivityPageState {
isRowOpen: boolean[];
devices: Device[]; devices: Device[];
} }
@ -98,7 +92,6 @@ interface Client {
/** /**
* @author Stan Silvert ssilvert@redhat.com (C) 2019 Red Hat Inc. * @author Stan Silvert ssilvert@redhat.com (C) 2019 Red Hat Inc.
*
*/ */
export class DeviceActivityPage extends React.Component<DeviceActivityPageProps, DeviceActivityPageState> { export class DeviceActivityPage extends React.Component<DeviceActivityPageProps, DeviceActivityPageState> {
@ -106,19 +99,12 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
super(props); super(props);
this.state = { this.state = {
isRowOpen: [],
devices: [] devices: []
}; };
this.fetchDevices(); this.fetchDevices();
} }
private onToggle = (row: number): void => {
const newIsRowOpen: boolean[] = this.state.isRowOpen;
newIsRowOpen[row] = !newIsRowOpen[row];
this.setState({isRowOpen: newIsRowOpen});
};
private signOutAll = () => { private signOutAll = () => {
AccountServiceClient.Instance.doDelete("/sessions") AccountServiceClient.Instance.doDelete("/sessions")
.then( (response: AxiosResponse<Object>) => { .then( (response: AxiosResponse<Object>) => {
@ -135,8 +121,6 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
} }
private fetchDevices(): void { private fetchDevices(): void {
const sessionsOnOpenRow: string[] = this.findSessionsOnOpenRow();
AccountServiceClient.Instance.doGet("/sessions/devices") AccountServiceClient.Instance.doGet("/sessions/devices")
.then((response: AxiosResponse<Object>) => { .then((response: AxiosResponse<Object>) => {
console.log({response}); console.log({response});
@ -145,43 +129,12 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
devices = this.moveCurrentToTop(devices); devices = this.moveCurrentToTop(devices);
this.setState({ this.setState({
isRowOpen: this.openRows(devices, sessionsOnOpenRow),
devices: devices devices: devices
}); });
}); });
} }
// calculating open rows this way assures that when a session is signed out
// or on refresh, the open rows stay open
private findSessionsOnOpenRow() : string[] {
let sessionsOnOpenRow: string[] = [];
this.state.devices.forEach( (device: Device, index: number) => {
if (this.state.isRowOpen[index]) {
device.sessions.forEach( (session: Session) => {
sessionsOnOpenRow.push(session.id);
})
}
});
return sessionsOnOpenRow;
}
private openRows(devices: Device[], sessionsOnOpenRow: string[]): boolean[] {
const openRows: boolean[] = new Array<boolean>().fill(false);
devices.forEach((device: Device, deviceIndex: number) => {
device.sessions.forEach( (session: Session) => {
if (sessionsOnOpenRow.includes(session.id)) {
openRows[deviceIndex] = true;
}
})
});
return openRows;
}
// current device and session should display at the top of their respective lists // current device and session should display at the top of their respective lists
private moveCurrentToTop(devices: Device[]): Device[] { private moveCurrentToTop(devices: Device[]): Device[] {
let currentDevice: Device = devices[0]; let currentDevice: Device = devices[0];
@ -208,32 +161,6 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
return moment(time * 1000).format('LLLL'); return moment(time * 1000).format('LLLL');
} }
private findDeviceName(device: Device): string {
let deviceName: string = device.device;
const deviceNameLower = deviceName.toLowerCase();
if (deviceNameLower.includes('other') || deviceNameLower.includes('generic') ) {
deviceName = this.findOS(device) + ' ' + this.findOSVersion(device);
}
if (deviceName.trim() === 'Other') {
deviceName = this.makeClientsString(device.sessions[0].clients);
}
return deviceName;
}
private findDeviceIcon(device: Device): React.ReactNode {
const deviceName = device.device.toLowerCase();
if (deviceName.includes('kindle') || deviceName.includes('ipad')) {
return (<TabletAltIcon size='lg' />);
}
if (device.mobile) return (<MobileAltIcon size='lg' />);
return (<LaptopIcon size='lg'/>);
}
private findBrowserIcon(session: Session): React.ReactNode { private findBrowserIcon(session: Session): React.ReactNode {
const browserName: string = session.browser.toLowerCase(); const browserName: string = session.browser.toLowerCase();
if (browserName.includes("chrom")) return (<ChromeIcon size='lg'/>); // chrome or chromium if (browserName.includes("chrom")) return (<ChromeIcon size='lg'/>); // chrome or chromium
@ -318,41 +245,17 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
/> />
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
{this.state.devices.map((device: Device, deviceIndex: number) => { <DataListItem aria-labelledby='sessions'>
return ( <Grid gutter='sm'>
<DataListItem key={'device-' + deviceIndex} aria-labelledby="ex-item1" isExpanded={this.state.isRowOpen[deviceIndex]}> <GridItem span={12}/> {/* <-- top spacing */}
<DataListItemRow onClick={() => this.onToggle(deviceIndex)}> {this.state.devices.map((device: Device, deviceIndex: number) => {
<DataListToggle return (
isExpanded={this.state.isRowOpen[deviceIndex]} <React.Fragment>
id={'deviceToggle-' + deviceIndex}
aria-controls="ex-expand1"
/>
<DataListItemCells
dataListCells={[
<DataListCell isIcon key={"icon-" + deviceIndex} width={1}>
{this.findDeviceIcon(device)}
</DataListCell>,
<DataListCell key={"os-" + deviceIndex} width={1}>
<strong>{this.findDeviceName(device)}</strong>
</DataListCell>,
<DataListCell key={"lastAccess-" + deviceIndex} width={5}>
{device.current && <p><span className='pf-c-badge pf-m-read'><strong><Msg msgKey="currentDevice"/></strong></span></p>}
{!device.current && <p><strong><Msg msgKey="lastAccess"/>: </strong><span>{this.time(device.lastAccess)}</span></p>}
</DataListCell>
]}
/>
</DataListItemRow>
<DataListContent
noPadding={false}
aria-label="Session Details"
id="ex-expand1"
isHidden={!this.state.isRowOpen[deviceIndex]}
>
<Grid gutter='sm'>
{device.sessions.map((session: Session, sessionIndex: number) => { {device.sessions.map((session: Session, sessionIndex: number) => {
return ( return (
<React.Fragment key={'device-' + deviceIndex + '-session-' + sessionIndex}> <React.Fragment key={'device-' + deviceIndex + '-session-' + sessionIndex}>
<GridItem span={3}> <GridItem span={3}>
<Stack> <Stack>
<StackItem isFilled={false}> <StackItem isFilled={false}>
@ -392,14 +295,15 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
</GridItem> </GridItem>
</React.Fragment> </React.Fragment>
)}) );
}
})}
</Grid> </React.Fragment>
</DataListContent> )
</DataListItem> })}
)})} <GridItem span={12}/> {/* <-- bottom spacing */}
</Grid>
</DataListItem>
</DataList> </DataList>
</StackItem> </StackItem>

View file

@ -949,7 +949,7 @@
'./icons/landmark-icon.js': '@empty', './icons/landmark-icon.js': '@empty',
'./icons/language-icon.js': '@empty', './icons/language-icon.js': '@empty',
'./icons/laptop-code-icon.js': '@empty', './icons/laptop-code-icon.js': '@empty',
//'./icons/laptop-icon.js': '@empty', './icons/laptop-icon.js': '@empty',
'./icons/laptop-medical-icon.js': '@empty', './icons/laptop-medical-icon.js': '@empty',
'./icons/laravel-icon.js': '@empty', './icons/laravel-icon.js': '@empty',
'./icons/lastfm-icon.js': '@empty', './icons/lastfm-icon.js': '@empty',
@ -1050,7 +1050,7 @@
'./icons/mix-icon.js': '@empty', './icons/mix-icon.js': '@empty',
'./icons/mixcloud-icon.js': '@empty', './icons/mixcloud-icon.js': '@empty',
'./icons/mizuni-icon.js': '@empty', './icons/mizuni-icon.js': '@empty',
//'./icons/mobile-alt-icon.js': '@empty', './icons/mobile-alt-icon.js': '@empty',
'./icons/mobile-icon.js': '@empty', './icons/mobile-icon.js': '@empty',
'./icons/modx-icon.js': '@empty', './icons/modx-icon.js': '@empty',
'./icons/monero-icon.js': '@empty', './icons/monero-icon.js': '@empty',
@ -1603,7 +1603,7 @@
'./icons/syringe-icon.js': '@empty', './icons/syringe-icon.js': '@empty',
'./icons/table-icon.js': '@empty', './icons/table-icon.js': '@empty',
'./icons/table-tennis-icon.js': '@empty', './icons/table-tennis-icon.js': '@empty',
//'./icons/tablet-alt-icon.js': '@empty', './icons/tablet-alt-icon.js': '@empty',
'./icons/tablet-icon.js': '@empty', './icons/tablet-icon.js': '@empty',
'./icons/tablets-icon.js': '@empty', './icons/tablets-icon.js': '@empty',
'./icons/tachometer-alt-icon.js': '@empty', './icons/tachometer-alt-icon.js': '@empty',