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

Save backend type in localStorage, and automatically open projects when ready #6728

Merged
merged 28 commits into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5e6600a
Save backend type; fix `-startup.project`
somebody1234 May 16, 2023
fdcda14
Attempt to fix for cloud
somebody1234 May 16, 2023
5a59a0e
Fix for cloud and implement automatic opening
somebody1234 May 17, 2023
9bf2f3f
Fixes
somebody1234 May 17, 2023
2c8be25
Add missing functionality
somebody1234 May 18, 2023
b100f22
Merge branch 'develop' into wip/sb/save-backend-type
somebody1234 May 18, 2023
434d96e
Switch default backend to local backend
somebody1234 May 19, 2023
ea21789
Merge branch 'develop' into wip/sb/save-backend-type
somebody1234 May 21, 2023
e3fa35d
Fix type error
somebody1234 May 21, 2023
9b2544a
Merge branch 'develop' into wip/sb/save-backend-type
somebody1234 May 23, 2023
82d230e
Merge branch 'develop' into wip/sb/save-backend-type
somebody1234 May 24, 2023
f7a7e08
Merge branch 'develop' into wip/sb/save-backend-type
somebody1234 May 28, 2023
2c1455c
Fix saving backend
somebody1234 May 28, 2023
cf99a78
Make loading message appear instantly
somebody1234 May 28, 2023
7fb83ea
Fix context menu positioning
somebody1234 May 28, 2023
4acb5ae
Fixes and QoL improvements
somebody1234 May 29, 2023
316af11
Style scrollbar on `document.body`
somebody1234 May 29, 2023
a9724c9
Merge branch 'develop' into wip/sb/save-backend-type
somebody1234 May 30, 2023
edf49d1
Merge branch 'develop' into wip/sb/save-backend-type
somebody1234 May 30, 2023
f166d76
Fix `-startup.project`; other minor fixes
somebody1234 May 30, 2023
aa89367
Open project immediately when creating from template; minor fix
somebody1234 May 31, 2023
50cc4cf
Finally fix spinner bugs
somebody1234 May 31, 2023
4d6d845
Merge branch 'develop' into wip/sb/save-backend-type
somebody1234 Jun 2, 2023
5582a63
Fix some minor bugs
somebody1234 Jun 2, 2023
92d661f
Fix bugs when closing project
somebody1234 Jun 2, 2023
51dfe41
Disallow deleting local projects while they are still running
somebody1234 Jun 5, 2023
f6c339c
Close modals when buttons are clicked
somebody1234 Jun 5, 2023
c3e5f4c
Merge branch 'develop' into wip/sb/save-backend-type
somebody1234 Jun 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions app/ide-desktop/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,30 @@ export default [
'no-undef': 'off',
},
},
{
files: [
'lib/dashboard/src/**/*.ts',
'lib/dashboard/src/**/*.mts',
'lib/dashboard/src/**/*.cts',
'lib/dashboard/src/**/*.tsx',
'lib/dashboard/src/**/*.mtsx',
'lib/dashboard/src/**/*.ctsx',
],
rules: {
'no-restricted-properties': [
'error',
{
object: 'console',
message: 'Avoid leaving debugging statements when committing code',
},
{
object: 'hooks',
property: 'useDebugState',
message: 'Avoid leaving debugging statements when committing code',
},
],
},
},
{
files: ['**/*.d.ts'],
rules: {
Expand Down
17 changes: 10 additions & 7 deletions app/ide-desktop/lib/content/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,13 +219,13 @@ class Main implements AppRunner {
const isOpeningMainEntryPoint =
contentConfig.OPTIONS.groups.startup.options.entry.value ===
contentConfig.OPTIONS.groups.startup.options.entry.default
const isNotOpeningProject =
contentConfig.OPTIONS.groups.startup.options.project.value === ''
if (
(isUsingAuthentication || isUsingNewDashboard) &&
isOpeningMainEntryPoint &&
isNotOpeningProject
) {
// This MUST be removed as it would otherwise override the `startup.project` passed
// explicitly in `ide.tsx`.
if (isOpeningMainEntryPoint && url.searchParams.has('startup.project')) {
url.searchParams.delete('startup.project')
history.replaceState(null, '', url.toString())
}
if ((isUsingAuthentication || isUsingNewDashboard) && isOpeningMainEntryPoint) {
this.runAuthentication(isInAuthenticationFlow, inputConfig)
} else {
void this.runApp(inputConfig)
Expand All @@ -235,6 +235,8 @@ class Main implements AppRunner {

/** Begins the authentication UI flow. */
runAuthentication(isInAuthenticationFlow: boolean, inputConfig?: StringConfig) {
const initialProjectName =
contentConfig.OPTIONS.groups.startup.options.project.value || null
/** TODO [NP]: https://github.com/enso-org/cloud-v2/issues/345
* `content` and `dashboard` packages **MUST BE MERGED INTO ONE**. The IDE
* should only have one entry point. Right now, we have two. One for the cloud
Expand All @@ -250,6 +252,7 @@ class Main implements AppRunner {
supportsLocalBackend: SUPPORTS_LOCAL_BACKEND,
supportsDeepLinks: SUPPORTS_DEEP_LINKS,
showDashboard: contentConfig.OPTIONS.groups.featurePreview.options.newDashboard.value,
initialProjectName,
onAuthenticated: () => {
if (isInAuthenticationFlow) {
const initialUrl = localStorage.getItem(INITIAL_URL_KEY)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ function Registration() {
<SvgIcon svg={svg.LOCK} />

<Input
required
id="password"
type="password"
name="password"
Expand All @@ -103,6 +104,7 @@ function Registration() {
<SvgIcon svg={svg.LOCK} />

<Input
required
id="password_confirmation"
type="password"
name="password_confirmation"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export function AuthProvider(props: AuthProviderProps) {
const { authService, onAuthenticated, children } = props
const { cognito } = authService
const { session, deinitializeSession } = sessionProvider.useSession()
const { setBackend } = backendProvider.useSetBackend()
const { setBackendWithoutSavingType } = backendProvider.useSetBackend()
const logger = loggerProvider.useLogger()
// This must not be `hooks.useNavigate` as `goOffline` would be inaccessible,
// and the function call would error.
Expand Down Expand Up @@ -211,7 +211,7 @@ export function AuthProvider(props: AuthProviderProps) {
// The backend MUST be the remote backend before login is finished.
// This is because the "set username" flow requires the remote backend.
if (!initialized || userSession == null) {
setBackend(backend)
setBackendWithoutSavingType(backend)
}
let organization
// eslint-disable-next-line no-restricted-syntax
Expand Down Expand Up @@ -286,7 +286,7 @@ export function AuthProvider(props: AuthProviderProps) {
const goOfflineInternal = () => {
setInitialized(true)
setUserSession(OFFLINE_USER_SESSION)
setBackend(new localBackend.LocalBackend())
setBackendWithoutSavingType(new localBackend.LocalBackend())
}

const goOffline = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export interface AppProps {
supportsDeepLinks: boolean
/** Whether the dashboard should be rendered. */
showDashboard: boolean
/** The name of the project to open on startup, if any. */
initialProjectName: string | null
onAuthenticated: () => void
appRunner: AppRunner
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/** @file Type definitions common between all backends. */

import * as dateTime from './dateTime'
import * as newtype from '../newtype'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ function ChangePasswordModal() {
type="password"
name="old_password"
placeholder="Old Password"
pattern={validation.PREVIOUS_PASSWORD_PATTERN}
title={validation.PREVIOUS_PASSWORD_TITLE}
value={oldPassword}
setValue={setOldPassword}
className="text-sm sm:text-base placeholder-gray-500 pl-10 pr-4 rounded-lg border border-gray-400 w-full py-2 focus:outline-none focus:border-blue-400"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/** @file Modal for confirming delete of any type of asset. */
import * as react from 'react'
import toast from 'react-hot-toast'

import * as modalProvider from '../../providers/modal'
Expand All @@ -23,14 +24,23 @@ function ConfirmDeleteModal(props: ConfirmDeleteModalProps) {
const { assetType, name, doDelete, onSuccess } = props
const { unsetModal } = modalProvider.useSetModal()

const [isSubmitting, setIsSubmitting] = react.useState(false)

const onSubmit = async () => {
unsetModal()
await toast.promise(doDelete(), {
loading: `Deleting ${assetType}...`,
success: `Deleted ${assetType}.`,
error: `Could not delete ${assetType}.`,
})
onSuccess()
if (!isSubmitting) {
try {
setIsSubmitting(true)
await toast.promise(doDelete(), {
loading: `Deleting ${assetType}...`,
success: `Deleted ${assetType}.`,
error: `Could not delete ${assetType}.`,
})
unsetModal()
onSuccess()
} finally {
setIsSubmitting(false)
}
}
}

return (
Expand All @@ -52,18 +62,25 @@ function ConfirmDeleteModal(props: ConfirmDeleteModalProps) {
</button>
Are you sure you want to delete the {assetType} '{name}'?
<div className="m-1">
<div
className="hover:cursor-pointer inline-block text-white bg-red-500 rounded-full px-4 py-1 m-1"
onClick={onSubmit}
<button
type="submit"
disabled={isSubmitting}
className={`hover:cursor-pointer inline-block text-white bg-red-500 rounded-full px-4 py-1 m-1 ${
isSubmitting ? 'opacity-50' : ''
}`}
>
Delete
</div>
<div
className="hover:cursor-pointer inline-block bg-gray-200 rounded-full px-4 py-1 m-1"
</button>
<button
type="button"
disabled={isSubmitting}
className={`hover:cursor-pointer inline-block bg-gray-200 rounded-full px-4 py-1 m-1 ${
isSubmitting ? 'opacity-50' : ''
}`}
onClick={unsetModal}
>
Cancel
</div>
</button>
</div>
</form>
</Modal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ export interface ContextMenuProps {
function ContextMenu(props: react.PropsWithChildren<ContextMenuProps>) {
const { children, event } = props
const contextMenuRef = react.useRef<HTMLDivElement>(null)
const [top, setTop] = react.useState(event.pageY)
// This must be the original height before the returned element affects the `scrollHeight`.
const [bodyHeight] = react.useState(document.body.scrollHeight)

react.useEffect(() => {
if (contextMenuRef.current != null) {
setTop(Math.min(top, bodyHeight - contextMenuRef.current.clientHeight))
const boundingBox = contextMenuRef.current.getBoundingClientRect()
const scrollBy = boundingBox.bottom - innerHeight + SCROLL_MARGIN
if (scrollBy > 0) {
Expand All @@ -39,7 +43,8 @@ function ContextMenu(props: react.PropsWithChildren<ContextMenuProps>) {
return (
<div
ref={contextMenuRef}
style={{ left: event.pageX, top: event.pageY }}
// The location must be offset by -0.5rem to balance out the `m-2`.
style={{ left: `calc(${event.pageX}px - 0.5rem)`, top: `calc(${top}px - 0.5rem)` }}
className="absolute bg-white rounded-lg shadow-soft flex flex-col flex-nowrap m-2"
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ import * as react from 'react'
/** Props for a {@link ContextMenuEntry}. */
export interface ContextMenuEntryProps {
disabled?: boolean
title?: string
onClick: (event: react.MouseEvent<HTMLButtonElement>) => void
}

// This component MUST NOT use `useState` because it is not rendered directly.
/** An item in a `ContextMenu`. */
function ContextMenuEntry(props: react.PropsWithChildren<ContextMenuEntryProps>) {
const { children, disabled, onClick } = props
const { children, disabled, title, onClick } = props
return (
<button
disabled={disabled}
title={title}
className={`${
disabled ? 'opacity-50' : ''
} p-1 hover:bg-gray-200 first:rounded-t-lg last:rounded-b-lg text-left`}
Expand Down
Loading