1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-11-23 11:02:35 +03:00

DM: Device type icon (#1783)

This commit is contained in:
Kerry
2023-10-06 22:24:04 +13:00
committed by GitHub
parent ddd1041985
commit f7e12e7301
17 changed files with 1016 additions and 380 deletions

View File

@@ -124,6 +124,7 @@ const BrowserSession: React.FC<Props> = ({ session }) => {
createdAt={createdAt} createdAt={createdAt}
finishedAt={finishedAt} finishedAt={finishedAt}
isCurrent={isCurrent} isCurrent={isCurrent}
deviceType={deviceInformation?.deviceType}
lastActiveIp={data.lastActiveIp || undefined} lastActiveIp={data.lastActiveIp || undefined}
lastActiveAt={lastActiveAt} lastActiveAt={lastActiveAt}
> >

View File

@@ -18,6 +18,7 @@ import { create } from "react-test-renderer";
import { describe, expect, it, beforeAll } from "vitest"; import { describe, expect, it, beforeAll } from "vitest";
import { makeFragmentData } from "../gql"; import { makeFragmentData } from "../gql";
import { Oauth2ApplicationType } from "../gql/graphql";
import { WithLocation } from "../test-utils/WithLocation"; import { WithLocation } from "../test-utils/WithLocation";
import { mockLocale } from "../test-utils/mockLocale"; import { mockLocale } from "../test-utils/mockLocale";
@@ -35,6 +36,7 @@ describe("<OAuth2Session />", () => {
clientId: "test-client-id", clientId: "test-client-id",
clientName: "Element", clientName: "Element",
clientUri: "https://element.io", clientUri: "https://element.io",
applicationType: Oauth2ApplicationType.Web,
}, },
}; };
@@ -68,4 +70,24 @@ describe("<OAuth2Session />", () => {
); );
expect(component.toJSON()).toMatchSnapshot(); expect(component.toJSON()).toMatchSnapshot();
}); });
it("renders correct icon for a native session", () => {
const session = makeFragmentData(
{
...defaultSession,
finishedAt,
client: {
...defaultSession.client,
applicationType: Oauth2ApplicationType.Native,
},
},
FRAGMENT,
);
const component = create(
<WithLocation>
<OAuth2Session session={session} />
</WithLocation>,
);
expect(component.toJSON()).toMatchSnapshot();
});
}); });

View File

