Skip to content

Commit

Permalink
Merge branch 'develop' into wip/procrat/block-graph-editor-in-fs-viz-…
Browse files Browse the repository at this point in the history
…6754

* develop:
  Handle `show-dashboard` event (#6837)
  Fix some dashboard issues (#6668)
  Fix JWT leak (#6815)
  Fix "set username" screen (#6824)
  Fallback to opened date when ordering projects (#6814)
  • Loading branch information
Procrat committed May 26, 2023
2 parents 9cae29a + 9a456b5 commit e80d293
Show file tree
Hide file tree
Showing 40 changed files with 621 additions and 422 deletions.
2 changes: 1 addition & 1 deletion app/ide-desktop/lib/client/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ await esbuild.build(BUNDLER_OPTIONS)
console.log('Linking GUI files.')
await fs.symlink(path.join(GUI_PATH, 'assets'), path.join(IDE_PATH, 'assets'), 'dir')

console.log('LinkingProject Manager files.')
console.log('Linking Project Manager files.')
await fs.symlink(PROJECT_MANAGER_BUNDLE, path.join(IDE_PATH, paths.PROJECT_MANAGER_BUNDLE), 'dir')

console.log('Spawning Electron process.')
Expand Down
8 changes: 7 additions & 1 deletion app/ide-desktop/lib/client/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@ const ALL_BUNDLES_READY = new Promise<Watches>((resolve, reject) => {
void dashboardBuilder.watch()

console.log('Bundling content.')
const contentOpts = contentBundler.bundlerOptionsFromEnv()
const contentOpts = contentBundler.bundlerOptionsFromEnv({
// This is in watch mode, however it runs its own server rather than an esbuild server.
devMode: false,
supportsLocalBackend: true,
supportsDeepLinks: false,
})
contentOpts.plugins.push({
name: 'enso-on-rebuild',
setup: build => {
Expand All @@ -103,6 +108,7 @@ const ALL_BUNDLES_READY = new Promise<Watches>((resolve, reject) => {
},
})
contentOpts.outdir = path.resolve(IDE_DIR_PATH, 'assets')
contentOpts.define.REDIRECT_OVERRIDE = JSON.stringify('http://localhost:8080')
const contentBuilder = await esbuild.context(contentOpts)
const content = await contentBuilder.rebuild()
console.log('Result of content bundling: ', content)
Expand Down
4 changes: 2 additions & 2 deletions app/ide-desktop/lib/content-config/src/config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"options": {
"authentication": {
"value": false,
"value": true,
"description": "Determines whether user authentication is enabled. This option is always true when executed in the cloud."
},
"dataCollection": {
Expand Down Expand Up @@ -116,7 +116,7 @@
"primary": false
},
"newDashboard": {
"value": false,
"value": true,
"description": "Determines whether the new dashboard with cloud integration is enabled."
},
"profiling": {
Expand Down
8 changes: 7 additions & 1 deletion app/ide-desktop/lib/content/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import * as bundler from './esbuild-config'
// =======================

try {
void esbuild.build(bundler.bundleOptions())
void esbuild.build(
bundler.bundleOptions({
devMode: false,
supportsLocalBackend: true,
supportsDeepLinks: true,
})
)
} catch (error) {
console.error(error)
throw error
Expand Down
40 changes: 30 additions & 10 deletions app/ide-desktop/lib/content/esbuild-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,20 @@ const THIS_PATH = pathModule.resolve(pathModule.dirname(url.fileURLToPath(import
// === Environment variables ===
// =============================

/** Arguments that must always be supplied, because they are not defined as
* environment variables. */
export interface PassthroughArguments {
/** `true` if in development mode (live-reload), `false` if in production mode. */
devMode: boolean
/** Whether the application may have the local backend running. */
supportsLocalBackend: boolean
/** Whether the application supports deep links. This is only true when using
* the installed app on macOS and Windows. */
supportsDeepLinks: boolean
}

/** Mandatory build options. */
export interface Arguments {
export interface Arguments extends PassthroughArguments {
/** List of files to be copied from WASM artifacts. */
wasmArtifacts: string
/** Directory with assets. Its contents are to be copied. */
Expand All @@ -44,17 +56,15 @@ export interface Arguments {
outputPath: string
/** The main JS bundle to load WASM and JS wasm-pack bundles. */
ensoglAppPath: string
/** `true` if in development mode (live-reload), `false` if in production mode. */
devMode: boolean
}

/** Get arguments from the environment. */
export function argumentsFromEnv(): Arguments {
export function argumentsFromEnv(passthroughArguments: PassthroughArguments): Arguments {
const wasmArtifacts = utils.requireEnv('ENSO_BUILD_GUI_WASM_ARTIFACTS')
const assetsPath = utils.requireEnv('ENSO_BUILD_GUI_ASSETS')
const outputPath = pathModule.resolve(utils.requireEnv('ENSO_BUILD_GUI'), 'assets')
const ensoglAppPath = utils.requireEnv('ENSO_BUILD_GUI_ENSOGL_APP')
return { wasmArtifacts, assetsPath, outputPath, ensoglAppPath, devMode: false }
return { ...passthroughArguments, wasmArtifacts, assetsPath, outputPath, ensoglAppPath }
}

// ===================
Expand All @@ -77,7 +87,15 @@ function git(command: string): string {

/** Generate the builder options. */
export function bundlerOptions(args: Arguments) {
const { outputPath, ensoglAppPath, wasmArtifacts, assetsPath, devMode } = args
const {
outputPath,
ensoglAppPath,
wasmArtifacts,
assetsPath,
devMode,
supportsLocalBackend,
supportsDeepLinks,
} = args
const buildOptions = {
// Disabling naming convention because these are third-party options.
/* eslint-disable @typescript-eslint/naming-convention */
Expand Down Expand Up @@ -138,6 +156,8 @@ export function bundlerOptions(args: Arguments) {
/** Overrides the redirect URL for OAuth logins in the production environment.
* This is needed for logins to work correctly under `./run gui watch`. */
REDIRECT_OVERRIDE: 'undefined',
SUPPORTS_LOCAL_BACKEND: JSON.stringify(supportsLocalBackend),
SUPPORTS_DEEP_LINKS: JSON.stringify(supportsDeepLinks),
},
sourcemap: true,
minify: true,
Expand Down Expand Up @@ -165,13 +185,13 @@ export function bundlerOptions(args: Arguments) {
*
* Note that they should be further customized as per the needs of the specific workflow
* (e.g. watch vs. build). */
export function bundlerOptionsFromEnv() {
return bundlerOptions(argumentsFromEnv())
export function bundlerOptionsFromEnv(passthroughArguments: PassthroughArguments) {
return bundlerOptions(argumentsFromEnv(passthroughArguments))
}

/** esbuild options for bundling the package for a one-off build.
*
* Relies on the environment variables to be set. */
export function bundleOptions() {
return bundlerOptionsFromEnv()
export function bundleOptions(passthroughArguments: PassthroughArguments) {
return bundlerOptionsFromEnv(passthroughArguments)
}
15 changes: 15 additions & 0 deletions app/ide-desktop/lib/content/globals.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/** @file Globals defined only in this module. */

declare global {
// These are top-level constants, and therefore should be `CONSTANT_CASE`.
/* eslint-disable @typescript-eslint/naming-convention */
/** Whether the */
/** Whether the application may have the local backend running. */
const SUPPORTS_LOCAL_BACKEND: boolean
/** Whether the application supports deep links. This is only true when using
* the installed app on macOS and Windows. */
const SUPPORTS_DEEP_LINKS: boolean
/* eslint-enable @typescript-eslint/naming-convention */
}

export {}
135 changes: 79 additions & 56 deletions app/ide-desktop/lib/content/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import * as semver from 'semver'

import * as authentication from 'enso-authentication'
import * as common from 'enso-common'
import * as contentConfig from 'enso-content-config'

import * as app from '../../../../../target/ensogl-pack/linked-dist'
Expand All @@ -16,6 +17,8 @@ const logger = app.log.logger
// === Constants ===
// =================

/** The name of the `localStorage` key storing the initial URL of the app. */
const INITIAL_URL_KEY = `${common.PRODUCT_NAME.toLowerCase()}-initial-url`
/** Path to the SSE endpoint over which esbuild sends events. */
const ESBUILD_PATH = '/esbuild'
/** SSE event indicating a build has finished. */
Expand All @@ -39,6 +42,10 @@ if (IS_DEV_MODE) {
location.href = location.href.toString()
})
void navigator.serviceWorker.register(SERVICE_WORKER_PATH)
} else {
void navigator.serviceWorker
.getRegistration()
.then(serviceWorker => serviceWorker?.unregister())
}

// =============
Expand Down Expand Up @@ -189,67 +196,83 @@ class Main implements AppRunner {

/** The entrypoint into the IDE. */
main(inputConfig?: StringConfig) {
contentConfig.OPTIONS.loadAll([app.urlParams()])
const isUsingAuthentication = contentConfig.OPTIONS.options.authentication.value
const isUsingNewDashboard =
contentConfig.OPTIONS.groups.featurePreview.options.newDashboard.value
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
) {
const hideAuth = () => {
const auth = document.getElementById('dashboard')
const ide = document.getElementById('root')
if (auth) {
auth.style.display = 'none'
}
if (ide) {
ide.hidden = false
}
}
/** This package is an Electron desktop app (i.e., not in the Cloud), so
* we're running on the desktop. */
/** 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
* and one for the desktop. */
const currentPlatform = contentConfig.OPTIONS.groups.startup.options.platform.value
let platform = authentication.Platform.desktop
if (currentPlatform === 'web') {
platform = authentication.Platform.cloud
/** Note: Signing out always redirects to `/`. It is impossible to make this work,
* as it is not possible to distinguish between having just logged out, and explicitly
* opening a page with no URL parameters set.
*
* Client-side routing endpoints are explicitly not supported for live-reload, as they are
* transitional pages that should not need live-reload when running `gui watch`. */
const url = new URL(location.href)
const isInAuthenticationFlow = url.searchParams.has('code') && url.searchParams.has('state')
const authenticationUrl = location.href
if (isInAuthenticationFlow) {
history.replaceState(null, '', localStorage.getItem(INITIAL_URL_KEY))
}
const parseOk = contentConfig.OPTIONS.loadAllAndDisplayHelpIfUnsuccessful([app.urlParams()])
if (isInAuthenticationFlow) {
history.replaceState(null, '', authenticationUrl)
} else {
localStorage.setItem(INITIAL_URL_KEY, location.href)
}
if (parseOk) {
const isUsingAuthentication = contentConfig.OPTIONS.options.authentication.value
const isUsingNewDashboard =
contentConfig.OPTIONS.groups.featurePreview.options.newDashboard.value
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.runAuthentication(isInAuthenticationFlow, inputConfig)
} else {
void this.runApp(inputConfig)
}
/** FIXME [PB]: https://github.com/enso-org/cloud-v2/issues/366
* React hooks rerender themselves multiple times. It is resulting in multiple
* Enso main scene being initialized. As a temporary workaround we check whether
* appInstance was already ran. Target solution should move running appInstance
* where it will be called only once. */
let appInstanceRan = false
const onAuthenticated = () => {
}
}

/** Begins the authentication UI flow. */
runAuthentication(isInAuthenticationFlow: boolean, inputConfig?: StringConfig) {
/** 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
* and one for the desktop. */
/** FIXME [PB]: https://github.com/enso-org/cloud-v2/issues/366
* React hooks rerender themselves multiple times. It is resulting in multiple
* Enso main scene being initialized. As a temporary workaround we check whether
* appInstance was already ran. Target solution should move running appInstance
* where it will be called only once. */
authentication.run({
appRunner: this,
logger,
supportsLocalBackend: SUPPORTS_LOCAL_BACKEND,
supportsDeepLinks: SUPPORTS_DEEP_LINKS,
showDashboard: contentConfig.OPTIONS.groups.featurePreview.options.newDashboard.value,
onAuthenticated: () => {
if (isInAuthenticationFlow) {
const initialUrl = localStorage.getItem(INITIAL_URL_KEY)
if (initialUrl != null) {
// This is not used past this point, however it is set to the initial URL
// to make refreshing work as expected.
history.replaceState(null, '', initialUrl)
}
}
if (!contentConfig.OPTIONS.groups.featurePreview.options.newDashboard.value) {
hideAuth()
if (!appInstanceRan) {
appInstanceRan = true
document.getElementById('enso-dashboard')?.remove()
const ide = document.getElementById('root')
if (ide) {
ide.hidden = false
}
if (this.app == null) {
void this.runApp(inputConfig)
}
}
}
authentication.run({
appRunner: this,
logger,
platform,
showDashboard:
contentConfig.OPTIONS.groups.featurePreview.options.newDashboard.value,
onAuthenticated,
})
} else {
void this.runApp(inputConfig)
}
},
})
}
}

Expand Down
6 changes: 5 additions & 1 deletion app/ide-desktop/lib/content/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ const HTTP_STATUS_OK = 200

/** Start the esbuild watcher. */
async function watch() {
const opts = bundler.bundleOptions()
const opts = bundler.bundleOptions({
devMode: true,
supportsLocalBackend: true,
supportsDeepLinks: false,
})
const builder = await esbuild.context(opts)
await builder.watch()
await builder.serve({
Expand Down
11 changes: 7 additions & 4 deletions app/ide-desktop/lib/content/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@ async function watch() {
// This MUST be called before `builder.watch()` as `tailwind.css` must be generated
// before the copy plugin runs.
await dashboardBuilder.watch()
const opts = bundler.bundlerOptions({
...bundler.argumentsFromEnv(),
devMode: true,
})
const opts = bundler.bundlerOptions(
bundler.argumentsFromEnv({
devMode: true,
supportsLocalBackend: true,
supportsDeepLinks: false,
})
)
opts.define.REDIRECT_OVERRIDE = JSON.stringify('http://localhost:8080')
opts.entryPoints.push({
in: path.resolve(THIS_PATH, 'src', 'serviceWorker.ts'),
Expand Down
Loading

0 comments on commit e80d293

Please sign in to comment.