Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a confirmation step for updating the project to cloud/local #6676

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
179 changes: 129 additions & 50 deletions packages/insomnia/src/ui/components/dropdowns/project-dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const ProjectDropdown: FC<Props> = ({ project, organizationId }) => {
useState(false);
const deleteProjectFetcher = useFetcher();
const updateProjectFetcher = useFetcher();
const [projectType, setProjectType] = useState<'local' | 'remote' | ''>('');

const projectActionList: ProjectActionItem[] = [
{
Expand Down Expand Up @@ -107,13 +108,27 @@ export const ProjectDropdown: FC<Props> = ({ project, organizationId }) => {
</Menu>
</Popover>
</MenuTrigger>
<ModalOverlay isOpen={isProjectSettingsModalOpen} onOpenChange={setIsProjectSettingsModalOpen} isDismissable className="w-full h-[--visual-viewport-height] fixed z-10 top-0 left-0 flex items-center justify-center bg-black/30">
<ModalOverlay
isOpen={isProjectSettingsModalOpen}
onOpenChange={isOpen => {
setProjectType('');
setIsProjectSettingsModalOpen(isOpen);
}}
isDismissable
className="w-full h-[--visual-viewport-height] fixed z-10 top-0 left-0 flex items-center justify-center bg-black/30"
>
<Modal className="max-w-2xl w-full rounded-md border border-solid border-[--hl-sm] p-[--padding-lg] max-h-full bg-[--color-bg] text-[--color-font]">
<Dialog onClose={() => setIsProjectSettingsModalOpen(false)} className="outline-none">
<Dialog
onClose={() => {
setIsProjectSettingsModalOpen(false);
setProjectType('');
}}
className="outline-none"
>
{({ close }) => (
<div className='flex flex-col gap-4'>
<div className='flex gap-2 items-center justify-between'>
<Heading className='text-2xl'>Project Settings</Heading>
<Heading className='text-2xl'>{projectType === 'local' ? 'Confirm conversion to local storage' : projectType === 'remote' ? 'Confirm cloud synchronization' : 'Project Settings'}</Heading>
<Button
className="flex flex-shrink-0 items-center justify-center aspect-square h-6 aria-pressed:bg-[--hl-sm] rounded-sm text-[--color-font] hover:bg-[--hl-xs] focus:ring-inset ring-1 ring-transparent focus:ring-[--hl-md] transition-all text-sm"
onPress={close}
Expand All @@ -127,62 +142,126 @@ export const ProjectDropdown: FC<Props> = ({ project, organizationId }) => {
<form
className='flex flex-col gap-4'
onSubmit={e => {
updateProjectFetcher.submit(e.currentTarget, {
action: `/organization/${organizationId}/project/${project._id}/update`,
method: 'post',
});
e.preventDefault();
const formData = new FormData(e.currentTarget);
const type = formData.get('type');
// If the project is local and the user is trying to change it to remote
if (type === 'remote' && !project.remoteId && !projectType) {
setProjectType('remote');
// If the project is remote and the user is trying to change it to local
} else if (type === 'local' && project.remoteId && !projectType) {
setProjectType('local');
} else {
updateProjectFetcher.submit(formData, {
action: `/organization/${organizationId}/project/${project._id}/update`,
method: 'post',
});

close();
close();
}
}}
>
<TextField
autoFocus
name="name"
defaultValue={project.name}
className="group relative flex-1 flex flex-col gap-2"
>
<Label className='text-sm text-[--hl]'>
Project name
</Label>
<Input
placeholder="My project"
className="py-1 placeholder:italic w-full pl-2 pr-7 rounded-sm border border-solid border-[--hl-sm] bg-[--color-bg] text-[--color-font] focus:outline-none focus:ring-1 focus:ring-[--hl-md] transition-colors"
/>
</TextField>
<RadioGroup name="type" defaultValue={project.remoteId ? 'remote' : 'local'} className="flex flex-col gap-2">
<Label className="text-sm text-[--hl]">
Project type
</Label>
<div className="flex gap-2">
<Radio
value="remote"
className="data-[selected]:border-[--color-surprise] data-[selected]:ring-2 data-[selected]:ring-[--color-surprise] hover:bg-[--hl-xs] focus:bg-[--hl-sm] border border-solid border-[--hl-md] rounded p-4 focus:outline-none transition-colors"
>
<Icon icon="globe" />
<Heading className="text-lg font-bold">Secure Cloud</Heading>
<p className='pt-2'>
End-to-end encrypted (E2EE) and synced securely to the cloud, ideal for collaboration.
<div className={`flex flex-col gap-4 ${projectType ? 'hidden' : ''}`}>
<TextField
autoFocus
name="name"
defaultValue={project.name}
className="group relative flex-1 flex flex-col gap-2"
>
<Label className='text-sm text-[--hl]'>
Project name
</Label>
<Input
placeholder="My project"
className="py-1 placeholder:italic w-full pl-2 pr-7 rounded-sm border border-solid border-[--hl-sm] bg-[--color-bg] text-[--color-font] focus:outline-none focus:ring-1 focus:ring-[--hl-md] transition-colors"
/>
</TextField>
<RadioGroup name="type" defaultValue={project.remoteId ? 'remote' : 'local'} className="flex flex-col gap-2">
<Label className="text-sm text-[--hl]">
Project type
</Label>
<div className="flex gap-2">
<Radio
value="remote"
className="data-[selected]:border-[--color-surprise] data-[selected]:ring-2 data-[selected]:ring-[--color-surprise] hover:bg-[--hl-xs] focus:bg-[--hl-sm] border border-solid border-[--hl-md] rounded p-4 focus:outline-none transition-colors"
>
<Icon icon="globe" />
<Heading className="text-lg font-bold">Secure Cloud</Heading>
<p className='pt-2'>
End-to-end encrypted (E2EE) and synced securely to the cloud, ideal for collaboration.
</p>
</Radio>
<Radio
isDisabled={isDefaultOrganizationProject(project)}
value="local"
className="data-[selected]:border-[--color-surprise] data-[disabled]:opacity-25 data-[selected]:ring-2 data-[selected]:ring-[--color-surprise] hover:bg-[--hl-xs] focus:bg-[--hl-sm] border border-solid border-[--hl-md] rounded p-4 focus:outline-none transition-colors"
>
<Icon icon="laptop" />
<Heading className="text-lg font-bold">Local Vault</Heading>
<p className="pt-2">
Stored locally only with no cloud. Ideal when collaboration is not needed.
</p>
</Radio>
</div>
</RadioGroup>
</div>

{projectType === 'local' && (
<div className='text-[--color-font] flex flex-col gap-4'>
<div className='flex flex-col gap-4'>
<p>
We will be converting your Cloud Sync project into a local project, and permanently remove all cloud data for this project from the cloud.
</p>
<ul className='text-left flex flex-col gap-2'>
<li><i className="fa fa-check text-emerald-600" /> The project will be 100% stored locally.</li>
<li><i className="fa fa-check text-emerald-600" /> Your collaborators will not be able to push and pull files anymore.</li>
<li><i className="fa fa-check text-emerald-600" /> The project will become local also for every existing collaborator.</li>
</ul>
<p>
You can still use Git Sync for local projects without using the cloud, and you can synchronize a local project back to the cloud if you decide to do so.
</p>
</Radio>
<Radio
isDisabled={isDefaultOrganizationProject(project)}
value="local"
className="data-[selected]:border-[--color-surprise] data-[disabled]:opacity-25 data-[selected]:ring-2 data-[selected]:ring-[--color-surprise] hover:bg-[--hl-xs] focus:bg-[--hl-sm] border border-solid border-[--hl-md] rounded p-4 focus:outline-none transition-colors"
>
<Icon icon="laptop" />
<Heading className="text-lg font-bold">Local Vault</Heading>
<p className="pt-2">
Stored locally only with no cloud. Ideal when collaboration is not needed.
<p className='flex gap-2 items-center'>
<Icon icon="triangle-exclamation" className='text-[--color-warning]' />
Remember to pull your latest project updates before this operation
</p>
</Radio>
</div>
</div>
</RadioGroup>
<div className="flex justify-end">
)}
{projectType === 'remote' && (
<div className='text-[--color-font] flex flex-col gap-4'>
<div className='flex flex-col gap-4'>
<p>
We will be synchronizing your local project to Insomnia's Cloud in a secure end-to-end encrypted format (E2EE) which will enable cloud collaboration.
</p>
<ul className='text-left flex flex-col gap-2'>
<li><i className="fa fa-check text-emerald-600" /> Your data in the cloud is end-to-end encrypted (E2EE) and secure.</li>
<li><i className="fa fa-check text-emerald-600" /> You can now collaborate with any amount of users and use cloud features.</li>
<li><i className="fa fa-check text-emerald-600" /> Your project will be always available on any client after logging in.</li>
</ul>
<p>
You can still use Git Sync for cloud projects.
</p>
</div>
</div>
)}
<div className="flex justify-end gap-2 items-center">
<Button
onPress={() => {
if (projectType) {
setProjectType('');
} else {
close();
}
}}
className="hover:no-underline hover:bg-opacity-90 border border-solid border-[--hl-md] py-2 px-3 text-[--color-font] transition-colors rounded-sm"
>
Cancel
</Button>
<Button
type="submit"
className="hover:no-underline bg-[#4000BF] hover:bg-opacity-90 border border-solid border-[--hl-md] py-2 px-3 text-[--color-font] transition-colors rounded-sm"
className="hover:no-underline bg-[--color-surprise] hover:bg-opacity-90 border border-solid border-[--hl-md] py-2 px-3 text-[--color-font-surprise] transition-colors rounded-sm"
>
Update
{projectType ? 'Confirm' : 'Update'}
</Button>
</div>
</form>
Expand Down