@@ -18,8 +18,10 @@ import { atomFamily } from "jotai/utils";
import { atomWithMutation } from "jotai-urql"; import { atomWithMutation } from "jotai-urql";
import { FragmentType, graphql, useFragment } from "../gql"; import { FragmentType, graphql, useFragment } from "../gql";
import { Oauth2ApplicationType } from "../gql/graphql";
import { Link } from "../routing"; import { Link } from "../routing";
import { getDeviceIdFromScope } from "../utils/deviceIdFromScope"; import { getDeviceIdFromScope } from "../utils/deviceIdFromScope";
import { DeviceType } from "../utils/parseUserAgent";
import { Session } from "./Session"; import { Session } from "./Session";
import EndSessionButton from "./Session/EndSessionButton"; import EndSessionButton from "./Session/EndSessionButton";
@@ -36,6 +38,7 @@ export const FRAGMENT = graphql(/* GraphQL */ `
id id
clientId clientId
clientName clientName
applicationType
logoUri logoUri
} }
} }
@@ -53,6 +56,18 @@ const END_SESSION_MUTATION = graphql(/* GraphQL */ `
} }
`); `);
const getDeviceTypeFromClientAppType = (
appType?: Oauth2ApplicationType | null,
): DeviceType => {
if (appType === Oauth2ApplicationType.Web) {
return DeviceType.Web;
}
if (appType === Oauth2ApplicationType.Native) {
return DeviceType.Mobile;
}
return DeviceType.Unknown;
};
export const endSessionFamily = atomFamily((id: string) => { export const endSessionFamily = atomFamily((id: string) => {
const endSession = atomWithMutation(END_SESSION_MUTATION); const endSession = atomWithMutation(END_SESSION_MUTATION);
@@ -89,6 +104,10 @@ const OAuth2Session: React.FC<Props> = ({ session }) => {
? parseISO(data.lastActiveAt) ? parseISO(data.lastActiveAt)
: undefined; : undefined;
const deviceType = getDeviceTypeFromClientAppType(
data.client.applicationType,
);
return ( return (
<Session <Session
id={data.id} id={data.id}
@@ -97,6 +116,7 @@ const OAuth2Session: React.FC<Props> = ({ session }) => {
finishedAt={finishedAt} finishedAt={finishedAt}
clientName={data.client.clientName || data.client.clientId || undefined} clientName={data.client.clientName || data.client.clientId || undefined}
clientLogoUri={data.client.logoUri || undefined} clientLogoUri={data.client.logoUri || undefined}
deviceType={deviceType}
lastActiveIp={data.lastActiveIp || undefined} lastActiveIp={data.lastActiveIp || undefined}
lastActiveAt={lastActiveAt} lastActiveAt={lastActiveAt}
> >

View File

@@ -0,0 +1,25 @@
/* Copyright 2023 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.icon {
color: var(--cpd-color-icon-primary);
background-color: var(--cpd-color-bg-subtle-primary);
flex: 0 0 var(--cpd-space-4x);
box-sizing: content-box;
height: var(--cpd-space-4x);
width: var(--cpd-space-4x);
padding: var(--cpd-space-2x);
border-radius: var(--cpd-space-2x);
}

View File

@@ -0,0 +1,60 @@
// Copyright 2022 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import type { Meta, StoryObj } from "@storybook/react";
import { DeviceType } from "../../utils/parseUserAgent";
import DeviceTypeIcon from "./DeviceTypeIcon";
const meta = {
title: "UI/Session/Device Type Icon",
component: DeviceTypeIcon,
tags: ["autodocs"],
args: {
deviceType: DeviceType.Unknown,
},
argTypes: {
deviceType: {
control: "select",
options: [
DeviceType.Unknown,
DeviceType.Desktop,
DeviceType.Mobile,
DeviceType.Web,
],
},
},
} satisfies Meta<typeof DeviceTypeIcon>;
export default meta;
type Story = StoryObj<typeof DeviceTypeIcon>;
export const Unknown: Story = {};
export const Desktop: Story = {
args: {
deviceType: DeviceType.Desktop,
},
};
export const Mobile: Story = {
args: {
deviceType: DeviceType.Mobile,
},
};
export const Web: Story = {
args: {
deviceType: DeviceType.Web,
},
};

View File

@@ -0,0 +1,46 @@
// Copyright 2023 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// @vitest-environment happy-dom
import { composeStory } from "@storybook/react";
import { render, cleanup } from "@testing-library/react";
import { describe, it, expect, afterEach } from "vitest";
import Meta, { Unknown, Desktop, Mobile, Web } from "./DeviceTypeIcon.stories";
describe("<DeviceTypeIcon />", () => {
afterEach(cleanup);
it("renders unknown device type", () => {
const Component = composeStory(Unknown, Meta);
const { container } = render(<Component />);
expect(container).toMatchSnapshot();
});
it("renders mobile device type", () => {
const Component = composeStory(Mobile, Meta);
const { container } = render(<Component />);
expect(container).toMatchSnapshot();
});
it("renders desktop device type", () => {
const Component = composeStory(Desktop, Meta);
const { container } = render(<Component />);
expect(container).toMatchSnapshot();
});
it("renders Web device type", () => {
const Component = composeStory(Web, Meta);
const { container } = render(<Component />);
expect(container).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,51 @@
// Copyright 2023 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import IconComputer from "@vector-im/compound-design-tokens/icons/computer.svg?react";
import IconMobile from "@vector-im/compound-design-tokens/icons/mobile.svg?react";
import IconUnknown from "@vector-im/compound-design-tokens/icons/unknown.svg?react";
import IconBrowser from "@vector-im/compound-design-tokens/icons/web-browser.svg?react";
import { FunctionComponent, SVGProps } from "react";
import { DeviceType } from "../../utils/parseUserAgent";
import styles from "./DeviceTypeIcon.module.css";
const deviceTypeToIcon: Record<
DeviceType,
FunctionComponent<SVGProps<SVGSVGElement> & { title?: string | undefined }>
> = {
[DeviceType.Unknown]: IconUnknown,
[DeviceType.Desktop]: IconComputer,
[DeviceType.Mobile]: IconMobile,
[DeviceType.Web]: IconBrowser,
};
const deviceTypeToLabel: Record<DeviceType, string> = {
[DeviceType.Unknown]: "Unknown device type",
[DeviceType.Desktop]: "Desktop",
[DeviceType.Mobile]: "Mobile",
[DeviceType.Web]: "Web",
};
const DeviceTypeIcon: React.FC<{ deviceType: DeviceType }> = ({
deviceType,
}) => {
const Icon = deviceTypeToIcon[deviceType];
const label = deviceTypeToLabel[deviceType];
return <Icon className={styles.icon} aria-label={label} />;
};
export default DeviceTypeIcon;

View File

@@ -13,10 +13,24 @@
* limitations under the License. * limitations under the License.
*/ */
.session {
display: flex;
flex-direction: row;
justify-content: flex-start;
gap:var(--cpd-space-4x);
}
.container {
display: flex;
flex: 1 1 100%;
flex-direction: column;
align-items: flex-start;
}
.session-name { .session-name {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
margin-top: var(--cpd-space-2x); margin-top: var(--cpd-space-1x);
margin-bottom: var(--cpd-space-1x); margin-bottom: var(--cpd-space-1x);
} }
@@ -37,4 +51,5 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-end; justify-content: flex-end;
width: 100%;
} }

View File

@@ -15,10 +15,12 @@
import { H6, Body, Badge } from "@vector-im/compound-web"; import { H6, Body, Badge } from "@vector-im/compound-web";
import { ReactNode } from "react"; import { ReactNode } from "react";
import { DeviceType } from "../../utils/parseUserAgent";
import Block from "../Block"; import Block from "../Block";
import DateTime from "../DateTime"; import DateTime from "../DateTime";
import ClientAvatar from "./ClientAvatar"; import ClientAvatar from "./ClientAvatar";
import DeviceTypeIcon from "./DeviceTypeIcon";
import LastActive from "./LastActive"; import LastActive from "./LastActive";
import styles from "./Session.module.css"; import styles from "./Session.module.css";
@@ -34,6 +36,7 @@ type SessionProps = {
clientName?: string; clientName?: string;
clientLogoUri?: string; clientLogoUri?: string;
isCurrent?: boolean; isCurrent?: boolean;
deviceType?: DeviceType;
lastActiveIp?: string; lastActiveIp?: string;
lastActiveAt?: Date; lastActiveAt?: Date;
}; };
@@ -48,9 +51,12 @@ const Session: React.FC<React.PropsWithChildren<SessionProps>> = ({
lastActiveAt, lastActiveAt,
isCurrent, isCurrent,
children, children,
deviceType,
}) => { }) => {
return ( return (
<Block> <Block className={styles.session}>
<DeviceTypeIcon deviceType={deviceType || DeviceType.Unknown} />
<div className={styles.container}>
{isCurrent && <Badge kind="success">Current</Badge>} {isCurrent && <Badge kind="success">Current</Badge>}
<H6 className={styles.sessionName} title={id}> <H6 className={styles.sessionName} title={id}>
{name || id} {name || id}
@@ -82,6 +88,7 @@ const Session: React.FC<React.PropsWithChildren<SessionProps>> = ({
</SessionMetadata> </SessionMetadata>
)} )}
{!!children && <div className={styles.sessionActions}>{children}</div>} {!!children && <div className={styles.sessionActions}>{children}</div>}
</div>
</Block> </Block>
); );
}; };

View File

@@ -0,0 +1,85 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<DeviceTypeIcon /> > renders Web device type 1`] = `
<div>
<svg
aria-label="Web"
class="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 20C3.45 20 2.97917 19.8042 2.5875 19.4125C2.19583 19.0208 2 18.55 2 18V6C2 5.45 2.19583 4.97917 2.5875 4.5875C2.97917 4.19583 3.45 4 4 4H20C20.55 4 21.0208 4.19583 21.4125 4.5875C21.8042 4.97917 22 5.45 22 6V18C22 18.55 21.8042 19.0208 21.4125 19.4125C21.0208 19.8042 20.55 20 20 20H4ZM4 18H20V8H4V18Z"
fill="currentColor"
/>
</svg>
</div>
`;
exports[`<DeviceTypeIcon /> > renders desktop device type 1`] = `
<div>
<svg
aria-label="Desktop"
class="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 18C3.45 18 2.97917 17.8042 2.5875 17.4125C2.19583 17.0208 2 16.55 2 16V5C2 4.45 2.19583 3.97917 2.5875 3.5875C2.97917 3.19583 3.45 3 4 3H20C20.55 3 21.0208 3.19583 21.4125 3.5875C21.8042 3.97917 22 4.45 22 5V16C22 16.55 21.8042 17.0208 21.4125 17.4125C21.0208 17.8042 20.55 18 20 18H4ZM4 16H20V5H4V16ZM2 21C1.71667 21 1.47917 20.9042 1.2875 20.7125C1.09583 20.5208 1 20.2833 1 20C1 19.7167 1.09583 19.4792 1.2875 19.2875C1.47917 19.0958 1.71667 19 2 19H22C22.2833 19 22.5208 19.0958 22.7125 19.2875C22.9042 19.4792 23 19.7167 23 20C23 20.2833 22.9042 20.5208 22.7125 20.7125C22.5208 20.9042 22.2833 21 22 21H2Z"
fill="currentColor"
/>
</svg>
</div>
`;
exports[`<DeviceTypeIcon /> > renders mobile device type 1`] = `
<div>
<svg
aria-label="Mobile"
class="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7 23C6.45 23 5.97917 22.8042 5.5875 22.4125C5.19583 22.0208 5 21.55 5 21V3C5 2.45 5.19583 1.97917 5.5875 1.5875C5.97917 1.19583 6.45 1 7 1H17C17.55 1 18.0208 1.19583 18.4125 1.5875C18.8042 1.97917 19 2.45 19 3V21C19 21.55 18.8042 22.0208 18.4125 22.4125C18.0208 22.8042 17.55 23 17 23H7ZM7 18H17V6H7V18Z"
fill="currentColor"
/>
</svg>
</div>
`;
exports[`<DeviceTypeIcon /> > renders unknown device type 1`] = `
<div>
<svg
aria-label="Unknown device type"
class="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 21C4.45 21 3.97917 20.8042 3.5875 20.4125C3.19583 20.0208 3 19.55 3 19V5C3 4.45 3.19583 3.97917 3.5875 3.5875C3.97917 3.19583 4.45 3 5 3H19C19.55 3 20.0208 3.19583 20.4125 3.5875C20.8042 3.97917 21 4.45 21 5V19C21 19.55 20.8042 20.0208 20.4125 20.4125C20.0208 20.8042 19.55 21 19 21H5ZM5 19H19V5H5V19Z"
fill="currentColor"
/>
<path
d="M11 10C11 9.44772 11.4477 9 12 9C12.5523 9 13 9.44772 13 10C13 10.3772 12.7915 10.7072 12.4788 10.8784C12.1685 11.0482 11.8204 11.2907 11.5387 11.6191C11.2528 11.9526 11 12.4196 11 13C11 13.5523 11.4477 14 12 14C12.5502 14 12.9966 13.5556 13 13.0062C13.0038 12.9954 13.017 12.9676 13.0569 12.9211C13.1336 12.8316 13.265 12.7281 13.4392 12.6327C14.3671 12.1247 15 11.137 15 10C15 8.34315 13.6569 7 12 7C10.3431 7 9 8.34315 9 10C9 10.5523 9.44772 11 10 11C10.5523 11 11 10.5523 11 10ZM12.9986 13.0115C12.9986 13.0115 12.9992 13.0101 12.9996 13.0074L12.9986 13.0115Z"
fill="currentColor"
/>
<path
d="M12 17C12.5523 17 13 16.5523 13 16C13 15.4477 12.5523 15 12 15C11.4477 15 11 15.4477 11 16C11 16.5523 11.4477 17 12 17Z"
fill="currentColor"
/>
</svg>
</div>
`;

View File

@@ -2,8 +2,33 @@
exports[`<Session /> > renders a finished session 1`] = ` exports[`<Session /> > renders a finished session 1`] = `
<div <div
className="_block_17898c" className="_block_17898c _session_634806"
> >
<svg
aria-label="Unknown device type"
className="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 21C4.45 21 3.97917 20.8042 3.5875 20.4125C3.19583 20.0208 3 19.55 3 19V5C3 4.45 3.19583 3.97917 3.5875 3.5875C3.97917 3.19583 4.45 3 5 3H19C19.55 3 20.0208 3.19583 20.4125 3.5875C20.8042 3.97917 21 4.45 21 5V19C21 19.55 20.8042 20.0208 20.4125 20.4125C20.0208 20.8042 19.55 21 19 21H5ZM5 19H19V5H5V19Z"
fill="currentColor"
/>
<path
d="M11 10C11 9.44772 11.4477 9 12 9C12.5523 9 13 9.44772 13 10C13 10.3772 12.7915 10.7072 12.4788 10.8784C12.1685 11.0482 11.8204 11.2907 11.5387 11.6191C11.2528 11.9526 11 12.4196 11 13C11 13.5523 11.4477 14 12 14C12.5502 14 12.9966 13.5556 13 13.0062C13.0038 12.9954 13.017 12.9676 13.0569 12.9211C13.1336 12.8316 13.265 12.7281 13.4392 12.6327C14.3671 12.1247 15 11.137 15 10C15 8.34315 13.6569 7 12 7C10.3431 7 9 8.34315 9 10C9 10.5523 9.44772 11 10 11C10.5523 11 11 10.5523 11 10ZM12.9986 13.0115C12.9986 13.0115 12.9992 13.0101 12.9996 13.0074L12.9986 13.0115Z"
fill="currentColor"
/>
<path
d="M12 17C12.5523 17 13 16.5523 13 16C13 15.4477 12.5523 15 12 15C11.4477 15 11 15.4477 11 16C11 16.5523 11.4477 17 12 17Z"
fill="currentColor"
/>
</svg>
<div
className="_container_634806"
>
<h6 <h6
className="_font-body-md-semibold_1jx6b_64 _sessionName_634806" className="_font-body-md-semibold_1jx6b_64 _sessionName_634806"
title="session-id" title="session-id"
@@ -31,13 +56,39 @@ exports[`<Session /> > renders a finished session 1`] = `
Thu, 29 Jun 2023, 03:35 Thu, 29 Jun 2023, 03:35
</time> </time>
</p> </p>
</div>
</div> </div>
`; `;
exports[`<Session /> > renders an active session 1`] = ` exports[`<Session /> > renders an active session 1`] = `
<div <div
className="_block_17898c" className="_block_17898c _session_634806"
> >
<svg
aria-label="Unknown device type"
className="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 21C4.45 21 3.97917 20.8042 3.5875 20.4125C3.19583 20.0208 3 19.55 3 19V5C3 4.45 3.19583 3.97917 3.5875 3.5875C3.97917 3.19583 4.45 3 5 3H19C19.55 3 20.0208 3.19583 20.4125 3.5875C20.8042 3.97917 21 4.45 21 5V19C21 19.55 20.8042 20.0208 20.4125 20.4125C20.0208 20.8042 19.55 21 19 21H5ZM5 19H19V5H5V19Z"
fill="currentColor"
/>
<path
d="M11 10C11 9.44772 11.4477 9 12 9C12.5523 9 13 9.44772 13 10C13 10.3772 12.7915 10.7072 12.4788 10.8784C12.1685 11.0482 11.8204 11.2907 11.5387 11.6191C11.2528 11.9526 11 12.4196 11 13C11 13.5523 11.4477 14 12 14C12.5502 14 12.9966 13.5556 13 13.0062C13.0038 12.9954 13.017 12.9676 13.0569 12.9211C13.1336 12.8316 13.265 12.7281 13.4392 12.6327C14.3671 12.1247 15 11.137 15 10C15 8.34315 13.6569 7 12 7C10.3431 7 9 8.34315 9 10C9 10.5523 9.44772 11 10 11C10.5523 11 11 10.5523 11 10ZM12.9986 13.0115C12.9986 13.0115 12.9992 13.0101 12.9996 13.0074L12.9986 13.0115Z"
fill="currentColor"
/>
<path
d="M12 17C12.5523 17 13 16.5523 13 16C13 15.4477 12.5523 15 12 15C11.4477 15 11 15.4477 11 16C11 16.5523 11.4477 17 12 17Z"
fill="currentColor"
/>
</svg>
<div
className="_container_634806"
>
<h6 <h6
className="_font-body-md-semibold_1jx6b_64 _sessionName_634806" className="_font-body-md-semibold_1jx6b_64 _sessionName_634806"
title="session-id" title="session-id"
@@ -54,13 +105,39 @@ exports[`<Session /> > renders an active session 1`] = `
Thu, 29 Jun 2023, 03:35 Thu, 29 Jun 2023, 03:35
</time> </time>
</p> </p>
</div>
</div> </div>
`; `;
exports[`<Session /> > renders ip address 1`] = ` exports[`<Session /> > renders ip address 1`] = `
<div <div
className="_block_17898c" className="_block_17898c _session_634806"
> >
<svg
aria-label="Unknown device type"
className="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 21C4.45 21 3.97917 20.8042 3.5875 20.4125C3.19583 20.0208 3 19.55 3 19V5C3 4.45 3.19583 3.97917 3.5875 3.5875C3.97917 3.19583 4.45 3 5 3H19C19.55 3 20.0208 3.19583 20.4125 3.5875C20.8042 3.97917 21 4.45 21 5V19C21 19.55 20.8042 20.0208 20.4125 20.4125C20.0208 20.8042 19.55 21 19 21H5ZM5 19H19V5H5V19Z"
fill="currentColor"
/>
<path
d="M11 10C11 9.44772 11.4477 9 12 9C12.5523 9 13 9.44772 13 10C13 10.3772 12.7915 10.7072 12.4788 10.8784C12.1685 11.0482 11.8204 11.2907 11.5387 11.6191C11.2528 11.9526 11 12.4196 11 13C11 13.5523 11.4477 14 12 14C12.5502 14 12.9966 13.5556 13 13.0062C13.0038 12.9954 13.017 12.9676 13.0569 12.9211C13.1336 12.8316 13.265 12.7281 13.4392 12.6327C14.3671 12.1247 15 11.137 15 10C15 8.34315 13.6569 7 12 7C10.3431 7 9 8.34315 9 10C9 10.5523 9.44772 11 10 11C10.5523 11 11 10.5523 11 10ZM12.9986 13.0115C12.9986 13.0115 12.9992 13.0101 12.9996 13.0074L12.9986 13.0115Z"
fill="currentColor"
/>
<path
d="M12 17C12.5523 17 13 16.5523 13 16C13 15.4477 12.5523 15 12 15C11.4477 15 11 15.4477 11 16C11 16.5523 11.4477 17 12 17Z"
fill="currentColor"
/>
</svg>
<div
className="_container_634806"
>
<h6 <h6
className="_font-body-md-semibold_1jx6b_64 _sessionName_634806" className="_font-body-md-semibold_1jx6b_64 _sessionName_634806"
title="session-id" title="session-id"
@@ -103,13 +180,39 @@ exports[`<Session /> > renders ip address 1`] = `
Element Element
</span> </span>
</p> </p>
</div>
</div> </div>
`; `;
exports[`<Session /> > uses client name when truthy 1`] = ` exports[`<Session /> > uses client name when truthy 1`] = `
<div <div
className="_block_17898c" className="_block_17898c _session_634806"
> >
<svg
aria-label="Unknown device type"
className="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 21C4.45 21 3.97917 20.8042 3.5875 20.4125C3.19583 20.0208 3 19.55 3 19V5C3 4.45 3.19583 3.97917 3.5875 3.5875C3.97917 3.19583 4.45 3 5 3H19C19.55 3 20.0208 3.19583 20.4125 3.5875C20.8042 3.97917 21 4.45 21 5V19C21 19.55 20.8042 20.0208 20.4125 20.4125C20.0208 20.8042 19.55 21 19 21H5ZM5 19H19V5H5V19Z"
fill="currentColor"
/>
<path
d="M11 10C11 9.44772 11.4477 9 12 9C12.5523 9 13 9.44772 13 10C13 10.3772 12.7915 10.7072 12.4788 10.8784C12.1685 11.0482 11.8204 11.2907 11.5387 11.6191C11.2528 11.9526 11 12.4196 11 13C11 13.5523 11.4477 14 12 14C12.5502 14 12.9966 13.5556 13 13.0062C13.0038 12.9954 13.017 12.9676 13.0569 12.9211C13.1336 12.8316 13.265 12.7281 13.4392 12.6327C14.3671 12.1247 15 11.137 15 10C15 8.34315 13.6569 7 12 7C10.3431 7 9 8.34315 9 10C9 10.5523 9.44772 11 10 11C10.5523 11 11 10.5523 11 10ZM12.9986 13.0115C12.9986 13.0115 12.9992 13.0101 12.9996 13.0074L12.9986 13.0115Z"
fill="currentColor"
/>
<path
d="M12 17C12.5523 17 13 16.5523 13 16C13 15.4477 12.5523 15 12 15C11.4477 15 11 15.4477 11 16C11 16.5523 11.4477 17 12 17Z"
fill="currentColor"
/>
</svg>
<div
className="_container_634806"
>
<h6 <h6
className="_font-body-md-semibold_1jx6b_64 _sessionName_634806" className="_font-body-md-semibold_1jx6b_64 _sessionName_634806"
title="session-id" title="session-id"
@@ -158,13 +261,39 @@ exports[`<Session /> > uses client name when truthy 1`] = `
Element Element
</span> </span>
</p> </p>
</div>
</div> </div>
`; `;
exports[`<Session /> > uses session name when truthy 1`] = ` exports[`<Session /> > uses session name when truthy 1`] = `
<div <div
className="_block_17898c" className="_block_17898c _session_634806"
> >
<svg
aria-label="Unknown device type"
className="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 21C4.45 21 3.97917 20.8042 3.5875 20.4125C3.19583 20.0208 3 19.55 3 19V5C3 4.45 3.19583 3.97917 3.5875 3.5875C3.97917 3.19583 4.45 3 5 3H19C19.55 3 20.0208 3.19583 20.4125 3.5875C20.8042 3.97917 21 4.45 21 5V19C21 19.55 20.8042 20.0208 20.4125 20.4125C20.0208 20.8042 19.55 21 19 21H5ZM5 19H19V5H5V19Z"
fill="currentColor"
/>
<path
d="M11 10C11 9.44772 11.4477 9 12 9C12.5523 9 13 9.44772 13 10C13 10.3772 12.7915 10.7072 12.4788 10.8784C12.1685 11.0482 11.8204 11.2907 11.5387 11.6191C11.2528 11.9526 11 12.4196 11 13C11 13.5523 11.4477 14 12 14C12.5502 14 12.9966 13.5556 13 13.0062C13.0038 12.9954 13.017 12.9676 13.0569 12.9211C13.1336 12.8316 13.265 12.7281 13.4392 12.6327C14.3671 12.1247 15 11.137 15 10C15 8.34315 13.6569 7 12 7C10.3431 7 9 8.34315 9 10C9 10.5523 9.44772 11 10 11C10.5523 11 11 10.5523 11 10ZM12.9986 13.0115C12.9986 13.0115 12.9992 13.0101 12.9996 13.0074L12.9986 13.0115Z"
fill="currentColor"
/>
<path
d="M12 17C12.5523 17 13 16.5523 13 16C13 15.4477 12.5523 15 12 15C11.4477 15 11 15.4477 11 16C11 16.5523 11.4477 17 12 17Z"
fill="currentColor"
/>
</svg>
<div
className="_container_634806"
>
<h6 <h6
className="_font-body-md-semibold_1jx6b_64 _sessionName_634806" className="_font-body-md-semibold_1jx6b_64 _sessionName_634806"
title="session-id" title="session-id"
@@ -192,5 +321,6 @@ exports[`<Session /> > uses session name when truthy 1`] = `
Thu, 29 Jun 2023, 03:35 Thu, 29 Jun 2023, 03:35
</time> </time>
</p> </p>
</div>
</div> </div>
`; `;

View File

@@ -2,8 +2,33 @@
exports[`<CompatSession /> > renders a finished session 1`] = ` exports[`<CompatSession /> > renders a finished session 1`] = `
<div <div
className="_block_17898c" className="_block_17898c _session_634806"
> >
<svg
aria-label="Unknown device type"
className="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 21C4.45 21 3.97917 20.8042 3.5875 20.4125C3.19583 20.0208 3 19.55 3 19V5C3 4.45 3.19583 3.97917 3.5875 3.5875C3.97917 3.19583 4.45 3 5 3H19C19.55 3 20.0208 3.19583 20.4125 3.5875C20.8042 3.97917 21 4.45 21 5V19C21 19.55 20.8042 20.0208 20.4125 20.4125C20.0208 20.8042 19.55 21 19 21H5ZM5 19H19V5H5V19Z"
fill="currentColor"
/>
<path
d="M11 10C11 9.44772 11.4477 9 12 9C12.5523 9 13 9.44772 13 10C13 10.3772 12.7915 10.7072 12.4788 10.8784C12.1685 11.0482 11.8204 11.2907 11.5387 11.6191C11.2528 11.9526 11 12.4196 11 13C11 13.5523 11.4477 14 12 14C12.5502 14 12.9966 13.5556 13 13.0062C13.0038 12.9954 13.017 12.9676 13.0569 12.9211C13.1336 12.8316 13.265 12.7281 13.4392 12.6327C14.3671 12.1247 15 11.137 15 10C15 8.34315 13.6569 7 12 7C10.3431 7 9 8.34315 9 10C9 10.5523 9.44772 11 10 11C10.5523 11 11 10.5523 11 10ZM12.9986 13.0115C12.9986 13.0115 12.9992 13.0101 12.9996 13.0074L12.9986 13.0115Z"
fill="currentColor"
/>
<path
d="M12 17C12.5523 17 13 16.5523 13 16C13 15.4477 12.5523 15 12 15C11.4477 15 11 15.4477 11 16C11 16.5523 11.4477 17 12 17Z"
fill="currentColor"
/>
</svg>
<div
className="_container_634806"
>
<h6 <h6
className="_font-body-md-semibold_1jx6b_64 _sessionName_634806" className="_font-body-md-semibold_1jx6b_64 _sessionName_634806"
title="session-id" title="session-id"
@@ -52,13 +77,39 @@ exports[`<CompatSession /> > renders a finished session 1`] = `
element.io element.io
</span> </span>
</p> </p>
</div>
</div> </div>
`; `;
exports[`<CompatSession /> > renders an active session 1`] = ` exports[`<CompatSession /> > renders an active session 1`] = `
<div <div
className="_block_17898c" className="_block_17898c _session_634806"
> >
<svg
aria-label="Unknown device type"
className="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 21C4.45 21 3.97917 20.8042 3.5875 20.4125C3.19583 20.0208 3 19.55 3 19V5C3 4.45 3.19583 3.97917 3.5875 3.5875C3.97917 3.19583 4.45 3 5 3H19C19.55 3 20.0208 3.19583 20.4125 3.5875C20.8042 3.97917 21 4.45 21 5V19C21 19.55 20.8042 20.0208 20.4125 20.4125C20.0208 20.8042 19.55 21 19 21H5ZM5 19H19V5H5V19Z"
fill="currentColor"
/>
<path
d="M11 10C11 9.44772 11.4477 9 12 9C12.5523 9 13 9.44772 13 10C13 10.3772 12.7915 10.7072 12.4788 10.8784C12.1685 11.0482 11.8204 11.2907 11.5387 11.6191C11.2528 11.9526 11 12.4196 11 13C11 13.5523 11.4477 14 12 14C12.5502 14 12.9966 13.5556 13 13.0062C13.0038 12.9954 13.017 12.9676 13.0569 12.9211C13.1336 12.8316 13.265 12.7281 13.4392 12.6327C14.3671 12.1247 15 11.137 15 10C15 8.34315 13.6569 7 12 7C10.3431 7 9 8.34315 9 10C9 10.5523 9.44772 11 10 11C10.5523 11 11 10.5523 11 10ZM12.9986 13.0115C12.9986 13.0115 12.9992 13.0101 12.9996 13.0074L12.9986 13.0115Z"
fill="currentColor"
/>
<path
d="M12 17C12.5523 17 13 16.5523 13 16C13 15.4477 12.5523 15 12 15C11.4477 15 11 15.4477 11 16C11 16.5523 11.4477 17 12 17Z"
fill="currentColor"
/>
</svg>
<div
className="_container_634806"
>
<h6 <h6
className="_font-body-md-semibold_1jx6b_64 _sessionName_634806" className="_font-body-md-semibold_1jx6b_64 _sessionName_634806"
title="session-id" title="session-id"
@@ -116,5 +167,6 @@ exports[`<CompatSession /> > renders an active session 1`] = `
End session End session
</button> </button>
</div> </div>
</div>
</div> </div>
`; `;

View File

@@ -2,8 +2,25 @@
exports[`<OAuth2Session /> > renders a finished session 1`] = ` exports[`<OAuth2Session /> > renders a finished session 1`] = `
<div <div
className="_block_17898c" className="_block_17898c _session_634806"
> >
<svg
aria-label="Web"
className="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 20C3.45 20 2.97917 19.8042 2.5875 19.4125C2.19583 19.0208 2 18.55 2 18V6C2 5.45 2.19583 4.97917 2.5875 4.5875C2.97917 4.19583 3.45 4 4 4H20C20.55 4 21.0208 4.19583 21.4125 4.5875C21.8042 4.97917 22 5.45 22 6V18C22 18.55 21.8042 19.0208 21.4125 19.4125C21.0208 19.8042 20.55 20 20 20H4ZM4 18H20V8H4V18Z"
fill="currentColor"
/>
</svg>
<div
className="_container_634806"
>
<h6 <h6
className="_font-body-md-semibold_1jx6b_64 _sessionName_634806" className="_font-body-md-semibold_1jx6b_64 _sessionName_634806"
title="session-id" title="session-id"
@@ -52,13 +69,31 @@ exports[`<OAuth2Session /> > renders a finished session 1`] = `
Element Element
</span> </span>
</p> </p>
</div>
</div> </div>
`; `;
exports[`<OAuth2Session /> > renders an active session 1`] = ` exports[`<OAuth2Session /> > renders an active session 1`] = `
<div <div
className="_block_17898c" className="_block_17898c _session_634806"
> >
<svg
aria-label="Web"
className="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 20C3.45 20 2.97917 19.8042 2.5875 19.4125C2.19583 19.0208 2 18.55 2 18V6C2 5.45 2.19583 4.97917 2.5875 4.5875C2.97917 4.19583 3.45 4 4 4H20C20.55 4 21.0208 4.19583 21.4125 4.5875C21.8042 4.97917 22 5.45 22 6V18C22 18.55 21.8042 19.0208 21.4125 19.4125C21.0208 19.8042 20.55 20 20 20H4ZM4 18H20V8H4V18Z"
fill="currentColor"
/>
</svg>
<div
className="_container_634806"
>
<h6 <h6
className="_font-body-md-semibold_1jx6b_64 _sessionName_634806" className="_font-body-md-semibold_1jx6b_64 _sessionName_634806"
title="session-id" title="session-id"
@@ -116,5 +151,79 @@ exports[`<OAuth2Session /> > renders an active session 1`] = `
End session End session
</button> </button>
</div> </div>
</div>
</div>
`;
exports[`<OAuth2Session /> > renders correct icon for a native session 1`] = `
<div
className="_block_17898c _session_634806"
>
<svg
aria-label="Mobile"
className="_icon_e677aa"
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7 23C6.45 23 5.97917 22.8042 5.5875 22.4125C5.19583 22.0208 5 21.55 5 21V3C5 2.45 5.19583 1.97917 5.5875 1.5875C5.97917 1.19583 6.45 1 7 1H17C17.55 1 18.0208 1.19583 18.4125 1.5875C18.8042 1.97917 19 2.45 19 3V21C19 21.55 18.8042 22.0208 18.4125 22.4125C18.0208 22.8042 17.55 23 17 23H7ZM7 18H17V6H7V18Z"
fill="currentColor"
/>
</svg>
<div
className="_container_634806"
>
<h6
className="_font-body-md-semibold_1jx6b_64 _sessionName_634806"
title="session-id"
>
<a
className=""
href="/session/abcd1234"
onClick={[Function]}
>
abcd1234
</a>
</h6>
<p
className="_font-body-sm-semibold_1jx6b_45 _sessionMetadata_634806"
>
Signed in
<time
dateTime="2023-06-29T03:35:17Z"
>
Thu, 29 Jun 2023, 03:35
</time>
</p>
<p
className="_font-body-sm-semibold_1jx6b_45 _sessionMetadata_634806"
data-finished={true}
>
Finished
<time
dateTime="2023-06-29T03:35:19Z"
>
Thu, 29 Jun 2023, 03:35
</time>
</p>
<p
className="_font-body-sm-regular_1jx6b_40 _sessionMetadata_634806"
>
1.2.3.4
</p>
<p
className="_font-body-sm-regular_1jx6b_40 _sessionMetadata_634806"
>
<span
className="_font-body-sm-semibold_1jx6b_45 _sessionMetadata_634806"
>
Element
</span>
</p>
</div>
</div> </div>
`; `;

View File

@@ -29,7 +29,7 @@ const documents = {
types.CompatSession_SessionFragmentDoc, types.CompatSession_SessionFragmentDoc,
"\n mutation EndCompatSession($id: ID!) {\n endCompatSession(input: { compatSessionId: $id }) {\n status\n compatSession {\n id\n finishedAt\n }\n }\n }\n": "\n mutation EndCompatSession($id: ID!) {\n endCompatSession(input: { compatSessionId: $id }) {\n status\n compatSession {\n id\n finishedAt\n }\n }\n }\n":
types.EndCompatSessionDocument, types.EndCompatSessionDocument,
"\n fragment OAuth2Session_session on Oauth2Session {\n id\n scope\n createdAt\n finishedAt\n lastActiveIp\n lastActiveAt\n client {\n id\n clientId\n clientName\n logoUri\n }\n }\n": "\n fragment OAuth2Session_session on Oauth2Session {\n id\n scope\n createdAt\n finishedAt\n lastActiveIp\n lastActiveAt\n client {\n id\n clientId\n clientName\n applicationType\n logoUri\n }\n }\n":
types.OAuth2Session_SessionFragmentDoc, types.OAuth2Session_SessionFragmentDoc,
"\n mutation EndOAuth2Session($id: ID!) {\n endOauth2Session(input: { oauth2SessionId: $id }) {\n status\n oauth2Session {\n id\n ...OAuth2Session_session\n }\n }\n }\n": "\n mutation EndOAuth2Session($id: ID!) {\n endOauth2Session(input: { oauth2SessionId: $id }) {\n status\n oauth2Session {\n id\n ...OAuth2Session_session\n }\n }\n }\n":
types.EndOAuth2SessionDocument, types.EndOAuth2SessionDocument,
@@ -145,8 +145,8 @@ export function graphql(
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */
export function graphql( export function graphql(
source: "\n fragment OAuth2Session_session on Oauth2Session {\n id\n scope\n createdAt\n finishedAt\n lastActiveIp\n lastActiveAt\n client {\n id\n clientId\n clientName\n logoUri\n }\n }\n", source: "\n fragment OAuth2Session_session on Oauth2Session {\n id\n scope\n createdAt\n finishedAt\n lastActiveIp\n lastActiveAt\n client {\n id\n clientId\n clientName\n applicationType\n logoUri\n }\n }\n",
): (typeof documents)["\n fragment OAuth2Session_session on Oauth2Session {\n id\n scope\n createdAt\n finishedAt\n lastActiveIp\n lastActiveAt\n client {\n id\n clientId\n clientName\n logoUri\n }\n }\n"]; ): (typeof documents)["\n fragment OAuth2Session_session on Oauth2Session {\n id\n scope\n createdAt\n finishedAt\n lastActiveIp\n lastActiveAt\n client {\n id\n clientId\n clientName\n applicationType\n logoUri\n }\n }\n"];
/** /**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */

View File

@@ -1208,6 +1208,7 @@ export type OAuth2Session_SessionFragment = {
id: string; id: string;
clientId: string; clientId: string;
clientName?: string | null; clientName?: string | null;
applicationType?: Oauth2ApplicationType | null;
logoUri?: string | null; logoUri?: string | null;
}; };
} & { " $fragmentName"?: "OAuth2Session_SessionFragment" }; } & { " $fragmentName"?: "OAuth2Session_SessionFragment" };
@@ -1743,6 +1744,10 @@ export const OAuth2Session_SessionFragmentDoc = {
{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "id" } },
{ kind: "Field", name: { kind: "Name", value: "clientId" } }, { kind: "Field", name: { kind: "Name", value: "clientId" } },
{ kind: "Field", name: { kind: "Name", value: "clientName" } }, { kind: "Field", name: { kind: "Name", value: "clientName" } },
{
kind: "Field",
name: { kind: "Name", value: "applicationType" },
},
{ kind: "Field", name: { kind: "Name", value: "logoUri" } }, { kind: "Field", name: { kind: "Name", value: "logoUri" } },
], ],
}, },
@@ -2600,6 +2605,10 @@ export const EndOAuth2SessionDocument = {
{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "id" } },
{ kind: "Field", name: { kind: "Name", value: "clientId" } }, { kind: "Field", name: { kind: "Name", value: "clientId" } },
{ kind: "Field", name: { kind: "Name", value: "clientName" } }, { kind: "Field", name: { kind: "Name", value: "clientName" } },
{
kind: "Field",
name: { kind: "Name", value: "applicationType" },
},
{ kind: "Field", name: { kind: "Name", value: "logoUri" } }, { kind: "Field", name: { kind: "Name", value: "logoUri" } },
], ],
}, },
@@ -3733,6 +3742,10 @@ export const AppSessionListDocument = {
{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "id" } },
{ kind: "Field", name: { kind: "Name", value: "clientId" } }, { kind: "Field", name: { kind: "Name", value: "clientId" } },
{ kind: "Field", name: { kind: "Name", value: "clientName" } }, { kind: "Field", name: { kind: "Name", value: "clientName" } },
{
kind: "Field",
name: { kind: "Name", value: "applicationType" },
},
{ kind: "Field", name: { kind: "Name", value: "logoUri" } }, { kind: "Field", name: { kind: "Name", value: "logoUri" } },
], ],
}, },

