Skip to content

Commit

Permalink
Implement copying password to clipboard
Browse files Browse the repository at this point in the history
  • Loading branch information
au2001 committed Oct 29, 2023
1 parent a363d76 commit d54f95e
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 6 deletions.
9 changes: 8 additions & 1 deletion meta/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@
"32": "./images/PasswordsExtensionIcon_32.png",
"128": "./images/PasswordsExtensionIcon_128.png"
},
"permissions": ["nativeMessaging", "activeTab", "scripting"],
"permissions": [
"nativeMessaging",
"activeTab",
"scripting",
"clipboardWrite"
],
"optional_permissions": ["tabs"],
"host_permissions": ["*://*/*"],
"background": {
"scripts": ["./background.js"]
},
Expand Down
15 changes: 14 additions & 1 deletion src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ browser.runtime.onMessage.addListener(async (message, sender) => {
message.loginName,
);

case "AUTO_FILL_PASSWORD":
case "AUTO_FILL_PASSWORD": {
const { username, password } = await getAPI().getPasswordForLoginName(
message.tabId,
message.url,
Expand Down Expand Up @@ -73,6 +73,19 @@ browser.runtime.onMessage.addListener(async (message, sender) => {
if (errors.length !== 0) throw errors.length === 1 ? errors[0] : errors;

return true;
}

case "COPY_PASSWORD": {
const { password } = await getAPI().getPasswordForLoginName(
message.tabId,
message.url,
message.loginName,
);

await navigator.clipboard.writeText(password);

return true;
}
}
} catch (e) {
console.error(e);
Expand Down
41 changes: 41 additions & 0 deletions src/popup/icons/copy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
interface Props {
title?: string;
desc?: string;
}

export function CopyIcon({
title,
desc,
...props
}: Props & React.SVGAttributes<SVGSVGElement>) {
const uuid = crypto.randomUUID().replace(/-/g, "");

return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
aria-labelledby={`${uuid}_title ${uuid}_desc`}
role="img"
{...props}
>
<title id={uuid}>{title}</title>
<desc id={uuid}>{desc}</desc>
<path
d="M2.41667 6.33333V13C2.41667 13.598 2.90201 14.0833 3.50001 14.0833H8.83334C9.43134 14.0833 9.91667 13.598 9.91667 13V6.33333C9.91667 5.73533 9.43134 5.25 8.83334 5.25H3.50001C2.90201 5.25 2.41667 5.73533 2.41667 6.33333Z"
stroke-width="1.5"
stroke-miterlimit="10"
stroke-linejoin="round"
/>
<path
d="M11.5 11.4167C12.098 11.4167 12.5833 10.9313 12.5833 10.3333V3.66667C12.5833 3.06867 12.098 2.58333 11.5 2.58333H6.16666C5.56866 2.58333 5.08333 3.06867 5.08333 3.66667"
stroke-width="1.5"
stroke-miterlimit="10"
stroke-linejoin="round"
/>
</svg>
);
}
18 changes: 17 additions & 1 deletion src/popup/icons/key.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
export function KeyIcon() {
interface Props {
title?: string;
desc?: string;
}

export function KeyIcon({
title,
desc,
...props
}: Props & React.SVGAttributes<SVGSVGElement>) {
const uuid = crypto.randomUUID().replace(/-/g, "");

return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="12"
height="23"
viewBox="0 0 12 23"
fill="currentColor"
aria-labelledby={`${uuid}_title ${uuid}_desc`}
role="img"
{...props}
>
<title id={uuid}>{title}</title>
<desc id={uuid}>{desc}</desc>
<path d="M5.98632812,22.9248047 C6.13606771,22.9345703 6.26953125,22.8808594 6.38671875,22.7636719 L9.296875,19.8535156 C9.4140625,19.7298177 9.47265625,19.593099 9.47265625,19.4433594 C9.47265625,19.2936198 9.4140625,19.1634115 9.296875,19.0527344 L7.56835938,17.3339844 L9.98046875,14.921875 C10.0846354,14.8177083 10.1367188,14.6907552 10.1367188,14.5410156 C10.1367188,14.391276 10.0748698,14.2545573 9.95117188,14.1308594 L7.59765625,11.7578125 C8.99739583,11.1783854 10.0732422,10.3841146 10.8251953,9.375 C11.5771484,8.36588542 11.953125,7.22981771 11.953125,5.96679688 C11.953125,5.14648438 11.8001302,4.375 11.4941406,3.65234375 C11.188151,2.9296875 10.7617188,2.29492188 10.2148438,1.74804688 C9.66796875,1.20117188 9.03320312,0.773111979 8.31054688,0.463867188 C7.58789062,0.154622396 6.80989583,0 5.9765625,0 C5.14322917,0 4.36523438,0.154622396 3.64257813,0.463867188 C2.91992188,0.773111979 2.28515625,1.20117188 1.73828125,1.74804688 C1.19140625,2.29492188 0.764973958,2.9280599 0.458984375,3.64746094 C0.152994792,4.36686198 0,5.13997396 0,5.96679688 C0,6.79361979 0.154622396,7.57486979 0.463867188,8.31054688 C0.773111979,9.04622396 1.2109375,9.69238281 1.77734375,10.2490234 C2.34375,10.8056641 3.01106771,11.233724 3.77929688,11.5332031 L3.77929688,20.5664063 C3.77929688,20.6901042 3.80045573,20.8056641 3.84277344,20.9130859 C3.88509115,21.0205078 3.95507812,21.1230469 4.05273438,21.2207031 L5.625,22.7734375 C5.71614583,22.8645833 5.83658854,22.9150391 5.98632812,22.9248047 Z M5.9765625,5.625 C5.53385417,5.625 5.15136719,5.4671224 4.82910156,5.15136719 C4.50683594,4.83561198 4.34570312,4.44986979 4.34570312,3.99414062 C4.34570312,3.54492188 4.50520833,3.16080729 4.82421875,2.84179688 C5.14322917,2.52278646 5.52734375,2.36328125 5.9765625,2.36328125 C6.43229167,2.36328125 6.81966146,2.52441406 7.13867188,2.84667969 C7.45768229,3.16894531 7.6171875,3.55143229 7.6171875,3.99414062 C7.6171875,4.44986979 7.45768229,4.83561198 7.13867188,5.15136719 C6.81966146,5.4671224 6.43229167,5.625 5.9765625,5.625 Z" />
</svg>
);
Expand Down
17 changes: 17 additions & 0 deletions src/popup/passwords.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,23 @@
}
}

> a {
display: flex;
padding: 4px 3px;
border-radius: 5px;

> svg {
color: $color-text-primary;
height: 16px;
width: 16px;
}

&:hover {
background-color: $color-background-3;
cursor: pointer;
}
}

&:hover {
background-color: $color-background-2;
cursor: pointer;
Expand Down
19 changes: 16 additions & 3 deletions src/popup/passwords.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useCurrentTab } from "./hooks";
import { LoadingView } from "./loading";
import { ErrorCode, ErrorView } from "./error";
import { KeyIcon } from "./icons/key";
import { CopyIcon } from "./icons/copy";
import styles from "./passwords.module.scss";

interface LoginName {
Expand Down Expand Up @@ -43,7 +44,10 @@ export function PasswordsView() {
fetchLoginNames(tab.id, tab.url);
}, [tab?.url]);

const handleAutoFillPassword = async (loginName: LoginName) => {
const handleAutoFillPassword = async (
loginName: LoginName,
action: "AUTO_FILL" | "COPY" = "AUTO_FILL",
) => {
if (tab?.id === undefined || tab?.url === undefined) return;

setError(undefined);
Expand All @@ -52,7 +56,7 @@ export function PasswordsView() {
// Can't use GET_PASSWORD_FOR_LOGIN_NAME here
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1292701
await browser.runtime.sendMessage({
cmd: "AUTO_FILL_PASSWORD",
cmd: `${action}_PASSWORD`,
tabId: tab.id,
url: tab.url,
loginName,
Expand Down Expand Up @@ -113,11 +117,20 @@ export function PasswordsView() {
handleAutoFillPassword(loginName);
}}
>
<KeyIcon />
<KeyIcon title="Password item" />
<div>
<span>{loginName.username}</span>
<span>{loginName.sites[0] ?? ""}</span>
</div>
<a
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleAutoFillPassword(loginName, "COPY");
}}
>
<CopyIcon title="Copy password to clipboard" />
</a>
</li>
))}
</ul>
Expand Down

0 comments on commit d54f95e

Please sign in to comment.