mirror of
https://github.com/outline/outline.git
synced 2025-12-21 02:29:41 -06:00
Add smart preloading of settings screens to reduce flicker (#9165)
This commit is contained in:
@@ -15,6 +15,7 @@ import Button from "~/components/Button";
|
|||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import Input from "~/components/Input";
|
import Input from "~/components/Input";
|
||||||
import InputSelectPermission from "~/components/InputSelectPermission";
|
import InputSelectPermission from "~/components/InputSelectPermission";
|
||||||
|
import { createLazyComponent } from "~/components/LazyLoad";
|
||||||
import Switch from "~/components/Switch";
|
import Switch from "~/components/Switch";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useBoolean from "~/hooks/useBoolean";
|
import useBoolean from "~/hooks/useBoolean";
|
||||||
@@ -22,7 +23,7 @@ import useCurrentTeam from "~/hooks/useCurrentTeam";
|
|||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import { EmptySelectValue } from "~/types";
|
import { EmptySelectValue } from "~/types";
|
||||||
|
|
||||||
const IconPicker = React.lazy(() => import("~/components/IconPicker"));
|
const IconPicker = createLazyComponent(() => import("~/components/IconPicker"));
|
||||||
|
|
||||||
export interface FormData {
|
export interface FormData {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -88,6 +89,11 @@ export const CollectionForm = observer(function CollectionForm_({
|
|||||||
|
|
||||||
const values = watch();
|
const values = watch();
|
||||||
|
|
||||||
|
// Preload the IconPicker component on mount
|
||||||
|
React.useEffect(() => {
|
||||||
|
void IconPicker.preload();
|
||||||
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// If the user hasn't picked an icon yet, go ahead and suggest one based on
|
// If the user hasn't picked an icon yet, go ahead and suggest one based on
|
||||||
// the name of the collection. It's the little things sometimes.
|
// the name of the collection. It's the little things sometimes.
|
||||||
@@ -202,7 +208,7 @@ export const CollectionForm = observer(function CollectionForm_({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const StyledIconPicker = styled(IconPicker)`
|
const StyledIconPicker = styled(IconPicker.Component)`
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
`;
|
`;
|
||||||
|
|||||||
47
app/components/LazyLoad.ts
Normal file
47
app/components/LazyLoad.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import lazyWithRetry from "~/utils/lazyWithRetry";
|
||||||
|
|
||||||
|
export interface LazyComponent<T extends React.ComponentType<any>> {
|
||||||
|
Component: React.LazyExoticComponent<T>;
|
||||||
|
preload: () => Promise<{ default: T }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LazyLoadOptions {
|
||||||
|
retries?: number;
|
||||||
|
interval?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a lazy-loaded component with preloading capability and automatic retries on failure.
|
||||||
|
*
|
||||||
|
* @param factory A function that returns a promise of a component (eg: () => import('./MyComponent'))
|
||||||
|
* @param options Optional configuration for retry behavior
|
||||||
|
* @returns An object containing the lazy Component and a preload function
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const MyComponent = createLazyComponent(() => import('./MyComponent'));
|
||||||
|
*
|
||||||
|
* function App() {
|
||||||
|
* return (
|
||||||
|
* <Suspense fallback={<div>Loading...</div>}>
|
||||||
|
* <MyComponent.Component />
|
||||||
|
* </Suspense>
|
||||||
|
* );
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Preload when needed:
|
||||||
|
* MyComponent.preload();
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function createLazyComponent<T extends React.ComponentType<any>>(
|
||||||
|
factory: () => Promise<{ default: T }>,
|
||||||
|
options: LazyLoadOptions = {}
|
||||||
|
): LazyComponent<T> {
|
||||||
|
const { retries, interval } = options;
|
||||||
|
|
||||||
|
return {
|
||||||
|
Component: lazyWithRetry(factory, retries, interval),
|
||||||
|
preload: factory,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { PopoverDisclosure, usePopoverState } from "reakit";
|
import { PopoverDisclosure, usePopoverState } from "reakit";
|
||||||
import EventBoundary from "@shared/components/EventBoundary";
|
import EventBoundary from "@shared/components/EventBoundary";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
|
import { createLazyComponent } from "~/components/LazyLoad";
|
||||||
import NudeButton from "~/components/NudeButton";
|
import NudeButton from "~/components/NudeButton";
|
||||||
import PlaceholderText from "~/components/PlaceholderText";
|
import PlaceholderText from "~/components/PlaceholderText";
|
||||||
import Popover from "~/components/Popover";
|
import Popover from "~/components/Popover";
|
||||||
@@ -12,7 +13,7 @@ import useOnClickOutside from "~/hooks/useOnClickOutside";
|
|||||||
import useWindowSize from "~/hooks/useWindowSize";
|
import useWindowSize from "~/hooks/useWindowSize";
|
||||||
import Tooltip from "../Tooltip";
|
import Tooltip from "../Tooltip";
|
||||||
|
|
||||||
const EmojiPanel = React.lazy(
|
const EmojiPanel = createLazyComponent(
|
||||||
() => import("~/components/IconPicker/components/EmojiPanel")
|
() => import("~/components/IconPicker/components/EmojiPanel")
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -104,6 +105,7 @@ const ReactionPicker: React.FC<Props> = ({
|
|||||||
aria-label={t("Reaction picker")}
|
aria-label={t("Reaction picker")}
|
||||||
className={className}
|
className={className}
|
||||||
onClick={handlePopoverButtonClick}
|
onClick={handlePopoverButtonClick}
|
||||||
|
onMouseEnter={() => EmojiPanel.preload()}
|
||||||
size={size}
|
size={size}
|
||||||
>
|
>
|
||||||
<ReactionIcon size={22} />
|
<ReactionIcon size={22} />
|
||||||
@@ -123,7 +125,7 @@ const ReactionPicker: React.FC<Props> = ({
|
|||||||
{popover.visible && (
|
{popover.visible && (
|
||||||
<React.Suspense fallback={<Placeholder />}>
|
<React.Suspense fallback={<Placeholder />}>
|
||||||
<EventBoundary>
|
<EventBoundary>
|
||||||
<EmojiPanel
|
<EmojiPanel.Component
|
||||||
height={300}
|
height={300}
|
||||||
panelWidth={panelWidth}
|
panelWidth={panelWidth}
|
||||||
query={query}
|
query={query}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ function SettingsSidebar() {
|
|||||||
<SidebarLink
|
<SidebarLink
|
||||||
key={item.path}
|
key={item.path}
|
||||||
to={item.path}
|
to={item.path}
|
||||||
|
onClickIntent={item.preload}
|
||||||
active={
|
active={
|
||||||
item.path !== settingsPath()
|
item.path !== settingsPath()
|
||||||
? location.pathname.startsWith(item.path)
|
? location.pathname.startsWith(item.path)
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ import React, { ComponentProps } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { integrationSettingsPath } from "@shared/utils/routeHelpers";
|
import { integrationSettingsPath } from "@shared/utils/routeHelpers";
|
||||||
import ZapierIcon from "~/components/Icons/ZapierIcon";
|
import ZapierIcon from "~/components/Icons/ZapierIcon";
|
||||||
|
import { createLazyComponent as lazy } from "~/components/LazyLoad";
|
||||||
import { Hook, PluginManager } from "~/utils/PluginManager";
|
import { Hook, PluginManager } from "~/utils/PluginManager";
|
||||||
import isCloudHosted from "~/utils/isCloudHosted";
|
import isCloudHosted from "~/utils/isCloudHosted";
|
||||||
import lazy from "~/utils/lazyWithRetry";
|
|
||||||
import { settingsPath } from "~/utils/routeHelpers";
|
import { settingsPath } from "~/utils/routeHelpers";
|
||||||
import { useComputed } from "./useComputed";
|
import { useComputed } from "./useComputed";
|
||||||
import useCurrentTeam from "./useCurrentTeam";
|
import useCurrentTeam from "./useCurrentTeam";
|
||||||
@@ -50,6 +50,7 @@ export type ConfigItem = {
|
|||||||
path: string;
|
path: string;
|
||||||
icon: React.FC<ComponentProps<typeof Icon>>;
|
icon: React.FC<ComponentProps<typeof Icon>>;
|
||||||
component: React.ComponentType;
|
component: React.ComponentType;
|
||||||
|
preload?: () => void;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
group: string;
|
group: string;
|
||||||
};
|
};
|
||||||
@@ -66,7 +67,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("Profile"),
|
name: t("Profile"),
|
||||||
path: settingsPath(),
|
path: settingsPath(),
|
||||||
component: Profile,
|
component: Profile.Component,
|
||||||
|
preload: Profile.preload,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
group: t("Account"),
|
group: t("Account"),
|
||||||
icon: ProfileIcon,
|
icon: ProfileIcon,
|
||||||
@@ -74,7 +76,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("Preferences"),
|
name: t("Preferences"),
|
||||||
path: settingsPath("preferences"),
|
path: settingsPath("preferences"),
|
||||||
component: Preferences,
|
component: Preferences.Component,
|
||||||
|
preload: Preferences.preload,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
group: t("Account"),
|
group: t("Account"),
|
||||||
icon: SettingsIcon,
|
icon: SettingsIcon,
|
||||||
@@ -82,7 +85,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("Notifications"),
|
name: t("Notifications"),
|
||||||
path: settingsPath("notifications"),
|
path: settingsPath("notifications"),
|
||||||
component: Notifications,
|
component: Notifications.Component,
|
||||||
|
preload: Notifications.preload,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
group: t("Account"),
|
group: t("Account"),
|
||||||
icon: EmailIcon,
|
icon: EmailIcon,
|
||||||
@@ -90,7 +94,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("API & Apps"),
|
name: t("API & Apps"),
|
||||||
path: settingsPath("api-and-apps"),
|
path: settingsPath("api-and-apps"),
|
||||||
component: APIAndApps,
|
component: APIAndApps.Component,
|
||||||
|
preload: APIAndApps.preload,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
group: t("Account"),
|
group: t("Account"),
|
||||||
icon: PadlockIcon,
|
icon: PadlockIcon,
|
||||||
@@ -99,7 +104,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("Details"),
|
name: t("Details"),
|
||||||
path: settingsPath("details"),
|
path: settingsPath("details"),
|
||||||
component: Details,
|
component: Details.Component,
|
||||||
|
preload: Details.preload,
|
||||||
enabled: can.update,
|
enabled: can.update,
|
||||||
group: t("Workspace"),
|
group: t("Workspace"),
|
||||||
icon: TeamIcon,
|
icon: TeamIcon,
|
||||||
@@ -107,7 +113,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("Security"),
|
name: t("Security"),
|
||||||
path: settingsPath("security"),
|
path: settingsPath("security"),
|
||||||
component: Security,
|
component: Security.Component,
|
||||||
|
preload: Security.preload,
|
||||||
enabled: can.update,
|
enabled: can.update,
|
||||||
group: t("Workspace"),
|
group: t("Workspace"),
|
||||||
icon: PadlockIcon,
|
icon: PadlockIcon,
|
||||||
@@ -115,7 +122,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("Features"),
|
name: t("Features"),
|
||||||
path: settingsPath("features"),
|
path: settingsPath("features"),
|
||||||
component: Features,
|
component: Features.Component,
|
||||||
|
preload: Features.preload,
|
||||||
enabled: can.update,
|
enabled: can.update,
|
||||||
group: t("Workspace"),
|
group: t("Workspace"),
|
||||||
icon: BeakerIcon,
|
icon: BeakerIcon,
|
||||||
@@ -123,7 +131,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("Members"),
|
name: t("Members"),
|
||||||
path: settingsPath("members"),
|
path: settingsPath("members"),
|
||||||
component: Members,
|
component: Members.Component,
|
||||||
|
preload: Members.preload,
|
||||||
enabled: can.listUsers,
|
enabled: can.listUsers,
|
||||||
group: t("Workspace"),
|
group: t("Workspace"),
|
||||||
icon: UserIcon,
|
icon: UserIcon,
|
||||||
@@ -131,7 +140,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("Groups"),
|
name: t("Groups"),
|
||||||
path: settingsPath("groups"),
|
path: settingsPath("groups"),
|
||||||
component: Groups,
|
component: Groups.Component,
|
||||||
|
preload: Groups.preload,
|
||||||
enabled: can.listGroups,
|
enabled: can.listGroups,
|
||||||
group: t("Workspace"),
|
group: t("Workspace"),
|
||||||
icon: GroupIcon,
|
icon: GroupIcon,
|
||||||
@@ -139,7 +149,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("Templates"),
|
name: t("Templates"),
|
||||||
path: settingsPath("templates"),
|
path: settingsPath("templates"),
|
||||||
component: Templates,
|
component: Templates.Component,
|
||||||
|
preload: Templates.preload,
|
||||||
enabled: can.readTemplate,
|
enabled: can.readTemplate,
|
||||||
group: t("Workspace"),
|
group: t("Workspace"),
|
||||||
icon: ShapesIcon,
|
icon: ShapesIcon,
|
||||||
@@ -147,7 +158,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("API Keys"),
|
name: t("API Keys"),
|
||||||
path: settingsPath("api-keys"),
|
path: settingsPath("api-keys"),
|
||||||
component: ApiKeys,
|
component: ApiKeys.Component,
|
||||||
|
preload: ApiKeys.preload,
|
||||||
enabled: can.listApiKeys,
|
enabled: can.listApiKeys,
|
||||||
group: t("Workspace"),
|
group: t("Workspace"),
|
||||||
icon: CodeIcon,
|
icon: CodeIcon,
|
||||||
@@ -155,7 +167,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("Applications"),
|
name: t("Applications"),
|
||||||
path: settingsPath("applications"),
|
path: settingsPath("applications"),
|
||||||
component: Applications,
|
component: Applications.Component,
|
||||||
|
preload: Applications.preload,
|
||||||
enabled: can.listOAuthClients,
|
enabled: can.listOAuthClients,
|
||||||
group: t("Workspace"),
|
group: t("Workspace"),
|
||||||
icon: InternetIcon,
|
icon: InternetIcon,
|
||||||
@@ -163,7 +176,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("Shared Links"),
|
name: t("Shared Links"),
|
||||||
path: settingsPath("shares"),
|
path: settingsPath("shares"),
|
||||||
component: Shares,
|
component: Shares.Component,
|
||||||
|
preload: Shares.preload,
|
||||||
enabled: can.listShares,
|
enabled: can.listShares,
|
||||||
group: t("Workspace"),
|
group: t("Workspace"),
|
||||||
icon: GlobeIcon,
|
icon: GlobeIcon,
|
||||||
@@ -171,7 +185,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("Import"),
|
name: t("Import"),
|
||||||
path: settingsPath("import"),
|
path: settingsPath("import"),
|
||||||
component: Import,
|
component: Import.Component,
|
||||||
|
preload: Import.preload,
|
||||||
enabled: can.createImport,
|
enabled: can.createImport,
|
||||||
group: t("Workspace"),
|
group: t("Workspace"),
|
||||||
icon: ImportIcon,
|
icon: ImportIcon,
|
||||||
@@ -179,7 +194,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: t("Export"),
|
name: t("Export"),
|
||||||
path: settingsPath("export"),
|
path: settingsPath("export"),
|
||||||
component: Export,
|
component: Export.Component,
|
||||||
|
preload: Export.preload,
|
||||||
enabled: can.createExport,
|
enabled: can.createExport,
|
||||||
group: t("Workspace"),
|
group: t("Workspace"),
|
||||||
icon: ExportIcon,
|
icon: ExportIcon,
|
||||||
@@ -188,7 +204,8 @@ const useSettingsConfig = () => {
|
|||||||
{
|
{
|
||||||
name: "Zapier",
|
name: "Zapier",
|
||||||
path: integrationSettingsPath("zapier"),
|
path: integrationSettingsPath("zapier"),
|
||||||
component: Zapier,
|
component: Zapier.Component,
|
||||||
|
preload: Zapier.preload,
|
||||||
enabled: can.update && isCloudHosted,
|
enabled: can.update && isCloudHosted,
|
||||||
group: t("Integrations"),
|
group: t("Integrations"),
|
||||||
icon: ZapierIcon,
|
icon: ZapierIcon,
|
||||||
@@ -208,7 +225,8 @@ const useSettingsConfig = () => {
|
|||||||
? integrationSettingsPath(plugin.id)
|
? integrationSettingsPath(plugin.id)
|
||||||
: settingsPath(plugin.id),
|
: settingsPath(plugin.id),
|
||||||
group: t(group),
|
group: t(group),
|
||||||
component: plugin.value.component,
|
component: plugin.value.component.Component,
|
||||||
|
preload: plugin.value.component.preload,
|
||||||
enabled: plugin.value.enabled
|
enabled: plugin.value.enabled
|
||||||
? plugin.value.enabled(team, user)
|
? plugin.value.enabled(team, user)
|
||||||
: can.update,
|
: can.update,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sortBy from "lodash/sortBy";
|
|||||||
import { action, observable } from "mobx";
|
import { action, observable } from "mobx";
|
||||||
import Team from "~/models/Team";
|
import Team from "~/models/Team";
|
||||||
import User from "~/models/User";
|
import User from "~/models/User";
|
||||||
|
import { LazyComponent } from "~/components/LazyLoad";
|
||||||
import { useComputed } from "~/hooks/useComputed";
|
import { useComputed } from "~/hooks/useComputed";
|
||||||
import Logger from "./Logger";
|
import Logger from "./Logger";
|
||||||
import isCloudHosted from "./isCloudHosted";
|
import isCloudHosted from "./isCloudHosted";
|
||||||
@@ -28,7 +29,7 @@ type PluginValueMap = {
|
|||||||
/** The displayed icon of the plugin. */
|
/** The displayed icon of the plugin. */
|
||||||
icon: React.ElementType;
|
icon: React.ElementType;
|
||||||
/** The settings screen somponent, should be lazy loaded. */
|
/** The settings screen somponent, should be lazy loaded. */
|
||||||
component: React.LazyExoticComponent<React.ComponentType>;
|
component: LazyComponent<React.ComponentType>;
|
||||||
/** Whether the plugin is enabled in the current context. */
|
/** Whether the plugin is enabled in the current context. */
|
||||||
enabled?: (team: Team, user: User) => boolean;
|
enabled?: (team: Team, user: User) => boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as React from "react";
|
import { createLazyComponent } from "~/components/LazyLoad";
|
||||||
import { Hook, PluginManager } from "~/utils/PluginManager";
|
import { Hook, PluginManager } from "~/utils/PluginManager";
|
||||||
import config from "../plugin.json";
|
import config from "../plugin.json";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
@@ -10,7 +10,7 @@ PluginManager.add([
|
|||||||
value: {
|
value: {
|
||||||
group: "Integrations",
|
group: "Integrations",
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
component: React.lazy(() => import("./Settings")),
|
component: createLazyComponent(() => import("./Settings")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as React from "react";
|
import { createLazyComponent } from "~/components/LazyLoad";
|
||||||
import { Hook, PluginManager } from "~/utils/PluginManager";
|
import { Hook, PluginManager } from "~/utils/PluginManager";
|
||||||
import config from "../plugin.json";
|
import config from "../plugin.json";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
@@ -10,7 +10,7 @@ PluginManager.add([
|
|||||||
value: {
|
value: {
|
||||||
group: "Integrations",
|
group: "Integrations",
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
component: React.lazy(() => import("./Settings")),
|
component: createLazyComponent(() => import("./Settings")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as React from "react";
|
import { createLazyComponent } from "~/components/LazyLoad";
|
||||||
import { Hook, PluginManager } from "~/utils/PluginManager";
|
import { Hook, PluginManager } from "~/utils/PluginManager";
|
||||||
import config from "../plugin.json";
|
import config from "../plugin.json";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
@@ -10,7 +10,7 @@ PluginManager.add([
|
|||||||
value: {
|
value: {
|
||||||
group: "Integrations",
|
group: "Integrations",
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
component: React.lazy(() => import("./Settings")),
|
component: createLazyComponent(() => import("./Settings")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { UserRole } from "@shared/types";
|
import { UserRole } from "@shared/types";
|
||||||
|
import { createLazyComponent } from "~/components/LazyLoad";
|
||||||
import { Hook, PluginManager } from "~/utils/PluginManager";
|
import { Hook, PluginManager } from "~/utils/PluginManager";
|
||||||
import config from "../plugin.json";
|
import config from "../plugin.json";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
@@ -11,7 +11,7 @@ PluginManager.add([
|
|||||||
value: {
|
value: {
|
||||||
group: "Integrations",
|
group: "Integrations",
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
component: React.lazy(() => import("./Settings")),
|
component: createLazyComponent(() => import("./Settings")),
|
||||||
enabled: (_, user) => user.role === UserRole.Admin,
|
enabled: (_, user) => user.role === UserRole.Admin,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { UserRole } from "@shared/types";
|
import { UserRole } from "@shared/types";
|
||||||
|
import { createLazyComponent } from "~/components/LazyLoad";
|
||||||
import { Hook, PluginManager } from "~/utils/PluginManager";
|
import { Hook, PluginManager } from "~/utils/PluginManager";
|
||||||
import config from "../plugin.json";
|
import config from "../plugin.json";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
@@ -11,7 +11,7 @@ PluginManager.add([
|
|||||||
value: {
|
value: {
|
||||||
group: "Integrations",
|
group: "Integrations",
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
component: React.lazy(() => import("./Settings")),
|
component: createLazyComponent(() => import("./Settings")),
|
||||||
enabled: (_, user) =>
|
enabled: (_, user) =>
|
||||||
[UserRole.Member, UserRole.Admin].includes(user.role),
|
[UserRole.Member, UserRole.Admin].includes(user.role),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { UserRole } from "@shared/types";
|
import { UserRole } from "@shared/types";
|
||||||
|
import { createLazyComponent } from "~/components/LazyLoad";
|
||||||
import { Hook, PluginManager } from "~/utils/PluginManager";
|
import { Hook, PluginManager } from "~/utils/PluginManager";
|
||||||
import config from "../plugin.json";
|
import config from "../plugin.json";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
@@ -11,7 +11,7 @@ PluginManager.add([
|
|||||||
value: {
|
value: {
|
||||||
group: "Integrations",
|
group: "Integrations",
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
component: React.lazy(() => import("./Settings")),
|
component: createLazyComponent(() => import("./Settings")),
|
||||||
enabled: (_, user) => user.role === UserRole.Admin,
|
enabled: (_, user) => user.role === UserRole.Admin,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as React from "react";
|
import { createLazyComponent } from "~/components/LazyLoad";
|
||||||
import { Hook, PluginManager } from "~/utils/PluginManager";
|
import { Hook, PluginManager } from "~/utils/PluginManager";
|
||||||
import config from "../plugin.json";
|
import config from "../plugin.json";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
@@ -10,7 +10,7 @@ PluginManager.add([
|
|||||||
value: {
|
value: {
|
||||||
group: "Integrations",
|
group: "Integrations",
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
component: React.lazy(() => import("./Settings")),
|
component: createLazyComponent(() => import("./Settings")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user