View File

@@ -233,20 +233,20 @@ const WEB_EXPECTED_RESULT = [
"8.0", "8.0",
), ),
makeDeviceExtendedInfo( makeDeviceExtendedInfo(
DeviceType.Web, DeviceType.Mobile,
"Apple", "Apple",
"iPhone", "iPhone",
"iOS", "iOS",
undefined, "8.4.1",
"Mobile Safari", "Mobile Safari",
"8.0", "8.0",
), ),
makeDeviceExtendedInfo( makeDeviceExtendedInfo(
DeviceType.Web, DeviceType.Mobile,
"Samsung", "Samsung",
"SM-G973U", "SM-G973U",
"Android", "Android",
undefined, "9",
"Chrome", "Chrome",
"69.0.3497.100", "69.0.3497.100",
), ),

View File

@@ -48,9 +48,6 @@ const getDeviceType = (
if (browser.name === "Electron") { if (browser.name === "Electron") {
return DeviceType.Desktop; return DeviceType.Desktop;
} }
if (browser.name) {
return DeviceType.Web;
}
if ( if (
device.type === "mobile" || device.type === "mobile" ||
operatingSystem.name?.includes("Android") || operatingSystem.name?.includes("Android") ||
@@ -58,6 +55,9 @@ const getDeviceType = (
) { ) {
return DeviceType.Mobile; return DeviceType.Mobile;
} }
if (browser.name) {
return DeviceType.Web;
}
return DeviceType.Unknown; return DeviceType.Unknown;
}; };