mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +00:00
304 lines
11 KiB
TypeScript
304 lines
11 KiB
TypeScript
/* tslint:disable:max-line-length */
|
|
"use strict";
|
|
|
|
import React, { Component } from "react";
|
|
import semver from "semver";
|
|
|
|
import options, { Options } from "../../lib/options";
|
|
|
|
import { BridgeInfo } from "../../lib/bridge";
|
|
import { getNextEllipsis } from "../../lib/utils";
|
|
|
|
|
|
const _ = browser.i18n.getMessage;
|
|
|
|
|
|
interface BridgeStatsProps {
|
|
info: BridgeInfo;
|
|
}
|
|
|
|
const BridgeStats = (props: BridgeStatsProps) => (
|
|
<table className="bridge__stats">
|
|
<tr>
|
|
<th>{ _("optionsBridgeStatsName") }</th>
|
|
<td>{ props.info.name }</td>
|
|
</tr>
|
|
<tr>
|
|
<th>{ _("optionsBridgeStatsVersion") }</th>
|
|
<td>{ props.info.version }</td>
|
|
</tr>
|
|
<tr>
|
|
<th>{ _("optionsBridgeStatsExpectedVersion") }</th>
|
|
<td>{ props.info.expectedVersion }</td>
|
|
</tr>
|
|
<tr>
|
|
<th>{ _("optionsBridgeStatsCompatibility") }</th>
|
|
<td>
|
|
{ props.info.isVersionCompatible
|
|
? props.info.isVersionExact
|
|
? _("optionsBridgeCompatible")
|
|
: _("optionsBridgeLikelyCompatible")
|
|
: _("optionsBridgeIncompatible") }
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>{ _("optionsBridgeStatsRecommendedAction") }</th>
|
|
<td>
|
|
{
|
|
props.info.isVersionCompatible
|
|
? _("optionsBridgeNoAction")
|
|
: props.info.isVersionOlder
|
|
? _("optionsBridgeOlderAction")
|
|
: props.info.isVersionNewer
|
|
? _("optionsBridgeNewerAction")
|
|
: _("optionsBridgeNoAction")
|
|
}
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
);
|
|
|
|
|
|
interface BridgeProps {
|
|
info?: BridgeInfo;
|
|
loading: boolean;
|
|
loadingTimedOut: boolean;
|
|
options?: Options;
|
|
onChange: (ev: React.ChangeEvent<HTMLInputElement>) => void;
|
|
}
|
|
|
|
interface BridgeState {
|
|
isCheckingUpdates: boolean;
|
|
isUpdateAvailable: boolean;
|
|
wasErrorCheckingUpdates: boolean;
|
|
checkUpdatesEllipsis: string;
|
|
updateStatus?: string;
|
|
}
|
|
|
|
export default class Bridge extends Component<BridgeProps, BridgeState> {
|
|
private updateData: any;
|
|
private updateStatusTimeout?: number;
|
|
|
|
constructor (props: BridgeProps) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
isCheckingUpdates: false
|
|
, isUpdateAvailable: false
|
|
, wasErrorCheckingUpdates: false
|
|
, checkUpdatesEllipsis: "..."
|
|
};
|
|
|
|
this.onCheckUpdates = this.onCheckUpdates.bind(this);
|
|
this.onCheckUpdatesResponse = this.onCheckUpdatesResponse.bind(this);
|
|
this.onCheckUpdatesError = this.onCheckUpdatesError.bind(this);
|
|
this.onUpdate = this.onUpdate.bind(this);
|
|
}
|
|
|
|
public render () {
|
|
const [ backupMessageStart, backupMessageEnd ]
|
|
= _("optionsBridgeBackupEnabled", "\0").split("\0");
|
|
|
|
return (
|
|
<div className="bridge">
|
|
{ this.props.loading
|
|
? ( <div className="bridge__loading">
|
|
{ _("optionsBridgeLoading") }
|
|
<progress></progress>
|
|
</div> )
|
|
: this.renderStatus() }
|
|
|
|
{ !this.props.loading && this.props.options &&
|
|
<div className="bridge__options">
|
|
<label className="option option--inline">
|
|
<div className="option__control">
|
|
<input name="bridgeBackupEnabled"
|
|
type="checkbox"
|
|
checked={ this.props.options.bridgeBackupEnabled }
|
|
onChange={ this.props.onChange } />
|
|
</div>
|
|
<div className="option__label">
|
|
{ backupMessageStart }
|
|
<input className="bridge__backup-host"
|
|
name="bridgeBackupHost"
|
|
type="text"
|
|
required
|
|
value={ this.props.options.bridgeBackupHost }
|
|
onChange={ this.props.onChange } />
|
|
:
|
|
<input className="bridge__backup-port"
|
|
name="bridgeBackupPort"
|
|
type="number"
|
|
required
|
|
min="1025"
|
|
max="65535"
|
|
value={ this.props.options.bridgeBackupPort }
|
|
onChange={ this.props.onChange } />
|
|
{ backupMessageEnd }
|
|
</div>
|
|
<div className="option__description">
|
|
{ _("optionsBridgeBackupEnabledDescription") }
|
|
</div>
|
|
</label>
|
|
</div> }
|
|
|
|
{ !this.props.loading &&
|
|
<div className="bridge__update-info">
|
|
{ this.state.isUpdateAvailable
|
|
? ( <div className="bridge__update">
|
|
<p className="bridge__update-label">
|
|
{ _("optionsBridgeUpdateAvailable") }
|
|
</p>
|
|
<div className="bridge__update-options">
|
|
<button className="bridge__update-start"
|
|
type="button"
|
|
onClick={ this.onUpdate }>
|
|
{ _("optionsBridgeUpdate") }
|
|
</button>
|
|
</div>
|
|
</div> )
|
|
: ( <button className="bridge__update-check"
|
|
type="button"
|
|
disabled={ this.state.isCheckingUpdates }
|
|
onClick={ this.onCheckUpdates }>
|
|
|
|
{ this.state.isCheckingUpdates
|
|
? _("optionsBridgeUpdateChecking"
|
|
, getNextEllipsis(this.state.checkUpdatesEllipsis))
|
|
: _("optionsBridgeUpdateCheck") }
|
|
</button> )}
|
|
|
|
<div className="bridge--update-status">
|
|
{ this.state.updateStatus && !this.state.isUpdateAvailable
|
|
&& this.state.updateStatus }
|
|
</div>
|
|
</div> }
|
|
</div>
|
|
);
|
|
}
|
|
|
|
private renderStatus () {
|
|
const infoClasses = `bridge__info ${!this.props.info
|
|
? this.props.loadingTimedOut
|
|
? "bridge__info--timed-out"
|
|
: "bridge__info--not-found"
|
|
: "bridge__info--found"}`;
|
|
|
|
let statusIcon: string;
|
|
let statusTitle: string;
|
|
let statusText: (string | null) = null;
|
|
|
|
if (this.props.loadingTimedOut) {
|
|
statusIcon = "assets/icons8-warn-120.png";
|
|
statusTitle = _("optionsBridgeIssueStatusTitle");
|
|
} else if (!this.props.info) {
|
|
statusIcon = "assets/icons8-cancel-120.png";
|
|
statusTitle = _("optionsBridgeNotFoundStatusTitle");
|
|
statusText = _("optionsBridgeNotFoundStatusText");
|
|
} else {
|
|
if (this.props.info.isVersionCompatible) {
|
|
statusIcon = "assets/icons8-ok-120.png";
|
|
statusTitle = _("optionsBridgeFoundStatusTitle");
|
|
} else {
|
|
statusIcon = "assets/icons8-warn-120.png";
|
|
statusTitle = _("optionsBridgeIssueStatusTitle");
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className={infoClasses}>
|
|
<div className="bridge__status">
|
|
<img className="bridge__status-icon"
|
|
width="60" height="60"
|
|
src={ statusIcon } />
|
|
|
|
<h2 className="bridge__status-title">
|
|
{ statusTitle }
|
|
</h2>
|
|
|
|
{ statusText &&
|
|
<p className="bridge__status-text">
|
|
{ statusText }
|
|
</p> }
|
|
</div>
|
|
|
|
{ this.props.info &&
|
|
<BridgeStats info={ this.props.info }/> }
|
|
</div>
|
|
);
|
|
}
|
|
|
|
private onCheckUpdates () {
|
|
this.setState({
|
|
isCheckingUpdates: true
|
|
});
|
|
|
|
const timeout = window.setInterval(() => {
|
|
this.setState(state => ({
|
|
checkUpdatesEllipsis: getNextEllipsis(
|
|
state.checkUpdatesEllipsis)
|
|
}));
|
|
}, 500);
|
|
|
|
fetch("https://api.github.com/repos/hensm/fx_cast/releases/latest")
|
|
.then(res => {
|
|
window.clearTimeout(timeout);
|
|
return res.json();
|
|
})
|
|
.then(this.onCheckUpdatesResponse)
|
|
.catch(this.onCheckUpdatesError);
|
|
}
|
|
|
|
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
|
|
});
|
|
|
|
if (!isUpdateAvailable) {
|
|
this.setState({
|
|
updateStatus: _("optionsBridgeUpdateStatusNoUpdates")
|
|
});
|
|
}
|
|
|
|
this.showUpdateStatus();
|
|
}
|
|
|
|
private onCheckUpdatesError () {
|
|
this.setState({
|
|
isCheckingUpdates: false
|
|
, wasErrorCheckingUpdates: true
|
|
, updateStatus: _("optionsBridgeUpdateStatusError")
|
|
});
|
|
|
|
this.showUpdateStatus();
|
|
}
|
|
|
|
private showUpdateStatus () {
|
|
if (this.updateStatusTimeout) {
|
|
window.clearTimeout(this.updateStatusTimeout);
|
|
}
|
|
this.updateStatusTimeout = window.setTimeout(() => {
|
|
this.setState({
|
|
updateStatus: undefined
|
|
});
|
|
}, 1500);
|
|
}
|
|
|
|
private async onUpdate () {
|
|
// Open downloads page
|
|
if (this.updateData.html_url) {
|
|
browser.tabs.create({
|
|
url: this.updateData.html_url
|
|
});
|
|
}
|
|
}
|
|
}
|