mirror of
https://github.com/outline/outline.git
synced 2025-03-28 14:34:35 +00:00
Use auth.availableTeams endpoint for workspace switching (#4585)
This commit is contained in:
@ -2,42 +2,46 @@ import { PlusIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import { stringToColor } from "@shared/utils/color";
|
||||
import RootStore from "~/stores/RootStore";
|
||||
import TeamNew from "~/scenes/TeamNew";
|
||||
import TeamLogo from "~/components/TeamLogo";
|
||||
import { createAction } from "~/actions";
|
||||
import { loadSessionsFromCookie } from "~/hooks/useSessions";
|
||||
import { TeamSection } from "../sections";
|
||||
|
||||
export const switchTeamList = getSessions().map((session) => {
|
||||
return createAction({
|
||||
name: session.name,
|
||||
section: TeamSection,
|
||||
keywords: "change switch workspace organization team",
|
||||
icon: () => (
|
||||
<StyledTeamLogo
|
||||
alt={session.name}
|
||||
model={{
|
||||
initial: session.name[0],
|
||||
avatarUrl: session.logoUrl,
|
||||
id: session.teamId,
|
||||
color: stringToColor(session.teamId),
|
||||
}}
|
||||
size={24}
|
||||
/>
|
||||
),
|
||||
visible: ({ currentTeamId }) => currentTeamId !== session.teamId,
|
||||
perform: () => (window.location.href = session.url),
|
||||
});
|
||||
});
|
||||
export const createTeamsList = ({ stores }: { stores: RootStore }) => {
|
||||
return (
|
||||
stores.auth.availableTeams?.map((session) =>
|
||||
createAction({
|
||||
name: session.name,
|
||||
section: TeamSection,
|
||||
keywords: "change switch workspace organization team",
|
||||
icon: () => (
|
||||
<StyledTeamLogo
|
||||
alt={session.name}
|
||||
model={{
|
||||
initial: session.name[0],
|
||||
avatarUrl: session.avatarUrl,
|
||||
id: session.id,
|
||||
color: stringToColor(session.id),
|
||||
}}
|
||||
size={24}
|
||||
/>
|
||||
),
|
||||
visible: ({ currentTeamId }) => currentTeamId !== session.id,
|
||||
perform: () => (window.location.href = session.url),
|
||||
})
|
||||
) ?? []
|
||||
);
|
||||
};
|
||||
|
||||
const switchTeam = createAction({
|
||||
export const switchTeam = createAction({
|
||||
name: ({ t }) => t("Switch workspace"),
|
||||
placeholder: ({ t }) => t("Select a workspace"),
|
||||
keywords: "change switch workspace organization team",
|
||||
section: TeamSection,
|
||||
visible: ({ currentTeamId }) =>
|
||||
getSessions({ exclude: currentTeamId }).length > 0,
|
||||
children: switchTeamList,
|
||||
visible: ({ stores }) =>
|
||||
!!stores.auth.availableTeams && stores.auth.availableTeams?.length > 1,
|
||||
children: createTeamsList,
|
||||
});
|
||||
|
||||
export const createTeam = createAction({
|
||||
@ -60,14 +64,6 @@ export const createTeam = createAction({
|
||||
},
|
||||
});
|
||||
|
||||
function getSessions(params?: { exclude?: string }) {
|
||||
const sessions = loadSessionsFromCookie();
|
||||
const otherSessions = sessions.filter(
|
||||
(session) => session.teamId !== params?.exclude
|
||||
);
|
||||
return otherSessions;
|
||||
}
|
||||
|
||||
const StyledTeamLogo = styled(TeamLogo)`
|
||||
border-radius: 2px;
|
||||
border: 0;
|
||||
|
@ -1,25 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { getCookie } from "tiny-cookie";
|
||||
|
||||
type Session = {
|
||||
url: string;
|
||||
logoUrl: string;
|
||||
name: string;
|
||||
teamId: string;
|
||||
};
|
||||
|
||||
export function loadSessionsFromCookie(): Session[] {
|
||||
const sessions = JSON.parse(getCookie("sessions") || "{}");
|
||||
return Object.keys(sessions).map((teamId) => ({
|
||||
teamId,
|
||||
...sessions[teamId],
|
||||
}));
|
||||
}
|
||||
|
||||
export default function useSessions(): [Session[], () => void] {
|
||||
const [sessions, setSessions] = React.useState(loadSessionsFromCookie);
|
||||
const reload = React.useCallback(() => {
|
||||
setSessions(loadSessionsFromCookie());
|
||||
}, []);
|
||||
return [sessions, reload];
|
||||
}
|
@ -5,23 +5,19 @@ import { MenuButton, useMenuState } from "reakit/Menu";
|
||||
import ContextMenu from "~/components/ContextMenu";
|
||||
import Template from "~/components/ContextMenu/Template";
|
||||
import { navigateToSettings, logout } from "~/actions/definitions/navigation";
|
||||
import { createTeam, switchTeamList } from "~/actions/definitions/teams";
|
||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||
import { createTeam, createTeamsList } from "~/actions/definitions/teams";
|
||||
import usePrevious from "~/hooks/usePrevious";
|
||||
import useSessions from "~/hooks/useSessions";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import separator from "~/menus/separator";
|
||||
|
||||
const OrganizationMenu: React.FC = ({ children }) => {
|
||||
const [sessions] = useSessions();
|
||||
const menu = useMenuState({
|
||||
unstable_offset: [4, -4],
|
||||
placement: "bottom-start",
|
||||
modal: true,
|
||||
});
|
||||
const { ui } = useStores();
|
||||
const { theme } = ui;
|
||||
const team = useCurrentTeam();
|
||||
const stores = useStores();
|
||||
const { theme } = stores.ui;
|
||||
const previousTheme = usePrevious(theme);
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -35,13 +31,13 @@ const OrganizationMenu: React.FC = ({ children }) => {
|
||||
// menu is not cached at all.
|
||||
const actions = React.useMemo(() => {
|
||||
return [
|
||||
...switchTeamList,
|
||||
...createTeamsList({ stores }),
|
||||
createTeam,
|
||||
separator(),
|
||||
navigateToSettings,
|
||||
logout,
|
||||
];
|
||||
}, [team.id, sessions]);
|
||||
}, [stores]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -19,6 +19,13 @@ const NO_REDIRECT_PATHS = ["/", "/create", "/home"];
|
||||
type PersistedData = {
|
||||
user?: User;
|
||||
team?: Team;
|
||||
availableTeams?: {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl: string;
|
||||
url: string;
|
||||
isSignedIn: boolean;
|
||||
}[];
|
||||
policies?: Policy[];
|
||||
};
|
||||
|
||||
@ -37,19 +44,28 @@ export type Config = {
|
||||
|
||||
export default class AuthStore {
|
||||
@observable
|
||||
user: User | null | undefined;
|
||||
user?: User | null;
|
||||
|
||||
@observable
|
||||
team: Team | null | undefined;
|
||||
team?: Team | null;
|
||||
|
||||
@observable
|
||||
token: string | null | undefined;
|
||||
availableTeams?: {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl: string;
|
||||
url: string;
|
||||
isSignedIn: boolean;
|
||||
}[];
|
||||
|
||||
@observable
|
||||
token?: string | null;
|
||||
|
||||
@observable
|
||||
policies: Policy[] = [];
|
||||
|
||||
@observable
|
||||
lastSignedIn: string | null | undefined;
|
||||
lastSignedIn?: string | null;
|
||||
|
||||
@observable
|
||||
isSaving = false;
|
||||
@ -58,7 +74,7 @@ export default class AuthStore {
|
||||
isSuspended = false;
|
||||
|
||||
@observable
|
||||
suspendedContactEmail: string | null | undefined;
|
||||
suspendedContactEmail?: string | null;
|
||||
|
||||
@observable
|
||||
config: Config | null | undefined;
|
||||
@ -133,6 +149,7 @@ export default class AuthStore {
|
||||
return {
|
||||
user: this.user,
|
||||
team: this.team,
|
||||
availableTeams: this.availableTeams,
|
||||
policies: this.policies,
|
||||
};
|
||||
}
|
||||
@ -156,6 +173,7 @@ export default class AuthStore {
|
||||
const { user, team } = res.data;
|
||||
this.user = new User(user, this);
|
||||
this.team = new Team(team, this);
|
||||
this.availableTeams = res.data.availableTeams;
|
||||
|
||||
if (env.SENTRY_DSN) {
|
||||
Sentry.configureScope(function (scope) {
|
||||
@ -214,6 +232,9 @@ export default class AuthStore {
|
||||
runInAction("AuthStore#updateUser", () => {
|
||||
this.user = null;
|
||||
this.team = null;
|
||||
this.availableTeams = this.availableTeams?.filter(
|
||||
(team) => team.id !== this.team?.id
|
||||
);
|
||||
this.policies = [];
|
||||
this.token = null;
|
||||
});
|
||||
|
Reference in New Issue
Block a user