diff --git a/packages/design-system/organisms/app-shell/AppShell.tsx b/packages/design-system/organisms/app-shell/AppShell.tsx
index c724e4e..bfb2e94 100644
--- a/packages/design-system/organisms/app-shell/AppShell.tsx
+++ b/packages/design-system/organisms/app-shell/AppShell.tsx
@@ -13,6 +13,7 @@ export type AppShellProps = {
small?: boolean;
activeGuildId?: string | null;
guilds?: GuildSlug[];
+ recentGuilds?: string[];
disableGuildPicker?: boolean;
};
@@ -26,6 +27,7 @@ export const AppShell = (props: AppShellProps) => (
guilds={props.guilds || []}
activeGuildId={props.activeGuildId || null}
user={props.user}
+ recentGuilds={props.recentGuilds}
/>
) : (
diff --git a/packages/design-system/organisms/masthead/Authed.tsx b/packages/design-system/organisms/masthead/Authed.tsx
index bb21c09..d25500e 100644
--- a/packages/design-system/organisms/masthead/Authed.tsx
+++ b/packages/design-system/organisms/masthead/Authed.tsx
@@ -22,6 +22,7 @@ type Props = {
activeGuildId: string | null;
guilds: GuildSlug[];
disableGuildPicker?: boolean;
+ recentGuilds: string[];
};
export const Authed = (props: Props) => {
@@ -65,7 +66,12 @@ export const Authed = (props: Props) => {
preferredWidth={560}
onExit={() => setServerPopoverState(false)}
>
- {() => }
+ {() => (
+
+ )}
diff --git a/packages/web/src/contexts/app-shell/AppShellContext.tsx b/packages/web/src/contexts/app-shell/AppShellContext.tsx
new file mode 100644
index 0000000..13432af
--- /dev/null
+++ b/packages/web/src/contexts/app-shell/AppShellContext.tsx
@@ -0,0 +1,35 @@
+import { AppShellProps } from '@roleypoly/design-system/organisms/app-shell';
+import * as React from 'react';
+import { useRecentGuilds } from '../recent-guilds/RecentGuildsContext';
+import { useSessionContext } from '../session/SessionContext';
+
+type AppShellPropsT = {
+ user: AppShellProps['user'];
+ guilds: AppShellProps['guilds'];
+ recentGuilds: AppShellProps['recentGuilds'];
+};
+
+export const AppShellPropsContext = React.createContext({
+ user: undefined,
+ guilds: undefined,
+ recentGuilds: [],
+});
+
+export const useAppShellProps = () => React.useContext(AppShellPropsContext);
+
+export const AppShellPropsProvider = (props: { children: React.ReactNode }) => {
+ const { session } = useSessionContext();
+ const { recentGuilds } = useRecentGuilds();
+
+ const appShellProps: AppShellPropsT = {
+ user: session?.user,
+ guilds: session?.guilds,
+ recentGuilds,
+ };
+
+ return (
+
+ {props.children}
+
+ );
+};
diff --git a/packages/web/src/contexts/recent-guilds/RecentGuildsContext.tsx b/packages/web/src/contexts/recent-guilds/RecentGuildsContext.tsx
new file mode 100644
index 0000000..3319e50
--- /dev/null
+++ b/packages/web/src/contexts/recent-guilds/RecentGuildsContext.tsx
@@ -0,0 +1,59 @@
+import * as React from 'react';
+
+type RecentGuildsT = {
+ recentGuilds: string[];
+ pushRecentGuild: (id: string) => void;
+};
+
+export const RecentGuilds = React.createContext({
+ recentGuilds: [],
+ pushRecentGuild: () => {},
+});
+
+export const useRecentGuilds = () => React.useContext(RecentGuilds);
+
+const saveState = (state: string[]) => {
+ localStorage.setItem('rp_recent_guilds', JSON.stringify(state));
+};
+
+const pullState = (): string[] => {
+ const rawState = localStorage.getItem('rp_recent_guilds');
+ if (!rawState) {
+ return [];
+ }
+
+ try {
+ return JSON.parse(rawState);
+ } catch (e) {
+ console.warn('RecentGuilds failed to re-hydrate saved state', e);
+ return [];
+ }
+};
+
+export const RecentGuildsProvider = (props: { children: React.ReactNode }) => {
+ const [recentGuilds, setRecentGuilds] = React.useState(pullState());
+
+ const recentGuildsData: RecentGuildsT = {
+ recentGuilds,
+ pushRecentGuild: (id: string) => {
+ const nextState = [
+ id,
+ ...recentGuilds.slice(0, 19).filter((guild) => guild !== id),
+ ];
+
+ if (recentGuilds[0] !== id) {
+ setRecentGuilds(nextState);
+ }
+ },
+ };
+
+ React.useEffect(() => {
+ saveState(recentGuilds);
+ }, [recentGuilds]);
+
+ return (
+
+ {props.children}
+
+ );
+};
diff --git a/packages/web/src/index.tsx b/packages/web/src/index.tsx
index 7d33083..1b9b869 100644
--- a/packages/web/src/index.tsx
+++ b/packages/web/src/index.tsx
@@ -2,15 +2,32 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { AppRouter } from './app-router/AppRouter';
import { ApiContextProvider } from './contexts/api/ApiContext';
+import { AppShellPropsProvider } from './contexts/app-shell/AppShellContext';
+import { RecentGuildsProvider } from './contexts/recent-guilds/RecentGuildsContext';
import { SessionContextProvider } from './contexts/session/SessionContext';
+const ProviderProvider = (props: {
+ providerChain: typeof ApiContextProvider[];
+ children: React.ReactNode;
+}) => {
+ return props.providerChain.reduceRight(
+ (acc, Provider) => {acc},
+ <>{props.children}>
+ );
+};
+
ReactDOM.render(
-
-
-
-
-
+
+
+
,
document.getElementById('root')
);
diff --git a/packages/web/src/pages/picker.tsx b/packages/web/src/pages/picker.tsx
index 707bd37..d64b207 100644
--- a/packages/web/src/pages/picker.tsx
+++ b/packages/web/src/pages/picker.tsx
@@ -3,6 +3,7 @@ import { RolePickerTemplate } from '@roleypoly/design-system/templates/role-pick
import { ServerSetupTemplate } from '@roleypoly/design-system/templates/server-setup';
import { PresentableGuild, RoleUpdate, UserGuildPermissions } from '@roleypoly/types';
import * as React from 'react';
+import { useRecentGuilds } from '../contexts/recent-guilds/RecentGuildsContext';
import { useSessionContext } from '../contexts/session/SessionContext';
import { makeRoleTransactions } from '../utils/roleTransactions';
@@ -12,6 +13,7 @@ type PickerProps = {
const Picker = (props: PickerProps) => {
const { session, authedFetch, isAuthenticated } = useSessionContext();
+ const { pushRecentGuild } = useRecentGuilds();
const [pickerData, setPickerData] = React.useState(
null
@@ -32,7 +34,11 @@ const Picker = (props: PickerProps) => {
};
fetchPickerData();
- }, [props.serverID, authedFetch]);
+ }, [props.serverID, authedFetch, pushRecentGuild]);
+
+ React.useCallback((serverID) => pushRecentGuild(serverID), [pushRecentGuild])(
+ props.serverID
+ );
if (!isAuthenticated) {
return ;