diff options
| author | Sebastian <sebasjm@gmail.com> | 2022-09-12 14:28:53 -0300 | 
|---|---|---|
| committer | Sebastian <sebasjm@gmail.com> | 2022-09-12 14:28:53 -0300 | 
| commit | ad63d4c0e1ab249362e1f684ac9dbe5fae2ef34e (patch) | |
| tree | dfc38c13a23e45f4916071c76e26049a57ebd63b /packages/taler-wallet-webextension/src | |
| parent | 27201416c7d234361507e6055ce7ed42c11c650e (diff) | |
add clipboard perms
Diffstat (limited to 'packages/taler-wallet-webextension/src')
| -rw-r--r-- | packages/taler-wallet-webextension/src/background.ts | 8 | ||||
| -rw-r--r-- | packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts (renamed from packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts) | 10 | ||||
| -rw-r--r-- | packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts | 73 | ||||
| -rw-r--r-- | packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts | 3 | ||||
| -rw-r--r-- | packages/taler-wallet-webextension/src/platform/api.ts | 4 | ||||
| -rw-r--r-- | packages/taler-wallet-webextension/src/platform/chrome.ts | 59 | ||||
| -rw-r--r-- | packages/taler-wallet-webextension/src/platform/dev.ts | 3 | ||||
| -rw-r--r-- | packages/taler-wallet-webextension/src/platform/firefox.ts | 18 | ||||
| -rw-r--r-- | packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx | 12 | ||||
| -rw-r--r-- | packages/taler-wallet-webextension/src/wallet/Settings.tsx | 47 | ||||
| -rw-r--r-- | packages/taler-wallet-webextension/src/wallet/Welcome.tsx | 4 | ||||
| -rw-r--r-- | packages/taler-wallet-webextension/src/wxBackend.ts | 14 | 
12 files changed, 211 insertions, 44 deletions
diff --git a/packages/taler-wallet-webextension/src/background.ts b/packages/taler-wallet-webextension/src/background.ts index 0085ee4fd..0e2ea3f3a 100644 --- a/packages/taler-wallet-webextension/src/background.ts +++ b/packages/taler-wallet-webextension/src/background.ts @@ -42,14 +42,6 @@ if (isFirefox) {    setupPlatform(chromeAPI);  } -try { -  platform.registerOnInstalled(() => { -    platform.openWalletPage("/welcome"); -  }); -} catch (e) { -  console.error(e); -} -  // setGlobalLogLevelFromString("trace")  platform.notifyWhenAppIsReady(() => {    wxMain(); diff --git a/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts b/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts index 06ae84593..727d653af 100644 --- a/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts +++ b/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts @@ -20,21 +20,21 @@ import { platform } from "../platform/api.js";  import { ToggleHandler } from "../mui/handlers.js";  import { TalerError } from "@gnu-taler/taler-wallet-core"; -export function useExtendedPermissions(): ToggleHandler { +export function useAutoOpenPermissions(): ToggleHandler {    const [enabled, setEnabled] = useState(false);    const [error, setError] = useState<TalerError | undefined>();    const toggle = async (): Promise<void> => { -    return handleExtendedPerm(enabled, setEnabled).catch((e) => { +    return handleAutoOpenPerm(enabled, setEnabled).catch((e) => {        setError(TalerError.fromException(e));      });    };    useEffect(() => { -    async function getExtendedPermValue(): Promise<void> { +    async function getValue(): Promise<void> {        const res = await wxApi.containsHeaderListener();        setEnabled(res.newValue);      } -    getExtendedPermValue(); +    getValue();    }, []);    return {      value: enabled, @@ -45,7 +45,7 @@ export function useExtendedPermissions(): ToggleHandler {    };  } -async function handleExtendedPerm( +async function handleAutoOpenPerm(    isEnabled: boolean,    onChange: (value: boolean) => void,  ): Promise<void> { diff --git a/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts b/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts new file mode 100644 index 000000000..c69b116b7 --- /dev/null +++ b/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts @@ -0,0 +1,73 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE.  See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> + */ + +import { useState, useEffect } from "preact/hooks"; +import * as wxApi from "../wxApi.js"; +import { platform } from "../platform/api.js"; +import { ToggleHandler } from "../mui/handlers.js"; +import { TalerError } from "@gnu-taler/taler-wallet-core"; + +export function useClipboardPermissions(): ToggleHandler { +  const [enabled, setEnabled] = useState(false); +  const [error, setError] = useState<TalerError | undefined>(); +  const toggle = async (): Promise<void> => { +    return handleClipboardPerm(enabled, setEnabled).catch((e) => { +      setError(TalerError.fromException(e)); +    }); +  }; + +  useEffect(() => { +    async function getValue(): Promise<void> { +      const res = await wxApi.containsHeaderListener(); +      setEnabled(res.newValue); +    } +    getValue(); +  }, []); + +  return { +    value: enabled, +    button: { +      onClick: toggle, +      error, +    }, +  }; +} + +async function handleClipboardPerm( +  isEnabled: boolean, +  onChange: (value: boolean) => void, +): Promise<void> { +  if (!isEnabled) { +    // We set permissions here, since apparently FF wants this to be done +    // as the result of an input event ... +    let granted: boolean; +    try { +      granted = await platform.getPermissionsApi().requestClipboardPermissions(); +    } catch (lastError) { +      onChange(false); +      throw lastError; +    } +    // const res = await wxApi.toggleHeaderListener(granted); +    onChange(granted); +  } else { +    try { +      await wxApi.toggleHeaderListener(false).then((r) => onChange(r.newValue)); +    } catch (e) { +      console.log(e); +    } +  } +  return; +} diff --git a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts index e1b08278b..75a92fd3c 100644 --- a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts +++ b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts @@ -31,7 +31,6 @@ export function useTalerActionURL(): [    );    const [dismissed, setDismissed] = useState(false);    const { findTalerUriInActiveTab, findTalerUriInClipboard } = useIocContext(); -    useEffect(() => {      async function check(): Promise<void> {        const clipUri = await findTalerUriInClipboard(); @@ -52,7 +51,7 @@ export function useTalerActionURL(): [        }      }      check(); -  }, [setTalerActionUrl]); +  }, []);    const url = dismissed ? undefined : talerActionUrl;    return [url, setDismissed]; diff --git a/packages/taler-wallet-webextension/src/platform/api.ts b/packages/taler-wallet-webextension/src/platform/api.ts index 23fd80ed7..da257c25f 100644 --- a/packages/taler-wallet-webextension/src/platform/api.ts +++ b/packages/taler-wallet-webextension/src/platform/api.ts @@ -37,6 +37,10 @@ export interface CrossBrowserPermissionsApi {    requestHostPermissions(): Promise<boolean>;    removeHostPermissions(): Promise<boolean>; +  containsClipboardPermissions(): Promise<boolean>; +  requestClipboardPermissions(): Promise<boolean>; +  removeClipboardPermissions(): Promise<boolean>; +    addPermissionsListener(      callback: (p: Permissions, lastError?: string) => void,    ): void; diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts b/packages/taler-wallet-webextension/src/platform/chrome.ts index 7311354c9..75900882f 100644 --- a/packages/taler-wallet-webextension/src/platform/chrome.ts +++ b/packages/taler-wallet-webextension/src/platform/chrome.ts @@ -77,6 +77,18 @@ const hostPermissions = {    origins: ["http://*/*", "https://*/*"],  }; +export function containsClipboardPermissions(): Promise<boolean> { +  return new Promise((res, rej) => { +    chrome.permissions.contains({ permissions: ["clipboardRead"] }, (resp) => { +      const le = chrome.runtime.lastError?.message; +      if (le) { +        rej(le); +      } +      res(resp); +    }); +  }); +} +  export function containsHostPermissions(): Promise<boolean> {    return new Promise((res, rej) => {      chrome.permissions.contains(hostPermissions, (resp) => { @@ -89,6 +101,18 @@ export function containsHostPermissions(): Promise<boolean> {    });  } +export async function requestClipboardPermissions(): Promise<boolean> { +  return new Promise((res, rej) => { +    chrome.permissions.request({ permissions: ["clipboardRead"] }, (resp) => { +      const le = chrome.runtime.lastError?.message; +      if (le) { +        rej(le); +      } +      res(resp); +    }) +  }); +} +  export async function requestHostPermissions(): Promise<boolean> {    return new Promise((res, rej) => {      chrome.permissions.request(hostPermissions, (resp) => { @@ -155,6 +179,18 @@ export async function removeHostPermissions(): Promise<boolean> {    });  } +export function removeClipboardPermissions(): Promise<boolean> { +  return new Promise((res, rej) => { +    chrome.permissions.remove({ permissions: ["clipboardRead"] }, (resp) => { +      const le = chrome.runtime.lastError?.message; +      if (le) { +        rej(le); +      } +      res(resp); +    }); +  }); +} +  function addPermissionsListener(    callback: (p: Permissions, lastError?: string) => void,  ): void { @@ -170,6 +206,9 @@ function getPermissionsApi(): CrossBrowserPermissionsApi {      containsHostPermissions,      requestHostPermissions,      removeHostPermissions, +    requestClipboardPermissions, +    removeClipboardPermissions, +    containsClipboardPermissions,    };  } @@ -382,11 +421,9 @@ function registerTalerHeaderListener(    }    async function tabListener(tabId: number, info: chrome.tabs.TabChangeInfo): Promise<void> { -    console.log("tab update", tabId, info)      if (tabId < 0) return;      if (info.status !== "complete") return;      const uri = await findTalerUriInTab(tabId); -    console.log("uri", uri)      if (!uri) return;      logger.info(`Found a Taler URI in the tab ${tabId}`)      callback(tabId, uri) @@ -585,7 +622,6 @@ async function registerIconChangeOnTalerContent(): Promise<void> {    chrome.tabs.onUpdated.addListener(      async (tabId, info: chrome.tabs.TabChangeInfo) => {        if (tabId < 0) return; -      logger.info("tab updated", tabId, info);        if (info.status !== "complete") return;        const uri = await findTalerUriInTab(tabId);        if (uri) { @@ -690,9 +726,22 @@ async function findTalerUriInTab(tabId: number): Promise<string | undefined> {    }  } +async function timeout(ms: number): Promise<void> { +  return new Promise(resolve => setTimeout(resolve, ms)); +}  async function findTalerUriInClipboard(): Promise<string | undefined> { -  const textInClipboard = await window.navigator.clipboard.readText(); -  return textInClipboard.startsWith("taler://") || textInClipboard.startsWith("taler+http://") ? textInClipboard : undefined +  try { +    //It looks like clipboard promise does not return, so we need a timeout +    const textInClipboard = await Promise.any([ +      timeout(100), +      window.navigator.clipboard.readText() +    ]) +    if (!textInClipboard) return; +    return textInClipboard.startsWith("taler://") || textInClipboard.startsWith("taler+http://") ? textInClipboard : undefined +  } catch (e) { +    logger.error("could not read clipboard", e) +    return undefined +  }  }  async function findTalerUriInActiveTab(): Promise<string | undefined> { diff --git a/packages/taler-wallet-webextension/src/platform/dev.ts b/packages/taler-wallet-webextension/src/platform/dev.ts index bb7e181c4..8a410b062 100644 --- a/packages/taler-wallet-webextension/src/platform/dev.ts +++ b/packages/taler-wallet-webextension/src/platform/dev.ts @@ -32,6 +32,9 @@ const api: PlatformAPI = {      containsHostPermissions: async () => true,      removeHostPermissions: async () => false,      requestHostPermissions: async () => false, +    containsClipboardPermissions: async () => true, +    removeClipboardPermissions: async () => false, +    requestClipboardPermissions: async () => false,    }),    getWalletWebExVersion: () => ({      version: "none", diff --git a/packages/taler-wallet-webextension/src/platform/firefox.ts b/packages/taler-wallet-webextension/src/platform/firefox.ts index a56e21f24..943168956 100644 --- a/packages/taler-wallet-webextension/src/platform/firefox.ts +++ b/packages/taler-wallet-webextension/src/platform/firefox.ts @@ -16,9 +16,12 @@  import { CrossBrowserPermissionsApi, Permissions, PlatformAPI } from "./api.js";  import chromePlatform, { -  containsHostPermissions as chromeContains, -  removeHostPermissions as chromeRemove, -  requestHostPermissions as chromeRequest, +  containsHostPermissions as chromeHostContains, +  removeHostPermissions as chromeHostRemove, +  requestHostPermissions as chromeHostRequest, +  containsClipboardPermissions as chromeClipContains, +  removeClipboardPermissions as chromeClipRemove, +  requestClipboardPermissions as chromeClipRequest,  } from "./chrome.js";  const api: PlatformAPI = { @@ -43,9 +46,12 @@ function addPermissionsListener(callback: (p: Permissions) => void): void {  function getPermissionsApi(): CrossBrowserPermissionsApi {    return {      addPermissionsListener, -    containsHostPermissions: chromeContains, -    requestHostPermissions: chromeRequest, -    removeHostPermissions: chromeRemove, +    containsHostPermissions: chromeHostContains, +    requestHostPermissions: chromeHostRequest, +    removeHostPermissions: chromeHostRemove, +    containsClipboardPermissions: chromeClipContains, +    removeClipboardPermissions: chromeClipRemove, +    requestClipboardPermissions: chromeClipRequest,    };  } diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx index 9e85a9bed..d0707952f 100644 --- a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx @@ -46,21 +46,24 @@ const version = {  export const AllOff = createExample(TestedComponent, {    deviceName: "this-is-the-device-name", -  permissionToggle: { value: false, button: {} }, +  autoOpenToggle: { value: false, button: {} }, +  clipboardToggle: { value: false, button: {} },    setDeviceName: () => Promise.resolve(),    ...version,  });  export const OneChecked = createExample(TestedComponent, {    deviceName: "this-is-the-device-name", -  permissionToggle: { value: false, button: {} }, +  autoOpenToggle: { value: false, button: {} }, +  clipboardToggle: { value: false, button: {} },    setDeviceName: () => Promise.resolve(),    ...version,  });  export const WithOneExchange = createExample(TestedComponent, {    deviceName: "this-is-the-device-name", -  permissionToggle: { value: false, button: {} }, +  autoOpenToggle: { value: false, button: {} }, +  clipboardToggle: { value: false, button: {} },    setDeviceName: () => Promise.resolve(),    knownExchanges: [      { @@ -80,7 +83,8 @@ export const WithOneExchange = createExample(TestedComponent, {  export const WithExchangeInDifferentState = createExample(TestedComponent, {    deviceName: "this-is-the-device-name", -  permissionToggle: { value: false, button: {} }, +  autoOpenToggle: { value: false, button: {} }, +  clipboardToggle: { value: false, button: {} },    setDeviceName: () => Promise.resolve(),    knownExchanges: [      { diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.tsx index 4a520c3bb..5219bbb38 100644 --- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx @@ -33,17 +33,19 @@ import { useDevContext } from "../context/devContext.js";  import { useTranslationContext } from "../context/translation.js";  import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";  import { useBackupDeviceName } from "../hooks/useBackupDeviceName.js"; -import { useExtendedPermissions } from "../hooks/useExtendedPermissions.js"; +import { useAutoOpenPermissions } from "../hooks/useAutoOpenPermissions.js";  import { ToggleHandler } from "../mui/handlers.js";  import { Pages } from "../NavigationBar.js";  import { buildTermsOfServiceStatus } from "../utils/index.js";  import * as wxApi from "../wxApi.js";  import { platform } from "../platform/api.js"; +import { useClipboardPermissions } from "../hooks/useClipboardPermissions.js";  const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;  export function SettingsPage(): VNode { -  const permissionToggle = useExtendedPermissions(); +  const autoOpenToggle = useAutoOpenPermissions(); +  const clipboardToggle = useClipboardPermissions();    const { devMode, toggleDevMode } = useDevContext();    const { name, update } = useBackupDeviceName();    const webex = platform.getWalletWebExVersion(); @@ -63,7 +65,8 @@ export function SettingsPage(): VNode {        knownExchanges={exchanges}        deviceName={name}        setDeviceName={update} -      permissionToggle={permissionToggle} +      autoOpenToggle={autoOpenToggle} +      clipboardToggle={clipboardToggle}        developerMode={devMode}        toggleDeveloperMode={toggleDevMode}        webexVersion={{ @@ -78,7 +81,8 @@ export function SettingsPage(): VNode {  export interface ViewProps {    deviceName: string;    setDeviceName: (s: string) => Promise<void>; -  permissionToggle: ToggleHandler; +  autoOpenToggle: ToggleHandler; +  clipboardToggle: ToggleHandler;    developerMode: boolean;    toggleDeveloperMode: () => Promise<void>;    knownExchanges: Array<ExchangeListItem>; @@ -91,7 +95,8 @@ export interface ViewProps {  export function SettingsView({    knownExchanges, -  permissionToggle, +  autoOpenToggle, +  clipboardToggle,    developerMode,    coreVersion,    webexVersion, @@ -102,10 +107,16 @@ export function SettingsView({    return (      <Fragment>        <section> -        {permissionToggle.button.error && ( +        {autoOpenToggle.button.error && (            <ErrorTalerOperation              title={<i18n.Translate>Could not toggle auto-open</i18n.Translate>} -            error={permissionToggle.button.error.errorDetail} +            error={autoOpenToggle.button.error.errorDetail} +          /> +        )} +        {clipboardToggle.button.error && ( +          <ErrorTalerOperation +            title={<i18n.Translate>Could not toggle clipboard</i18n.Translate>} +            error={clipboardToggle.button.error.errorDetail}            />          )}          <SubTitle> @@ -117,15 +128,31 @@ export function SettingsView({                Automatically open wallet based on page content              </i18n.Translate>            } -          name="perm" +          name="autoOpen" +          description={ +            <i18n.Translate> +              Enabling this option below will make using the wallet faster, but +              requires more permissions from your browser. +            </i18n.Translate> +          } +          enabled={autoOpenToggle.value!} +          onToggle={autoOpenToggle.button.onClick!} +        /> +        <Checkbox +          label={ +            <i18n.Translate> +              Automatically check clipboard for Taler URI +            </i18n.Translate> +          } +          name="clipboard"            description={              <i18n.Translate>                Enabling this option below will make using the wallet faster, but                requires more permissions from your browser.              </i18n.Translate>            } -          enabled={permissionToggle.value!} -          onToggle={permissionToggle.button.onClick!} +          enabled={clipboardToggle.value!} +          onToggle={clipboardToggle.button.onClick!}          />          <SubTitle> diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx index 0f327640e..659a6c2cf 100644 --- a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx @@ -26,12 +26,12 @@ import { Checkbox } from "../components/Checkbox.js";  import { SubTitle, Title } from "../components/styled/index.js";  import { useTranslationContext } from "../context/translation.js";  import { useDiagnostics } from "../hooks/useDiagnostics.js"; -import { useExtendedPermissions } from "../hooks/useExtendedPermissions.js"; +import { useAutoOpenPermissions } from "../hooks/useAutoOpenPermissions.js";  import { ToggleHandler } from "../mui/handlers.js";  import { platform } from "../platform/api.js";  export function WelcomePage(): VNode { -  const permissionToggle = useExtendedPermissions(); +  const permissionToggle = useAutoOpenPermissions();    const [diagnostics, timedOut] = useDiagnostics();    return (      <View diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts b/packages/taler-wallet-webextension/src/wxBackend.ts index 0835aae12..60b250453 100644 --- a/packages/taler-wallet-webextension/src/wxBackend.ts +++ b/packages/taler-wallet-webextension/src/wxBackend.ts @@ -330,11 +330,21 @@ export async function wxMain(): Promise<void> {    platform.registerAllIncomingConnections();    try { -    platform.registerTalerHeaderListener(parseTalerUriAndRedirect); +    platform.registerOnInstalled(() => { +      platform.openWalletPage("/welcome"); + +      // +      try { +        platform.registerTalerHeaderListener(parseTalerUriAndRedirect); +      } catch (e) { +        logger.error("could not register header listener", e); +      } +    });    } catch (e) { -    logger.error("could not register header listener", e); +    console.error(e);    } +    // On platforms that support it, also listen to external    // modification of permissions.    platform.getPermissionsApi().addPermissionsListener((perm, lastError) => {  | 
