@openedx/frontend-base 1.0.0-alpha.48 → 1.0.0-alpha.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/shell/header/app.js +1 -0
- package/dist/shell/header/app.js.map +1 -1
- package/dist/shell/header/course-bar/masquerade/MasqueradeBar.js +4 -5
- package/dist/shell/header/course-bar/masquerade/MasqueradeBar.js.map +1 -1
- package/dist/shell/header/course-bar/masquerade/data/api.js +10 -2
- package/dist/shell/header/course-bar/masquerade/data/api.js.map +1 -1
- package/dist/shell/header/course-bar/masquerade/hooks.d.ts +0 -1
- package/dist/shell/header/course-bar/masquerade/hooks.js +5 -14
- package/dist/shell/header/course-bar/masquerade/hooks.js.map +1 -1
- package/dist/shell/header/course-bar/masquerade/messages.d.ts +0 -5
- package/dist/shell/header/course-bar/masquerade/messages.js +0 -5
- package/dist/shell/header/course-bar/masquerade/messages.js.map +1 -1
- package/package.json +1 -1
package/dist/shell/header/app.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../../shell/header/app.tsx"],"names":[],"mappings":"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","sourcesContent":["import { WidgetOperationTypes } from '../../runtime';\nimport { App } from '../../types';\nimport Logo from '../Logo';\nimport LinkMenuItem from '../menus/LinkMenuItem';\nimport ProfileLinkMenuItem from '../menus/ProfileLinkMenuItem';\nimport AnonymousMenu from './anonymous-menu/AnonymousMenu';\nimport LoginButton from './anonymous-menu/LoginButton';\nimport RegisterButton from './anonymous-menu/RegisterButton';\nimport AuthenticatedMenu from './AuthenticatedMenu';\nimport DesktopLayout from './desktop/DesktopLayout';\nimport PrimaryNavLinks from './desktop/PrimaryNavLinks';\nimport SecondaryNavLinks from './desktop/SecondaryNavLinks';\nimport MobileLayout from './mobile/MobileLayout';\nimport MobileNavLinks from './mobile/MobileNavLinks';\n\nimport messages from '../Shell.messages';\nimport CourseTabsNavigation from './course-bar/navigation/CourseTabsNavigation';\nimport MasqueradeBar from './course-bar/masquerade/MasqueradeBar';\nimport { isCourseBarMasqueradeRoute, isCourseBarRoute } from './course-bar/utils';\nimport { appId } from './constants';\nimport './app.scss';\n\nconst config: App = {\n appId,\n slots: [\n // Layouts\n {\n slotId: 'org.openedx.frontend.slot.header.desktop.v1',\n id: 'org.openedx.frontend.widget.header.desktopLayout.v1',\n op: WidgetOperationTypes.APPEND,\n component: DesktopLayout\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobile.v1',\n id: 'org.openedx.frontend.widget.header.mobileLayout.v1',\n op: WidgetOperationTypes.APPEND,\n component: MobileLayout\n },\n\n // Desktop\n {\n slotId: 'org.openedx.frontend.slot.header.desktopLeft.v1',\n id: 'org.openedx.frontend.widget.header.desktopLogo.v1',\n op: WidgetOperationTypes.APPEND,\n element: <Logo />,\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopLeft.v1',\n id: 'org.openedx.frontend.widget.header.desktopPrimaryLinks.v1',\n op: WidgetOperationTypes.APPEND,\n component: PrimaryNavLinks\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopRight.v1',\n id: 'org.openedx.frontend.widget.header.desktopSecondaryLinks.v1',\n op: WidgetOperationTypes.APPEND,\n component: SecondaryNavLinks\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopRight.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AuthenticatedMenu />,\n condition: {\n authenticated: true,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.authenticatedMenu.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenuProfile.v1',\n op: WidgetOperationTypes.APPEND,\n element: (\n <ProfileLinkMenuItem\n label={messages['header.user.menu.profile']}\n role=\"org.openedx.frontend.role.profile\"\n variant=\"dropdownItem\"\n />\n )\n },\n {\n slotId: 'org.openedx.frontend.slot.header.authenticatedMenu.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenuAccount.v1',\n op: WidgetOperationTypes.APPEND,\n element: (\n <LinkMenuItem\n label={messages['header.user.menu.account']}\n role=\"org.openedx.frontend.role.account\"\n variant=\"dropdownItem\"\n />\n )\n },\n {\n slotId: 'org.openedx.frontend.slot.header.authenticatedMenu.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenuLogout.v1',\n op: WidgetOperationTypes.APPEND,\n element: (\n <LinkMenuItem\n label={messages['header.user.menu.logout']}\n role=\"org.openedx.frontend.role.logout\"\n variant=\"dropdownItem\"\n />\n )\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopRight.v1',\n id: 'org.openedx.frontend.widget.header.desktopAnonymousMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AnonymousMenu />,\n condition: {\n authenticated: false,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.anonymousMenu.v1',\n id: 'org.openedx.frontend.widget.header.anonymousMenuLogin.v1',\n op: WidgetOperationTypes.APPEND,\n component: LoginButton,\n },\n {\n slotId: 'org.openedx.frontend.slot.header.anonymousMenu.v1',\n id: 'org.openedx.frontend.widget.header.anonymousMenuRegister.v1',\n op: WidgetOperationTypes.APPEND,\n component: RegisterButton,\n },\n\n // Mobile\n {\n slotId: 'org.openedx.frontend.slot.header.mobileCenter.v1',\n id: 'org.openedx.frontend.widget.header.mobileLogo.v1',\n op: WidgetOperationTypes.APPEND,\n element: <Logo />,\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileMenu.v1',\n id: 'org.openedx.frontend.widget.header.mobileMenuLinks.v1',\n op: WidgetOperationTypes.APPEND,\n component: MobileNavLinks\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileRight.v1',\n id: 'org.openedx.frontend.widget.header.mobileAuthenticatedMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AuthenticatedMenu />,\n condition: {\n authenticated: true,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileRight.v1',\n id: 'org.openedx.frontend.widget.header.mobileAnonymousMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AnonymousMenu />,\n condition: {\n authenticated: false,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.courseNavigationBar.v1',\n id: 'org.openedx.frontend.widget.header.courseNavigationBar.v1',\n op: WidgetOperationTypes.APPEND,\n component: CourseTabsNavigation,\n condition: {\n callback: () => isCourseBarRoute(),\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.masqueradeBar.v1',\n id: 'org.openedx.frontend.widget.header.masqueradeBar.v1',\n op: WidgetOperationTypes.APPEND,\n component: MasqueradeBar,\n condition: {\n callback: () => isCourseBarMasqueradeRoute(),\n }\n }\n ]\n};\n\nexport default config;\n"]}
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../../shell/header/app.tsx"],"names":[],"mappings":"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","sourcesContent":["import { WidgetOperationTypes } from '../../runtime';\nimport { App } from '../../types';\nimport Logo from '../Logo';\nimport LinkMenuItem from '../menus/LinkMenuItem';\nimport ProfileLinkMenuItem from '../menus/ProfileLinkMenuItem';\nimport AnonymousMenu from './anonymous-menu/AnonymousMenu';\nimport LoginButton from './anonymous-menu/LoginButton';\nimport RegisterButton from './anonymous-menu/RegisterButton';\nimport AuthenticatedMenu from './AuthenticatedMenu';\nimport DesktopLayout from './desktop/DesktopLayout';\nimport PrimaryNavLinks from './desktop/PrimaryNavLinks';\nimport SecondaryNavLinks from './desktop/SecondaryNavLinks';\nimport MobileLayout from './mobile/MobileLayout';\nimport MobileNavLinks from './mobile/MobileNavLinks';\n\nimport messages from '../Shell.messages';\nimport CourseTabsNavigation from './course-bar/navigation/CourseTabsNavigation';\nimport MasqueradeBar from './course-bar/masquerade/MasqueradeBar';\nimport { isCourseBarMasqueradeRoute, isCourseBarRoute } from './course-bar/utils';\nimport { appId } from './constants';\nimport './app.scss';\n\nconst config: App = {\n appId,\n slots: [\n // Layouts\n {\n slotId: 'org.openedx.frontend.slot.header.desktop.v1',\n id: 'org.openedx.frontend.widget.header.desktopLayout.v1',\n op: WidgetOperationTypes.APPEND,\n component: DesktopLayout\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobile.v1',\n id: 'org.openedx.frontend.widget.header.mobileLayout.v1',\n op: WidgetOperationTypes.APPEND,\n component: MobileLayout\n },\n\n // Desktop\n {\n slotId: 'org.openedx.frontend.slot.header.desktopLeft.v1',\n id: 'org.openedx.frontend.widget.header.desktopLogo.v1',\n op: WidgetOperationTypes.APPEND,\n element: <Logo />,\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopLeft.v1',\n id: 'org.openedx.frontend.widget.header.desktopPrimaryLinks.v1',\n op: WidgetOperationTypes.APPEND,\n component: PrimaryNavLinks\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopRight.v1',\n id: 'org.openedx.frontend.widget.header.desktopSecondaryLinks.v1',\n op: WidgetOperationTypes.APPEND,\n component: SecondaryNavLinks\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopRight.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AuthenticatedMenu />,\n condition: {\n authenticated: true,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.authenticatedMenu.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenuProfile.v1',\n op: WidgetOperationTypes.APPEND,\n element: (\n <ProfileLinkMenuItem\n label={messages['header.user.menu.profile']}\n role=\"org.openedx.frontend.role.profile\"\n variant=\"dropdownItem\"\n />\n )\n },\n {\n slotId: 'org.openedx.frontend.slot.header.authenticatedMenu.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenuAccount.v1',\n op: WidgetOperationTypes.APPEND,\n element: (\n <LinkMenuItem\n label={messages['header.user.menu.account']}\n role=\"org.openedx.frontend.role.account\"\n variant=\"dropdownItem\"\n />\n )\n },\n {\n slotId: 'org.openedx.frontend.slot.header.authenticatedMenu.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenuLogout.v1',\n op: WidgetOperationTypes.APPEND,\n element: (\n <LinkMenuItem\n label={messages['header.user.menu.logout']}\n role=\"org.openedx.frontend.role.logout\"\n variant=\"dropdownItem\"\n />\n )\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopRight.v1',\n id: 'org.openedx.frontend.widget.header.desktopAnonymousMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AnonymousMenu />,\n condition: {\n authenticated: false,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.anonymousMenu.v1',\n id: 'org.openedx.frontend.widget.header.anonymousMenuLogin.v1',\n op: WidgetOperationTypes.APPEND,\n component: LoginButton,\n },\n {\n slotId: 'org.openedx.frontend.slot.header.anonymousMenu.v1',\n id: 'org.openedx.frontend.widget.header.anonymousMenuRegister.v1',\n op: WidgetOperationTypes.APPEND,\n component: RegisterButton,\n },\n\n // Mobile\n {\n slotId: 'org.openedx.frontend.slot.header.mobileCenter.v1',\n id: 'org.openedx.frontend.widget.header.mobileLogo.v1',\n op: WidgetOperationTypes.APPEND,\n element: <Logo />,\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileMenu.v1',\n id: 'org.openedx.frontend.widget.header.mobileMenuLinks.v1',\n op: WidgetOperationTypes.APPEND,\n component: MobileNavLinks\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileRight.v1',\n id: 'org.openedx.frontend.widget.header.mobileAuthenticatedMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AuthenticatedMenu />,\n condition: {\n authenticated: true,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileRight.v1',\n id: 'org.openedx.frontend.widget.header.mobileAnonymousMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AnonymousMenu />,\n condition: {\n authenticated: false,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.courseNavigationBar.v1',\n id: 'org.openedx.frontend.widget.header.courseNavigationBar.v1',\n op: WidgetOperationTypes.APPEND,\n component: CourseTabsNavigation,\n condition: {\n callback: () => isCourseBarRoute(),\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.masqueradeBar.v1',\n id: 'org.openedx.frontend.widget.header.masqueradeBar.v1',\n op: WidgetOperationTypes.APPEND,\n component: MasqueradeBar,\n condition: {\n authenticated: true,\n callback: () => isCourseBarMasqueradeRoute(),\n }\n }\n ]\n};\n\nexport default config;\n"]}
|
|
@@ -11,13 +11,12 @@ export default function MasqueradeBar() {
|
|
|
11
11
|
const { formatMessage } = useIntl();
|
|
12
12
|
const { courseId = '', unitId = '' } = useParams();
|
|
13
13
|
const masquerade = useMasqueradeState(courseId);
|
|
14
|
-
const { errorMessage, isLoading, isDenied
|
|
15
|
-
/* Render nothing while we wait for the first response, and when the
|
|
16
|
-
*
|
|
17
|
-
* partial bar plus an alert. */
|
|
14
|
+
const { errorMessage, isLoading, isDenied } = masquerade;
|
|
15
|
+
/* Render nothing while we wait for the first response, and when the load
|
|
16
|
+
* fails or the server says this user can't masquerade. */
|
|
18
17
|
if (isLoading || isDenied) {
|
|
19
18
|
return null;
|
|
20
19
|
}
|
|
21
|
-
return (_jsx(MasqueradeContext.Provider, { value: masquerade, children: _jsxs("div", { role: "region", "aria-label": formatMessage(messages.ariaLabel), children: [_jsx("div", { className: "bg-primary text-white", children: _jsx(Container, { fluid: true, size: "xl", children: _jsxs("div", { className: "py-3 d-md-flex justify-content-end align-items-start", children: [
|
|
20
|
+
return (_jsx(MasqueradeContext.Provider, { value: masquerade, children: _jsxs("div", { role: "region", "aria-label": formatMessage(messages.ariaLabel), children: [_jsx("div", { className: "bg-primary text-white", children: _jsx(Container, { fluid: true, size: "xl", children: _jsxs("div", { className: "py-3 d-md-flex justify-content-end align-items-start", children: [_jsx("div", { className: "align-items-center flex-grow-1 d-md-flex mx-1 my-1", children: _jsx(MasqueradeWidget, {}) }), _jsx(StudioLink, { courseId: courseId, unitId: unitId })] }) }) }), errorMessage && (_jsx(Container, { fluid: true, size: "xl", className: "mt-3", children: _jsx(Alert, { variant: "warning", role: "alert", dismissible: false, className: "mb-0", children: formatErrorMessage(formatMessage, errorMessage) }) }))] }) }));
|
|
22
21
|
}
|
|
23
22
|
//# sourceMappingURL=MasqueradeBar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MasqueradeBar.js","sourceRoot":"","sources":["../../../../../shell/header/course-bar/masquerade/MasqueradeBar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,gBAAgB,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,UAAU,MAAM,cAAc,CAAC;AACtC,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC,MAAM,CAAC,OAAO,UAAU,aAAa;IACnC,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IACpC,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,SAAS,EAAE,CAAC;IACnD,MAAM,UAAU,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,
|
|
1
|
+
{"version":3,"file":"MasqueradeBar.js","sourceRoot":"","sources":["../../../../../shell/header/course-bar/masquerade/MasqueradeBar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,gBAAgB,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,UAAU,MAAM,cAAc,CAAC;AACtC,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC,MAAM,CAAC,OAAO,UAAU,aAAa;IACnC,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IACpC,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,SAAS,EAAE,CAAC;IACnD,MAAM,UAAU,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,UAAU,CAAC;IAEzD;8DAC0D;IAC1D,IAAI,SAAS,IAAI,QAAQ,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CACL,KAAC,iBAAiB,CAAC,QAAQ,IAAC,KAAK,EAAE,UAAU,YAC3C,eAAK,IAAI,EAAC,QAAQ,gBAAa,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC,aAC9D,cAAK,SAAS,EAAC,uBAAuB,YACpC,KAAC,SAAS,IAAC,KAAK,QAAC,IAAI,EAAC,IAAI,YACxB,eAAK,SAAS,EAAC,sDAAsD,aACnE,cAAK,SAAS,EAAC,oDAAoD,YACjE,KAAC,gBAAgB,KAAG,GAChB,EACN,KAAC,UAAU,IAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAI,IAC9C,GACI,GACR,EACL,YAAY,IAAI,CACf,KAAC,SAAS,IAAC,KAAK,QAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,MAAM,YACzC,KAAC,KAAK,IAAC,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,OAAO,EAAC,WAAW,EAAE,KAAK,EAAE,SAAS,EAAC,MAAM,YACvE,kBAAkB,CAAC,aAAa,EAAE,YAAY,CAAC,GAC1C,GACE,CACb,IACG,GACqB,CAC9B,CAAC;AACJ,CAAC","sourcesContent":["import { useIntl } from '@openedx/frontend-base';\nimport { Alert, Container } from '@openedx/paragon';\nimport { useParams } from 'react-router-dom';\n\nimport { MasqueradeContext } from './MasqueradeContext';\nimport MasqueradeWidget from './masquerade-widget';\nimport { formatErrorMessage, useMasqueradeState } from './hooks';\nimport StudioLink from './StudioLink';\nimport messages from './messages';\n\nexport default function MasqueradeBar() {\n const { formatMessage } = useIntl();\n const { courseId = '', unitId = '' } = useParams();\n const masquerade = useMasqueradeState(courseId);\n const { errorMessage, isLoading, isDenied } = masquerade;\n\n /* Render nothing while we wait for the first response, and when the load\n * fails or the server says this user can't masquerade. */\n if (isLoading || isDenied) {\n return null;\n }\n\n return (\n <MasqueradeContext.Provider value={masquerade}>\n <div role=\"region\" aria-label={formatMessage(messages.ariaLabel)}>\n <div className=\"bg-primary text-white\">\n <Container fluid size=\"xl\">\n <div className=\"py-3 d-md-flex justify-content-end align-items-start\">\n <div className=\"align-items-center flex-grow-1 d-md-flex mx-1 my-1\">\n <MasqueradeWidget />\n </div>\n <StudioLink courseId={courseId} unitId={unitId} />\n </div>\n </Container>\n </div>\n {errorMessage && (\n <Container fluid size=\"xl\" className=\"mt-3\">\n <Alert variant=\"warning\" role=\"alert\" dismissible={false} className=\"mb-0\">\n {formatErrorMessage(formatMessage, errorMessage)}\n </Alert>\n </Container>\n )}\n </div>\n </MasqueradeContext.Provider>\n );\n}\n"]}
|
|
@@ -10,9 +10,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { getSiteConfig, camelCaseObject, getAuthenticatedHttpClient } from '@openedx/frontend-base';
|
|
11
11
|
export function getMasqueradeOptions(courseId) {
|
|
12
12
|
return __awaiter(this, void 0, void 0, function* () {
|
|
13
|
+
var _a;
|
|
13
14
|
const url = `${getSiteConfig().lmsBaseUrl}/courses/${courseId}/masquerade`;
|
|
14
|
-
const
|
|
15
|
-
|
|
15
|
+
const response = yield getAuthenticatedHttpClient().get(url, {});
|
|
16
|
+
/* The LMS 302s to /login instead of returning 403 when masquerade is denied. */
|
|
17
|
+
if (((_a = response.request) === null || _a === void 0 ? void 0 : _a.responseURL) && response.request.responseURL !== url) {
|
|
18
|
+
const error = Object.assign(new Error(`Masquerade endpoint redirected to ${response.request.responseURL}`), {
|
|
19
|
+
customAttributes: { httpErrorStatus: 403 },
|
|
20
|
+
});
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
return camelCaseObject(response.data);
|
|
16
24
|
});
|
|
17
25
|
}
|
|
18
26
|
export function postMasqueradeOptions(courseId, payload) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../../../../shell/header/course-bar/masquerade/data/api.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAkCpG,MAAM,UAAgB,oBAAoB,CAAC,QAAgB
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../../../../shell/header/course-bar/masquerade/data/api.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAkCpG,MAAM,UAAgB,oBAAoB,CAAC,QAAgB;;;QACzD,MAAM,GAAG,GAAG,GAAG,aAAa,EAAE,CAAC,UAAU,YAAY,QAAQ,aAAa,CAAC;QAC3E,MAAM,QAAQ,GAAG,MAAM,0BAA0B,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACjE,gFAAgF;QAChF,IAAI,CAAA,MAAA,QAAQ,CAAC,OAAO,0CAAE,WAAW,KAAI,QAAQ,CAAC,OAAO,CAAC,WAAW,KAAK,GAAG,EAAE,CAAC;YAC1E,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,qCAAqC,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,EAAE;gBAC1G,gBAAgB,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE;aAC3C,CAAC,CAAC;YACH,MAAM,KAAK,CAAC;QACd,CAAC;QACD,OAAO,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;CAAA;AAED,MAAM,UAAgB,qBAAqB,CAAC,QAAgB,EAAE,OAA0B;;QACtF,MAAM,GAAG,GAAG,GAAG,aAAa,EAAE,CAAC,UAAU,YAAY,QAAQ,aAAa,CAAC;QAC3E,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,0BAA0B,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACvE,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;CAAA","sourcesContent":["import { getSiteConfig, camelCaseObject, getAuthenticatedHttpClient } from '@openedx/frontend-base';\n\nexport type Role = 'staff' | 'student';\n\nexport interface ActiveMasqueradeData {\n role: Role,\n userName: string | null,\n userPartitionId: number | null,\n groupId: number | null,\n groupName: string | null,\n}\n\nexport interface MasqueradeOption {\n name: string,\n role: Role,\n userName?: string,\n groupId?: number,\n userPartitionId?: number,\n}\n\nexport interface MasqueradeStatus {\n success: boolean,\n error?: string,\n active: ActiveMasqueradeData,\n available: MasqueradeOption[],\n}\n\nexport interface MasqueradePayload {\n role?: Role,\n user_name?: string,\n group_id?: number,\n user_partition_id?: number,\n}\n\nexport async function getMasqueradeOptions(courseId: string): Promise<MasqueradeStatus> {\n const url = `${getSiteConfig().lmsBaseUrl}/courses/${courseId}/masquerade`;\n const response = await getAuthenticatedHttpClient().get(url, {});\n /* The LMS 302s to /login instead of returning 403 when masquerade is denied. */\n if (response.request?.responseURL && response.request.responseURL !== url) {\n const error = Object.assign(new Error(`Masquerade endpoint redirected to ${response.request.responseURL}`), {\n customAttributes: { httpErrorStatus: 403 },\n });\n throw error;\n }\n return camelCaseObject(response.data);\n}\n\nexport async function postMasqueradeOptions(courseId: string, payload: MasqueradePayload): Promise<MasqueradeStatus> {\n const url = `${getSiteConfig().lmsBaseUrl}/courses/${courseId}/masquerade`;\n const { data } = await getAuthenticatedHttpClient().post(url, payload);\n return camelCaseObject(data);\n}\n"]}
|
|
@@ -55,25 +55,19 @@ function getHttpStatus(error) {
|
|
|
55
55
|
return (_a = error === null || error === void 0 ? void 0 : error.customAttributes) === null || _a === void 0 ? void 0 : _a.httpErrorStatus;
|
|
56
56
|
}
|
|
57
57
|
/*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* failures (network, 5xx) are treated as "couldn't load" and surface as an
|
|
61
|
-
* alert.
|
|
58
|
+
* Anything other than a successful load with `success: true` hides the bar:
|
|
59
|
+
* 403, 200 with `success: false`, network errors, 5xx, redirected requests.
|
|
62
60
|
*/
|
|
63
61
|
function isQueryDenied(query) {
|
|
64
62
|
if (query.isError) {
|
|
65
|
-
return
|
|
63
|
+
return true;
|
|
66
64
|
}
|
|
67
65
|
return query.data !== undefined && !query.data.success;
|
|
68
66
|
}
|
|
69
67
|
export function formatErrorMessage(formatMessage, error) {
|
|
70
68
|
return typeof error === 'string' ? error : formatMessage(error);
|
|
71
69
|
}
|
|
72
|
-
function pickErrorMessage(
|
|
73
|
-
/* Denial is handled by hiding the bar; only surface truly-failed loads here. */
|
|
74
|
-
if (query.isError && getHttpStatus(query.error) !== 403) {
|
|
75
|
-
return messages.failedToLoadOptions;
|
|
76
|
-
}
|
|
70
|
+
function pickErrorMessage(mutation) {
|
|
77
71
|
if (mutation.isError) {
|
|
78
72
|
return getHttpStatus(mutation.error) === 404
|
|
79
73
|
? messages.noStudentFound
|
|
@@ -174,10 +168,8 @@ export function useMasqueradeState(courseId) {
|
|
|
174
168
|
}, [pendingOption, active]);
|
|
175
169
|
const showUserNameInput = active.userName !== null
|
|
176
170
|
|| (pendingOption === null || pendingOption === void 0 ? void 0 : pendingOption.userName) !== undefined;
|
|
177
|
-
const errorMessage = pickErrorMessage(
|
|
171
|
+
const errorMessage = pickErrorMessage(mutation);
|
|
178
172
|
const isDenied = isQueryDenied(query);
|
|
179
|
-
/* Query landed but we couldn't load options — bar shows but widget hides. */
|
|
180
|
-
const isUnreachable = query.isError && getHttpStatus(query.error) !== 403;
|
|
181
173
|
/* Loading until the first response (success or failure) lands. */
|
|
182
174
|
const isLoading = query.isLoading;
|
|
183
175
|
const select = (option) => {
|
|
@@ -206,7 +198,6 @@ export function useMasqueradeState(courseId) {
|
|
|
206
198
|
isSubmitting: mutation.isPending,
|
|
207
199
|
isLoading,
|
|
208
200
|
isDenied,
|
|
209
|
-
isUnreachable,
|
|
210
201
|
};
|
|
211
202
|
}
|
|
212
203
|
//# sourceMappingURL=hooks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../../../shell/header/course-bar/masquerade/hooks.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport type { MessageDescriptor } from 'react-intl';\nimport { useLocation, useNavigate } from 'react-router-dom';\n\nimport {\n ActiveMasqueradeData,\n MasqueradeOption,\n MasqueradeStatus,\n MasqueradePayload,\n getMasqueradeOptions,\n postMasqueradeOptions,\n} from './data/api';\nimport {\n CourseHomeCourseMetadata,\n courseHomeCourseMetadataQueryKey,\n findActiveTab,\n getCourseHomeCourseMetadata,\n} from '../data/service';\nimport { isClientRoute } from '../utils';\nimport messages from './messages';\n\nconst defaultActive: ActiveMasqueradeData = {\n role: 'staff',\n groupId: null,\n groupName: null,\n userName: null,\n userPartitionId: null,\n};\n\nfunction toMasqueradePayload(option: MasqueradeOption): MasqueradePayload {\n const payload: MasqueradePayload = { role: option.role };\n if (option.groupId !== undefined && option.userPartitionId !== undefined) {\n payload.group_id = option.groupId;\n payload.user_partition_id = option.userPartitionId;\n }\n return payload;\n}\n\n/*\n * Whether `option` represents the active masquerade. Three shapes:\n * - userName defined: the \"Specific Student...\" prompt; active when the\n * server says we're masquerading as some specific user.\n * - groupId defined: a group option; active when the group ids match.\n * - neither: a plain role option (e.g. Staff); active when no group/user\n * is set.\n */\nexport function isOptionSelected(option: MasqueradeOption, active: ActiveMasqueradeData): boolean {\n if (option.role !== active.role) {\n return false;\n }\n if (option.userName !== undefined) {\n return active.userName !== null;\n }\n if (option.groupId !== undefined) {\n return option.groupId === active.groupId\n && option.userPartitionId === active.userPartitionId;\n }\n return active.userName === null && active.groupId === null;\n}\n\ninterface HttpishError {\n customAttributes?: { httpErrorStatus?: number },\n}\n\nfunction getHttpStatus(error: unknown): number | undefined {\n return (error as HttpishError | null)?.customAttributes?.httpErrorStatus;\n}\n\n/*\n * The server tells us \"no, you can't masquerade here\" via either HTTP 403 or\n * a 200 with `success: false`. Both mean: hide the bar entirely. Other\n * failures (network, 5xx) are treated as \"couldn't load\" and surface as an\n * alert.\n */\nfunction isQueryDenied(query: { isError: boolean, error: unknown, data: MasqueradeStatus | undefined }): boolean {\n if (query.isError) {\n return getHttpStatus(query.error) === 403;\n }\n return query.data !== undefined && !query.data.success;\n}\n\n/*\n * Errors are returned as MessageDescriptors when the frontend can pick the\n * message, or as a raw string when the server's `data.error` is more specific\n * than anything we'd hand-pick. formatErrorMessage handles both at the\n * render site.\n */\nexport type MasqueradeErrorMessage = MessageDescriptor | string;\n\nexport function formatErrorMessage(\n formatMessage: (descriptor: MessageDescriptor) => string,\n error: MasqueradeErrorMessage,\n): string {\n return typeof error === 'string' ? error : formatMessage(error);\n}\n\nfunction pickErrorMessage(\n query: { isError: boolean, error: unknown, data: MasqueradeStatus | undefined },\n mutation: { isError: boolean, error: unknown, data: MasqueradeStatus | undefined },\n): MasqueradeErrorMessage | null {\n /* Denial is handled by hiding the bar; only surface truly-failed loads here. */\n if (query.isError && getHttpStatus(query.error) !== 403) {\n return messages.failedToLoadOptions;\n }\n if (mutation.isError) {\n return getHttpStatus(mutation.error) === 404\n ? messages.noStudentFound\n : messages.genericSubmitError;\n }\n if (mutation.data && !mutation.data.success) {\n return mutation.data.error || messages.genericSubmitError;\n }\n return null;\n}\n\nexport interface MasqueradeState {\n active: ActiveMasqueradeData,\n available: MasqueradeOption[],\n pendingOption: MasqueradeOption | null,\n showUserNameInput: boolean,\n userName: string,\n setUserName: (value: string) => void,\n select: (option: MasqueradeOption) => void,\n submitUserName: () => void,\n errorMessage: MasqueradeErrorMessage | null,\n isSubmitting: boolean,\n isLoading: boolean,\n isDenied: boolean,\n isUnreachable: boolean,\n}\n\nexport function useMasqueradeState(courseId: string): MasqueradeState {\n const queryClient = useQueryClient();\n const navigate = useNavigate();\n const location = useLocation();\n /*\n * Tracks the option the user just clicked, so the toggle and active-marker\n * can update before the server confirms. Cleared once a mutation succeeds\n * (the server's active state becomes the truth) or replaced when the user\n * picks something else.\n */\n const [pendingOption, setPendingOption] = useState<MasqueradeOption | null>(null);\n const [userName, setUserName] = useState('');\n\n const query = useQuery({\n queryKey: ['masquerade', courseId],\n queryFn: () => getMasqueradeOptions(courseId),\n });\n\n const mutation = useMutation({\n mutationFn: (payload: MasqueradePayload) => postMasqueradeOptions(courseId, payload),\n onSuccess: async (data, payload) => {\n if (!data?.success) {\n /* Server rejected the pick. Clear the optimistic pending state so the\n * UI snaps back to the real active option, except for userName\n * submissions where the input must stay open for retry. */\n if (payload.user_name === undefined) {\n setPendingOption(null);\n }\n return;\n }\n\n queryClient.invalidateQueries();\n\n /* The course-tabs metadata is the source of truth for \"what pages this\n * user can see in this course.\" Fetch the post-masquerade tabs and\n * redirect to the first one if the current path no longer appears. */\n const meta = await queryClient.fetchQuery<CourseHomeCourseMetadata>({\n queryKey: courseHomeCourseMetadataQueryKey(courseId),\n queryFn: () => getCourseHomeCourseMetadata(courseId),\n });\n const fallback = meta.tabs[0];\n if (!fallback) {\n return;\n }\n const stillVisible = findActiveTab(meta.tabs, location.pathname);\n if (stillVisible) {\n return;\n }\n const targetPath = new URL(fallback.url).pathname;\n if (isClientRoute(targetPath)) {\n navigate(targetPath, { replace: true });\n } else {\n window.location.assign(fallback.url);\n }\n },\n onError: (_error, payload) => {\n /* Same as the !success branch: revert the optimistic pick, but keep the\n * userName input open so the user can fix a typo and retry. */\n if (payload.user_name === undefined) {\n setPendingOption(null);\n }\n },\n });\n\n const active = (query.data?.success && query.data.active) || defaultActive;\n const available = (query.data?.success && query.data.available) || [];\n\n /*\n * Seed the input from active.userName once, on the first successful query.\n * After that the input belongs to the user — refetches (e.g. triggered by\n * a masquerade change in another tab) must not clobber what they typed.\n */\n const hasSeededRef = useRef(false);\n useEffect(() => {\n if (hasSeededRef.current || !query.isSuccess) {\n return;\n }\n hasSeededRef.current = true;\n if (active.userName) {\n setUserName(active.userName);\n }\n }, [query.isSuccess, active.userName]);\n\n /*\n * Clear pendingOption once the refetched active state actually reflects it.\n * Clearing in onSuccess instead would briefly flash the old active state\n * between mutation success and the refetch landing.\n */\n useEffect(() => {\n if (pendingOption && isOptionSelected(pendingOption, active)) {\n setPendingOption(null);\n }\n }, [pendingOption, active]);\n\n const showUserNameInput = active.userName !== null\n || pendingOption?.userName !== undefined;\n\n const errorMessage = pickErrorMessage(query, mutation);\n const isDenied = isQueryDenied(query);\n /* Query landed but we couldn't load options — bar shows but widget hides. */\n const isUnreachable = query.isError && getHttpStatus(query.error) !== 403;\n /* Loading until the first response (success or failure) lands. */\n const isLoading = query.isLoading;\n\n const select = (option: MasqueradeOption) => {\n /* Clear any prior mutation error so it doesn't leak into the new selection. */\n mutation.reset();\n setPendingOption(option);\n if (option.userName !== undefined) {\n setUserName(option.userName);\n return;\n }\n mutation.mutate(toMasqueradePayload(option));\n };\n\n const submitUserName = () => {\n mutation.mutate({ role: 'student', user_name: userName });\n };\n\n return {\n active,\n available,\n pendingOption,\n showUserNameInput,\n userName,\n setUserName,\n select,\n submitUserName,\n errorMessage,\n isSubmitting: mutation.isPending,\n isLoading,\n isDenied,\n isUnreachable,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../../../shell/header/course-bar/masquerade/hooks.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport type { MessageDescriptor } from 'react-intl';\nimport { useLocation, useNavigate } from 'react-router-dom';\n\nimport {\n ActiveMasqueradeData,\n MasqueradeOption,\n MasqueradeStatus,\n MasqueradePayload,\n getMasqueradeOptions,\n postMasqueradeOptions,\n} from './data/api';\nimport {\n CourseHomeCourseMetadata,\n courseHomeCourseMetadataQueryKey,\n findActiveTab,\n getCourseHomeCourseMetadata,\n} from '../data/service';\nimport { isClientRoute } from '../utils';\nimport messages from './messages';\n\nconst defaultActive: ActiveMasqueradeData = {\n role: 'staff',\n groupId: null,\n groupName: null,\n userName: null,\n userPartitionId: null,\n};\n\nfunction toMasqueradePayload(option: MasqueradeOption): MasqueradePayload {\n const payload: MasqueradePayload = { role: option.role };\n if (option.groupId !== undefined && option.userPartitionId !== undefined) {\n payload.group_id = option.groupId;\n payload.user_partition_id = option.userPartitionId;\n }\n return payload;\n}\n\n/*\n * Whether `option` represents the active masquerade. Three shapes:\n * - userName defined: the \"Specific Student...\" prompt; active when the\n * server says we're masquerading as some specific user.\n * - groupId defined: a group option; active when the group ids match.\n * - neither: a plain role option (e.g. Staff); active when no group/user\n * is set.\n */\nexport function isOptionSelected(option: MasqueradeOption, active: ActiveMasqueradeData): boolean {\n if (option.role !== active.role) {\n return false;\n }\n if (option.userName !== undefined) {\n return active.userName !== null;\n }\n if (option.groupId !== undefined) {\n return option.groupId === active.groupId\n && option.userPartitionId === active.userPartitionId;\n }\n return active.userName === null && active.groupId === null;\n}\n\ninterface HttpishError {\n customAttributes?: { httpErrorStatus?: number },\n}\n\nfunction getHttpStatus(error: unknown): number | undefined {\n return (error as HttpishError | null)?.customAttributes?.httpErrorStatus;\n}\n\n/*\n * Anything other than a successful load with `success: true` hides the bar:\n * 403, 200 with `success: false`, network errors, 5xx, redirected requests.\n */\nfunction isQueryDenied(query: { isError: boolean, error: unknown, data: MasqueradeStatus | undefined }): boolean {\n if (query.isError) {\n return true;\n }\n return query.data !== undefined && !query.data.success;\n}\n\n/*\n * Errors are returned as MessageDescriptors when the frontend can pick the\n * message, or as a raw string when the server's `data.error` is more specific\n * than anything we'd hand-pick. formatErrorMessage handles both at the\n * render site.\n */\nexport type MasqueradeErrorMessage = MessageDescriptor | string;\n\nexport function formatErrorMessage(\n formatMessage: (descriptor: MessageDescriptor) => string,\n error: MasqueradeErrorMessage,\n): string {\n return typeof error === 'string' ? error : formatMessage(error);\n}\n\nfunction pickErrorMessage(\n mutation: { isError: boolean, error: unknown, data: MasqueradeStatus | undefined },\n): MasqueradeErrorMessage | null {\n if (mutation.isError) {\n return getHttpStatus(mutation.error) === 404\n ? messages.noStudentFound\n : messages.genericSubmitError;\n }\n if (mutation.data && !mutation.data.success) {\n return mutation.data.error || messages.genericSubmitError;\n }\n return null;\n}\n\nexport interface MasqueradeState {\n active: ActiveMasqueradeData,\n available: MasqueradeOption[],\n pendingOption: MasqueradeOption | null,\n showUserNameInput: boolean,\n userName: string,\n setUserName: (value: string) => void,\n select: (option: MasqueradeOption) => void,\n submitUserName: () => void,\n errorMessage: MasqueradeErrorMessage | null,\n isSubmitting: boolean,\n isLoading: boolean,\n isDenied: boolean,\n}\n\nexport function useMasqueradeState(courseId: string): MasqueradeState {\n const queryClient = useQueryClient();\n const navigate = useNavigate();\n const location = useLocation();\n /*\n * Tracks the option the user just clicked, so the toggle and active-marker\n * can update before the server confirms. Cleared once a mutation succeeds\n * (the server's active state becomes the truth) or replaced when the user\n * picks something else.\n */\n const [pendingOption, setPendingOption] = useState<MasqueradeOption | null>(null);\n const [userName, setUserName] = useState('');\n\n const query = useQuery({\n queryKey: ['masquerade', courseId],\n queryFn: () => getMasqueradeOptions(courseId),\n });\n\n const mutation = useMutation({\n mutationFn: (payload: MasqueradePayload) => postMasqueradeOptions(courseId, payload),\n onSuccess: async (data, payload) => {\n if (!data?.success) {\n /* Server rejected the pick. Clear the optimistic pending state so the\n * UI snaps back to the real active option, except for userName\n * submissions where the input must stay open for retry. */\n if (payload.user_name === undefined) {\n setPendingOption(null);\n }\n return;\n }\n\n queryClient.invalidateQueries();\n\n /* The course-tabs metadata is the source of truth for \"what pages this\n * user can see in this course.\" Fetch the post-masquerade tabs and\n * redirect to the first one if the current path no longer appears. */\n const meta = await queryClient.fetchQuery<CourseHomeCourseMetadata>({\n queryKey: courseHomeCourseMetadataQueryKey(courseId),\n queryFn: () => getCourseHomeCourseMetadata(courseId),\n });\n const fallback = meta.tabs[0];\n if (!fallback) {\n return;\n }\n const stillVisible = findActiveTab(meta.tabs, location.pathname);\n if (stillVisible) {\n return;\n }\n const targetPath = new URL(fallback.url).pathname;\n if (isClientRoute(targetPath)) {\n navigate(targetPath, { replace: true });\n } else {\n window.location.assign(fallback.url);\n }\n },\n onError: (_error, payload) => {\n /* Same as the !success branch: revert the optimistic pick, but keep the\n * userName input open so the user can fix a typo and retry. */\n if (payload.user_name === undefined) {\n setPendingOption(null);\n }\n },\n });\n\n const active = (query.data?.success && query.data.active) || defaultActive;\n const available = (query.data?.success && query.data.available) || [];\n\n /*\n * Seed the input from active.userName once, on the first successful query.\n * After that the input belongs to the user — refetches (e.g. triggered by\n * a masquerade change in another tab) must not clobber what they typed.\n */\n const hasSeededRef = useRef(false);\n useEffect(() => {\n if (hasSeededRef.current || !query.isSuccess) {\n return;\n }\n hasSeededRef.current = true;\n if (active.userName) {\n setUserName(active.userName);\n }\n }, [query.isSuccess, active.userName]);\n\n /*\n * Clear pendingOption once the refetched active state actually reflects it.\n * Clearing in onSuccess instead would briefly flash the old active state\n * between mutation success and the refetch landing.\n */\n useEffect(() => {\n if (pendingOption && isOptionSelected(pendingOption, active)) {\n setPendingOption(null);\n }\n }, [pendingOption, active]);\n\n const showUserNameInput = active.userName !== null\n || pendingOption?.userName !== undefined;\n\n const errorMessage = pickErrorMessage(mutation);\n const isDenied = isQueryDenied(query);\n /* Loading until the first response (success or failure) lands. */\n const isLoading = query.isLoading;\n\n const select = (option: MasqueradeOption) => {\n /* Clear any prior mutation error so it doesn't leak into the new selection. */\n mutation.reset();\n setPendingOption(option);\n if (option.userName !== undefined) {\n setUserName(option.userName);\n return;\n }\n mutation.mutate(toMasqueradePayload(option));\n };\n\n const submitUserName = () => {\n mutation.mutate({ role: 'student', user_name: userName });\n };\n\n return {\n active,\n available,\n pendingOption,\n showUserNameInput,\n userName,\n setUserName,\n select,\n submitUserName,\n errorMessage,\n isSubmitting: mutation.isPending,\n isLoading,\n isDenied,\n };\n}\n"]}
|
|
@@ -15,11 +15,6 @@ const messages = defineMessages({
|
|
|
15
15
|
defaultMessage: 'Studio',
|
|
16
16
|
description: 'Button to view in studio',
|
|
17
17
|
},
|
|
18
|
-
failedToLoadOptions: {
|
|
19
|
-
id: 'masqueradeBar.error.failedToLoadOptions',
|
|
20
|
-
defaultMessage: 'Unable to load masquerade options.',
|
|
21
|
-
description: 'Error shown when masquerade options cannot be retrieved from the server.',
|
|
22
|
-
},
|
|
23
18
|
noStudentFound: {
|
|
24
19
|
id: 'masqueradeBar.error.noStudentFound',
|
|
25
20
|
defaultMessage: 'No student with this username or email could be found.',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../../../shell/header/course-bar/masquerade/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,SAAS,EAAE;QACT,EAAE,EAAE,yBAAyB;QAC7B,cAAc,EAAE,gBAAgB;QAChC,WAAW,EAAE,yDAAyD;KACvE;IACD,iBAAiB,EAAE;QACjB,EAAE,EAAE,0BAA0B;QAC9B,cAAc,EAAE,iBAAiB;QACjC,WAAW,EAAE,yCAAyC;KACvD;IACD,WAAW,EAAE;QACX,EAAE,EAAE,sBAAsB;QAC1B,cAAc,EAAE,QAAQ;QACxB,WAAW,EAAE,0BAA0B;KACxC;IACD,
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../../../shell/header/course-bar/masquerade/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,SAAS,EAAE;QACT,EAAE,EAAE,yBAAyB;QAC7B,cAAc,EAAE,gBAAgB;QAChC,WAAW,EAAE,yDAAyD;KACvE;IACD,iBAAiB,EAAE;QACjB,EAAE,EAAE,0BAA0B;QAC9B,cAAc,EAAE,iBAAiB;QACjC,WAAW,EAAE,yCAAyC;KACvD;IACD,WAAW,EAAE;QACX,EAAE,EAAE,sBAAsB;QAC1B,cAAc,EAAE,QAAQ;QACxB,WAAW,EAAE,0BAA0B;KACxC;IACD,cAAc,EAAE;QACd,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,wDAAwD;QACxE,WAAW,EAAE,wEAAwE;KACtF;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,0CAA0C;QAC1D,WAAW,EAAE,2DAA2D;KACzE;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '../../../../runtime';\n\nconst messages = defineMessages({\n ariaLabel: {\n id: 'masqueradeBar.ariaLabel',\n defaultMessage: 'Masquerade bar',\n description: 'Accessible label identifying the masquerade bar region.',\n },\n titleViewCourseIn: {\n id: 'masqueradeBar.viewCourse',\n defaultMessage: 'View course in:',\n description: 'Button to view the course in the studio',\n },\n titleStudio: {\n id: 'masqueradeBar.studio',\n defaultMessage: 'Studio',\n description: 'Button to view in studio',\n },\n noStudentFound: {\n id: 'masqueradeBar.error.noStudentFound',\n defaultMessage: 'No student with this username or email could be found.',\n description: 'Error shown when masquerading by username and the user does not exist.',\n },\n genericSubmitError: {\n id: 'masqueradeBar.error.genericSubmit',\n defaultMessage: 'An error has occurred; please try again.',\n description: 'Generic error shown when the masquerade submission fails.',\n },\n});\n\nexport default messages;\n"]}
|