Auto-reconnect if camera already found; catch all errors

This commit is contained in:
Ingvar Stepanyan
2021-07-19 17:57:50 +00:00
parent 826aca5b9d
commit e62ab51f4c

73
ui.js
View File

@@ -4,11 +4,14 @@ import initModule from './libapi.mjs';
/** @typedef {InstanceType<import('./libapi.mjs').Module['Context']>} Context */ /** @typedef {InstanceType<import('./libapi.mjs').Module['Context']>} Context */
/** @typedef {import('./libapi.mjs').Config} Config */ /** @typedef {import('./libapi.mjs').Config} Config */
if (new URLSearchParams(location.search).has('debug')) { let isDebug = new URLSearchParams(location.search).has('debug');
if (isDebug) {
// @ts-ignore // @ts-ignore
await import('preact/debug'); await import('preact/debug');
} }
/** This function should be called once user has selected the camera and other operations can begin. */
let prepareContext; let prepareContext;
/** Schedules an exclusive async operation on the global context. */ /** Schedules an exclusive async operation on the global context. */
@@ -246,21 +249,60 @@ class CaptureButton extends Component {
} }
} }
/** @typedef {{ type: 'Initial' } | { type: 'Status', message: string } | { type: 'Config', config: Config }} AppState */ /** @typedef {{ type: 'CameraPicker' } | { type: 'Status', message: string } | { type: 'Config', config: Config }} AppState */
const DEVICE_CLASS = 6; // PTP
const DEVICE_SUBCLASS = 1; // MTP
/** @extends Component<null, AppState> */ /** @extends Component<null, AppState> */
class App extends Component { class App extends Component {
state = /** @type {AppState} */ ({ type: 'Initial' }); state = /** @type {AppState} */ ({
type: 'Status',
message: 'Looking for cameras...'
});
constructor(...args) {
super(...args);
// @ts-ignore
navigator.usb.getDevices().then(devices => {
for (let dev of devices) {
for (let conf of dev.configurations) {
for (let intf of conf.interfaces) {
for (let alt of intf.alternates) {
if (
alt.interfaceClass === DEVICE_CLASS &&
alt.interfaceSubclass === DEVICE_SUBCLASS
) {
return this.connectToCamera();
}
}
}
}
}
this.setState({ type: 'CameraPicker' });
});
window.onerror = message => {
this.setState({
type: 'Status',
message: `${message}`
});
};
}
selectDevice = async () => { selectDevice = async () => {
// @ts-ignore
await navigator.usb.requestDevice({ await navigator.usb.requestDevice({
filters: [ filters: [
{ {
classCode: 6, // PTP classCode: DEVICE_CLASS,
subclassCode: 1 // MTP subclassCode: DEVICE_SUBCLASS
} }
] ]
}); });
this.connectToCamera();
};
connectToCamera() {
prepareContext(); prepareContext();
this.setState({ type: 'Status', message: 'Connecting...' }); this.setState({ type: 'Status', message: 'Connecting...' });
(async () => { (async () => {
@@ -269,25 +311,22 @@ class App extends Component {
await new Promise(resolve => setTimeout(resolve, 100)); await new Promise(resolve => setTimeout(resolve, 100));
} }
})(); })();
}; }
async refreshConfig() { async refreshConfig() {
try { let config = await scheduleOp(context => context.configToJS());
this.setState({ if (!isDebug) {
type: 'Config', delete config.children.other;
config: await scheduleOp(context => context.configToJS())
});
} catch (e) {
this.setState({
type: 'Status',
message: String(e)
});
} }
this.setState({
type: 'Config',
config
});
} }
render(/** @type {App['props']} */ props, /** @type {App['state']} */ state) { render(/** @type {App['props']} */ props, /** @type {App['state']} */ state) {
switch (state.type) { switch (state.type) {
case 'Initial': case 'CameraPicker':
return h( return h(
'div', 'div',
{ class: 'center-parent' }, { class: 'center-parent' },