Use auth.availableTeams endpoint for workspace switching (#4585)

This commit is contained in:
Tom Moor
2022-12-17 17:17:02 -08:00
committed by GitHub
parent 1995a3fb19
commit f8ba393f7c
4 changed files with 61 additions and 73 deletions

View File

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

View File

@@ -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];
}

View File

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

View File

@@ -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;
}); });