Auto-reconnect if camera already found; catch all errors
This commit is contained in:
69
ui.js
69
ui.js
@@ -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());
|
||||||
|
if (!isDebug) {
|
||||||
|
delete config.children.other;
|
||||||
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
type: 'Config',
|
type: 'Config',
|
||||||
config: await scheduleOp(context => context.configToJS())
|
config
|
||||||
});
|
});
|
||||||
} catch (e) {
|
|
||||||
this.setState({
|
|
||||||
type: 'Status',
|
|
||||||
message: String(e)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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' },
|
||||||
|
|||||||
Reference in New Issue
Block a user