From d4d55ea59e1ef3784a5eb74ab00162fc37f40d8d Mon Sep 17 00:00:00 2001 From: hensm Date: Tue, 12 Feb 2019 09:06:57 +0000 Subject: [PATCH] Add bridge downloads to options page --- ext/src/_locales/en/messages.json | 21 ++- ext/src/lib/utils.js | 10 ++ ext/src/manifest.json | 4 +- ext/src/options/Bridge.jsx | 252 ++++++++++++++++++++++++------ ext/src/options/styles/index.css | 52 +++++- ext/src/popup/index.jsx | 13 +- ext/src/popup/styles/mac.css | 44 +++--- 7 files changed, 310 insertions(+), 86 deletions(-) create mode 100644 ext/src/lib/utils.js diff --git a/ext/src/_locales/en/messages.json b/ext/src/_locales/en/messages.json index 3a97187..a776b10 100755 --- a/ext/src/_locales/en/messages.json +++ b/ext/src/_locales/en/messages.json @@ -21,14 +21,17 @@ , "optionsBridgeLoading": { "message": "Loading bridge info..." } - , "optionsBridgeFoundStatusText": { + , "optionsBridgeFoundStatusTitle": { "message": "Bridge found" } - , "optionsBridgeIssueStatusText": { + , "optionsBridgeIssueStatusTitle": { "message": "Bridge issue" } + , "optionsBridgeNotFoundStatusTitle": { + "message": "Bridge not found" + } , "optionsBridgeNotFoundStatusText": { - "message": "Bridge not found. Try downloading and installing the latest version." + "message": "Try downloading and installing the latest version." } , "optionsBridgeInfo": { "message": "Bridge info:" @@ -66,6 +69,18 @@ , "optionsBridgeNoAction": { "message": "No action needed." } + , "optionsBridgeDownloadsGet": { + "message": "Fetch downloads" + } + , "optionsBridgeDownloadsLoading": { + "message": "Fetching downloads$1" + } + , "optionsBridgeDownloadsGetFailed": { + "message": "Failed to fetch downloads" + } + , "optionsBridgeDownloadsTitle": { + "message": "Bridge downloads" + } , "optionsMediaCategoryName": { "message": "Media casting" diff --git a/ext/src/lib/utils.js b/ext/src/lib/utils.js new file mode 100644 index 0000000..c5315ef --- /dev/null +++ b/ext/src/lib/utils.js @@ -0,0 +1,10 @@ +"use strict"; + +export function getNextEllipsis (ellipsis) { + return do { + if (ellipsis === "") "."; + else if (ellipsis === ".") ".."; + else if (ellipsis === "..") "..."; + else if (ellipsis === "...") ""; + }; +} diff --git a/ext/src/manifest.json b/ext/src/manifest.json index 2d3ff29..c646bd1 100755 --- a/ext/src/manifest.json +++ b/ext/src/manifest.json @@ -31,7 +31,9 @@ "page": "options/index.html" } , "permissions": [ - "menus" + "downloads" + , "downloads.open" + , "menus" , "nativeMessaging" , "storage" , "webNavigation" diff --git a/ext/src/options/Bridge.jsx b/ext/src/options/Bridge.jsx index 67fe330..47d88ad 100644 --- a/ext/src/options/Bridge.jsx +++ b/ext/src/options/Bridge.jsx @@ -1,7 +1,45 @@ import React, { Component } from "react"; +import { getNextEllipsis } from "../lib/utils"; const _ = browser.i18n.getMessage; +const ENDPOINT_URL = "https://api.github.com/repos/hensm/fx_cast/releases/14720978"; + + +async function downloadApp (info, platform) { + const download = browser.downloads.download({ + filename: info[platform].name + , url: info[platform].url + }); +} + +const BridgeDownloads = (props) => ( +
+ + + +
+ + +
+
+); + const BridgeStats = (props) => ( @@ -49,54 +87,176 @@ const BridgeStats = (props) => (
); -export default (props) => ( -
- { do { - if (props.loading) { -
- { _("optionsBridgeLoading") } - -
- } else { - const infoClasses = `bridge__info ${props.info - ? "bridge__info--found" - : "bridge__info--not-found"}`; +export default class Bridge extends Component { + constructor (props) { + super(props); - const [ statusIcon, statusText ] = do { - if (!props.info) { - [ "assets/icons8-cancel-120.png" - , _("optionsBridgeNotFoundStatusText") ] - } else { - if (props.info.isVersionCompatible) { - [ "assets/icons8-ok-120.png" - , _("optionsBridgeFoundStatusText") ] - } else { - [ "assets/icons8-warn-120.png" - , _("optionsBridgeIssueStatusText") ] - } - } + this.onGetDownloads = this.onGetDownloads.bind(this); + this.onGetDownloadsResponse = this.onGetDownloadsResponse.bind(this); + this.onGetDownloadsError = this.onGetDownloadsError.bind(this); + + this.state = { + downloads: null + , isLoadingDownloads: false + , wasErrorLoadingDownloads: false + , downloadsLoadingEllipsis: "..." + }; + } + + + onGetDownloads () { + this.setState({ + isLoadingDownloads: true + }); + + const timeout = setInterval(() => { + this.setState(state => ({ + downloadsLoadingEllipsis: getNextEllipsis( + state.downloadsLoadingEllipsis) + })); + }, 500); + + fetch(ENDPOINT_URL) + .then(res => { + window.clearTimeout(timeout); + return res.json() + }) + .then(this.onGetDownloadsResponse) + .catch(this.onGetDownloadsError); + } + + async onGetDownloadsResponse (res) { + const platformInfo = await browser.runtime.getPlatformInfo(); + const downloads = res.assets + .reduce((acc, asset) => { + const download = { + name: asset.name + , url: asset.browser_download_url }; -
-
- + const platformExtensions = { + "exe": "win" + , "pkg": "mac" + , "deb": "deb" + , "rpm": "rpm" + }; -

- { statusText } -

-
+ const fileExtension = asset.name.match(/.*\.(.*)$/).pop(); - { do { - if (props.info) { - - } else { - // TODO: Download links - } - }} -
- } - }} -
-); + if (fileExtension in platformExtensions) { + const platform = platformExtensions[fileExtension]; + acc[platform] = download; + } + + return acc; + }, { platform: platformInfo.os }); + + this.setState({ + isLoadingDownloads: false + , downloads + }); + } + + onGetDownloadsError (err) { + this.setState({ + isLoadingDownloads: false + , wasErrorLoadingDownloads: true + }); + } + + + render () { + return ( +
+ { do { + if (this.props.loading) { +
+ { _("optionsBridgeLoading") } + +
+ } else { + const infoClasses = `bridge__info ${this.props.info + ? "bridge__info--found" + : "bridge__info--not-found"}`; + + const [ statusIcon, statusTitle, statusText ] = do { + if (!this.props.info) { + [ "assets/icons8-cancel-120.png" + , _("optionsBridgeNotFoundStatusTitle") + , _("optionsBridgeNotFoundStatusText") ] + } else { + if (this.props.info.isVersionCompatible) { + [ "assets/icons8-ok-120.png" + , _("optionsBridgeFoundStatusTitle") ] + } else { + [ "assets/icons8-warn-120.png" + , _("optionsBridgeIssueStatusTitle") ] + } + } + }; + +
+
+ + +

+ { statusTitle } +

+ + { do { + if (statusText) { +

+ { statusText } +

+ } + }} +
+ + { do { + if (this.props.info) { + + } + }} +
+ } + }} + + { do { + if (!this.props.loading + && (!this.props.info + || !this.props.info.isVersionCompatible)) { +
+

+ { _("optionsBridgeDownloadsTitle") } +

+ { do { + if (this.state.downloads) { + + } else if (this.state.wasErrorLoadingDownloads) { +
+ { _("optionsBridgeDownloadsGetFailed") } +
+ } else { + + } + }} +
+ } + }} +
+ ); + } +} diff --git a/ext/src/options/styles/index.css b/ext/src/options/styles/index.css index 7d55a20..846de40 100644 --- a/ext/src/options/styles/index.css +++ b/ext/src/options/styles/index.css @@ -1,5 +1,6 @@ :root { - --border-color: rgb(225, 225, 225);; + --border-color: rgb(225, 225, 225); + --secondary-color: rgb(125, 125, 125); } *:invalid { @@ -23,7 +24,7 @@ } #status-line { - color: graytext; + color: var(--secondary-color); } @@ -73,13 +74,21 @@ padding-inline-end: 25px; } -.bridge__status-text { +.bridge__status-title { margin: initial; font-weight: 500; font-size: 1.5em; white-space: nowrap; } +.bridge__status-text { + margin: initial; + margin-top: 5px; + font-size: 1.15em; + font-weight: 300; + text-align: center; +} + .bridge__info--not-found .bridge__status { flex-direction: row; } @@ -89,7 +98,7 @@ .bridge__info--not-found .bridge__status-icon { margin-inline-end: 10px; } -.bridge__info--not-found .bridge__status-text { +.bridge__info--not-found .bridge__status-title { font-weight: normal; white-space: normal; } @@ -108,6 +117,37 @@ vertical-align: top; } +.bridge__download-info { + margin-top: 30px; + display: flex; + flex-direction: column; + width: -moz-available; +} + +.bridge__download-info-get { + align-self: flex-start; + justify-content: center; +} + +.bridge__download-info-title { + font-weight: 500; + font-size: 1.25em; +} + +.bridge-downloads { + display: flex; +} + +.bridge-downloads__download { + +} + +.bridge-downloads__linux { + display: flex; + flex-direction: column; +} + + .category { border: initial; @@ -120,7 +160,7 @@ } .category:disabled { - color: graytext; + color: var(--secondary-color); } #form > .category:not(:first-child) { @@ -148,7 +188,7 @@ } .category__description { - color: graytext; + color: var(--secondary-color); margin-top: initial; max-width: 60ch; } diff --git a/ext/src/popup/index.jsx b/ext/src/popup/index.jsx index ac47d28..d9e4064 100755 --- a/ext/src/popup/index.jsx +++ b/ext/src/popup/index.jsx @@ -3,6 +3,8 @@ import React, { Component } from "react"; import ReactDOM from "react-dom"; +import { getNextEllipsis } from "../lib/utils"; + const _ = browser.i18n.getMessage; // macOS styles @@ -168,14 +170,9 @@ class Receiver extends Component { }); setInterval(() => { - this.setState({ - ellipsis: do { - if (this.state.ellipsis === "") "."; - else if (this.state.ellipsis === ".") ".."; - else if (this.state.ellipsis === "..") "..."; - else if (this.state.ellipsis === "...") ""; - } - }); + this.setState(state => ({ + ellipsis: getNextEllipsis(state.ellipsis) + })); }, 500); } diff --git a/ext/src/popup/styles/mac.css b/ext/src/popup/styles/mac.css index 4c753a2..1616f28 100755 --- a/ext/src/popup/styles/mac.css +++ b/ext/src/popup/styles/mac.css @@ -1,22 +1,22 @@ -body { - background: rgb(236, 236, 236); - font: menu; -} - -button, -select { - font: inherit; -} - -button:not([disabled]):hover:active { - color: -moz-mac-buttonactivetext; -} - -.receiver-address, -.receiver-status { - font: message-box; -} - -.receiver-connect { - height: 22px; -} +body { + background: rgb(236, 236, 236); + font: menu; +} + +button, +select { + font: inherit; +} + +button:not([disabled]):hover:active { + color: -moz-mac-buttonactivetext; +} + +.receiver-address, +.receiver-status { + font: message-box; +} + +.receiver-connect { + height: 22px; +}