diff --git a/app/actions/definitions/teams.tsx b/app/actions/definitions/teams.tsx index 3eae35035..fcb8c9d85 100644 --- a/app/actions/definitions/teams.tsx +++ b/app/actions/definitions/teams.tsx @@ -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: () => ( - - ), - 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: () => ( + + ), + 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; diff --git a/app/hooks/useSessions.ts b/app/hooks/useSessions.ts deleted file mode 100644 index 83f64aa83..000000000 --- a/app/hooks/useSessions.ts +++ /dev/null @@ -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]; -} diff --git a/app/menus/OrganizationMenu.tsx b/app/menus/OrganizationMenu.tsx index f7778b32f..2c1d11578 100644 --- a/app/menus/OrganizationMenu.tsx +++ b/app/menus/OrganizationMenu.tsx @@ -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 ( <> diff --git a/app/stores/AuthStore.ts b/app/stores/AuthStore.ts index 3f745098d..9b196323c 100644 --- a/app/stores/AuthStore.ts +++ b/app/stores/AuthStore.ts @@ -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; });