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:
Saumya Pandey
2022-03-13 09:38:36 +05:30
committed by GitHub
parent e30f6e937c
commit c979d003e4
7 changed files with 254 additions and 37 deletions

View File

@@ -174,7 +174,6 @@ export const rootNavigationActions = [
navigateToTemplates,
navigateToArchive,
navigateToTrash,
navigateToSettings,
openAPIDocumentation,
openFeedbackUrl,
openBugReportUrl,

View File

@@ -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

View 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;

View 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;

View File

@@ -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" />

View File

@@ -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

View File

@@ -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.",