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 * as React from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { stringToColor } from "@shared/utils/color";
|
import { stringToColor } from "@shared/utils/color";
|
||||||
|
import RootStore from "~/stores/RootStore";
|
||||||
import TeamNew from "~/scenes/TeamNew";
|
import TeamNew from "~/scenes/TeamNew";
|
||||||
import TeamLogo from "~/components/TeamLogo";
|
import TeamLogo from "~/components/TeamLogo";
|
||||||
import { createAction } from "~/actions";
|
import { createAction } from "~/actions";
|
||||||
import { loadSessionsFromCookie } from "~/hooks/useSessions";
|
|
||||||
import { TeamSection } from "../sections";
|
import { TeamSection } from "../sections";
|
||||||
|
|
||||||
export const switchTeamList = getSessions().map((session) => {
|
export const createTeamsList = ({ stores }: { stores: RootStore }) => {
|
||||||
return createAction({
|
return (
|
||||||
name: session.name,
|
stores.auth.availableTeams?.map((session) =>
|
||||||
section: TeamSection,
|
createAction({
|
||||||
keywords: "change switch workspace organization team",
|
name: session.name,
|
||||||
icon: () => (
|
section: TeamSection,
|
||||||
<StyledTeamLogo
|
keywords: "change switch workspace organization team",
|
||||||
alt={session.name}
|
icon: () => (
|
||||||
model={{
|
<StyledTeamLogo
|
||||||
initial: session.name[0],
|
alt={session.name}
|
||||||
avatarUrl: session.logoUrl,
|
model={{
|
||||||
id: session.teamId,
|
initial: session.name[0],
|
||||||
color: stringToColor(session.teamId),
|
avatarUrl: session.avatarUrl,
|
||||||
}}
|
id: session.id,
|
||||||
size={24}
|
color: stringToColor(session.id),
|
||||||
/>
|
}}
|
||||||
),
|
size={24}
|
||||||
visible: ({ currentTeamId }) => currentTeamId !== session.teamId,
|
/>
|
||||||
perform: () => (window.location.href = session.url),
|
),
|
||||||
});
|
visible: ({ currentTeamId }) => currentTeamId !== session.id,
|
||||||
});
|
perform: () => (window.location.href = session.url),
|
||||||
|
})
|
||||||
|
) ?? []
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const switchTeam = createAction({
|
export const switchTeam = createAction({
|
||||||
name: ({ t }) => t("Switch workspace"),
|
name: ({ t }) => t("Switch workspace"),
|
||||||
placeholder: ({ t }) => t("Select a workspace"),
|
placeholder: ({ t }) => t("Select a workspace"),
|
||||||
keywords: "change switch workspace organization team",
|
keywords: "change switch workspace organization team",
|
||||||
section: TeamSection,
|
section: TeamSection,
|
||||||
visible: ({ currentTeamId }) =>
|
visible: ({ stores }) =>
|
||||||
getSessions({ exclude: currentTeamId }).length > 0,
|
!!stores.auth.availableTeams && stores.auth.availableTeams?.length > 1,
|
||||||
children: switchTeamList,
|
children: createTeamsList,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const createTeam = createAction({
|
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)`
|
const StyledTeamLogo = styled(TeamLogo)`
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
border: 0;
|
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 ContextMenu from "~/components/ContextMenu";
|
||||||
import Template from "~/components/ContextMenu/Template";
|
import Template from "~/components/ContextMenu/Template";
|
||||||
import { navigateToSettings, logout } from "~/actions/definitions/navigation";
|
import { navigateToSettings, logout } from "~/actions/definitions/navigation";
|
||||||
import { createTeam, switchTeamList } from "~/actions/definitions/teams";
|
import { createTeam, createTeamsList } from "~/actions/definitions/teams";
|
||||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
|
||||||
import usePrevious from "~/hooks/usePrevious";
|
import usePrevious from "~/hooks/usePrevious";
|
||||||
import useSessions from "~/hooks/useSessions";
|
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import separator from "~/menus/separator";
|
import separator from "~/menus/separator";
|
||||||
|
|
||||||
const OrganizationMenu: React.FC = ({ children }) => {
|
const OrganizationMenu: React.FC = ({ children }) => {
|
||||||
const [sessions] = useSessions();
|
|
||||||
const menu = useMenuState({
|
const menu = useMenuState({
|
||||||
unstable_offset: [4, -4],
|
unstable_offset: [4, -4],
|
||||||
placement: "bottom-start",
|
placement: "bottom-start",
|
||||||
modal: true,
|
modal: true,
|
||||||
});
|
});
|
||||||
const { ui } = useStores();
|
const stores = useStores();
|
||||||
const { theme } = ui;
|
const { theme } = stores.ui;
|
||||||
const team = useCurrentTeam();
|
|
||||||
const previousTheme = usePrevious(theme);
|
const previousTheme = usePrevious(theme);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -35,13 +31,13 @@ const OrganizationMenu: React.FC = ({ children }) => {
|
|||||||
// menu is not cached at all.
|
// menu is not cached at all.
|
||||||
const actions = React.useMemo(() => {
|
const actions = React.useMemo(() => {
|
||||||
return [
|
return [
|
||||||
...switchTeamList,
|
...createTeamsList({ stores }),
|
||||||
createTeam,
|
createTeam,
|
||||||
separator(),
|
separator(),
|
||||||
navigateToSettings,
|
navigateToSettings,
|
||||||
logout,
|
logout,
|
||||||
];
|
];
|
||||||
}, [team.id, sessions]);
|
}, [stores]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -19,6 +19,13 @@ const NO_REDIRECT_PATHS = ["/", "/create", "/home"];
|
|||||||
type PersistedData = {
|
type PersistedData = {
|
||||||
user?: User;
|
user?: User;
|
||||||
team?: Team;
|
team?: Team;
|
||||||
|
availableTeams?: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
avatarUrl: string;
|
||||||
|
url: string;
|
||||||
|
isSignedIn: boolean;
|
||||||
|
}[];
|
||||||
policies?: Policy[];
|
policies?: Policy[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -37,19 +44,28 @@ export type Config = {
|
|||||||
|
|
||||||
export default class AuthStore {
|
export default class AuthStore {
|
||||||
@observable
|
@observable
|
||||||
user: User | null | undefined;
|
user?: User | null;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
team: Team | null | undefined;
|
team?: Team | null;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
token: string | null | undefined;
|
availableTeams?: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
avatarUrl: string;
|
||||||
|
url: string;
|
||||||
|
isSignedIn: boolean;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
@observable
|
||||||
|
token?: string | null;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
policies: Policy[] = [];
|
policies: Policy[] = [];
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
lastSignedIn: string | null | undefined;
|
lastSignedIn?: string | null;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
isSaving = false;
|
isSaving = false;
|
||||||
@@ -58,7 +74,7 @@ export default class AuthStore {
|
|||||||
isSuspended = false;
|
isSuspended = false;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
suspendedContactEmail: string | null | undefined;
|
suspendedContactEmail?: string | null;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
config: Config | null | undefined;
|
config: Config | null | undefined;
|
||||||
@@ -133,6 +149,7 @@ export default class AuthStore {
|
|||||||
return {
|
return {
|
||||||
user: this.user,
|
user: this.user,
|
||||||
team: this.team,
|
team: this.team,
|
||||||
|
availableTeams: this.availableTeams,
|
||||||
policies: this.policies,
|
policies: this.policies,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -156,6 +173,7 @@ export default class AuthStore {
|
|||||||
const { user, team } = res.data;
|
const { user, team } = res.data;
|
||||||
this.user = new User(user, this);
|
this.user = new User(user, this);
|
||||||
this.team = new Team(team, this);
|
this.team = new Team(team, this);
|
||||||
|
this.availableTeams = res.data.availableTeams;
|
||||||
|
|
||||||
if (env.SENTRY_DSN) {
|
if (env.SENTRY_DSN) {
|
||||||
Sentry.configureScope(function (scope) {
|
Sentry.configureScope(function (scope) {
|
||||||
@@ -214,6 +232,9 @@ export default class AuthStore {
|
|||||||
runInAction("AuthStore#updateUser", () => {
|
runInAction("AuthStore#updateUser", () => {
|
||||||
this.user = null;
|
this.user = null;
|
||||||
this.team = null;
|
this.team = null;
|
||||||
|
this.availableTeams = this.availableTeams?.filter(
|
||||||
|
(team) => team.id !== this.team?.id
|
||||||
|
);
|
||||||
this.policies = [];
|
this.policies = [];
|
||||||
this.token = null;
|
this.token = null;
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user