From e761d4756a365e4c8ed1f07586917dbca9c24aab Mon Sep 17 00:00:00 2001 From: hensm Date: Sat, 10 Sep 2022 19:48:46 +0100 Subject: [PATCH] Set browser action details to match cast session states --- ext/src/_locales/en/messages.json | 17 +++++++ ext/src/background/action.ts | 75 +++++++++++++++++++++++++++++ ext/src/background/background.ts | 11 +---- ext/src/background/castManager.ts | 33 +++++++++++++ ext/src/background/deviceManager.ts | 16 ++++++ 5 files changed, 143 insertions(+), 9 deletions(-) create mode 100644 ext/src/background/action.ts diff --git a/ext/src/_locales/en/messages.json b/ext/src/_locales/en/messages.json index f741622..82e2c2a 100755 --- a/ext/src/_locales/en/messages.json +++ b/ext/src/_locales/en/messages.json @@ -8,6 +8,23 @@ "description": "Description of the extension shown in the add-ons manager." }, + "actionTitleDefault": { + "message": "fx_cast: Ready to cast", + "description": "Title for toolbar button in default state." + }, + "actionTitleDisabled": { + "message": "fx_cast: Unable to cast", + "description": "Title for toolbar button in disabled state." + }, + "actionTitleConnecting": { + "message": "fx_cast: Connecting", + "description": "Title for toolbar button whilst connecting." + }, + "actionTitleConnected": { + "message": "fx_cast: Connected", + "description": "Title for toolbar button whilst connected." + }, + "popupBridgeErrorBanner": { "message": "There is a problem with the bridge!", "description": "Bridge error banner message." diff --git a/ext/src/background/action.ts b/ext/src/background/action.ts new file mode 100644 index 0000000..dee30a6 --- /dev/null +++ b/ext/src/background/action.ts @@ -0,0 +1,75 @@ +import logger from "../lib/logger"; +import castManager from "./castManager"; + +const _ = browser.i18n.getMessage; + +const ACTION_ICON_DEFAULT_DARK = "icons/cast-default-dark.svg"; +const ACTION_ICON_DEFAULT_LIGHT = "icons/cast-default-light.svg"; +const ACTION_ICON_CONNECTING_DARK = "icons/cast-connecting-dark.svg"; +const ACTION_ICON_CONNECTING_LIGHT = "icons/cast-connecting-light.svg"; +const ACTION_ICON_CONNECTED = "icons/cast-connected.svg"; +const ACTION_ICON_DISABLED_DARK = "icons/cast-disabled-dark.svg"; +const ACTION_ICON_DISABLED_LIGHT = "icons/cast-disabled-light.svg"; + +const isDarkTheme = window.matchMedia("(prefers-color-scheme: dark)").matches; + +export enum ActionState { + Default, + Connecting, + Connected, + Disabled +} + +/** Updates action details depending on given state. */ +export function updateActionState(state: ActionState, tabId?: number) { + let title: string; + let path: string; + switch (state) { + case ActionState.Default: + title = _("actionTitleDefault"); + path = isDarkTheme + ? ACTION_ICON_DEFAULT_LIGHT + : ACTION_ICON_DEFAULT_DARK; + break; + case ActionState.Connecting: + title = _("actionTitleConnecting"); + path = isDarkTheme + ? ACTION_ICON_CONNECTING_LIGHT + : ACTION_ICON_CONNECTING_DARK; + break; + case ActionState.Connected: + title = _("actionTitleConnected"); + path = ACTION_ICON_CONNECTED; + break; + case ActionState.Disabled: + title = _("actionTitleDisabled"); + path = isDarkTheme + ? ACTION_ICON_DISABLED_LIGHT + : ACTION_ICON_DISABLED_DARK; + break; + } + + if (state === ActionState.Disabled) { + browser.browserAction.disable(tabId); + } else { + browser.browserAction.enable(tabId); + } + + browser.browserAction.setTitle({ tabId, title }); + browser.browserAction.setIcon({ tabId, path }); +} + +export function initAction() { + logger.info("init (action)"); + + updateActionState(ActionState.Default); + + browser.browserAction.onClicked.addListener(async tab => { + if (tab.id === undefined) { + logger.error("Tab ID not found in browser action handler."); + return; + } + + castManager.triggerCast(tab.id); + }); +} diff --git a/ext/src/background/background.ts b/ext/src/background/background.ts index 0d71550..a01283a 100755 --- a/ext/src/background/background.ts +++ b/ext/src/background/background.ts @@ -9,6 +9,7 @@ import messaging from "../messaging"; import castManager from "./castManager"; import deviceManager from "./deviceManager"; +import { initAction } from "./action"; import { initMenus } from "./menus"; import { initWhitelist } from "./whitelist"; @@ -129,6 +130,7 @@ async function init() { await deviceManager.init(); await castManager.init(); + await initAction(); await initMenus(); await initWhitelist(); @@ -139,15 +141,6 @@ async function init() { break; } }); - - browser.browserAction.onClicked.addListener(async tab => { - if (tab.id === undefined) { - logger.error("Tab ID not found in browser action handler."); - return; - } - - castManager.triggerCast(tab.id); - }); } cacheBaseConfig(); diff --git a/ext/src/background/castManager.ts b/ext/src/background/castManager.ts index 17621e2..c5f79f6 100644 --- a/ext/src/background/castManager.ts +++ b/ext/src/background/castManager.ts @@ -27,6 +27,7 @@ import ReceiverSelector, { } from "./ReceiverSelector"; import deviceManager from "./deviceManager"; +import { ActionState, updateActionState } from "./action"; type AnyPort = Port | TypedMessagePort; @@ -85,6 +86,13 @@ async function createCastSession(opts: { destroyCastInstance(opts.instance) ); + if (opts.instance.contentContext?.tabId) { + updateActionState( + ActionState.Connecting, + opts.instance.contentContext?.tabId + ); + } + return session; } @@ -161,6 +169,10 @@ function destroyCastInstance(instance: CastInstance) { ); } + if (instance.contentContext?.tabId) { + updateActionState(ActionState.Default, instance.contentContext?.tabId); + } + activeInstances.delete(instance); } @@ -227,6 +239,13 @@ const castManager = new (class { }); delete instance.session; + + if (instance.contentContext?.tabId) { + updateActionState( + ActionState.Default, + instance.contentContext?.tabId + ); + } } } @@ -407,6 +426,13 @@ async function handleBridgeMessage(instance: CastInstance, message: Message) { } }); + if (instance.contentContext?.tabId) { + updateActionState( + ActionState.Connected, + instance.contentContext?.tabId + ); + } + break; } @@ -529,6 +555,13 @@ async function handleContentMessage(instance: CastInstance, message: Message) { } }); + if (instance.contentContext?.tabId) { + updateActionState( + ActionState.Connected, + instance.contentContext?.tabId + ); + } + break sessionLoop; } } diff --git a/ext/src/background/deviceManager.ts b/ext/src/background/deviceManager.ts index 4e42f44..085c435 100644 --- a/ext/src/background/deviceManager.ts +++ b/ext/src/background/deviceManager.ts @@ -13,6 +13,8 @@ import type { } from "../cast/sdk/types"; import { PlayerState } from "../cast/sdk/media/enums"; +import { ActionState, updateActionState } from "./action"; + interface EventMap { deviceUp: { deviceInfo: ReceiverDevice }; deviceDown: { deviceId: string }; @@ -45,6 +47,8 @@ export default new (class extends TypedEventTarget { async refresh() { this.bridgePort?.disconnect(); + updateActionState(ActionState.Disabled); + try { this.bridgeInfo = await bridge.getInfo(); // eslint-disable-next-line no-empty @@ -65,6 +69,14 @@ export default new (class extends TypedEventTarget { } } + private updateAction() { + if (this.receiverDevices.size > 0) { + updateActionState(ActionState.Default); + } else { + updateActionState(ActionState.Disabled); + } + } + getBridgeInfo() { return this.bridgeInfo; } @@ -142,6 +154,8 @@ export default new (class extends TypedEventTarget { }) ); + this.updateAction(); + break; } @@ -157,6 +171,8 @@ export default new (class extends TypedEventTarget { }) ); + this.updateAction(); + break; }