Add file media type to receiver selectors

This commit is contained in:
hensm
2019-07-09 16:04:11 +01:00
parent 70e05566fa
commit 615f158832
10 changed files with 141 additions and 29 deletions

View File

@@ -8,15 +8,9 @@ class AppDelegate : NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching (_ aNotification: Notification) { func applicationDidFinishLaunching (_ aNotification: Notification) {
let window = NSPanel( let window = NSWindow(
contentRect: NSZeroRect contentRect: NSZeroRect
, styleMask: [ , styleMask: [ .titled, .closable ]
.titled
, .closable
, .hudWindow
, .utilityWindow
, .nonactivatingPanel
]
, backing: .buffered , backing: .buffered
, defer: false) , defer: false)

View File

@@ -7,13 +7,15 @@ class ViewController : NSViewController {
var mediaTypePopUpButton: NSPopUpButton! var mediaTypePopUpButton: NSPopUpButton!
var receiverViews = [ReceiverView]() var receiverViews = [ReceiverView]()
var filePath: String?
override func loadView () { override func loadView () {
let visualEffectView = NSVisualEffectView() /*let visualEffectView = NSVisualEffectView()
visualEffectView.blendingMode = .behindWindow visualEffectView.blendingMode = .behindWindow
visualEffectView.state = .active visualEffectView.state = .active*/
self.view = visualEffectView self.view = NSView()
} }
override func viewDidLoad () { override func viewDidLoad () {
@@ -71,34 +73,35 @@ class ViewController : NSViewController {
initData.i18n_mediaTypeApp initData.i18n_mediaTypeApp
, initData.i18n_mediaTypeTab , initData.i18n_mediaTypeTab
, initData.i18n_mediaTypeScreen , initData.i18n_mediaTypeScreen
, initData.i18n_mediaTypeFile
]) ])
let mediaTypePopUpButtonMenu = self.mediaTypePopUpButton.menu!
mediaTypePopUpButtonMenu.delegate = self
let appItem = self.mediaTypePopUpButton let appItem = self.mediaTypePopUpButton
.item(withTitle: initData.i18n_mediaTypeApp)! .item(withTitle: initData.i18n_mediaTypeApp)!
let tabItem = self.mediaTypePopUpButton let tabItem = self.mediaTypePopUpButton
.item(withTitle: initData.i18n_mediaTypeTab)! .item(withTitle: initData.i18n_mediaTypeTab)!
let screenItem = self.mediaTypePopUpButton let screenItem = self.mediaTypePopUpButton
.item(withTitle: initData.i18n_mediaTypeScreen)! .item(withTitle: initData.i18n_mediaTypeScreen)!
let fileItem = self.mediaTypePopUpButton
.item(withTitle: initData.i18n_mediaTypeFile)!
mediaTypePopUpButtonMenu
.insertItem(NSMenuItem.separator()
, at: mediaTypePopUpButtonMenu.index(of: fileItem))
// Set tags to enum value // Set tags to enum value
appItem.tag = MediaType.app.rawValue appItem.tag = MediaType.app.rawValue
tabItem.tag = MediaType.tab.rawValue tabItem.tag = MediaType.tab.rawValue
screenItem.tag = MediaType.screen.rawValue screenItem.tag = MediaType.screen.rawValue
fileItem.tag = MediaType.file.rawValue
if (initData.availableMediaTypes & appItem.tag) == 0 { for item in self.mediaTypePopUpButton.itemArray {
self.mediaTypePopUpButton if (initData.availableMediaTypes & item.tag) == 0 {
.item(withTitle: initData.i18n_mediaTypeApp)? item.isEnabled = false
.isEnabled = false }
}
if (initData.availableMediaTypes & tabItem.tag) == 0 {
self.mediaTypePopUpButton
.item(withTitle: initData.i18n_mediaTypeTab)?
.isEnabled = false
}
if (initData.availableMediaTypes & screenItem.tag) == 0 {
self.mediaTypePopUpButton
.item(withTitle: initData.i18n_mediaTypeScreen)?
.isEnabled = false
} }
self.mediaTypePopUpButton.selectItem( self.mediaTypePopUpButton.selectItem(
@@ -155,6 +158,50 @@ class ViewController : NSViewController {
} }
} }
extension ViewController : NSMenuDelegate {
func menuDidClose (_ menu: NSMenu) {
let mediaType = MediaType(
rawValue: self.mediaTypePopUpButton.selectedItem!.tag)!
let fileItem = self.mediaTypePopUpButton
.item(at: self.mediaTypePopUpButton.indexOfItem(
withTag: MediaType.file.rawValue))!
if (mediaType == .file) {
let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.allowedFileTypes = [ "aac", "mp3", "mp4", "wav", "webm" ]
panel.canChooseDirectories = false
panel.canCreateDirectories = false
panel.canChooseFiles = true
panel.beginSheetModal(for: self.view.window!) { (result) in
if (result == .OK) {
let url = panel.urls[0]
let fileName = url.lastPathComponent
// Truncate file name and set as title
fileItem.title = fileName.count > 12
? "\(fileName.prefix(12))..."
: fileName
self.filePath = url.path
return
} else {
// Re-select the default media type item
self.mediaTypePopUpButton.selectItem(
withTag: self.initData.defaultMediaType.rawValue)
}
}
}
// Reset file item
fileItem.title = self.initData.i18n_mediaTypeFile
self.filePath = nil
}
}
extension ViewController : ReceiverViewDelegate { extension ViewController : ReceiverViewDelegate {
func didCast (_ receiver: Receiver) { func didCast (_ receiver: Receiver) {
@@ -173,7 +220,8 @@ extension ViewController : ReceiverViewDelegate {
let selection = ReceiverSelection( let selection = ReceiverSelection(
receiver: receiver receiver: receiver
, mediaType: mediaType) , mediaType: mediaType
, filePath: self.filePath ?? nil)
let jsonData = try JSONEncoder().encode(selection) let jsonData = try JSONEncoder().encode(selection)
let jsonString = String(data: jsonData, encoding: .utf8) let jsonString = String(data: jsonData, encoding: .utf8)

View File

@@ -8,6 +8,7 @@ struct InitData : Codable {
let i18n_mediaTypeApp: String let i18n_mediaTypeApp: String
let i18n_mediaTypeTab: String let i18n_mediaTypeTab: String
let i18n_mediaTypeScreen: String let i18n_mediaTypeScreen: String
let i18n_mediaTypeFile: String
let i18n_mediaSelectCastLabel: String let i18n_mediaSelectCastLabel: String
let i18n_mediaSelectToLabel: String let i18n_mediaSelectToLabel: String
} }

View File

@@ -5,4 +5,5 @@ enum MediaType: Int, Codable {
case app = 1 case app = 1
case tab = 2 case tab = 2
case screen = 4 case screen = 4
case file = 8
} }

View File

@@ -1,4 +1,5 @@
struct ReceiverSelection : Codable { struct ReceiverSelection : Codable {
let receiver: Receiver let receiver: Receiver
let mediaType: MediaType let mediaType: MediaType
let filePath: String?
} }

View File

@@ -21,6 +21,10 @@
"message": "Screen" "message": "Screen"
, "description": "Receiver selector media type <option> text for screen." , "description": "Receiver selector media type <option> text for screen."
} }
, "popupMediaTypeFile": {
"message": "Browse..."
, "description": "Receiver selector media type <option> text for opening a file selector dialog."
}
, "popupMediaSelectCastLabel": { , "popupMediaSelectCastLabel": {
"message": "Cast" "message": "Cast"

View File

@@ -70,6 +70,7 @@ export default class NativeMacReceiverSelector
, i18n_mediaTypeApp: _("popupMediaTypeApp") , i18n_mediaTypeApp: _("popupMediaTypeApp")
, i18n_mediaTypeTab: _("popupMediaTypeTab") , i18n_mediaTypeTab: _("popupMediaTypeTab")
, i18n_mediaTypeScreen: _("popupMediaTypeScreen") , i18n_mediaTypeScreen: _("popupMediaTypeScreen")
, i18n_mediaTypeFile: _("popupMediaTypeFile")
, i18n_mediaSelectCastLabel: _("popupMediaSelectCastLabel") , i18n_mediaSelectCastLabel: _("popupMediaSelectCastLabel")
, i18n_mediaSelectToLabel: _("popupMediaSelectToLabel") , i18n_mediaSelectToLabel: _("popupMediaSelectToLabel")
}) })

