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

feat(loading): merge github fetch with the loading bar #6630

Merged
merged 21 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 14 additions & 3 deletions editor/src/components/canvas/canvas-loading-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Global, css } from '@emotion/react'
import { useColorTheme } from '../../uuiui'
import { useEditorState } from '../editor/store/store-hook'
import { Substores } from '../editor/store/store-hook'
import { getTotalImportStatusAndResult } from '../../core/shared/import/import-operation-service'
import type { TotalImportResult } from '../../core/shared/import/import-operation-types'

export const CanvasLoadingScreen = React.memo(() => {
const colorTheme = useColorTheme()
Expand All @@ -17,17 +19,26 @@ export const CanvasLoadingScreen = React.memo(() => {
'CanvasLoadingScreen importWizardOpen',
)

const totalImportResult: TotalImportResult = React.useMemo(
() => getTotalImportStatusAndResult(importState),
[importState],
)

const importingStoppedStyleOverride = React.useMemo(
() =>
// if the importing was stopped, we want to pause the shimmer animation
(importWizardOpen && importState.importStatus.status === 'done') ||
importState.importStatus.status === 'paused'
(importWizardOpen && totalImportResult.importStatus.status === 'done') ||
totalImportResult.importStatus.status === 'paused'
? {
background: colorTheme.codeEditorShimmerPrimary.value,
animation: 'none',
}
: {},
[importWizardOpen, importState.importStatus.status, colorTheme.codeEditorShimmerPrimary.value],
[
importWizardOpen,
totalImportResult.importStatus.status,
colorTheme.codeEditorShimmerPrimary.value,
],
)

return (
Expand Down
12 changes: 12 additions & 0 deletions editor/src/components/editor/actions/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@ import {
import { addToastToState, includeToast, removeToastFromState } from './toast-helpers'
import { AspectRatioLockedProp } from '../../aspect-ratio'
import {
getDependenciesStatus,
refreshDependencies,
removeModulesFromNodeModules,
} from '../../../core/shared/dependencies'
Expand Down Expand Up @@ -629,6 +630,8 @@ import { getParseCacheOptions } from '../../../core/shared/parse-cache-utils'
import { styleP } from '../../inspector/inspector-common'
import {
getUpdateOperationResult,
notifyOperationFinished,
notifyOperationStarted,
notifyImportStatusToDiscord,
} from '../../../core/shared/import/import-operation-service'
import { updateRequirements } from '../../../core/shared/import/project-health-check/utopia-requirements-service'
Expand Down Expand Up @@ -6319,6 +6322,8 @@ export async function load(
// this action is now async!
const migratedModel = applyMigrations(model)
const npmDependencies = dependenciesWithEditorRequirements(migratedModel.projectContents)
// side effect ☢️
notifyOperationStarted(dispatch, { type: 'refreshDependencies' })
liady marked this conversation as resolved.
Show resolved Hide resolved
const fetchNodeModulesResult = await fetchNodeModules(
dispatch,
npmDependencies,
Expand All @@ -6333,6 +6338,13 @@ export async function load(
fetchNodeModulesResult.dependenciesNotFound,
)

// side effect ☢️
notifyOperationFinished(
dispatch,
{ type: 'refreshDependencies' },
getDependenciesStatus(packageResult),
)

const codeResultCache: CodeResultCache = generateCodeResultCache(
// TODO is this sufficient here?
migratedModel.projectContents,
Expand Down
8 changes: 8 additions & 0 deletions editor/src/components/editor/import-wizard/import-wizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,14 @@ function ActionButtons() {
fontSize: 14,
cursor: 'pointer',
}
React.useEffect(() => {
if (
importResult.importStatus.status == 'done' &&
importResult.result == ImportOperationResult.Success
) {
hideWizard()
}
}, [importResult, hideWizard])
if (
importResult.importStatus.status === 'in-progress' ||
importResult.importStatus.status === 'not-started'
Expand Down
174 changes: 174 additions & 0 deletions editor/src/components/editor/loading-screen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import React from 'react'
import { Substores, useEditorState } from './store/store-hook'
import { getImportOperationTextAsJsx } from './import-wizard/import-wizard-helpers'
import { getTotalImportStatusAndResult } from '../../core/shared/import/import-operation-service'
import type { TotalImportResult } from '../../core/shared/import/import-operation-types'
import type { Theme } from '../../uuiui'
import { useColorTheme } from '../../uuiui'
import { getCurrentTheme } from './store/editor-state'
import ReactDOM from 'react-dom'

export function LoadingEditorComponent() {
const colorTheme = useColorTheme()

const currentTheme: Theme = useEditorState(
Substores.theme,
(store) => getCurrentTheme(store.userState),
'currentTheme',
)

const importState = useEditorState(
Substores.restOfEditor,
(store) => store.editor.importState,
'LoadingEditorComponent importState',
)

const githubRepo = useEditorState(
Substores.userState,
(store) => store.userState.githubState.gitRepoToLoad,
'LoadingEditorComponent githubRepoToLoad',
)

const totalImportResult: TotalImportResult = React.useMemo(
() => getTotalImportStatusAndResult(importState),
[importState],
)

const projectId = useEditorState(
Substores.restOfEditor,
(store) => store.editor.id,
'LoadingEditorComponent projectId',
)

const cleared = React.useRef(false)

const currentOperationToShow: {
text: React.ReactNode
id: string
timeDone: number | null | undefined
timeStarted: number | null | undefined
} | null = React.useMemo(() => {
if (totalImportResult.importStatus.status == 'not-started') {
if (projectId == null) {
return {
text: 'Loading Editor...',
id: 'loading-editor',
timeDone: null,
timeStarted: null,
}
} else {
return {
text: `Parsing files`,
id: 'parseFiles',
timeDone: null,
timeStarted: null,
}
}
}
for (const op of importState.importOperations) {
if (op?.children?.length == 0 || op.type == 'refreshDependencies') {
if (op.timeStarted != null && op.timeDone == null) {
return {
text: getImportOperationTextAsJsx(op),
id: op.id ?? op.type,
timeDone: op.timeDone,
timeStarted: op.timeStarted,
}
}
}
if (op.type !== 'refreshDependencies') {
for (const child of op.children ?? []) {
if (child.timeStarted != null && child.timeDone == null) {
return {
text: getImportOperationTextAsJsx(child),
id: child.id ?? child.type,
timeDone: child.timeDone,
timeStarted: child.timeStarted,
}
}
}
}
}
return {
text: 'Loading Editor...',
id: 'loading-editor',
timeDone: null,
timeStarted: null,
}
}, [totalImportResult, importState.importOperations, projectId])

const shouldBeCleared = React.useMemo(() => {
return (
cleared.current ||
(totalImportResult.importStatus.status == 'done' &&
(githubRepo == null || totalImportResult.result == 'criticalError')) ||
totalImportResult.importStatus.status == 'paused'
)
}, [totalImportResult, githubRepo])

React.useEffect(() => {
if (shouldBeCleared) {
const loadingScreenWrapper = document.getElementById('loading-screen-wrapper')
if (loadingScreenWrapper != null) {
loadingScreenWrapper.remove()
}
}
}, [shouldBeCleared])

const portal = React.useRef(document.getElementById('loading-screen-progress-bar-portal')).current
const hasMounted = React.useRef(false)
if (portal == null) {
return null
}

if (shouldBeCleared) {
cleared.current = true
return null
}

if (!hasMounted.current) {
portal.innerHTML = ''
hasMounted.current = true
}

const themeStyle =
currentTheme === 'dark'
? `
.editor-loading-screen { background-color: ${colorTheme.bg6.value} }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean we're relying on some defaults for light mode? This feels a little like we're kinda patching it rather than fixing it.

Copy link
Contributor Author

@liady liady Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in a sense this is "patching", since we want to match the CSS behavior in index.html where the dark mode "overrides" the light mode defaults (that also apply here).
I didn't want to change anything in case we have a light theme, so to not create a possible divergence in color from the light "default" that is defined in the base index.html css.

.utopia-logo-pyramid.light { display: none; }
.utopia-logo-pyramid.dark { display: block; }
`
: ''

return ReactDOM.createPortal(
<React.Fragment>
<style>{themeStyle}</style>
<div className='progress-bar-shell' style={{ borderColor: colorTheme.fg0.value }}>
<div
className='progress-bar-progress animation-progress'
style={{
transform: 'translateX(-180px)',
animationName: 'animation-keyframes-2',
backgroundColor: colorTheme.fg0.value,
}}
></div>
</div>
<div>
<ul className='loading-screen-import-operations'>
{currentOperationToShow != null ? (
<li
style={{
listStyle: 'none',
color: colorTheme.fg0.value,
}}
key={currentOperationToShow.id}
>
{currentOperationToShow.text}
</li>
) : null}
</ul>
</div>
</React.Fragment>,
portal,
)
}
19 changes: 17 additions & 2 deletions editor/src/components/editor/store/dispatch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ import {
} from '../../../core/performance/performance-utils'
import { getParseCacheOptions } from '../../../core/shared/parse-cache-utils'
import { resetUpdatedProperties } from '../../canvas/plugins/style-plugins'
import {
notifyOperationFinished,
notifyOperationStarted,
} from '../../../core/shared/import/import-operation-service'
import { ImportOperationResult } from '../../../core/shared/import/import-operation-types'
import { updateImportStatus } from '../actions/action-creators'

type DispatchResultFields = {
nothingChanged: boolean
Expand Down Expand Up @@ -327,6 +333,7 @@ function maybeRequestModelUpdate(
// Should anything need to be sent across, do so here.
if (filesToUpdate.length > 0) {
const { endMeasure } = startPerformanceMeasure('file-parse', { uniqueId: true })
notifyOperationStarted(dispatch, { type: 'parseFiles' })
const parseFinished = getParseResult(
workers,
filesToUpdate,
Expand All @@ -337,6 +344,7 @@ function maybeRequestModelUpdate(
getParseCacheOptions(),
)
.then((parseResult) => {
notifyOperationFinished(dispatch, { type: 'parseFiles' }, ImportOperationResult.Success)
const duration = endMeasure()
if (isConcurrencyLoggingEnabled() && filesToUpdate.length > 1) {
console.info(
Expand Down Expand Up @@ -364,12 +372,19 @@ function maybeRequestModelUpdate(
)
}

dispatch([EditorActions.mergeWithPrevUndo(actionsToDispatch)])
dispatch([
EditorActions.mergeWithPrevUndo(actionsToDispatch),
updateImportStatus({ status: 'done' }),
])
return true
})
.catch((e) => {
console.error('error during parse', e)
dispatch([EditorActions.clearParseOrPrintInFlight()])
notifyOperationFinished(dispatch, { type: 'parseFiles' }, ImportOperationResult.Error)
dispatch([
EditorActions.clearParseOrPrintInFlight(),
updateImportStatus({ status: 'done' }),
])
return true
})
return {
Expand Down
3 changes: 0 additions & 3 deletions editor/src/components/github/github-repository-clone-flow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,8 @@ import { notice } from '../common/notice'
import { isFeatureEnabled } from '../../utils/feature-switches'
import {
notifyOperationCriticalError,
notifyOperationFinished,
startImportProcess,
updateProjectImportStatus,
} from '../../core/shared/import/import-operation-service'
import { ImportOperationResult } from '../../core/shared/import/import-operation-types'

export const LoadActionsDispatched = 'loadActionDispatched'

Expand Down
4 changes: 3 additions & 1 deletion editor/src/core/shared/dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ function isPackageMissing(status: PackageDetails): boolean {
return status.status === 'error' || status.status === 'not-found'
}

function getDependenciesStatus(loadedPackagesStatus: PackageStatusMap): ImportOperationResult {
export function getDependenciesStatus(
loadedPackagesStatus: PackageStatusMap,
): ImportOperationResult {
if (Object.values(loadedPackagesStatus).every(isPackageMissing)) {
return ImportOperationResult.Error
}
Expand Down
1 change: 0 additions & 1 deletion editor/src/core/shared/github/operations/load-branch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ import {
notifyOperationStarted,
pauseImport,
startImportProcess,
updateProjectImportStatus,
} from '../../import/import-operation-service'
import {
RequirementResolutionResult,
Expand Down
4 changes: 2 additions & 2 deletions editor/src/core/shared/import/import-operation-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import { sendDiscordMessage } from '../../../components/editor/server'
import type { DiscordEndpointSiteImport, DiscordMessageType } from 'utopia-shared/src/types'
import { getImportOperationText } from '../../../components/editor/import-wizard/import-wizard-helpers'

export function startImportProcess(dispatch: EditorDispatch) {
export function startImportProcess(dispatch: EditorDispatch, customSteps?: ImportOperation[]) {
const actions: EditorAction[] = [
updateImportStatus({ status: 'in-progress' }),
updateImportOperations(
[
customSteps ?? [
{ type: 'loadBranch' },
{ type: 'checkRequirementsPreParse' },
{ type: 'parseFiles' },
Expand Down
7 changes: 7 additions & 0 deletions editor/src/templates/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ import { getParserWorkerCount } from '../core/workers/common/concurrency-utils'
import { canMeasurePerformance } from '../core/performance/performance-utils'
import { getChildGroupsForNonGroupParents } from '../components/canvas/canvas-strategies/strategies/fragment-like-helpers'
import { EditorModes } from '../components/editor/editor-modes'
import { startImportProcess } from '../core/shared/import/import-operation-service'
import { LoadingEditorComponent } from '../components/editor/loading-screen'

if (PROBABLY_ELECTRON) {
let { webFrame } = requireElectron()
Expand Down Expand Up @@ -267,6 +269,10 @@ export class Editor {
projectName: string,
project: PersistentModel,
) => {
startImportProcess(this.boundDispatch, [
{ type: 'parseFiles' },
{ type: 'refreshDependencies' },
])
await load(this.boundDispatch, project, projectName, projectId, builtInDependencies)
PubSub.publish(LoadActionsDispatched, { projectId: projectId })
}
Expand Down Expand Up @@ -768,6 +774,7 @@ export const EditorRoot: React.FunctionComponent<{
<AnimationContext.Provider
value={{ animate: animate, scope: animationScope }}
>
<LoadingEditorComponent />
<EditorComponent />
</AnimationContext.Provider>
</UiJsxCanvasCtxAtom.Provider>
Expand Down
Loading
Loading