fix: navigate to all the pages of settings through command bar (#3226)
* fix: create useAuthorizedSettingsConfig * use config to render routes * translations and icon * mount in CommandBar * memo * Update app/hooks/useSettingsAction.tsx Co-authored-by: Tom Moor <tom.moor@gmail.com> * fix: add actions into settings action * remove comment * fix: update shares * fix: Remove Slack/Zapier from translations Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
@@ -174,7 +174,6 @@ export const rootNavigationActions = [
|
||||
navigateToTemplates,
|
||||
navigateToArchive,
|
||||
navigateToTrash,
|
||||
navigateToSettings,
|
||||
openAPIDocumentation,
|
||||
openFeedbackUrl,
|
||||
openBugReportUrl,
|
||||
|
||||
@@ -10,6 +10,7 @@ import CommandBarResults from "~/components/CommandBarResults";
|
||||
import SearchActions from "~/components/SearchActions";
|
||||
import rootActions from "~/actions/root";
|
||||
import useCommandBarActions from "~/hooks/useCommandBarActions";
|
||||
import useSettingsActions from "~/hooks/useSettingsAction";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import { CommandBarAction } from "~/types";
|
||||
import { metaDisplay } from "~/utils/keyboard";
|
||||
@@ -18,8 +19,13 @@ import Text from "./Text";
|
||||
function CommandBar() {
|
||||
const { t } = useTranslation();
|
||||
const { ui } = useStores();
|
||||
const settingsActions = useSettingsActions();
|
||||
const commandBarActions = React.useMemo(
|
||||
() => [...rootActions, settingsActions],
|
||||
[settingsActions]
|
||||
);
|
||||
|
||||
useCommandBarActions(rootActions);
|
||||
useCommandBarActions(commandBarActions);
|
||||
|
||||
const { rootAction } = useKBar((state) => ({
|
||||
rootAction: state.currentRootActionId
|
||||
|
||||
196
app/hooks/useAuthorizedSettingsConfig.ts
Normal file
196
app/hooks/useAuthorizedSettingsConfig.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
import {
|
||||
NewDocumentIcon,
|
||||
EmailIcon,
|
||||
ProfileIcon,
|
||||
PadlockIcon,
|
||||
CodeIcon,
|
||||
UserIcon,
|
||||
GroupIcon,
|
||||
LinkIcon,
|
||||
TeamIcon,
|
||||
BeakerIcon,
|
||||
DownloadIcon,
|
||||
} from "outline-icons";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Details from "~/scenes/Settings/Details";
|
||||
import Export from "~/scenes/Settings/Export";
|
||||
import Features from "~/scenes/Settings/Features";
|
||||
import Groups from "~/scenes/Settings/Groups";
|
||||
import Import from "~/scenes/Settings/Import";
|
||||
import Members from "~/scenes/Settings/Members";
|
||||
import Notifications from "~/scenes/Settings/Notifications";
|
||||
import Profile from "~/scenes/Settings/Profile";
|
||||
import Security from "~/scenes/Settings/Security";
|
||||
import Shares from "~/scenes/Settings/Shares";
|
||||
import Slack from "~/scenes/Settings/Slack";
|
||||
import Tokens from "~/scenes/Settings/Tokens";
|
||||
import Zapier from "~/scenes/Settings/Zapier";
|
||||
import SlackIcon from "~/components/SlackIcon";
|
||||
import ZapierIcon from "~/components/ZapierIcon";
|
||||
import env from "~/env";
|
||||
import useCurrentTeam from "./useCurrentTeam";
|
||||
import usePolicy from "./usePolicy";
|
||||
|
||||
type SettingsGroups = "Account" | "Team" | "Integrations";
|
||||
type SettingsPage =
|
||||
| "Profile"
|
||||
| "Notifications"
|
||||
| "Api"
|
||||
| "Details"
|
||||
| "Security"
|
||||
| "Features"
|
||||
| "Members"
|
||||
| "Groups"
|
||||
| "Shares"
|
||||
| "Import"
|
||||
| "Export"
|
||||
| "Slack"
|
||||
| "Zapier";
|
||||
|
||||
export type ConfigItem = {
|
||||
name: string;
|
||||
path: string;
|
||||
icon: React.FC<any>;
|
||||
component: () => JSX.Element;
|
||||
enabled: boolean;
|
||||
group: SettingsGroups;
|
||||
};
|
||||
|
||||
type ConfigType = {
|
||||
[key in SettingsPage]: ConfigItem;
|
||||
};
|
||||
|
||||
const isHosted = env.DEPLOYMENT === "hosted";
|
||||
|
||||
const useAuthorizedSettingsConfig = () => {
|
||||
const team = useCurrentTeam();
|
||||
const can = usePolicy(team.id);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config: ConfigType = React.useMemo(
|
||||
() => ({
|
||||
Profile: {
|
||||
name: t("Profile"),
|
||||
path: "/settings",
|
||||
component: Profile,
|
||||
enabled: true,
|
||||
group: t("Account"),
|
||||
icon: ProfileIcon,
|
||||
},
|
||||
Notifications: {
|
||||
name: t("Notifications"),
|
||||
path: "/settings/notifications",
|
||||
component: Notifications,
|
||||
enabled: true,
|
||||
group: t("Account"),
|
||||
icon: EmailIcon,
|
||||
},
|
||||
Api: {
|
||||
name: t("API Tokens"),
|
||||
path: "/settings/tokens",
|
||||
component: Tokens,
|
||||
enabled: can.createApiKey,
|
||||
group: t("Account"),
|
||||
icon: CodeIcon,
|
||||
},
|
||||
// Team group
|
||||
Details: {
|
||||
name: t("Details"),
|
||||
path: "/settings/details",
|
||||
component: Details,
|
||||
enabled: can.update,
|
||||
group: t("Team"),
|
||||
icon: TeamIcon,
|
||||
},
|
||||
Security: {
|
||||
name: t("Security"),
|
||||
path: "/settings/security",
|
||||
component: Security,
|
||||
enabled: can.update,
|
||||
group: t("Team"),
|
||||
icon: PadlockIcon,
|
||||
},
|
||||
Features: {
|
||||
name: t("Features"),
|
||||
path: "/settings/features",
|
||||
component: Features,
|
||||
enabled: can.update,
|
||||
group: t("Team"),
|
||||
icon: BeakerIcon,
|
||||
},
|
||||
Members: {
|
||||
name: t("Members"),
|
||||
path: "/settings/members",
|
||||
component: Members,
|
||||
enabled: true,
|
||||
group: t("Team"),
|
||||
icon: UserIcon,
|
||||
},
|
||||
Groups: {
|
||||
name: t("Groups"),
|
||||
path: "/settings/groups",
|
||||
component: Groups,
|
||||
enabled: true,
|
||||
group: t("Team"),
|
||||
icon: GroupIcon,
|
||||
},
|
||||
Shares: {
|
||||
name: t("Share Links"),
|
||||
path: "/settings/shares",
|
||||
component: Shares,
|
||||
enabled: true,
|
||||
group: t("Team"),
|
||||
icon: LinkIcon,
|
||||
},
|
||||
Import: {
|
||||
name: t("Import"),
|
||||
path: "/settings/import",
|
||||
component: Import,
|
||||
enabled: can.manage,
|
||||
group: t("Team"),
|
||||
icon: NewDocumentIcon,
|
||||
},
|
||||
Export: {
|
||||
name: t("Export"),
|
||||
path: "/settings/export",
|
||||
component: Export,
|
||||
enabled: can.export,
|
||||
group: t("Team"),
|
||||
icon: DownloadIcon,
|
||||
},
|
||||
// Intergrations
|
||||
Slack: {
|
||||
name: "Slack",
|
||||
path: "/settings/integrations/slack",
|
||||
component: Slack,
|
||||
enabled: can.update && (!!env.SLACK_KEY || isHosted),
|
||||
group: t("Integrations"),
|
||||
icon: SlackIcon,
|
||||
},
|
||||
Zapier: {
|
||||
name: "Zapier",
|
||||
path: "/settings/integrations/zapier",
|
||||
component: Zapier,
|
||||
enabled: can.update && isHosted,
|
||||
group: t("Integrations"),
|
||||
icon: ZapierIcon,
|
||||
},
|
||||
}),
|
||||
[can.createApiKey, can.export, can.manage, can.update, t]
|
||||
);
|
||||
|
||||
const enabledConfigs = React.useMemo(
|
||||
() =>
|
||||
Object.keys(config).reduce(
|
||||
(acc, key: SettingsPage) =>
|
||||
config[key].enabled ? [...acc, config[key]] : acc,
|
||||
[]
|
||||
),
|
||||
[config]
|
||||
);
|
||||
|
||||
return enabledConfigs;
|
||||
};
|
||||
|
||||
export default useAuthorizedSettingsConfig;
|
||||
38
app/hooks/useSettingsAction.tsx
Normal file
38
app/hooks/useSettingsAction.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { SettingsIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { createAction } from "~/actions";
|
||||
import { NavigationSection } from "~/actions/sections";
|
||||
import history from "~/utils/history";
|
||||
import useAuthorizedSettingsConfig from "./useAuthorizedSettingsConfig";
|
||||
|
||||
const useSettingsActions = () => {
|
||||
const config = useAuthorizedSettingsConfig();
|
||||
const actions = React.useMemo(() => {
|
||||
return config.map((item) => {
|
||||
const Icon = item.icon;
|
||||
return {
|
||||
id: item.path,
|
||||
name: item.name,
|
||||
icon: <Icon color="currentColor" />,
|
||||
section: NavigationSection,
|
||||
perform: () => history.push(item.path),
|
||||
};
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
const navigateToSettings = React.useMemo(
|
||||
() =>
|
||||
createAction({
|
||||
name: ({ t }) => t("Settings"),
|
||||
section: NavigationSection,
|
||||
shortcut: ["g", "s"],
|
||||
icon: <SettingsIcon />,
|
||||
children: () => actions,
|
||||
}),
|
||||
[actions]
|
||||
);
|
||||
|
||||
return navigateToSettings;
|
||||
};
|
||||
|
||||
export default useSettingsActions;
|
||||
@@ -1,42 +1,21 @@
|
||||
import * as React from "react";
|
||||
import { Switch, Redirect } from "react-router-dom";
|
||||
import Details from "~/scenes/Settings/Details";
|
||||
import Export from "~/scenes/Settings/Export";
|
||||
import Features from "~/scenes/Settings/Features";
|
||||
import Groups from "~/scenes/Settings/Groups";
|
||||
import Import from "~/scenes/Settings/Import";
|
||||
import Members from "~/scenes/Settings/Members";
|
||||
import Notifications from "~/scenes/Settings/Notifications";
|
||||
import Profile from "~/scenes/Settings/Profile";
|
||||
import Security from "~/scenes/Settings/Security";
|
||||
import Shares from "~/scenes/Settings/Shares";
|
||||
import Slack from "~/scenes/Settings/Slack";
|
||||
import Tokens from "~/scenes/Settings/Tokens";
|
||||
import Zapier from "~/scenes/Settings/Zapier";
|
||||
import Route from "~/components/ProfiledRoute";
|
||||
import env from "~/env";
|
||||
|
||||
const isHosted = env.DEPLOYMENT === "hosted";
|
||||
import useAuthorizedSettingsConfig from "~/hooks/useAuthorizedSettingsConfig";
|
||||
|
||||
export default function SettingsRoutes() {
|
||||
const configs = useAuthorizedSettingsConfig();
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact path="/settings" component={Profile} />
|
||||
<Route exact path="/settings/details" component={Details} />
|
||||
<Route exact path="/settings/security" component={Security} />
|
||||
<Route exact path="/settings/members" component={Members} />
|
||||
<Route exact path="/settings/features" component={Features} />
|
||||
<Route exact path="/settings/groups" component={Groups} />
|
||||
<Route exact path="/settings/shares" component={Shares} />
|
||||
<Route exact path="/settings/tokens" component={Tokens} />
|
||||
<Route exact path="/settings/notifications" component={Notifications} />
|
||||
<Route exact path="/settings/integrations/slack" component={Slack} />
|
||||
{isHosted && (
|
||||
<Route exact path="/settings/integrations/zapier" component={Zapier} />
|
||||
)}
|
||||
<Route exact path="/settings/import" component={Import} />
|
||||
<Route exact path="/settings/export" component={Export} />
|
||||
|
||||
{configs.map((config) => (
|
||||
<Route
|
||||
exact
|
||||
key={config.path}
|
||||
path={config.path}
|
||||
component={config.component}
|
||||
/>
|
||||
))}
|
||||
{/* old routes */}
|
||||
<Redirect from="/settings/import-export" to="/settings/export" />
|
||||
<Redirect from="/settings/people" to="/settings/members" />
|
||||
|
||||
@@ -9,8 +9,8 @@ import ZapierIcon from "~/components/ZapierIcon";
|
||||
function Zapier() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Scene title={t("Zapier")} icon={<ZapierIcon color="currentColor" />}>
|
||||
<Heading>{t("Zapier")}</Heading>
|
||||
<Scene title="Zapier" icon={<ZapierIcon color="currentColor" />}>
|
||||
<Heading>Zapier</Heading>
|
||||
<Text type="secondary">
|
||||
<Trans>
|
||||
Zapier is a platform that allows Outline to easily integrate with
|
||||
|
||||
@@ -622,7 +622,6 @@
|
||||
"You can create an unlimited amount of personal tokens to authenticate\n with the API. Tokens have the same permissions as your user account.\n For more details see the <em>developer documentation</em>.": "You can create an unlimited amount of personal tokens to authenticate\n with the API. Tokens have the same permissions as your user account.\n For more details see the <em>developer documentation</em>.",
|
||||
"Tokens": "Tokens",
|
||||
"Create a token": "Create a token",
|
||||
"Zapier": "Zapier",
|
||||
"Zapier is a platform that allows Outline to easily integrate with thousands of other business tools. Head over to Zapier to setup a \"Zap\" and start programmatically interacting with Outline.'": "Zapier is a platform that allows Outline to easily integrate with thousands of other business tools. Head over to Zapier to setup a \"Zap\" and start programmatically interacting with Outline.'",
|
||||
"Open Zapier": "Open Zapier",
|
||||
"There are no templates just yet.": "There are no templates just yet.",
|
||||
|
||||
Reference in New Issue
Block a user