Skip to content

Commit f7854c1

Browse files
christianalfoniCompuIves
authored andcommitted
Add modals concept (codesandbox#2430)
* Add modals concept * type fixes * wip * Frozen modal back in with new modal concept * Fix typings * type fixes
1 parent f54b2b6 commit f7854c1

File tree

11 files changed

+174
-84
lines changed

11 files changed

+174
-84
lines changed

packages/app/src/app/overmind/factories.ts

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Contributor } from '@codesandbox/common/lib/types';
2-
import { json } from 'overmind';
2+
import { json, IState, IDerive } from 'overmind';
33
import { AsyncAction } from '.';
44

55
export const withLoadApp = <T>(
@@ -68,20 +68,101 @@ export const withLoadApp = <T>(
6868
export const withOwnedSandbox = <T>(
6969
continueAction: AsyncAction<T>
7070
): AsyncAction<T> => async (context, payload) => {
71-
const { state, actions, effects } = context;
72-
73-
if (
74-
!state.editor.currentSandbox.owned ||
75-
(state.editor.currentSandbox.owned &&
76-
state.editor.currentSandbox.isFrozen &&
77-
effects.browser.confirm(
78-
'This sandbox is frozen, and will be forked. Do you want to continue?'
79-
))
80-
) {
71+
const { state, actions } = context;
72+
73+
if (!state.editor.currentSandbox.owned) {
8174
await actions.editor.internal.forkSandbox({
8275
sandboxId: state.editor.currentId,
8376
});
77+
} else if (
78+
state.editor.currentSandbox.isFrozen &&
79+
state.editor.sessionFrozen
80+
) {
81+
const modalResponse = await actions.modals.forkFrozenModal.open();
82+
83+
if (modalResponse === 'fork') {
84+
await actions.editor.internal.forkSandbox({
85+
sandboxId: state.editor.currentId,
86+
});
87+
} else if (modalResponse === 'unfreeze') {
88+
state.editor.sessionFrozen = false;
89+
}
8490
}
8591

8692
return continueAction(context, payload);
8793
};
94+
95+
export const createModals = <
96+
T extends {
97+
[name: string]: {
98+
state?: IState;
99+
result?: unknown;
100+
};
101+
}
102+
>(
103+
modals: T
104+
): {
105+
state?: {
106+
current: keyof T;
107+
} & {
108+
[K in keyof T]: T[K]['state'] & { isCurrent: IDerive<any, any, boolean> }
109+
};
110+
actions?: {
111+
[K in keyof T]: {
112+
open: AsyncAction<
113+
T[K]['state'] extends IState ? T[K]['state'] : void,
114+
T[K]['result']
115+
>;
116+
close: AsyncAction<T[K]['result']>;
117+
}
118+
};
119+
} => {
120+
function createModal(name, modal) {
121+
let resolver;
122+
123+
const open: AsyncAction<any, any> = async ({ state }, newState = {}) => {
124+
state.modals.current = name;
125+
126+
Object.assign(state.modals[name], newState);
127+
128+
return new Promise(resolve => {
129+
resolver = resolve;
130+
});
131+
};
132+
133+
const close: AsyncAction<T> = async ({ state }, payload) => {
134+
state.modals.current = null;
135+
resolver(payload || modal.result);
136+
};
137+
138+
return {
139+
state: {
140+
...modal.state,
141+
isCurrent(_, root) {
142+
return root.modals.current === name;
143+
},
144+
},
145+
actions: {
146+
open,
147+
close,
148+
},
149+
};
150+
}
151+
152+
return Object.keys(modals).reduce(
153+
(aggr, name) => {
154+
const modal = createModal(name, modals[name]);
155+
156+
aggr.state[name] = modal.state;
157+
aggr.actions[name] = modal.actions;
158+
159+
return aggr;
160+
},
161+
{
162+
state: {
163+
current: null,
164+
},
165+
actions: {},
166+
}
167+
) as any;
168+
};

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import * as git from './namespaces/git';
2626
import * as explore from './namespaces/explore';
2727
import * as profile from './namespaces/profile';
2828
import * as server from './namespaces/server';
29+
import { createModals } from './factories';
30+
import * as modals from './modals';
2931

3032
export const config = merge(
3133
{
@@ -48,6 +50,7 @@ export const config = merge(
4850
explore,
4951
profile,
5052
server,
53+
modals: createModals(modals),
5154
})
5255
);
5356

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const forkFrozenModal = {
2+
result: 'fork' as 'fork' | 'cancel' | 'unfreeze',
3+
};
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { inject, hooksObserver } from 'app/componentConnectors';
2+
import React from 'react';
3+
import Modal from 'app/components/Modal';
4+
import useKeyPressEvent from 'react-use/lib/useKeyPressEvent';
5+
import { Button } from '@codesandbox/common/lib/components/Button';
6+
import {
7+
Container,
8+
Title,
9+
Close,
10+
Actions,
11+
UnlockButton,
12+
Enter,
13+
} from './elements';
14+
15+
export const ForkFrozenSandboxModal = inject('store', 'signals')(
16+
hooksObserver(
17+
({
18+
store: {
19+
modals,
20+
editor: {
21+
currentSandbox: { customTemplate },
22+
},
23+
},
24+
signals: { modals: modalsActions, editor },
25+
}) => {
26+
const type = customTemplate ? 'template' : 'sandbox';
27+
28+
const unlock = () => {
29+
modalsActions.forkFrozenModal.close('unfreeze');
30+
};
31+
32+
const fork = (event?: { defaultPrevented: boolean }) => {
33+
if (event && !event.defaultPrevented) {
34+
modalsActions.forkFrozenModal.close('fork');
35+
}
36+
};
37+
38+
useKeyPressEvent('Enter', fork);
39+
40+
return (
41+
<Modal
42+
isOpen={modals.forkFrozenModal.isCurrent}
43+
width={450}
44+
onClose={() => modalsActions.forkFrozenModal.close()}
45+
>
46+
<Container>
47+
<Close
48+
onClick={() => modalsActions.forkFrozenModal.close('cancel')}
49+
/>
50+
<Title>Frozen {customTemplate ? 'Template' : 'Sandbox'}</Title>
51+
<p>
52+
This {type} is frozen, which means you can’t make edits without
53+
unfreezing it.
54+
</p>
55+
<p>
56+
Do you want to unfreeze the {type} for this session or make a
57+
fork?
58+
</p>
59+
<Actions>
60+
<UnlockButton onClick={unlock}>Unfreeze</UnlockButton>
61+
<Button small onClick={fork}>
62+
Fork
63+
<Enter />
64+
</Button>
65+
</Actions>
66+
</Container>
67+
</Modal>
68+
);
69+
}
70+
)
71+
);

packages/app/src/app/pages/common/Modals/ForkFrozenSandboxModal/elements.ts renamed to packages/app/src/app/pages/Sandbox/Editor/ForkFrozenSandboxModal/elements.ts

File renamed without changes.

packages/app/src/app/pages/common/Modals/ForkFrozenSandboxModal/index.ts renamed to packages/app/src/app/pages/Sandbox/Editor/ForkFrozenSandboxModal/index.ts

File renamed without changes.

packages/app/src/app/pages/Sandbox/Editor/Workspace/Project/Frozen/Frozen.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,12 @@ interface IFrozenProps {
1414
export const Frozen = inject('store', 'signals')(
1515
hooksObserver(
1616
({
17-
isFrozen,
1817
signals: {
1918
editor: { frozenUpdated, sessionFreezeOverride },
2019
},
2120
store: {
2221
editor: {
23-
currentSandbox: { customTemplate },
22+
currentSandbox: { isFrozen, customTemplate },
2423
sessionFrozen,
2524
},
2625
},
@@ -63,7 +62,7 @@ export const Frozen = inject('store', 'signals')(
6362
</FreezeContainer>
6463
</PropertyValue>
6564
</Item>
66-
{customTemplate && !sessionFrozen && (
65+
{!sessionFrozen && (
6766
<FrozenWarning>Edits are enabled for this session</FrozenWarning>
6867
)}
6968
</>

packages/app/src/app/pages/Sandbox/Editor/Workspace/Project/Project.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ export const Project = inject('store', 'signals')(
142142
</Explanation>
143143
)}
144144

145-
{editable && <Frozen isFrozen={sandbox.isFrozen} />}
145+
{editable && <Frozen />}
146146

147147
{sandbox.forkedFromSandbox && (
148148
<Item>

packages/app/src/app/pages/Sandbox/Editor/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Fullscreen from '@codesandbox/common/lib/components/flex/Fullscreen';
77
import getTemplateDefinition from '@codesandbox/common/lib/templates';
88
import codesandbox from '@codesandbox/common/lib/themes/codesandbox.json';
99

10+
import ForkFrozenSandboxModal from './ForkFrozenSandboxModal';
1011
import { Container } from './elements';
1112
import { Workspace } from './Workspace';
1213
import Content from './Content';
@@ -153,6 +154,7 @@ class ContentSplit extends React.Component {
153154
)}
154155
</div>
155156
</Fullscreen>
157+
<ForkFrozenSandboxModal />
156158
</Container>
157159
</ThemeProvider>
158160
);

packages/app/src/app/pages/common/Modals/ForkFrozenSandboxModal/ForkFrozenSandboxModal.tsx

Lines changed: 0 additions & 64 deletions
This file was deleted.

0 commit comments

Comments
 (0)