View File

@@ -7,11 +7,13 @@ export enum ReceiverSelectorMediaType {
App = 1 App = 1
, Tab = 2 , Tab = 2
, Screen = 4 , Screen = 4
, File = 8
} }
export interface ReceiverSelection { export interface ReceiverSelection {
receiver: Receiver; receiver: Receiver;
mediaType: ReceiverSelectorMediaType; mediaType: ReceiverSelectorMediaType;
filePath?: string;
} }
export type ReceiverSelectorSelectedEvent = CustomEvent<ReceiverSelection>; export type ReceiverSelectorSelectedEvent = CustomEvent<ReceiverSelection>;

View File

@@ -31,6 +31,7 @@ export default class ApiConfig {
, public _availableMediaTypes: ReceiverSelectorMediaType , public _availableMediaTypes: ReceiverSelectorMediaType
= ReceiverSelectorMediaType.App = ReceiverSelectorMediaType.App
| ReceiverSelectorMediaType.Tab | ReceiverSelectorMediaType.Tab
| ReceiverSelectorMediaType.Screen) { | ReceiverSelectorMediaType.Screen
| ReceiverSelectorMediaType.File) {
} }
} }

View File

@@ -30,11 +30,13 @@ interface PopupAppState {
mediaType: ReceiverSelectorMediaType; mediaType: ReceiverSelectorMediaType;
availableMediaTypes: ReceiverSelectorMediaType; availableMediaTypes: ReceiverSelectorMediaType;
isLoading: boolean; isLoading: boolean;
filePath: string;
} }
class PopupApp extends Component<{}, PopupAppState> { class PopupApp extends Component<{}, PopupAppState> {
private port: browser.runtime.Port; private port: browser.runtime.Port;
private win: browser.windows.Window; private win: browser.windows.Window;
private defaultMediaType: ReceiverSelectorMediaType;
constructor (props: {}) { constructor (props: {}) {
super(props); super(props);
@@ -44,6 +46,7 @@ class PopupApp extends Component<{}, PopupAppState> {
, mediaType: ReceiverSelectorMediaType.App , mediaType: ReceiverSelectorMediaType.App
, availableMediaTypes: ReceiverSelectorMediaType.App , availableMediaTypes: ReceiverSelectorMediaType.App
, isLoading: false , isLoading: false
, filePath: null
}; };
// Store window ref // Store window ref
@@ -63,9 +66,11 @@ class PopupApp extends Component<{}, PopupAppState> {
this.port.onMessage.addListener((message: Message) => { this.port.onMessage.addListener((message: Message) => {
switch (message.subject) { switch (message.subject) {
case "popup:/populateReceiverList": { case "popup:/populateReceiverList": {
this.defaultMediaType = message.data.defaultMediaType;
this.setState({ this.setState({
receivers: message.data.receivers receivers: message.data.receivers
, mediaType: message.data.defaultMediaType , mediaType: this.defaultMediaType
, availableMediaTypes: message.data.availableMediaTypes , availableMediaTypes: message.data.availableMediaTypes
}); });
@@ -93,6 +98,18 @@ class PopupApp extends Component<{}, PopupAppState> {
} }
public render () { public render () {
let truncatedFileName: string;
if (this.state.filePath) {
const filePath = this.state.filePath;
const fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
truncatedFileName = fileName.length > 12
? `${fileName.substring(0, 12)}...`
: fileName;
}
return ( return (
<div> <div>
<div className="media-select"> <div className="media-select">
@@ -115,6 +132,16 @@ class PopupApp extends Component<{}, PopupAppState> {
& ReceiverSelectorMediaType.Screen) }> & ReceiverSelectorMediaType.Screen) }>
{ _("popupMediaTypeScreen") } { _("popupMediaTypeScreen") }
</option> </option>
<option disabled>
</option>
<option value={ ReceiverSelectorMediaType.File }
disabled={ !(this.state.availableMediaTypes
& ReceiverSelectorMediaType.File) }>
{ this.state.filePath
? truncatedFileName
: _("popupMediaTypeFile") }
</option>
</select> </select>
{ _("popupMediaSelectToLabel") } { _("popupMediaSelectToLabel") }
</div> </div>
@@ -140,13 +167,45 @@ class PopupApp extends Component<{}, PopupAppState> {
, data: { , data: {
receiver receiver
, mediaType: this.state.mediaType , mediaType: this.state.mediaType
, filePath: this.state.filePath
} }
}); });
} }
private onSelectChange (ev: React.ChangeEvent<HTMLSelectElement>) { private onSelectChange (ev: React.ChangeEvent<HTMLSelectElement>) {
const mediaType = parseInt(ev.target.value);
if (mediaType === ReceiverSelectorMediaType.File) {
try {
const filePath = window.prompt();
// Validate URL
const fileUrl = new URL(filePath.startsWith("file://")
? filePath
: `file://${filePath}`);
this.setState({
mediaType
, filePath
});
return;
} catch (err) {
// Don't need to handle any errors
}
// Set media type to default if failed to set filePath
this.setState({
mediaType: this.defaultMediaType
});
} else {
this.setState({
mediaType
});
}
this.setState({ this.setState({
mediaType: parseInt(ev.target.value) filePath: null
}); });
} }
} }