Skip to content

Commit 780cfb5

Browse files
feat(sidebar): Sidebar overmind state (#7000)
* Add sidebar as a separate overmind namespace * Render sidebar items based on sidebar state * Only add to state what we need * Fix types after new generated gql types were added * Dont use collections in sidebarstate * Move if statement and fix types
1 parent e0138f6 commit 780cfb5

File tree

10 files changed

+215
-13
lines changed

10 files changed

+215
-13
lines changed

packages/app/src/app/graphql/types.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,8 @@ export type ProSubscription = {
580580
paymentProvider: Maybe<SubscriptionPaymentProvider>;
581581
quantity: Maybe<Scalars['Int']>;
582582
status: SubscriptionStatus;
583+
trialEnd: Maybe<Scalars['DateTime']>;
584+
trialStart: Maybe<Scalars['DateTime']>;
583585
type: SubscriptionType;
584586
unitPrice: Maybe<Scalars['Int']>;
585587
updateBillingUrl: Maybe<Scalars['String']>;
@@ -3394,6 +3396,50 @@ export type RecentNotificationsQuery = { __typename?: 'RootQueryType' } & {
33943396
>;
33953397
};
33963398

3399+
export type SidebarSyncedSandboxFragmentFragment = {
3400+
__typename?: 'Sandbox';
3401+
} & Pick<Sandbox, 'id'>;
3402+
3403+
export type SidebarTemplateFragmentFragment = {
3404+
__typename?: 'Template';
3405+
} & Pick<Template, 'id'>;
3406+
3407+
export type PersonalSidebarDataQueryVariables = Exact<{ [key: string]: never }>;
3408+
3409+
export type PersonalSidebarDataQuery = { __typename?: 'RootQueryType' } & {
3410+
me: Maybe<
3411+
{ __typename?: 'CurrentUser' } & {
3412+
sandboxes: Array<
3413+
{ __typename?: 'Sandbox' } & SidebarSyncedSandboxFragmentFragment
3414+
>;
3415+
templates: Array<
3416+
{ __typename?: 'Template' } & SidebarTemplateFragmentFragment
3417+
>;
3418+
}
3419+
>;
3420+
};
3421+
3422+
export type TeamSidebarDataQueryVariables = Exact<{
3423+
id: Scalars['UUID4'];
3424+
}>;
3425+
3426+
export type TeamSidebarDataQuery = { __typename?: 'RootQueryType' } & {
3427+
me: Maybe<
3428+
{ __typename?: 'CurrentUser' } & {
3429+
team: Maybe<
3430+
{ __typename?: 'Team' } & {
3431+
sandboxes: Array<
3432+
{ __typename?: 'Sandbox' } & SidebarSyncedSandboxFragmentFragment
3433+
>;
3434+
templates: Array<
3435+
{ __typename?: 'Template' } & SidebarTemplateFragmentFragment
3436+
>;
3437+
}
3438+
>;
3439+
}
3440+
>;
3441+
};
3442+
33973443
export type TeamsQueryVariables = Exact<{ [key: string]: never }>;
33983444

33993445
export type TeamsQuery = { __typename?: 'RootQueryType' } & {

packages/app/src/app/overmind/effects/gql/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import * as teamsQueries from './teams/queries';
1111
import * as dashboardQueries from './dashboard/queries';
1212
import * as dashboardMutations from './dashboard/mutations';
1313

14+
import * as sidebarQueries from './sidebar/queries';
15+
1416
import * as notificationsQueries from './notifications/queries';
1517
import * as notificationsMutations from './notifications/mutations';
1618

@@ -24,6 +26,7 @@ export default graphql({
2426
...commentsQueries,
2527
...teamsQueries,
2628
...dashboardQueries,
29+
...sidebarQueries,
2730
...notificationsQueries,
2831
},
2932
mutations: {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { gql } from 'overmind-graphql';
2+
3+
export const sidebarSyncedSandboxFragment = gql`
4+
fragment sidebarSyncedSandboxFragment on Sandbox {
5+
id
6+
}
7+
`;
8+
9+
export const sidebarTemplateFragment = gql`
10+
fragment sidebarTemplateFragment on Template {
11+
id
12+
}
13+
`;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { gql, Query } from 'overmind-graphql';
2+
3+
import {
4+
PersonalSidebarDataQuery,
5+
TeamSidebarDataQuery,
6+
TeamSidebarDataQueryVariables,
7+
} from 'app/graphql/types';
8+
9+
import {
10+
sidebarSyncedSandboxFragment,
11+
sidebarTemplateFragment,
12+
} from './fragments';
13+
14+
export const getPersonalSidebarData: Query<
15+
PersonalSidebarDataQuery,
16+
undefined
17+
> = gql`
18+
query PersonalSidebarData {
19+
me {
20+
sandboxes(hasOriginalGit: true) {
21+
...sidebarSyncedSandboxFragment
22+
}
23+
templates {
24+
...sidebarTemplateFragment
25+
}
26+
}
27+
}
28+
${sidebarSyncedSandboxFragment}
29+
${sidebarTemplateFragment}
30+
`;
31+
32+
export const getTeamSidebarData: Query<
33+
TeamSidebarDataQuery,
34+
TeamSidebarDataQueryVariables
35+
> = gql`
36+
query TeamSidebarData($id: UUID4!) {
37+
me {
38+
team(id: $id) {
39+
sandboxes(hasOriginalGit: true) {
40+
...sidebarSyncedSandboxFragment
41+
}
42+
templates {
43+
...sidebarTemplateFragment
44+
}
45+
}
46+
}
47+
}
48+
${sidebarSyncedSandboxFragment}
49+
${sidebarTemplateFragment}
50+
`;

packages/app/src/app/overmind/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { createModals } from './factories';
1313
import * as modals from './modals';
1414
import * as comments from './namespaces/comments';
1515
import * as dashboard from './namespaces/dashboard';
16+
import * as sidebar from './namespaces/sidebar';
1617
import * as deployment from './namespaces/deployment';
1718
import * as editor from './namespaces/editor';
1819
import * as explore from './namespaces/explore';
@@ -43,6 +44,7 @@ export const config = merge(
4344
live,
4445
workspace,
4546
dashboard,
47+
sidebar,
4648
deployment,
4749
files,
4850
git,
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type { Context } from 'app/overmind';
2+
import type {
3+
SidebarSyncedSandboxFragmentFragment,
4+
SidebarTemplateFragmentFragment,
5+
} from 'app/graphql/types';
6+
7+
export const getSidebarData = async (
8+
{ state, effects }: Context,
9+
teamId?: string
10+
) => {
11+
const {
12+
gql: { queries },
13+
} = effects;
14+
15+
try {
16+
let sandboxes: SidebarSyncedSandboxFragmentFragment[] | null;
17+
let templates: SidebarTemplateFragmentFragment[] | null;
18+
19+
if (teamId) {
20+
/**
21+
* Fetch data for the selected team
22+
*/
23+
const result = await queries.getTeamSidebarData({ id: teamId });
24+
25+
sandboxes = result.me?.team?.sandboxes || null;
26+
templates = result.me?.team?.templates || null;
27+
} else {
28+
/**
29+
* Fetch data for the user
30+
*/
31+
const result = await queries.getPersonalSidebarData();
32+
33+
sandboxes = result.me?.sandboxes || null;
34+
templates = result.me?.templates || null;
35+
}
36+
37+
const hasSyncedSandboxes = sandboxes && sandboxes.length > 0;
38+
const hasTemplates = templates && templates.length > 0;
39+
40+
state.sidebar = {
41+
hasSyncedSandboxes,
42+
hasTemplates,
43+
};
44+
} catch {
45+
effects.notificationToast.error(
46+
`There was a problem getting your data for the sidebar.`
47+
);
48+
}
49+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import * as actions from './actions';
2+
import { state } from './state';
3+
4+
export { state, actions };
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* SidebarState, required to be named just State. Can be an interface instead
3+
* of a type though.
4+
*
5+
* ❗️ TODO: Add sidebar notification indicator state
6+
*/
7+
export interface State {
8+
hasSyncedSandboxes: boolean | null;
9+
hasTemplates: boolean | null;
10+
}
11+
12+
/**
13+
* Default state for the sidebar
14+
*/
15+
export const state: State = {
16+
hasSyncedSandboxes: null,
17+
hasTemplates: null,
18+
};

packages/app/src/app/pages/Dashboard/Sidebar/index.tsx

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,18 @@ export const Sidebar: React.FC<SidebarProps> = ({
6666
} = state;
6767

6868
React.useEffect(() => {
69+
// Used to fetch collections
6970
actions.dashboard.getAllFolders();
7071
actions.dashboard.getStarredRepos();
7172
}, [actions.dashboard, state.activeTeam]);
7273

74+
React.useEffect(() => {
75+
// Used to check for templates and synced sandboxes
76+
actions.sidebar.getSidebarData(
77+
state.activeTeam !== personalWorkspaceId ? state.activeTeam : undefined
78+
);
79+
}, [state.activeTeam, personalWorkspaceId, actions.sidebar]);
80+
7381
React.useEffect(() => {
7482
if (state.activeTeam) {
7583
const team = dashboard.teams.find(({ id }) => id === state.activeTeam);
@@ -252,12 +260,16 @@ export const Sidebar: React.FC<SidebarProps> = ({
252260
path={dashboardUrls.drafts(activeTeam)}
253261
icon="file"
254262
/>
255-
<RowItem
256-
name="Templates"
257-
page="templates"
258-
path={dashboardUrls.templates(activeTeam)}
259-
icon="star"
260-
/>
263+
264+
{state.sidebar.hasTemplates ? (
265+
<RowItem
266+
name="Templates"
267+
page="templates"
268+
path={dashboardUrls.templates(activeTeam)}
269+
icon="star"
270+
/>
271+
) : null}
272+
261273
<NestableRowItem
262274
name="All sandboxes"
263275
path={dashboardUrls.sandboxes('/', activeTeam)}
@@ -278,12 +290,16 @@ export const Sidebar: React.FC<SidebarProps> = ({
278290
icon="server"
279291
/>
280292
)}
281-
<RowItem
282-
name="Synced"
283-
page="synced-sandboxes"
284-
path={dashboardUrls.syncedSandboxes(activeTeam)}
285-
icon="sync"
286-
/>
293+
294+
{state.sidebar.hasSyncedSandboxes ? (
295+
<RowItem
296+
name="Synced"
297+
page="synced-sandboxes"
298+
path={dashboardUrls.syncedSandboxes(activeTeam)}
299+
icon="sync"
300+
/>
301+
) : null}
302+
287303
<RowItem
288304
name="Archive"
289305
page="archive"

packages/app/src/app/pages/Pro/legacy-pages/WorkspacePlanSelection.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,8 @@ const PlanCard: React.FC<{
538538
plan: Plan;
539539
billingInterval: Plan['billingInterval'];
540540
setBillingInterval: (billingInterval: Plan['billingInterval']) => void;
541-
currentSubscription: ProSubscription | null;
541+
// Omitting new trialEnd and trialStart
542+
currentSubscription: Omit<ProSubscription, 'trialEnd' | 'trialStart'> | null;
542543
}> = ({ plan, billingInterval, setBillingInterval, currentSubscription }) => {
543544
const isSelected = plan.billingInterval === billingInterval;
544545
const isCurrent =

0 commit comments

Comments
 (0)