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

Document "How to implement a progress bar" #197

Closed
adamziel opened this issue Apr 12, 2023 · 3 comments
Closed

Document "How to implement a progress bar" #197

adamziel opened this issue Apr 12, 2023 · 3 comments
Labels
[Type] Developer Experience [Type] Documentation Improvements or additions to documentation

Comments

@adamziel
Copy link
Collaborator

No description provided.

@adamziel adamziel added [Type] Documentation Improvements or additions to documentation [Type] Developer Experience labels Apr 12, 2023
@adamziel
Copy link
Collaborator Author

Very rough notes:

The ProgressObserver class makes progress bars easier. First, create a new instance:

import { ProgressObserver } from '@php-wasm/progress';
const progress = new ProgressObserver() 

Here's how to use it:

Some actions, like fetch, give us atomic progress updates along the way. Use partialObserver for these. When you call progress.partialObserver, it gives you a listener to use with playground.onDownloadProgress(). That takes care of the WP and PHP loading part.

const client = await connectPlayground( /* ... */ );
await client.onDownloadProgress(
	progressObserver.partialObserver(
		bootProgressBudget,
		'Preparing WordPress...'
	)
);
await client.isReady();

To monitor plugin fetching, you’ll need to use cloneResponseMonitorProgress from the same npm package:

// ...
await playground.isReady();

let themeResponse = await fetch('/plugin-proxy?theme=pendant';
themeResponse = cloneResponseMonitorProgress(
	themeResponse,
	progress.partialObserver(
		20, // This fetch only accounts for 20% of total progress
		`Installing Pendant theme...`
	)
);

Other actions, like submitting a form to install a plugin, don’t give us detailed info how far are we. for the latter I just say “this action slowly increments the progress bar from 75% to 85% and we hope it finishes before the progress bar gets there”:

progress.slowlyIncrementBy(25);
.progress-bar.slowly-incrementing {
	transition: opacity linear 0.25s, width ease-in 4s;
}

There are two components to Playground-related progress bars:

  • Loading of WordPress and php.wasm
  • Plugin download and installation

I use separate “budgets” for each action that takes time:

	const progressBudgets = {
		login: 0,
		plugins: Math.min(config.plugins.length * 15, 45),
		theme: config.theme ? 20 : 0,
	};

	const totalFeaturesProgressBudgets = sumValues(progressBudgets);
	const bootProgressBudget = 100 - totalFeaturesProgressBudgets;

ProgressObserver is exported from @php-wasm/progress, this import map helps:

{
    "import": {
        "@php-wasm/progress": "https://unpkg.com/@php-wasm/progress/index.js"
    }
}

ProgressObserver is just a traffic control type of thing to give you separate progress budgets for different parts of the flow. The last missing bit is updating the UI.

progress.addEventListener('progress', function(e) {
  const percentLoaded = e.detail;
  myProgressBarDiv.style.width = `${percentLoaded}%`;
});

And then you’ll need to display some UI component and increment its width.

Playground uses the following Progress Bar React component:

const Progress = ({ mode, percentFull }: any) => {
	const classes = classNames([css.progressBar, css.isDefinite], {
		[css.slowlyIncrementing]: mode === 'slowly-increment',
	});
	return (
		<div className={`${css.wrapper} ${css.wrapperDefinite}`}>
			<div className={classes} style={{ width: `${percentFull}%` }} />
		</div>
	);
};

and the entire trick behind “slowly incrementing” is 4s here:

.progress-bar.slowly-incrementing {
	transition: opacity linear 0.25s, width ease-in 4s;
}

@akirk
Copy link
Member

akirk commented Apr 13, 2023

I have implemented a simple HTML+JS one in https://github.com/akirk/wp-glotpress-playground/blob/3807e0fe0b71cbb29637cd9ff0b6bb251650800d/index.php#L46-L140

iframe#wp, div#progress {
	width: 1200px;
	height: 800px;
}
div#progress {
	position: absolute;
	display: flex;
	align-items: center;
	justify-content: center;
	background: white;
	margin: 2em .1em;
	border: 1px solid black;
	height: 770px;
}
div#progressinner {
	width: 600px;
}
div#progressbarholder {
	height: 1em;
	border: 1px solid black;
}
div#progressbar {
	width: 0;
	height: 1em;
	background: black;
	transition: opacity linear 0.25s, width ease-in .5s;
}
div#progresstext {
	text-align: center;
	margin-top: .5em;
}

with this HTML

<div id="progress">
	<div id="progressinner">
		<div id="progressbarholder"><div id="progressbar"></div></div>
		<div id="progresstext"></div>
	</div>
</div>
<iframe id="wp"></iframe>

And a progress bar advancer:

let totalPercentage = 0;

function progress( percentage, text ) {
	totalPercentage += percentage;
	if ( totalPercentage >= 100 ) {
		document.getElementById( 'progress' ).style.display = 'none';
		return;
	}
	document.getElementById( 'progressbar' ).style.width = totalPercentage + '%';
	document.getElementById( 'progresstext' ).textContent = text;
}
progress( 1, 'Preparing WordPress...' );

@adamziel
Copy link
Collaborator Author

adamziel commented Jun 2, 2023

This issue no longer blocks folks from using a progress bar – the remote endpoint now ships a progress bar component so that the client doesn’t have to implement it on its own:

@adamziel adamziel closed this as completed Jun 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Type] Developer Experience [Type] Documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

2 participants