diff --git a/localtypings/pxtarget.d.ts b/localtypings/pxtarget.d.ts index 37ed2f20b5a3..849f56516517 100644 --- a/localtypings/pxtarget.d.ts +++ b/localtypings/pxtarget.d.ts @@ -522,6 +522,7 @@ declare namespace pxt { dragFileImage?: string; connectDeviceImage?: string; + disconnectDeviceImage?: string; selectDeviceImage?: string; connectionSuccessImage?: string; incompatibleHardwareImage?: string; diff --git a/webapp/src/cmds.ts b/webapp/src/cmds.ts index a75f5dd246b1..1cc4ce0e674b 100644 --- a/webapp/src/cmds.ts +++ b/webapp/src/cmds.ts @@ -135,6 +135,35 @@ function showUploadInstructionsAsync( }).then(() => { }); } +export function showReconnectDeviceInstructionsAsync( + confirmAsync: (options: core.PromptOptions) => Promise +): Promise { + const boardName = pxt.appTarget.appTheme.boardName || lf("device"); + const helpUrl = pxt.appTarget.appTheme.usbDocs; + const jsx = webusb.renderDisconnectDeviceDialog(); + const body = lf("Your {0} appears to have stalled; please disconnect any battery and usb connection, and try again.", boardName); + return confirmAsync({ + header: lf("{0} Connection failed...", boardName), + body, + jsx, + hasCloseIcon: true, + hideAgree: true, + helpUrl, + bigHelpButton: true, + className: 'downloaddialog', + buttons: [ + { + label: lf("Done"), + className: "primary", + onclick: () => { + pxt.tickEvent('downloaddialog.done') + core.hideDialog(); + } + }, + ] + }).then(() => { }); +} + export function nativeHostPostMessageFunction(): (msg: NativeHostMessage) => void { const webkit = (window).webkit; if (webkit @@ -261,6 +290,9 @@ export async function hidDeployCoreAsync(resp: pxtc.CompileResult, d?: pxt.comma // device is locked or used by another tab pxt.tickEvent("hid.flash.devicelocked"); log(`error: device locked`); + } else if (e.type == "inittimeout") { + pxt.tickEvent("hid.flash.inittimeout"); + await showReconnectDeviceInstructionsAsync(core.confirmAsync); } else { pxt.tickEvent("hid.flash.error"); log(`hid error ${e.message}`) @@ -461,8 +493,12 @@ export async function maybeReconnectAsync(pairIfDeviceNotFound = false, skipIfCo await wrapper.reconnectAsync(); return true; } catch (e) { - if (e.type == "devicenotfound") + if (e.type == "devicenotfound") { return !!pairIfDeviceNotFound && pairAsync(); + } else if (e.type == "inittimeout") { + pxt.tickEvent("hid.flash.inittimeout"); + await showReconnectDeviceInstructionsAsync(core.confirmAsync); + } throw e; } } finally { diff --git a/webapp/src/webusb.tsx b/webapp/src/webusb.tsx index bd57d960b0f7..69654b7fccaa 100644 --- a/webapp/src/webusb.tsx +++ b/webapp/src/webusb.tsx @@ -515,6 +515,25 @@ export function renderUnpairDialog() { return { header, jsx, helpUrl }; } +export function renderDisconnectDeviceDialog() { + const boardName = getBoardName(); + const disconnectImage = theme().disconnectDeviceImage; + + return <> + {disconnectImage && {lf("Image} +
+ {lf("Your {0} appears to have stalled", boardName)} +
+
+ {lf("Please disconnect any battery and usb connection, and try again.")} +
+ ; +} + export async function showDeviceForgottenDialog(confirmAsync: ConfirmAsync) { const boardName = getBoardName(); const deviceForgottenImage = theme().usbDeviceForgottenImage;