frontend: allow setting custom names to sessions
This commit is contained in:
@@ -258,6 +258,11 @@
|
||||
"last_active_label": "Last Active",
|
||||
"name_for_platform": "{{name}} for {{platform}}",
|
||||
"scopes_label": "Scopes",
|
||||
"set_device_name": {
|
||||
"help": "Set a name that will help you identify this device.",
|
||||
"label": "Device name",
|
||||
"title": "Edit device name"
|
||||
},
|
||||
"signed_in_label": "Signed in",
|
||||
"title": "Device details",
|
||||
"unknown_browser": "Unknown browser",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { TooltipProvider } from "@vector-im/compound-web";
|
||||
import { beforeAll, describe, expect, it } from "vitest";
|
||||
import { makeFragmentData } from "../../gql";
|
||||
import { mockLocale } from "../../test-utils/mockLocale";
|
||||
@@ -33,7 +34,9 @@ describe("<CompatSessionDetail>", () => {
|
||||
const data = makeFragmentData({ ...baseSession }, FRAGMENT);
|
||||
|
||||
const { container, getByText, queryByText } = render(
|
||||
<CompatSessionDetail session={data} />,
|
||||
<TooltipProvider>
|
||||
<CompatSessionDetail session={data} />
|
||||
</TooltipProvider>,
|
||||
);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
@@ -51,7 +54,9 @@ describe("<CompatSessionDetail>", () => {
|
||||
);
|
||||
|
||||
const { container, getByText, queryByText } = render(
|
||||
<CompatSessionDetail session={data} />,
|
||||
<TooltipProvider>
|
||||
<CompatSessionDetail session={data} />
|
||||
</TooltipProvider>,
|
||||
);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
@@ -69,7 +74,9 @@ describe("<CompatSessionDetail>", () => {
|
||||
);
|
||||
|
||||
const { container, getByText, queryByText } = render(
|
||||
<CompatSessionDetail session={data} />,
|
||||
<TooltipProvider>
|
||||
<CompatSessionDetail session={data} />
|
||||
</TooltipProvider>,
|
||||
);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
@@ -4,17 +4,28 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { VisualList } from "@vector-im/compound-web";
|
||||
import { parseISO } from "date-fns";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { type FragmentType, graphql, useFragment } from "../../gql";
|
||||
import { graphqlRequest } from "../../graphql";
|
||||
import simplifyUrl from "../../utils/simplifyUrl";
|
||||
import DateTime from "../DateTime";
|
||||
import EndCompatSessionButton from "../Session/EndCompatSessionButton";
|
||||
import LastActive from "../Session/LastActive";
|
||||
import EditSessionName from "./EditSessionName";
|
||||
import SessionHeader from "./SessionHeader";
|
||||
import * as Info from "./SessionInfo";
|
||||
|
||||
const SET_SESSION_NAME_MUTATION = graphql(/* GraphQL */ `
|
||||
mutation SetCompatSessionName($sessionId: ID!, $displayName: String!) {
|
||||
setCompatSessionName(input: { compatSessionId: $sessionId, humanName: $displayName }) {
|
||||
status
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
export const FRAGMENT = graphql(/* GraphQL */ `
|
||||
fragment CompatSession_detail on CompatSession {
|
||||
id
|
||||
@@ -47,6 +58,19 @@ type Props = {
|
||||
const CompatSessionDetail: React.FC<Props> = ({ session }) => {
|
||||
const data = useFragment(FRAGMENT, session);
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const setDisplayName = useMutation({
|
||||
mutationFn: (displayName: string) =>
|
||||
graphqlRequest({
|
||||
query: SET_SESSION_NAME_MUTATION,
|
||||
variables: { sessionId: data.id, displayName },
|
||||
}),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["sessionDetail", data.id] });
|
||||
queryClient.invalidateQueries({ queryKey: ["sessionsOverview"] });
|
||||
},
|
||||
});
|
||||
|
||||
const deviceName =
|
||||
data.userAgent?.model ??
|
||||
@@ -67,7 +91,10 @@ const CompatSessionDetail: React.FC<Props> = ({ session }) => {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-10">
|
||||
<SessionHeader to="/sessions">{sessionName}</SessionHeader>
|
||||
<SessionHeader to="/sessions">
|
||||
{sessionName}
|
||||
<EditSessionName mutation={setDisplayName} deviceName={sessionName} />
|
||||
</SessionHeader>
|
||||
<Info.DataSection>
|
||||
<Info.DataSectionHeader>
|
||||
{t("frontend.session.title")}
|
||||
|
||||
100
frontend/src/components/SessionDetail/EditSessionName.tsx
Normal file
100
frontend/src/components/SessionDetail/EditSessionName.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
|
||||
import IconEdit from "@vector-im/compound-design-tokens/assets/web/icons/edit";
|
||||
import { Button, Form, IconButton, Tooltip } from "@vector-im/compound-web";
|
||||
import {
|
||||
type ComponentPropsWithoutRef,
|
||||
forwardRef,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import * as Dialog from "../Dialog";
|
||||
import LoadingSpinner from "../LoadingSpinner";
|
||||
|
||||
import type { UseMutationResult } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
// This needs to be its own component because else props and refs aren't passed properly in the trigger
|
||||
const EditButton = forwardRef<
|
||||
HTMLButtonElement,
|
||||
{ label: string } & ComponentPropsWithoutRef<"button">
|
||||
>(({ label, ...props }, ref) => (
|
||||
<Tooltip label={label}>
|
||||
<IconButton
|
||||
ref={ref}
|
||||
type="button"
|
||||
size="var(--cpd-space-6x)"
|
||||
style={{ marginInline: "var(--cpd-space-2x)" }}
|
||||
{...props}
|
||||
>
|
||||
<IconEdit />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
));
|
||||
|
||||
type Props = {
|
||||
mutation: UseMutationResult<unknown, unknown, string, unknown>;
|
||||
deviceName: string;
|
||||
};
|
||||
|
||||
const EditSessionName: React.FC<Props> = ({ mutation, deviceName }) => {
|
||||
const { t } = useTranslation();
|
||||
const fieldRef = useRef<HTMLInputElement>(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const onSubmit = async (
|
||||
event: React.FormEvent<HTMLFormElement>,
|
||||
): Promise<void> => {
|
||||
event.preventDefault();
|
||||
|
||||
const form = event.currentTarget;
|
||||
const formData = new FormData(form);
|
||||
const displayName = formData.get("name") as string;
|
||||
await mutation.mutateAsync(displayName);
|
||||
setOpen(false);
|
||||
};
|
||||
return (
|
||||
<Dialog.Dialog
|
||||
trigger={<EditButton label={t("action.edit")} />}
|
||||
open={open}
|
||||
onOpenChange={(open) => {
|
||||
// Reset the form when the dialog is opened or closed
|
||||
fieldRef.current?.form?.reset();
|
||||
setOpen(open);
|
||||
}}
|
||||
>
|
||||
<Dialog.Title>{t("frontend.session.set_device_name.title")}</Dialog.Title>
|
||||
|
||||
<Form.Root onSubmit={onSubmit}>
|
||||
<Form.Field name="name">
|
||||
<Form.Label>{t("frontend.session.set_device_name.label")}</Form.Label>
|
||||
|
||||
<Form.TextControl
|
||||
type="text"
|
||||
required
|
||||
defaultValue={deviceName}
|
||||
ref={fieldRef}
|
||||
/>
|
||||
|
||||
<Form.HelpMessage>
|
||||
{t("frontend.session.set_device_name.help")}
|
||||
</Form.HelpMessage>
|
||||
</Form.Field>
|
||||
|
||||
<Form.Submit disabled={mutation.isPending}>
|
||||
{mutation.isPending && <LoadingSpinner inline />}
|
||||
{t("action.save")}
|
||||
</Form.Submit>
|
||||
</Form.Root>
|
||||
|
||||
<Dialog.Close asChild>
|
||||
<Button kind="tertiary">{t("action.cancel")}</Button>
|
||||
</Dialog.Close>
|
||||
</Dialog.Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditSessionName;
|
||||
@@ -11,6 +11,7 @@ import { beforeAll, describe, expect, it } from "vitest";
|
||||
import { makeFragmentData } from "../../gql";
|
||||
import { mockLocale } from "../../test-utils/mockLocale";
|
||||
|
||||
import { TooltipProvider } from "@vector-im/compound-web";
|
||||
import render from "../../test-utils/render";
|
||||
import OAuth2SessionDetail, { FRAGMENT } from "./OAuth2SessionDetail";
|
||||
|
||||
@@ -39,7 +40,9 @@ describe("<OAuth2SessionDetail>", () => {
|
||||
const data = makeFragmentData(baseSession, FRAGMENT);
|
||||
|
||||
const { asFragment, getByText, queryByText } = render(
|
||||
<OAuth2SessionDetail session={data} />,
|
||||
<TooltipProvider>
|
||||
<OAuth2SessionDetail session={data} />
|
||||
</TooltipProvider>,
|
||||
);
|
||||
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
@@ -57,7 +60,9 @@ describe("<OAuth2SessionDetail>", () => {
|
||||
);
|
||||
|
||||
const { asFragment, getByText, queryByText } = render(
|
||||
<OAuth2SessionDetail session={data} />,
|
||||
<TooltipProvider>
|
||||
<OAuth2SessionDetail session={data} />
|
||||
</TooltipProvider>,
|
||||
);
|
||||
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
|
||||
@@ -4,17 +4,28 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { parseISO } from "date-fns";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { type FragmentType, graphql, useFragment } from "../../gql";
|
||||
import { graphqlRequest } from "../../graphql";
|
||||
import { getDeviceIdFromScope } from "../../utils/deviceIdFromScope";
|
||||
import DateTime from "../DateTime";
|
||||
import ClientAvatar from "../Session/ClientAvatar";
|
||||
import EndOAuth2SessionButton from "../Session/EndOAuth2SessionButton";
|
||||
import LastActive from "../Session/LastActive";
|
||||
import EditSessionName from "./EditSessionName";
|
||||
import SessionHeader from "./SessionHeader";
|
||||
import * as Info from "./SessionInfo";
|
||||
|
||||
const SET_SESSION_NAME_MUTATION = graphql(/* GraphQL */ `
|
||||
mutation SetOAuth2SessionName($sessionId: ID!, $displayName: String!) {
|
||||
setOauth2SessionName(input: { oauth2SessionId: $sessionId, humanName: $displayName }) {
|
||||
status
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
export const FRAGMENT = graphql(/* GraphQL */ `
|
||||
fragment OAuth2Session_detail on Oauth2Session {
|
||||
id
|
||||
@@ -50,6 +61,19 @@ type Props = {
|
||||
const OAuth2SessionDetail: React.FC<Props> = ({ session }) => {
|
||||
const data = useFragment(FRAGMENT, session);
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const setDisplayName = useMutation({
|
||||
mutationFn: (displayName: string) =>
|
||||
graphqlRequest({
|
||||
query: SET_SESSION_NAME_MUTATION,
|
||||
variables: { sessionId: data.id, displayName },
|
||||
}),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["sessionDetail", data.id] });
|
||||
queryClient.invalidateQueries({ queryKey: ["sessionsOverview"] });
|
||||
},
|
||||
});
|
||||
|
||||
const deviceId = getDeviceIdFromScope(data.scope);
|
||||
const clientName = data.client.clientName || data.client.clientId;
|
||||
@@ -70,7 +94,9 @@ const OAuth2SessionDetail: React.FC<Props> = ({ session }) => {
|
||||
<div className="flex flex-col gap-10">
|
||||
<SessionHeader to="/sessions">
|
||||
{clientName}: {deviceName}
|
||||
<EditSessionName mutation={setDisplayName} deviceName={deviceName} />
|
||||
</SessionHeader>
|
||||
|
||||
<Info.DataSection>
|
||||
<Info.DataSectionHeader>
|
||||
{t("frontend.session.title")}
|
||||
|
||||
@@ -27,9 +27,38 @@ exports[`<CompatSessionDetail> > renders a compatability session details 1`] = `
|
||||
<h3
|
||||
class="_typography_6v6n8_153 _font-heading-md-semibold_6v6n8_112"
|
||||
>
|
||||
element.io
|
||||
:
|
||||
Unknown device
|
||||
element.io: Unknown device
|
||||
<button
|
||||
aria-controls="radix-«r0»"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="dialog"
|
||||
aria-labelledby="«r3»"
|
||||
class="_icon-button_m2erp_8"
|
||||
data-state="closed"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: var(--cpd-space-6x);"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_zr2a0_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M15.706 2.637a2 2 0 0 1 2.829 0l2.828 2.828a2 2 0 0 1 0 2.829L9.605 20.052a1 1 0 0 1-.465.263L3.483 21.73a1 1 0 0 1-1.212-1.213l1.414-5.657a1 1 0 0 1 .263-.465zm1.224 7.262L14.102 7.07l-8.544 8.544-.943 3.771 3.771-.943z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</h3>
|
||||
</header>
|
||||
<section
|
||||
@@ -238,7 +267,7 @@ exports[`<CompatSessionDetail> > renders a compatability session details 1`] = `
|
||||
</ul>
|
||||
</section>
|
||||
<button
|
||||
aria-controls="radix-«r0»"
|
||||
aria-controls="radix-«r8»"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="dialog"
|
||||
class="_button_vczzf_8 _has-icon_vczzf_57 _destructive_vczzf_107"
|
||||
@@ -294,9 +323,38 @@ exports[`<CompatSessionDetail> > renders a compatability session without an ssoL
|
||||
<h3
|
||||
class="_typography_6v6n8_153 _font-heading-md-semibold_6v6n8_112"
|
||||
>
|
||||
abcd1234
|
||||
:
|
||||
Unknown device
|
||||
abcd1234: Unknown device
|
||||
<button
|
||||
aria-controls="radix-«rc»"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="dialog"
|
||||
aria-labelledby="«rf»"
|
||||
class="_icon-button_m2erp_8"
|
||||
data-state="closed"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: var(--cpd-space-6x);"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_zr2a0_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M15.706 2.637a2 2 0 0 1 2.829 0l2.828 2.828a2 2 0 0 1 0 2.829L9.605 20.052a1 1 0 0 1-.465.263L3.483 21.73a1 1 0 0 1-1.212-1.213l1.414-5.657a1 1 0 0 1 .263-.465zm1.224 7.262L14.102 7.07l-8.544 8.544-.943 3.771 3.771-.943z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</h3>
|
||||
</header>
|
||||
<section
|
||||
@@ -488,22 +546,10 @@ exports[`<CompatSessionDetail> > renders a compatability session without an ssoL
|
||||
Unknown device
|
||||
</p>
|
||||
</li>
|
||||
<li
|
||||
class="flex flex-col min-w-0"
|
||||
>
|
||||
<h5
|
||||
class="_typography_6v6n8_153 _font-body-sm-regular_6v6n8_31 text-secondary"
|
||||
>
|
||||
Uri
|
||||
</h5>
|
||||
<p
|
||||
class="_typography_6v6n8_153 _font-body-md-regular_6v6n8_50 text-ellipsis overflow-hidden"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<button
|
||||
aria-controls="radix-«r3»"
|
||||
aria-controls="radix-«rk»"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="dialog"
|
||||
class="_button_vczzf_8 _has-icon_vczzf_57 _destructive_vczzf_107"
|
||||
@@ -559,9 +605,38 @@ exports[`<CompatSessionDetail> > renders a finished compatability session detail
|
||||
<h3
|
||||
class="_typography_6v6n8_153 _font-heading-md-semibold_6v6n8_112"
|
||||
>
|
||||
element.io
|
||||
:
|
||||
Unknown device
|
||||
element.io: Unknown device
|
||||
<button
|
||||
aria-controls="radix-«ro»"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="dialog"
|
||||
aria-labelledby="«rr»"
|
||||
class="_icon-button_m2erp_8"
|
||||
data-state="closed"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: var(--cpd-space-6x);"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_zr2a0_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M15.706 2.637a2 2 0 0 1 2.829 0l2.828 2.828a2 2 0 0 1 0 2.829L9.605 20.052a1 1 0 0 1-.465.263L3.483 21.73a1 1 0 0 1-1.212-1.213l1.414-5.657a1 1 0 0 1 .263-.465zm1.224 7.262L14.102 7.07l-8.544 8.544-.943 3.771 3.771-.943z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</h3>
|
||||
</header>
|
||||
<section
|
||||
|
||||
@@ -28,6 +28,37 @@ exports[`<OAuth2SessionDetail> > renders a finished session details 1`] = `
|
||||
class="_typography_6v6n8_153 _font-heading-md-semibold_6v6n8_112"
|
||||
>
|
||||
Element: Unknown device
|
||||
<button
|
||||
aria-controls="radix-«rc»"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="dialog"
|
||||
aria-labelledby="«rf»"
|
||||
class="_icon-button_m2erp_8"
|
||||
data-state="closed"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: var(--cpd-space-6x);"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_zr2a0_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M15.706 2.637a2 2 0 0 1 2.829 0l2.828 2.828a2 2 0 0 1 0 2.829L9.605 20.052a1 1 0 0 1-.465.263L3.483 21.73a1 1 0 0 1-1.212-1.213l1.414-5.657a1 1 0 0 1 .263-.465zm1.224 7.262L14.102 7.07l-8.544 8.544-.943 3.771 3.771-.943z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</h3>
|
||||
</header>
|
||||
<section
|
||||
@@ -307,6 +338,37 @@ exports[`<OAuth2SessionDetail> > renders session details 1`] = `
|
||||
class="_typography_6v6n8_153 _font-heading-md-semibold_6v6n8_112"
|
||||
>
|
||||
Element: Unknown device
|
||||
<button
|
||||
aria-controls="radix-«r0»"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="dialog"
|
||||
aria-labelledby="«r3»"
|
||||
class="_icon-button_m2erp_8"
|
||||
data-state="closed"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: var(--cpd-space-6x);"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_zr2a0_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M15.706 2.637a2 2 0 0 1 2.829 0l2.828 2.828a2 2 0 0 1 0 2.829L9.605 20.052a1 1 0 0 1-.465.263L3.483 21.73a1 1 0 0 1-1.212-1.213l1.414-5.657a1 1 0 0 1 .263-.465zm1.224 7.262L14.102 7.07l-8.544 8.544-.943 3.771 3.771-.943z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</h3>
|
||||
</header>
|
||||
<section
|
||||
@@ -537,7 +599,7 @@ exports[`<OAuth2SessionDetail> > renders session details 1`] = `
|
||||
</ul>
|
||||
</section>
|
||||
<button
|
||||
aria-controls="radix-«r0»"
|
||||
aria-controls="radix-«r8»"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="dialog"
|
||||
class="_button_vczzf_8 _has-icon_vczzf_57 _destructive_vczzf_107"
|
||||
|
||||
@@ -33,7 +33,9 @@ type Documents = {
|
||||
"\n fragment EndOAuth2SessionButton_session on Oauth2Session {\n id\n\n userAgent {\n name\n model\n os\n deviceType\n }\n\n client {\n clientId\n clientName\n applicationType\n logoUri\n }\n }\n": typeof types.EndOAuth2SessionButton_SessionFragmentDoc,
|
||||
"\n mutation EndOAuth2Session($id: ID!) {\n endOauth2Session(input: { oauth2SessionId: $id }) {\n status\n oauth2Session {\n id\n }\n }\n }\n": typeof types.EndOAuth2SessionDocument,
|
||||
"\n fragment BrowserSession_detail on BrowserSession {\n id\n createdAt\n finishedAt\n ...EndBrowserSessionButton_session\n userAgent {\n name\n model\n os\n }\n lastActiveIp\n lastActiveAt\n lastAuthentication {\n id\n createdAt\n }\n user {\n id\n username\n }\n }\n": typeof types.BrowserSession_DetailFragmentDoc,
|
||||
"\n mutation SetCompatSessionName($sessionId: ID!, $displayName: String!) {\n setCompatSessionName(input: { compatSessionId: $sessionId, humanName: $displayName }) {\n status\n }\n }\n": typeof types.SetCompatSessionNameDocument,
|
||||
"\n fragment CompatSession_detail on CompatSession {\n id\n createdAt\n deviceId\n finishedAt\n lastActiveIp\n lastActiveAt\n humanName\n\n ...EndCompatSessionButton_session\n\n userAgent {\n name\n os\n model\n }\n\n ssoLogin {\n id\n redirectUri\n }\n }\n": typeof types.CompatSession_DetailFragmentDoc,
|
||||
"\n mutation SetOAuth2SessionName($sessionId: ID!, $displayName: String!) {\n setOauth2SessionName(input: { oauth2SessionId: $sessionId, humanName: $displayName }) {\n status\n }\n }\n": typeof types.SetOAuth2SessionNameDocument,
|
||||
"\n fragment OAuth2Session_detail on Oauth2Session {\n id\n scope\n createdAt\n finishedAt\n lastActiveIp\n lastActiveAt\n humanName\n\n ...EndOAuth2SessionButton_session\n\n userAgent {\n name\n model\n os\n }\n\n client {\n id\n clientId\n clientName\n clientUri\n logoUri\n }\n }\n": typeof types.OAuth2Session_DetailFragmentDoc,
|
||||
"\n fragment UserEmail_email on UserEmail {\n id\n email\n }\n": typeof types.UserEmail_EmailFragmentDoc,
|
||||
"\n mutation RemoveEmail($id: ID!, $password: String) {\n removeEmail(input: { userEmailId: $id, password: $password }) {\n status\n\n user {\n id\n }\n }\n }\n": typeof types.RemoveEmailDocument,
|
||||
@@ -87,7 +89,9 @@ const documents: Documents = {
|
||||
"\n fragment EndOAuth2SessionButton_session on Oauth2Session {\n id\n\n userAgent {\n name\n model\n os\n deviceType\n }\n\n client {\n clientId\n clientName\n applicationType\n logoUri\n }\n }\n": types.EndOAuth2SessionButton_SessionFragmentDoc,
|
||||
"\n mutation EndOAuth2Session($id: ID!) {\n endOauth2Session(input: { oauth2SessionId: $id }) {\n status\n oauth2Session {\n id\n }\n }\n }\n": types.EndOAuth2SessionDocument,
|
||||
"\n fragment BrowserSession_detail on BrowserSession {\n id\n createdAt\n finishedAt\n ...EndBrowserSessionButton_session\n userAgent {\n name\n model\n os\n }\n lastActiveIp\n lastActiveAt\n lastAuthentication {\n id\n createdAt\n }\n user {\n id\n username\n }\n }\n": types.BrowserSession_DetailFragmentDoc,
|
||||
"\n mutation SetCompatSessionName($sessionId: ID!, $displayName: String!) {\n setCompatSessionName(input: { compatSessionId: $sessionId, humanName: $displayName }) {\n status\n }\n }\n": types.SetCompatSessionNameDocument,
|
||||
"\n fragment CompatSession_detail on CompatSession {\n id\n createdAt\n deviceId\n finishedAt\n lastActiveIp\n lastActiveAt\n humanName\n\n ...EndCompatSessionButton_session\n\n userAgent {\n name\n os\n model\n }\n\n ssoLogin {\n id\n redirectUri\n }\n }\n": types.CompatSession_DetailFragmentDoc,
|
||||
"\n mutation SetOAuth2SessionName($sessionId: ID!, $displayName: String!) {\n setOauth2SessionName(input: { oauth2SessionId: $sessionId, humanName: $displayName }) {\n status\n }\n }\n": types.SetOAuth2SessionNameDocument,
|
||||
"\n fragment OAuth2Session_detail on Oauth2Session {\n id\n scope\n createdAt\n finishedAt\n lastActiveIp\n lastActiveAt\n humanName\n\n ...EndOAuth2SessionButton_session\n\n userAgent {\n name\n model\n os\n }\n\n client {\n id\n clientId\n clientName\n clientUri\n logoUri\n }\n }\n": types.OAuth2Session_DetailFragmentDoc,
|
||||
"\n fragment UserEmail_email on UserEmail {\n id\n email\n }\n": types.UserEmail_EmailFragmentDoc,
|
||||
"\n mutation RemoveEmail($id: ID!, $password: String) {\n removeEmail(input: { userEmailId: $id, password: $password }) {\n status\n\n user {\n id\n }\n }\n }\n": types.RemoveEmailDocument,
|
||||
@@ -195,10 +199,18 @@ export function graphql(source: "\n mutation EndOAuth2Session($id: ID!) {\n
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment BrowserSession_detail on BrowserSession {\n id\n createdAt\n finishedAt\n ...EndBrowserSessionButton_session\n userAgent {\n name\n model\n os\n }\n lastActiveIp\n lastActiveAt\n lastAuthentication {\n id\n createdAt\n }\n user {\n id\n username\n }\n }\n"): typeof import('./graphql').BrowserSession_DetailFragmentDoc;
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n mutation SetCompatSessionName($sessionId: ID!, $displayName: String!) {\n setCompatSessionName(input: { compatSessionId: $sessionId, humanName: $displayName }) {\n status\n }\n }\n"): typeof import('./graphql').SetCompatSessionNameDocument;
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment CompatSession_detail on CompatSession {\n id\n createdAt\n deviceId\n finishedAt\n lastActiveIp\n lastActiveAt\n humanName\n\n ...EndCompatSessionButton_session\n\n userAgent {\n name\n os\n model\n }\n\n ssoLogin {\n id\n redirectUri\n }\n }\n"): typeof import('./graphql').CompatSession_DetailFragmentDoc;
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n mutation SetOAuth2SessionName($sessionId: ID!, $displayName: String!) {\n setOauth2SessionName(input: { oauth2SessionId: $sessionId, humanName: $displayName }) {\n status\n }\n }\n"): typeof import('./graphql').SetOAuth2SessionNameDocument;
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
|
||||
@@ -1767,11 +1767,27 @@ export type BrowserSession_DetailFragment = (
|
||||
& { ' $fragmentRefs'?: { 'EndBrowserSessionButton_SessionFragment': EndBrowserSessionButton_SessionFragment } }
|
||||
) & { ' $fragmentName'?: 'BrowserSession_DetailFragment' };
|
||||
|
||||
export type SetCompatSessionNameMutationVariables = Exact<{
|
||||
sessionId: Scalars['ID']['input'];
|
||||
displayName: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type SetCompatSessionNameMutation = { __typename?: 'Mutation', setCompatSessionName: { __typename?: 'SetCompatSessionNamePayload', status: SetCompatSessionNameStatus } };
|
||||
|
||||
export type CompatSession_DetailFragment = (
|
||||
{ __typename?: 'CompatSession', id: string, createdAt: string, deviceId?: string | null, finishedAt?: string | null, lastActiveIp?: string | null, lastActiveAt?: string | null, humanName?: string | null, userAgent?: { __typename?: 'UserAgent', name?: string | null, os?: string | null, model?: string | null } | null, ssoLogin?: { __typename?: 'CompatSsoLogin', id: string, redirectUri: string } | null }
|
||||
& { ' $fragmentRefs'?: { 'EndCompatSessionButton_SessionFragment': EndCompatSessionButton_SessionFragment } }
|
||||
) & { ' $fragmentName'?: 'CompatSession_DetailFragment' };
|
||||
|
||||
export type SetOAuth2SessionNameMutationVariables = Exact<{
|
||||
sessionId: Scalars['ID']['input'];
|
||||
displayName: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type SetOAuth2SessionNameMutation = { __typename?: 'Mutation', setOauth2SessionName: { __typename?: 'SetOAuth2SessionNamePayload', status: SetOAuth2SessionNameStatus } };
|
||||
|
||||
export type OAuth2Session_DetailFragment = (
|
||||
{ __typename?: 'Oauth2Session', id: string, scope: string, createdAt: string, finishedAt?: string | null, lastActiveIp?: string | null, lastActiveAt?: string | null, humanName?: string | null, userAgent?: { __typename?: 'UserAgent', name?: string | null, model?: string | null, os?: string | null } | null, client: { __typename?: 'Oauth2Client', id: string, clientId: string, clientName?: string | null, clientUri?: string | null, logoUri?: string | null } }
|
||||
& { ' $fragmentRefs'?: { 'EndOAuth2SessionButton_SessionFragment': EndOAuth2SessionButton_SessionFragment } }
|
||||
@@ -2431,6 +2447,24 @@ export const EndOAuth2SessionDocument = new TypedDocumentString(`
|
||||
}
|
||||
}
|
||||
`) as unknown as TypedDocumentString<EndOAuth2SessionMutation, EndOAuth2SessionMutationVariables>;
|
||||
export const SetCompatSessionNameDocument = new TypedDocumentString(`
|
||||
mutation SetCompatSessionName($sessionId: ID!, $displayName: String!) {
|
||||
setCompatSessionName(
|
||||
input: {compatSessionId: $sessionId, humanName: $displayName}
|
||||
) {
|
||||
status
|
||||
}
|
||||
}
|
||||
`) as unknown as TypedDocumentString<SetCompatSessionNameMutation, SetCompatSessionNameMutationVariables>;
|
||||
export const SetOAuth2SessionNameDocument = new TypedDocumentString(`
|
||||
mutation SetOAuth2SessionName($sessionId: ID!, $displayName: String!) {
|
||||
setOauth2SessionName(
|
||||
input: {oauth2SessionId: $sessionId, humanName: $displayName}
|
||||
) {
|
||||
status
|
||||
}
|
||||
}
|
||||
`) as unknown as TypedDocumentString<SetOAuth2SessionNameMutation, SetOAuth2SessionNameMutationVariables>;
|
||||
export const RemoveEmailDocument = new TypedDocumentString(`
|
||||
mutation RemoveEmail($id: ID!, $password: String) {
|
||||
removeEmail(input: {userEmailId: $id, password: $password}) {
|
||||
@@ -3094,6 +3128,50 @@ export const mockEndOAuth2SessionMutation = (resolver: GraphQLResponseResolver<E
|
||||
options
|
||||
)
|
||||
|
||||
/**
|
||||
* @param resolver A function that accepts [resolver arguments](https://mswjs.io/docs/api/graphql#resolver-argument) and must always return the instruction on what to do with the intercepted request. ([see more](https://mswjs.io/docs/concepts/response-resolver#resolver-instructions))
|
||||
* @param options Options object to customize the behavior of the mock. ([see more](https://mswjs.io/docs/api/graphql#handler-options))
|
||||
* @see https://mswjs.io/docs/basics/response-resolver
|
||||
* @example
|
||||
* mockSetCompatSessionNameMutation(
|
||||
* ({ query, variables }) => {
|
||||
* const { sessionId, displayName } = variables;
|
||||
* return HttpResponse.json({
|
||||
* data: { setCompatSessionName }
|
||||
* })
|
||||
* },
|
||||
* requestOptions
|
||||
* )
|
||||
*/
|
||||
export const mockSetCompatSessionNameMutation = (resolver: GraphQLResponseResolver<SetCompatSessionNameMutation, SetCompatSessionNameMutationVariables>, options?: RequestHandlerOptions) =>
|
||||
graphql.mutation<SetCompatSessionNameMutation, SetCompatSessionNameMutationVariables>(
|
||||
'SetCompatSessionName',
|
||||
resolver,
|
||||
options
|
||||
)
|
||||
|
||||
/**
|
||||
* @param resolver A function that accepts [resolver arguments](https://mswjs.io/docs/api/graphql#resolver-argument) and must always return the instruction on what to do with the intercepted request. ([see more](https://mswjs.io/docs/concepts/response-resolver#resolver-instructions))
|
||||
* @param options Options object to customize the behavior of the mock. ([see more](https://mswjs.io/docs/api/graphql#handler-options))
|
||||
* @see https://mswjs.io/docs/basics/response-resolver
|
||||
* @example
|
||||
* mockSetOAuth2SessionNameMutation(
|
||||
* ({ query, variables }) => {
|
||||
* const { sessionId, displayName } = variables;
|
||||
* return HttpResponse.json({
|
||||
* data: { setOauth2SessionName }
|
||||
* })
|
||||
* },
|
||||
* requestOptions
|
||||
* )
|
||||
*/
|
||||
export const mockSetOAuth2SessionNameMutation = (resolver: GraphQLResponseResolver<SetOAuth2SessionNameMutation, SetOAuth2SessionNameMutationVariables>, options?: RequestHandlerOptions) =>
|
||||
graphql.mutation<SetOAuth2SessionNameMutation, SetOAuth2SessionNameMutationVariables>(
|
||||
'SetOAuth2SessionName',
|
||||
resolver,
|
||||
options
|
||||
)
|
||||
|
||||
/**
|
||||
* @param resolver A function that accepts [resolver arguments](https://mswjs.io/docs/api/graphql#resolver-argument) and must always return the instruction on what to do with the intercepted request. ([see more](https://mswjs.io/docs/concepts/response-resolver#resolver-instructions))
|
||||
* @param options Options object to customize the behavior of the mock. ([see more](https://mswjs.io/docs/api/graphql#handler-options))
|
||||
|
||||
Reference in New Issue
Block a user