From 353923b1707812dfec878c5e73b711316e2b5b49 Mon Sep 17 00:00:00 2001 From: Perry Mitchell Date: Wed, 27 Mar 2024 15:47:51 +0200 Subject: [PATCH 1/3] Bump version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9f0dfaaf..e4a8ffd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "buttercup-browser-extension", - "version": "3.0.0", + "version": "3.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "buttercup-browser-extension", - "version": "3.0.0", + "version": "3.1.0", "license": "MIT", "devDependencies": { "@babel/core": "^7.23.3", diff --git a/package.json b/package.json index e4bb0578..2e61aba7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "buttercup-browser-extension", - "version": "3.0.0", + "version": "3.1.0", "description": "Buttercup browser extension", "exports": "./dist/background/index.js", "type": "module", From c10d1333c6fd74168c7c2d49212f83c99dca3a9b Mon Sep 17 00:00:00 2001 From: Perry Mitchell Date: Tue, 2 Apr 2024 22:09:48 +0300 Subject: [PATCH 2/3] Add input button setting to popup --- package-lock.json | 35 +++++++++++++ package.json | 1 + source/background/services/config.ts | 3 +- .../popup/components/pages/SettingsPage.tsx | 50 ++++++++++++++++++- source/shared/i18n/translations/en.json | 6 +++ source/shared/library/version.ts | 4 +- source/shared/styles/base.sass | 1 + source/shared/types.ts | 6 +++ 8 files changed, 102 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index e4a8ffd7..d6f68bd0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@blueprintjs/core": "^4.20.2", "@blueprintjs/icons": "^4.16.0", "@blueprintjs/popover2": "^1.14.11", + "@blueprintjs/select": "^4.9.24", "@buttercup/channel-queue": "^1.4.0", "@buttercup/locust": "^2.2.1", "@buttercup/ui": "^6.2.2", @@ -1951,6 +1952,28 @@ "react-dom": "^16.8.0 || ^17 || ^18" } }, + "node_modules/@blueprintjs/select": { + "version": "4.9.24", + "resolved": "https://registry.npmjs.org/@blueprintjs/select/-/select-4.9.24.tgz", + "integrity": "sha512-OTjesxH/7UZvM7yAdHJ5u3sIjX1N8Rs4CQQ22AfqNl82SIROqkuXI31XEl6YNX1GsYfmAMiw0K7XohEKOMXR5g==", + "dev": true, + "dependencies": { + "@blueprintjs/core": "^4.20.2", + "@blueprintjs/popover2": "^1.14.11", + "classnames": "^2.3.1", + "tslib": "~2.5.0" + }, + "peerDependencies": { + "@types/react": "^16.14.32 || 17 || 18", + "react": "^16.8 || 17 || 18", + "react-dom": "^16.8 || 17 || 18" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@buttercup/channel-queue": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@buttercup/channel-queue/-/channel-queue-1.4.0.tgz", @@ -16137,6 +16160,18 @@ } } }, + "@blueprintjs/select": { + "version": "4.9.24", + "resolved": "https://registry.npmjs.org/@blueprintjs/select/-/select-4.9.24.tgz", + "integrity": "sha512-OTjesxH/7UZvM7yAdHJ5u3sIjX1N8Rs4CQQ22AfqNl82SIROqkuXI31XEl6YNX1GsYfmAMiw0K7XohEKOMXR5g==", + "dev": true, + "requires": { + "@blueprintjs/core": "^4.20.2", + "@blueprintjs/popover2": "^1.14.11", + "classnames": "^2.3.1", + "tslib": "~2.5.0" + } + }, "@buttercup/channel-queue": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@buttercup/channel-queue/-/channel-queue-1.4.0.tgz", diff --git a/package.json b/package.json index 2e61aba7..a9221265 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@blueprintjs/core": "^4.20.2", "@blueprintjs/icons": "^4.16.0", "@blueprintjs/popover2": "^1.14.11", + "@blueprintjs/select": "^4.9.24", "@buttercup/channel-queue": "^1.4.0", "@buttercup/locust": "^2.2.1", "@buttercup/ui": "^6.2.2", diff --git a/source/background/services/config.ts b/source/background/services/config.ts index a0e34259..aff91eda 100644 --- a/source/background/services/config.ts +++ b/source/background/services/config.ts @@ -1,8 +1,9 @@ import { getSyncValue, setSyncValue } from "./storage.js"; -import { Configuration, SyncStorageItem } from "../types.js"; +import { Configuration, InputButtonType, SyncStorageItem } from "../types.js"; const DEFAULTS: Configuration = { entryIcons: true, + inputButtonDefault: InputButtonType.LargeButton, saveNewLogins: true, theme: "light", useSystemTheme: true diff --git a/source/popup/components/pages/SettingsPage.tsx b/source/popup/components/pages/SettingsPage.tsx index 1072cd07..ae140ca8 100644 --- a/source/popup/components/pages/SettingsPage.tsx +++ b/source/popup/components/pages/SettingsPage.tsx @@ -1,6 +1,7 @@ import React, { Fragment, useCallback, useMemo, useState } from "react"; import styled from "styled-components"; -import { Alert, Button, Callout, Classes, Intent, Switch } from "@blueprintjs/core"; +import { Alert, Button, Callout, Classes, Intent, MenuItem, Switch } from "@blueprintjs/core"; +import { ItemRendererProps, Select } from "@blueprintjs/select"; import { t } from "../../../shared/i18n/trans.js"; import { useConfig } from "../../../shared/hooks/config.js"; import { ErrorMessage } from "../../../shared/components/ErrorMessage.js"; @@ -9,6 +10,11 @@ import { getToaster } from "../../../shared/services/notifications.js"; import { localisedErrorMessage } from "../../../shared/library/error.js"; import { useAllLoginCredentials } from "../../hooks/credentials.js"; import { createNewTab, getExtensionURL } from "../../../shared/library/extension.js"; +import { InputButtonType } from "../../types.js"; + +interface InputButtonTypeItem { + name: string, type: InputButtonType; +} const Container = styled.div` display: flex; @@ -26,11 +32,37 @@ const SettingSection = styled(Callout)` padding: 9px; `; +function renderInputButtonTypeItem(item: InputButtonTypeItem, props: ItemRendererProps) { + const { handleClick, handleFocus, modifiers } = props; + return ( + + ); +} + export function SettingsPage() { const [config, configError, setValue] = useConfig(); const [showConfirmReset, setShowConfirmReset] = useState(false); const { value: allCredentials } = useAllLoginCredentials(); const hasSavedCredentials = useMemo(() => Array.isArray(allCredentials) && allCredentials.length > 0, [allCredentials]); + const inputButtonItems: Array = useMemo(() => Object.values(InputButtonType).map(type => ({ + name: t(`config.input-button-type.${type}`), + type + })), []); + const activeInputButtonItem = useMemo( + () => inputButtonItems.find(item => item.type === config?.inputButtonDefault), + [config, inputButtonItems]); + const handleInputButtonItemSelect = useCallback((item: InputButtonTypeItem) => { + setValue("inputButtonDefault", item.type); + }, [setValue]); const handleOpenDisabledDomains = useCallback(async () => { try { await createNewTab(getExtensionURL("full.html#/disabled-domains")); @@ -110,6 +142,22 @@ export function SettingsPage() { )} + + + ; export interface Configuration { entryIcons: boolean; + inputButtonDefault: InputButtonType; saveNewLogins: boolean; theme: "light" | "dark"; useSystemTheme: boolean; @@ -107,6 +108,11 @@ export interface ElementRect { height: number; } +export enum InputButtonType { + InnerIcon = "innericon", + LargeButton = "largebutton" +} + export enum InputType { OTP = "otp", UserPassword = "user-password" From 30c33ce2fb269a2e101fc4b9a9bec3a868d66c5f Mon Sep 17 00:00:00 2001 From: Perry Mitchell Date: Tue, 2 Apr 2024 22:18:46 +0300 Subject: [PATCH 3/3] Connect input btn pref for tab launcher --- source/tab/services/config.ts | 13 +++++++++++++ source/tab/services/form.ts | 2 +- source/tab/services/formDetection.ts | 16 ++++++++++++---- source/tab/ui/launch.ts | 22 ++++++++++++++++++---- 4 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 source/tab/services/config.ts diff --git a/source/tab/services/config.ts b/source/tab/services/config.ts new file mode 100644 index 00000000..99b7b386 --- /dev/null +++ b/source/tab/services/config.ts @@ -0,0 +1,13 @@ +import { Layerr } from "layerr"; +import { sendBackgroundMessage } from "../../shared/services/messaging.js"; +import { BackgroundMessageType, Configuration } from "../types.js"; + +export async function getConfig(): Promise { + const resp = await sendBackgroundMessage({ + type: BackgroundMessageType.GetConfiguration + }); + if (resp.error) { + throw new Layerr(resp.error, "Failed fetching configuration"); + } + return resp.config; +} diff --git a/source/tab/services/form.ts b/source/tab/services/form.ts index 667eb787..f349e246 100644 --- a/source/tab/services/form.ts +++ b/source/tab/services/form.ts @@ -27,7 +27,7 @@ export function fillFormDetails(frameEvent: FrameEvent) { export async function initialise() { // Watch for forms - waitAndAttachLaunchButtons((input, loginTarget, inputType) => { + await waitAndAttachLaunchButtons((input, loginTarget, inputType) => { FORM.currentFormID = ulid(); FORM.currentLoginTarget = loginTarget; if (FRAME.isTop) { diff --git a/source/tab/services/formDetection.ts b/source/tab/services/formDetection.ts index 5668dda4..eb78ada2 100644 --- a/source/tab/services/formDetection.ts +++ b/source/tab/services/formDetection.ts @@ -3,6 +3,7 @@ import { attachLaunchButton } from "../ui/launch.js"; import { watchCredentialsOnTarget } from "./logins/watcher.js"; import { processTargetAutoLogin } from "./autoLogin.js"; import { InputType } from "../types.js"; +import { getConfig } from "./config.js"; const TARGET_SEARCH_INTERVAL = 1000; @@ -28,19 +29,26 @@ function onIdentifiedTarget(callback: (target: LoginTarget) => void) { }; } -export function waitAndAttachLaunchButtons( +export async function waitAndAttachLaunchButtons( onInputActivate: (input: HTMLInputElement, loginTarget: LoginTarget, inputType: InputType) => void ) { + const config = await getConfig(); onIdentifiedTarget((loginTarget: LoginTarget) => { const { otpField, usernameField, passwordField } = loginTarget; if (otpField) { - attachLaunchButton(otpField, (el) => onInputActivate(el, loginTarget, InputType.OTP)); + attachLaunchButton(otpField, config.inputButtonDefault, (el) => + onInputActivate(el, loginTarget, InputType.OTP) + ); } if (passwordField) { - attachLaunchButton(passwordField, (el) => onInputActivate(el, loginTarget, InputType.UserPassword)); + attachLaunchButton(passwordField, config.inputButtonDefault, (el) => + onInputActivate(el, loginTarget, InputType.UserPassword) + ); } if (usernameField) { - attachLaunchButton(usernameField, (el) => onInputActivate(el, loginTarget, InputType.UserPassword)); + attachLaunchButton(usernameField, config.inputButtonDefault, (el) => + onInputActivate(el, loginTarget, InputType.UserPassword) + ); } watchCredentialsOnTarget(loginTarget); processTargetAutoLogin(loginTarget).catch(console.error); diff --git a/source/tab/ui/launch.ts b/source/tab/ui/launch.ts index 4991eadf..5ead1f77 100644 --- a/source/tab/ui/launch.ts +++ b/source/tab/ui/launch.ts @@ -6,11 +6,16 @@ import { onBodyWidthResize } from "../library/resize.js"; import { getExtensionURL } from "../../shared/library/extension.js"; import BUTTON_BACKGROUND_IMAGE_RES from "../../../resources/content-button-background.png"; import INPUT_BACKGROUND_IMAGE_RES from "../../../resources/buttercup-simple-150.png"; +import { InputButtonType } from "../types.js"; const BUTTON_BACKGROUND_IMAGE = getExtensionURL(BUTTON_BACKGROUND_IMAGE_RES); const INPUT_BACKGROUND_IMAGE = getExtensionURL(INPUT_BACKGROUND_IMAGE_RES); -export function attachLaunchButton(input: HTMLInputElement, onClick: (input: HTMLInputElement) => void) { +export function attachLaunchButton( + input: HTMLInputElement, + buttonType: InputButtonType, + onClick: (input: HTMLInputElement) => void +): void { if (input.dataset.bcup === "attached" || itemIsIgnored(input)) { return; } @@ -24,8 +29,11 @@ export function attachLaunchButton(input: HTMLInputElement, onClick: (input: HTM setTimeout(tryToAttach, 250); return; } - renderButtonStyle(input, () => onClick(input), tryToAttach, bounds); - // renderInternalStyle(input, () => onClick(input), tryToAttach, bounds); + if (buttonType === InputButtonType.LargeButton) { + renderButtonStyle(input, () => onClick(input), tryToAttach, bounds); + } else if (buttonType === InputButtonType.InnerIcon) { + renderInternalStyle(input, () => onClick(input), tryToAttach, bounds); + } }; tryToAttach(); } @@ -41,6 +49,7 @@ function renderInternalStyle( const imageSize = height * 0.6; const rightOffset = 8; const buttonArea = imageSize + rightOffset + 4; + const originalAutocomplete = input.getAttribute("autocomplete") ?? null; setStyle(input, { backgroundImage: `url(${INPUT_BACKGROUND_IMAGE})`, backgroundSize: `${imageSize}px`, @@ -52,16 +61,21 @@ function renderInternalStyle( if (event.offsetX >= input.offsetWidth - buttonArea) { event.preventDefault(); event.stopPropagation(); - // toggleInputDialog(input, DIALOG_TYPE_ENTRY_PICKER); onClick(); } }; input.onmousemove = (event) => { if (event.offsetX >= input.offsetWidth - buttonArea) { + input.setAttribute("autocomplete", "off"); setStyle(input, { cursor: "pointer" }); } else { + if (originalAutocomplete) { + input.setAttribute("autocomplete", originalAutocomplete); + } else { + input.removeAttribute("autocomplete"); + } setStyle(input, { cursor: "unset" });