diff --git a/.changeset/nine-poets-appear.md b/.changeset/nine-poets-appear.md new file mode 100644 index 000000000..c096486c1 --- /dev/null +++ b/.changeset/nine-poets-appear.md @@ -0,0 +1,5 @@ +--- +'@keystatic/core': patch +--- + +Show logged in user in header in cloud mode diff --git a/packages/keystatic/src/app/shell/data.tsx b/packages/keystatic/src/app/shell/data.tsx index 9017d639d..2aa559513 100644 --- a/packages/keystatic/src/app/shell/data.tsx +++ b/packages/keystatic/src/app/shell/data.tsx @@ -34,6 +34,7 @@ import { isDefined } from 'emery'; import { getAuth } from '../auth'; import { ViewerContext, SidebarFooter_viewer } from './viewer-data'; import { parseRepoConfig, serializeRepoConfig } from '../repo-config'; +import { z } from 'zod'; export function fetchLocalTree(sha: string) { if (treeCache.has(sha)) { @@ -101,6 +102,53 @@ export function LocalAppShellProvider(props: { ); } +const cloudInfoSchema = z.object({ + user: z.object({ + name: z.string(), + email: z.string(), + avatarUrl: z.string().optional(), + }), + project: z.object({ + name: z.string(), + }), + team: z.object({ + name: z.string(), + slug: z.string(), + images: z.boolean(), + }), +}); + +const CloudInfo = createContext>(null); + +export function useCloudInfo() { + return useContext(CloudInfo); +} + +export function CloudInfoProvider(props: { + children: ReactNode; + config: Config; +}) { + const data = useData( + useCallback(async () => { + if (!props.config.cloud?.project) throw new Error('no cloud project set'); + const result = await fetch(`${KEYSTATIC_CLOUD_API_URL}/v1/info`, { + headers: { + ...KEYSTATIC_CLOUD_HEADERS, + Authorization: `Bearer ${await getAuth(props.config).then( + auth => auth?.accessToken + )}`, + }, + }).then(x => x.json()); + return cloudInfoSchema.parse(result); + }, [props.config]) + ); + return ( + + {props.children} + + ); +} + export const GitHubAppShellDataContext = createContext, OperationVariables diff --git a/packages/keystatic/src/app/shell/topbar.tsx b/packages/keystatic/src/app/shell/topbar.tsx index 5e6f6dc6c..9c7b6f64c 100644 --- a/packages/keystatic/src/app/shell/topbar.tsx +++ b/packages/keystatic/src/app/shell/topbar.tsx @@ -46,7 +46,11 @@ import { import { ZapLogo } from './common'; import { useAppState, useConfig } from './context'; -import { BranchInfoContext, GitHubAppShellDataContext } from './data'; +import { + BranchInfoContext, + GitHubAppShellDataContext, + useCloudInfo, +} from './data'; import { useViewer } from './viewer-data'; import { useThemeContext } from './theme'; import { serializeRepoConfig } from '../repo-config'; @@ -87,6 +91,7 @@ export const SidebarHeader = () => { // ----------------------------------------------------------------------------- function CloudHeader({ config }: { config: CloudConfig }) { + const cloudInfo = useCloudInfo(); return ( @@ -104,7 +109,17 @@ function CloudHeader({ config }: { config: CloudConfig }) { - {/* */} + ); } @@ -113,6 +128,7 @@ function CloudHeader({ config }: { config: CloudConfig }) { // ----------------------------------------------------------------------------- function GithubHeader({ config }: { config: GitHubConfig }) { + const user = useViewer(); return ( @@ -133,7 +149,17 @@ function GithubHeader({ config }: { config: GitHubConfig }) { - + ); } @@ -259,8 +285,11 @@ function ThemeMenu() { // User controls // ----------------------------------------------------------------------------- -function UserMenu() { - let user = useViewer(); +function UserMenu({ + user, +}: { + user: { name: string; avatarUrl?: string; login: string } | undefined; +}) { let config = useConfig(); const menuItems = useMemo(() => { let items = [ @@ -273,7 +302,7 @@ function UserMenu() { if (isCloudConfig(config)) { items.unshift({ id: 'manage', - label: 'Manage account', + label: 'Account', icon: userIcon, }); } @@ -315,7 +344,7 @@ function UserMenu() { /> - {user.name ?? user.login} + {user.name} {user.login} diff --git a/packages/keystatic/src/app/ui.tsx b/packages/keystatic/src/app/ui.tsx index c18d6eb6a..da81c20e7 100644 --- a/packages/keystatic/src/app/ui.tsx +++ b/packages/keystatic/src/app/ui.tsx @@ -33,6 +33,7 @@ import { AppSlugProvider } from './onboarding/install-app'; import { useRouter, Router, RouterProvider } from './router'; import { isCloudConfig, isGitHubConfig, redirectToCloudAuth } from './utils'; import { + CloudInfoProvider, GitHubAppShellDataContext, GitHubAppShellDataProvider, } from './shell/data'; @@ -107,11 +108,17 @@ function PageInner({ config }: { config: Config }) { return ; } let wrapper: (element: ReactElement) => ReactElement = x => x; + if (isCloudConfig(config)) { + wrapper = element => ( + {element} + ); + } if (isGitHubConfig(config) || isCloudConfig(config)) { + const origWrapper = wrapper; wrapper = element => ( - {element} + {origWrapper(element)} );