diff --git a/ext/src/global.d.ts b/ext/src/global.d.ts index 2f4cb16..54ba330 100644 --- a/ext/src/global.d.ts +++ b/ext/src/global.d.ts @@ -16,3 +16,16 @@ declare namespace browser.runtime { error: { message: string }; } } + +// Allow default attribute on + + ) + : ( )} - fetch(ENDPOINT_URL) - .then(res => { - window.clearTimeout(timeout); - return res.json() - }) - .then(this.onCheckUpdatesResponse) - .catch(this.onCheckUpdatesError); +
+ { this.state.updateStatus && !this.state.isUpdateAvailable + && this.state.updateStatus } +
+ } + + ); } - showUpdateStatus () { - if (this.updateStatusTimeout) { - window.clearTimeout(this.updateStatusTimeout); - } - this.updateStatusTimeout = window.setTimeout(() => { - this.setState({ - updateStatus: null - }); - }, 1500); - } - - async onUpdate () { - // Current window to base centered position on - const win = await browser.windows.getCurrent(); - const centeredProps = getWindowCenteredProps(win, 400, 150); - - const updaterPopup = await browser.windows.create({ - url: "../updater/index.html" - , type: "popup" - , ...centeredProps - }); - - // Size/position not set correctly on creation (bug?) - await browser.windows.update(updaterPopup.id, { - ...centeredProps - }); - - browser.runtime.onConnect.addListener(port => { - if (port.name === "updater") { - const asset = this.updateData.assets.find(asset => { - const fileExtension = asset.name.match(/.*\.(.*)$/).pop(); - const currentPlatform = (this.props.platform === "linux") - ? this.state.packageType - : this.props.platform; - - switch (fileExtension) { - case "exe": return "win" === currentPlatform; - case "pkg": return "mac" === currentPlatform; - case "deb": return "deb" === currentPlatform; - case "rpm": return "rpm" === currentPlatform; - } - }); - - port.postMessage({ - subject: "updater:/updateData" - , data: asset - }); - - port.onDisconnect.addListener(() => { - browser.windows.remove(updaterPopup.id); - }); - } - }); - } - - - async onCheckUpdatesResponse (res) { - const isUpdateAvailable = !this.props.info || semver.lt( - this.props.info.version, res.tag_name); - - if (isUpdateAvailable) { - this.updateData = res; - } - - this.setState({ - isCheckingUpdates: false - , isUpdateAvailable - , updateStatus: !isUpdateAvailable - ? _("optionsBridgeUpdateStatusNoUpdates") - : null - }); - - this.showUpdateStatus(); - } - - onCheckUpdatesError (err) { - this.setState({ - isCheckingUpdates: false - , wasErrorCheckingUpdates: true - , updateStatus: _("optionsBridgeUpdateStatusError") - }); - - this.showUpdateStatus(); - } - - onPackageTypeChange (ev) { - this.setState({ - packageType: ev.target.value - }); - } - - - renderStatus () { + private renderStatus () { const infoClasses = `bridge__info ${this.props.info ? "bridge__info--found" : "bridge__info--not-found"}`; - let statusIcon; - let statusTitle; - let statusText; + let statusIcon: string; + let statusTitle: string; + let statusText: string; if (!this.props.info) { statusIcon = "assets/icons8-cancel-120.png"; @@ -237,7 +211,7 @@ export default class Bridge extends Component { } else { if (this.props.info.isVersionCompatible) { statusIcon = "assets/icons8-ok-120.png"; - statusTitle = _("optionsBridgeFoundStatusTitle"; + statusTitle = _("optionsBridgeFoundStatusTitle"); } else { statusIcon = "assets/icons8-warn-120.png"; statusTitle = _("optionsBridgeIssueStatusTitle"); @@ -267,65 +241,115 @@ export default class Bridge extends Component { ); } - render () { - return ( -
- { this.props.loading - ? (
- { _("optionsBridgeLoading") } - -
- : this.renderStatus() } + private onCheckUpdates () { + this.setState({ + isCheckingUpdates: true + }); - { !this.props.loading && -
- { this.state.isUpdateAvailable - ? ( -
-

- { _("optionsBridgeUpdateAvailable") } -

-
- { this.props.platform === "linux" && - } - -
-
- ) : ( - - )} + fetch(ENDPOINT_URL) + .then(res => { + window.clearTimeout(timeout); + return res.json(); + }) + .then(this.onCheckUpdatesResponse) + .catch(this.onCheckUpdatesError); + } -
- { this.state.updateStatus && !this.state.isUpdateAvailable - && this.state.updateStatus } -
-
} -
- ); + private showUpdateStatus () { + if (this.updateStatusTimeout) { + window.clearTimeout(this.updateStatusTimeout); + } + this.updateStatusTimeout = window.setTimeout(() => { + this.setState({ + updateStatus: null + }); + }, 1500); + } + + private async onUpdate () { + // Current window to base centered position on + const win = await browser.windows.getCurrent(); + const centeredProps = getWindowCenteredProps(win, 400, 150); + + const updaterPopup = await browser.windows.create({ + url: "../updater/index.html" + , type: "popup" + , ...centeredProps + }); + + // Size/position not set correctly on creation (bug?) + await browser.windows.update(updaterPopup.id, { + ...centeredProps + }); + + browser.runtime.onConnect.addListener(port => { + if (port.name === "updater") { + const asset = this.updateData.assets.find((currentAsset: any) => { + const fileExtension = currentAsset.name.match(/.*\.(.*)$/).pop(); + const currentPlatform = (this.props.platform === "linux") + ? this.state.packageType + : this.props.platform; + + switch (fileExtension) { + case "exe": return "win" === currentPlatform; + case "pkg": return "mac" === currentPlatform; + case "deb": return "deb" === currentPlatform; + case "rpm": return "rpm" === currentPlatform; + } + }); + + port.postMessage({ + subject: "updater:/updateData" + , data: asset + }); + + port.onDisconnect.addListener(() => { + browser.windows.remove(updaterPopup.id); + }); + } + }); + } + + + private async onCheckUpdatesResponse (res: any) { + const isUpdateAvailable = !this.props.info || semver.lt( + this.props.info.version, res.tag_name); + + if (isUpdateAvailable) { + this.updateData = res; + } + + this.setState({ + isCheckingUpdates: false + , isUpdateAvailable + , updateStatus: !isUpdateAvailable + ? _("optionsBridgeUpdateStatusNoUpdates") + : null + }); + + this.showUpdateStatus(); + } + + private onCheckUpdatesError () { + this.setState({ + isCheckingUpdates: false + , wasErrorCheckingUpdates: true + , updateStatus: _("optionsBridgeUpdateStatusError") + }); + + this.showUpdateStatus(); + } + + private onPackageTypeChange (ev: React.ChangeEvent) { + this.setState({ + packageType: ev.target.value + }); } } diff --git a/ext/src/options/EditableList.jsx b/ext/src/options/EditableList.tsx similarity index 82% rename from ext/src/options/EditableList.jsx rename to ext/src/options/EditableList.tsx index a1b33cb..ee51bc1 100644 --- a/ext/src/options/EditableList.jsx +++ b/ext/src/options/EditableList.tsx @@ -1,12 +1,34 @@ +/* tslint:disable:max-line-length */ +"use strict"; + import React, { Component } from "react"; import EditableListItem from "./EditableListItem"; const _ = browser.i18n.getMessage; -export default class EditableList extends Component { - constructor (props) { +interface EditableListProps { + data: string[]; + itemPattern: RegExp; + onChange (data: string[]): void; + itemPatternError (err?: string): string; +} + +interface EditableListState { + items: Set; + addingNewItem: boolean; + rawView: boolean; + rawViewValue: string; +} + +export default class EditableList extends Component< + EditableListProps, EditableListState> { + + private rawViewTextArea: HTMLTextAreaElement; + + constructor (props: EditableListProps) { super(props); + this.state = { items: new Set(this.props.data) , addingNewItem: false @@ -24,99 +46,6 @@ export default class EditableList extends Component { this.handleNewItemEdit = this.handleNewItemEdit.bind(this); } - handleItemRemove (item) { - this.setState(currentState => { - const newItems = new Set(currentState.items); - newItems.delete(item); - return { - items: newItems - }; - }, () => { - this.props.onChange(Array.from(this.state.items)); - }); - } - - handleItemEdit (item, newValue) { - this.setState(currentState => ({ - items: new Set([...currentState.items] - .map(item_ => item_ === item ? newValue : item_)) - }), () => { - this.props.onChange(Array.from(this.state.items)); - }); - } - - handleSwitchView () { - this.setState(currentState => { - if (currentState.rawView) { - return { - rawView: false - , rawViewValue: "" - }; - } - - return { - rawView: true - , rawViewValue: Array.from(currentState.items.values()).join("\n") - }; - }); - } - - handleSaveRaw () { - this.setState(currentState => { - const newItems = currentState.rawViewValue.split("\n") - .filter(item => item !== ""); - - if ("itemPattern" in this.props) { - for (const item of newItems) { - if (!this.props.itemPattern.test(item)) { - this.rawViewTextArea.setCustomValidity( - this.props.itemPatternError(item)); - return; - } - } - - this.rawViewTextArea.setCustomValidity(""); - } - - return { - items: new Set(newItems) - }; - }, () => { - this.props.onChange(Array.from(this.state.items)); - }); - } - - handleRawViewTextAreaChange (ev) { - if (this.rawViewTextArea.scrollHeight > this.rawViewTextArea.clientHeight) { - this.rawViewTextArea.style.height = `${this.rawViewTextArea.scrollHeight}px`; - } - - this.setState({ - rawViewValue: ev.target.value - }); - } - - handleAddItem () { - this.setState({ - addingNewItem: true - }); - } - - handleNewItemRemove () { - this.setState({ - addingNewItem: false - }); - } - - handleNewItemEdit (item, newItem) { - this.setState(currentState => ({ - items: [ ...currentState.items, newItem ] - , addingNewItem: false - }), () => { - this.props.onChange(Array.from(this.state.items)); - }); - } - render () { const items = Array.from(this.state.items.values()); @@ -142,11 +71,11 @@ export default class EditableList extends Component { rows={ items.length} value={ this.state.rawViewValue} onChange={ this.handleRawViewTextAreaChange } - ref={ el => { this.rawViewTextArea = el }}> + ref={ el => { this.rawViewTextArea = el; }}> ) : (