mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-10 17:49:58 +00:00
241 lines
6.6 KiB
TypeScript
Executable File
241 lines
6.6 KiB
TypeScript
Executable File
/* tslint:disable:max-line-length */
|
|
"use strict";
|
|
|
|
import React, { Component } from "react";
|
|
import ReactDOM from "react-dom";
|
|
|
|
import { getNextEllipsis } from "../lib/utils";
|
|
import * as types from "../types";
|
|
|
|
const _ = browser.i18n.getMessage;
|
|
|
|
// macOS styles
|
|
browser.runtime.getPlatformInfo()
|
|
.then(platformInfo => {
|
|
if (platformInfo.os === "mac") {
|
|
const link = document.createElement("link");
|
|
link.rel = "stylesheet";
|
|
link.href = "styles/mac.css";
|
|
document.head.appendChild(link);
|
|
}
|
|
});
|
|
|
|
|
|
const winWidth = 350;
|
|
let winHeight = 200;
|
|
|
|
let frameHeight: number;
|
|
let frameWidth: number;
|
|
|
|
|
|
interface PopupAppState {
|
|
receivers: types.Receiver[];
|
|
selectedMedia: string;
|
|
isLoading: boolean;
|
|
}
|
|
|
|
class PopupApp extends Component<{}, PopupAppState> {
|
|
private port: browser.runtime.Port;
|
|
private win: browser.windows.Window;
|
|
|
|
constructor (props: {}) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
receivers: []
|
|
, selectedMedia: "app"
|
|
, isLoading: false
|
|
};
|
|
|
|
// Store window ref
|
|
browser.windows.getCurrent().then(win => {
|
|
this.win = win;
|
|
frameHeight = win.height - window.innerHeight;
|
|
frameWidth = win.width - window.innerWidth;
|
|
});
|
|
|
|
this.onSelectChange = this.onSelectChange.bind(this);
|
|
this.onCast = this.onCast.bind(this);
|
|
}
|
|
|
|
componentDidMount () {
|
|
const backgroundPort = browser.runtime.connect({
|
|
name: "popup"
|
|
});
|
|
|
|
backgroundPort.onMessage.addListener((message: types.Message) => {
|
|
if (message.subject === "popup:/assignShim") {
|
|
this.setPort(message.data.tabId
|
|
, message.data.frameId);
|
|
}
|
|
});
|
|
}
|
|
|
|
render () {
|
|
const shareMedia =
|
|
this.state.selectedMedia === "tab"
|
|
|| this.state.selectedMedia === "screen";
|
|
|
|
return (
|
|
<div>
|
|
<div className="media-select">
|
|
Cast
|
|
<select value={ this.state.selectedMedia }
|
|
onChange={ this.onSelectChange }
|
|
className="media-select-dropdown">
|
|
<option value="app" disabled={ shareMedia }>this site's app</option>
|
|
<option value="tab" disabled={ !shareMedia }>Tab</option>
|
|
<option value="screen" disabled={ !shareMedia }>Screen</option>
|
|
</select>
|
|
to:
|
|
</div>
|
|
<ul className="receivers">
|
|
{ this.state.receivers.map((receiver, i) => {
|
|
return (
|
|
<Receiver receiver={ receiver }
|
|
onCast={ this.onCast }
|
|
isLoading={ this.state.isLoading }
|
|
key={ i }/>
|
|
);
|
|
})}
|
|
</ul>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
private async setPort (shimTabId: number, shimFrameId: number) {
|
|
if (this.port) {
|
|
this.port.disconnect();
|
|
}
|
|
|
|
this.port = browser.tabs.connect(shimTabId, {
|
|
name: "popup"
|
|
, frameId: shimFrameId
|
|
});
|
|
|
|
this.port.postMessage({
|
|
subject: "shim:/popupReady"
|
|
});
|
|
|
|
this.port.onMessage.addListener((message: types.Message) => {
|
|
switch (message.subject) {
|
|
case "popup:/populateReceiverList": {
|
|
this.setState({
|
|
receivers: message.data.receivers
|
|
, selectedMedia: message.data.selectedMedia
|
|
}, () => {
|
|
// Get height of content without window decoration
|
|
winHeight = document.body.clientHeight + frameHeight;
|
|
|
|
// Adjust height to fit content
|
|
browser.windows.update(this.win.id, {
|
|
height: winHeight
|
|
});
|
|
});
|
|
|
|
break;
|
|
}
|
|
|
|
case "popup:/close": {
|
|
window.close();
|
|
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private onCast (receiver: types.Receiver) {
|
|
this.setState({
|
|
isLoading: true
|
|
});
|
|
|
|
this.port.postMessage({
|
|
subject: "shim:/selectReceiver"
|
|
, data: {
|
|
receiver
|
|
, selectedMedia: this.state.selectedMedia
|
|
, a: 5
|
|
}
|
|
});
|
|
}
|
|
|
|
private onSelectChange (ev: React.ChangeEvent<HTMLSelectElement>) {
|
|
this.setState({
|
|
selectedMedia: ev.target.value
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
interface ReceiverProps {
|
|
receiver: types.Receiver;
|
|
isLoading: boolean;
|
|
onCast (receiver: types.Receiver): void;
|
|
}
|
|
|
|
interface ReceiverState {
|
|
ellipsis: string;
|
|
isLoading: boolean;
|
|
}
|
|
|
|
class Receiver extends Component<ReceiverProps, ReceiverState> {
|
|
constructor (props: ReceiverProps) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
isLoading: false
|
|
, ellipsis: ""
|
|
};
|
|
|
|
this.onClick = this.onClick.bind(this);
|
|
}
|
|
|
|
onClick () {
|
|
this.props.onCast(this.props.receiver);
|
|
|
|
this.setState({
|
|
isLoading: true
|
|
});
|
|
|
|
setInterval(() => {
|
|
this.setState(state => ({
|
|
ellipsis: getNextEllipsis(state.ellipsis)
|
|
}));
|
|
|
|
}, 500);
|
|
}
|
|
|
|
render () {
|
|
return (
|
|
<li className="receiver">
|
|
<div className="receiver-name">
|
|
{ this.props.receiver.friendlyName }
|
|
</div>
|
|
<div className="receiver-address">
|
|
{ `${this.props.receiver.address}:${this.props.receiver.port}` }
|
|
</div>
|
|
<div className="receiver-status">
|
|
{ this.props.receiver.currentApp &&
|
|
`- ${this.props.receiver.currentApp}` }
|
|
</div>
|
|
<button className="receiver-connect"
|
|
onClick={ this.onClick }
|
|
disabled={this.props.isLoading}>
|
|
{ this.state.isLoading
|
|
? _("popupCastingButtonLabel") +
|
|
(this.state.isLoading
|
|
? this.state.ellipsis
|
|
: "")
|
|
: _("popupCastButtonLabel") }
|
|
</button>
|
|
</li>
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
ReactDOM.render(
|
|
<PopupApp />
|
|
, document.querySelector("#root"));
|