mirror of
https://github.com/XFox111/PasswordGeneratorExtension.git
synced 2026-04-22 08:08:01 +03:00
Major 3.0: Complete overhaul of the codebase, new features, bugfixes … (#224)
* Major 3.0: Complete overhaul of the codebase, new features, bugfixes and more (#223)
Complete overhaul for v3.0 (see related PR)
* Fix for workflows matrix
* Reverted PR workflow to manual node setup
* Revert "Reverted PR workflow to manual node setup"
This reverts commit 491afd58ab.
* Replaced web-ext lint action
* Workflow fix
* Deprecated autocopy feature
* Zero-day patch: Batch 1 (#231)
* Bump @types/react-dom from 18.2.8 to 18.2.15 (#230)
Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 18.2.8 to 18.2.15.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)
---
updated-dependencies:
- dependency-name: "@types/react-dom"
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump typescript from 4.9.5 to 5.2.2 (#228)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.9.5 to 5.2.2.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.9.5...v5.2.2)
---
updated-dependencies:
- dependency-name: typescript
dependency-type: direct:development
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump @fluentui/react-icons from 2.0.218 to 2.0.222 (#227)
Bumps [@fluentui/react-icons](https://github.com/microsoft/fluentui-system-icons) from 2.0.218 to 2.0.222.
- [Commits](https://github.com/microsoft/fluentui-system-icons/commits)
---
updated-dependencies:
- dependency-name: "@fluentui/react-icons"
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump dev-build-deploy/release-me from 0.14.0 to 0.15.0 (#225)
Bumps [dev-build-deploy/release-me](https://github.com/dev-build-deploy/release-me) from 0.14.0 to 0.15.0.
- [Release notes](https://github.com/dev-build-deploy/release-me/releases)
- [Changelog](https://github.com/dev-build-deploy/release-me/blob/main/docs/get-release.md)
- [Commits](https://github.com/dev-build-deploy/release-me/compare/v0.14.0...v0.15.0)
---
updated-dependencies:
- dependency-name: dev-build-deploy/release-me
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump sass from 1.69.0 to 1.69.5 (#229)
Bumps [sass](https://github.com/sass/dart-sass) from 1.69.0 to 1.69.5.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.69.0...1.69.5)
---
updated-dependencies:
- dependency-name: sass
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eugene Fox <eugene.xfox@outlook.com>
* Bump @types/webextension-polyfill from 0.10.4 to 0.10.6 (#226)
Bumps [@types/webextension-polyfill](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/webextension-polyfill) from 0.10.4 to 0.10.6.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/webextension-polyfill)
---
updated-dependencies:
- dependency-name: "@types/webextension-polyfill"
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eugene Fox <eugene.xfox@outlook.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Zero-day patch: Batch 2 (#236)
* Bump @fluentui/react-components from 9.34.0 to 9.39.0 (#235)
Bumps [@fluentui/react-components](https://github.com/microsoft/fluentui) from 9.34.0 to 9.39.0.
- [Release notes](https://github.com/microsoft/fluentui/releases)
- [Changelog](https://github.com/microsoft/fluentui/blob/master/azure-pipelines.release-fluentui.yml)
- [Commits](https://github.com/microsoft/fluentui/compare/@fluentui/react-components_v9.34.0...@fluentui/react-components_v9.39.0)
---
updated-dependencies:
- dependency-name: "@fluentui/react-components"
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump @vitejs/plugin-react-swc from 3.4.0 to 3.4.1 (#234)
Bumps [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/vitejs/vite-plugin-react-swc/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-react-swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-react-swc/compare/v3.4.0...v3.4.1)
---
updated-dependencies:
- dependency-name: "@vitejs/plugin-react-swc"
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump @types/react from 18.2.24 to 18.2.37 (#233)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.24 to 18.2.37.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)
---
updated-dependencies:
- dependency-name: "@types/react"
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump vite from 4.4.10 to 4.5.0 (#232)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.4.10 to 4.5.0.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.5.0/packages/vite)
---
updated-dependencies:
- dependency-name: vite
dependency-type: direct:development
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
import { makeStyles, shorthands, tokens } from "@fluentui/react-components";
|
||||
|
||||
export const useStyles = makeStyles({
|
||||
root:
|
||||
{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
...shorthands.gap(tokens.spacingVerticalM),
|
||||
paddingBottom: tokens.spacingVerticalS,
|
||||
},
|
||||
horizontalContainer:
|
||||
{
|
||||
display: "flex",
|
||||
...shorthands.gap(tokens.spacingHorizontalSNudge),
|
||||
},
|
||||
});
|
||||
@@ -1,67 +1,66 @@
|
||||
import { AccordionItem, AccordionHeader, AccordionPanel, Link, Text, Button } from "@fluentui/react-components";
|
||||
import * as fui from "@fluentui/react-components";
|
||||
import { InfoRegular, PersonFeedbackRegular } from "@fluentui/react-icons";
|
||||
import { ReactComponent as BuyMeACoffee } from "../Assets/BuyMeACoffee.svg";
|
||||
import React from "react";
|
||||
import { loc } from "../Utils/Localization";
|
||||
import * as Platform from "react-device-detect";
|
||||
import Package from "../../package.json";
|
||||
import BuyMeACoffee from "../Assets/BuyMeACoffee.svg?react";
|
||||
import { bmcDarkTheme, bmcLightTheme } from "../Data/BmcTheme";
|
||||
import { GetFeedbackLink, GithubLink, PersonalLink } from "../Data/Links";
|
||||
import { GetLocaleString as loc } from "../Utils/Localization";
|
||||
import { useTheme } from "../Utils/Theme";
|
||||
import { useStyles } from "./AboutSection.styles";
|
||||
|
||||
export default class AboutSection extends React.Component
|
||||
export default function AboutSection(): JSX.Element
|
||||
{
|
||||
public render(): JSX.Element
|
||||
{
|
||||
return (
|
||||
<AccordionItem value="about">
|
||||
<AccordionHeader as="h2" icon={ <InfoRegular /> }>{ loc("About") }</AccordionHeader>
|
||||
<AccordionPanel>
|
||||
<section className="stack gap fadeIn">
|
||||
<Text as="p">
|
||||
{ loc("Developed by Eugene Fox") } (<Link href="https://twitter.com/xfox111" target="_blank">@xfox111</Link>)
|
||||
<br />
|
||||
{ loc("Licensed under") } <Link href="https://github.com/XFox111/PasswordGeneratorExtension/blob/master/LICENSE" target="_blank">{ loc("MIT license") }</Link>
|
||||
</Text>
|
||||
<Text as="p">
|
||||
{ loc("Want to contribute translation for your language?") } <Link href="https://github.com/XFox111/PasswordGeneratorExtension/blob/master/CONTRIBUTING.md" target="_blank">{ loc("Read this to get started") }</Link>
|
||||
</Text>
|
||||
<Text as="p">
|
||||
<Link href="https://xfox111.net/" target="_blank">{ loc("My website") }</Link>
|
||||
<br />
|
||||
<Link href="https://github.com/xfox111/PasswordGeneratorExtension" target="_blank">{ loc("Source code") }</Link>
|
||||
<br />
|
||||
<Link href="https://github.com/XFox111/PasswordGeneratorExtension/releases/latest" target="_blank">{ loc("Changelog") }</Link>
|
||||
</Text>
|
||||
const theme = useTheme(bmcLightTheme, bmcDarkTheme);
|
||||
const cls = useStyles();
|
||||
|
||||
<div className="stack horizontal gap">
|
||||
<Button
|
||||
as="a" target="_blank"
|
||||
href={ this.GetFeedbackLink() }
|
||||
appearance="primary" icon={ <PersonFeedbackRegular /> }>
|
||||
const link = (text: string, href: string): JSX.Element => (
|
||||
<fui.Link target="_blank" href={ href }>{ text }</fui.Link>
|
||||
);
|
||||
|
||||
{ loc("Leave feedback") }
|
||||
</Button>
|
||||
const buttonProps = (href: string, icon: JSX.Element): fui.ButtonProps => (
|
||||
{
|
||||
as: "a", target: "_blank", href,
|
||||
appearance: "primary", icon
|
||||
}
|
||||
);
|
||||
|
||||
<Button
|
||||
as="a" target="_blank"
|
||||
href="https://buymeacoffee.com/xfox111"
|
||||
className="bmc" appearance="primary" icon={ <BuyMeACoffee /> }>
|
||||
return (
|
||||
<fui.AccordionItem value="about">
|
||||
<fui.AccordionHeader as="h2" icon={ <InfoRegular /> }>{ loc("about@title") }</fui.AccordionHeader>
|
||||
<fui.AccordionPanel className={ cls.root }>
|
||||
<header className={ cls.horizontalContainer }>
|
||||
<fui.Subtitle1 as="h1">{ loc("name") }</fui.Subtitle1>
|
||||
<fui.Caption1 as="span">v{ Package.version }</fui.Caption1>
|
||||
</header>
|
||||
|
||||
{ loc("Buy me a coffee") }
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
);
|
||||
}
|
||||
<fui.Text as="p">
|
||||
{ loc("about@developedBy") } ({ link("@xfox111", PersonalLink.Twitter) })
|
||||
<br />
|
||||
{ loc("about@licensedUnder") } { link(loc("about@mitLicense"), GithubLink.License) }
|
||||
</fui.Text>
|
||||
|
||||
private GetFeedbackLink(): string
|
||||
{
|
||||
if (Platform.isEdgeChromium)
|
||||
return "https://microsoftedge.microsoft.com/addons/detail/password-generator/manimdhobjbkfpeeehlhhneookiokpbj";
|
||||
else if (Platform.isChrome)
|
||||
return "https://chrome.google.com/webstore/detail/password-generator/jnjobgjobffgmgfnkpkjfjkkfhfikmfl";
|
||||
else if (Platform.isFirefox)
|
||||
return "https://addons.mozilla.org/en-US/firefox/addon/easy-password-generator";
|
||||
else
|
||||
return "mailto:feedback@xfox111.net";
|
||||
}
|
||||
<fui.Text as="p">
|
||||
{ loc("about@translationCta") }<br />
|
||||
{ link(loc("about@translationCtaButton"), GithubLink.TranslationGuide) }
|
||||
</fui.Text>
|
||||
|
||||
<fui.Text as="p">
|
||||
{ link(loc("about@website"), PersonalLink.Website) } <br />
|
||||
{ link(loc("about@sourceCode"), GithubLink.Repository) } <br />
|
||||
{ link(loc("about@changelog"), GithubLink.Changelog) }
|
||||
</fui.Text>
|
||||
|
||||
<div className={ cls.horizontalContainer }>
|
||||
<fui.Button { ...buttonProps(GetFeedbackLink(), <PersonFeedbackRegular />) }>
|
||||
{ loc("about@feedback") }
|
||||
</fui.Button>
|
||||
<fui.FluentProvider theme={ theme }>
|
||||
<fui.Button { ...buttonProps(PersonalLink.BuyMeACoffee, <BuyMeACoffee />) }>
|
||||
{ loc("about@sponsor") }
|
||||
</fui.Button>
|
||||
</fui.FluentProvider>
|
||||
</div>
|
||||
</fui.AccordionPanel>
|
||||
</fui.AccordionItem>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
import { Button, Text, Dialog, DialogTrigger, DialogSurface, DialogTitle, DialogBody, DialogActions, DialogContent, Table, TableHeader, TableRow, TableHeaderCell, TableBody, TableCell } from "@fluentui/react-components";
|
||||
import { QuestionCircleRegular } from "@fluentui/react-icons";
|
||||
import React from "react";
|
||||
import Generator from "../Utils/Generator";
|
||||
import { loc } from "../Utils/Localization";
|
||||
|
||||
export default class CharacterHelpDialog extends React.Component
|
||||
{
|
||||
public render(): JSX.Element
|
||||
{
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger>
|
||||
<Button appearance="subtle" style={ { marginLeft: 5 } } icon={ <QuestionCircleRegular /> } />
|
||||
</DialogTrigger>
|
||||
<DialogSurface aria-label="label">
|
||||
<DialogBody>
|
||||
<DialogTitle>{ loc("Character options") }</DialogTitle>
|
||||
<DialogContent>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHeaderCell>{ loc("Set_name") }</TableHeaderCell>
|
||||
<TableHeaderCell>{ loc("Characters") }</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell>{ loc("Lowercase") }</TableCell>
|
||||
<TableCell>
|
||||
<Text font="monospace">{ Generator.Lowercase.substring(0, 10) }{ loc(", etc.") }</Text>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>{ loc("Uppercase") }</TableCell>
|
||||
<TableCell>
|
||||
<Text font="monospace">{ Generator.Uppercase.substring(0, 10) }{ loc(", etc.") }</Text>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>{ loc("Numeric") }</TableCell>
|
||||
<TableCell>
|
||||
<Text font="monospace">{ Generator.Numeric }</Text>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>{ loc("Special symbols") }</TableCell>
|
||||
<TableCell>
|
||||
<Text font="monospace">{ Generator.SpecialCharacters }</Text>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>{ loc("Ambiguous") }</TableCell>
|
||||
<TableCell>
|
||||
<Text font="monospace">{ Generator.AmbiguousCharacters }</Text>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>{ loc("Similar") }</TableCell>
|
||||
<TableCell>
|
||||
<Text font="monospace">{ Generator.SimilarCharacters }</Text>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<DialogTrigger>
|
||||
<Button appearance="secondary">{ loc("OK") }</Button>
|
||||
</DialogTrigger>
|
||||
</DialogActions>
|
||||
</DialogBody>
|
||||
</DialogSurface>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { makeStyles, shorthands, tokens } from "@fluentui/react-components";
|
||||
|
||||
export const useStyles = makeStyles({
|
||||
root:
|
||||
{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
},
|
||||
input:
|
||||
{
|
||||
fontFamily: tokens.fontFamilyMonospace,
|
||||
},
|
||||
lengthContainer:
|
||||
{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr auto",
|
||||
alignItems: "center",
|
||||
paddingRight: tokens.spacingHorizontalM,
|
||||
},
|
||||
optionsSpacing:
|
||||
{
|
||||
...shorthands.padding(tokens.spacingVerticalS, tokens.spacingHorizontalS),
|
||||
},
|
||||
optionsLabel:
|
||||
{
|
||||
"> div[role=note].fui-InfoButton__info":
|
||||
{
|
||||
zIndex: 1,
|
||||
},
|
||||
},
|
||||
copyIcon:
|
||||
{
|
||||
animationName: "scaleUpIn",
|
||||
animationDuration: tokens.durationSlow,
|
||||
animationTimingFunction: tokens.curveEasyEaseMax,
|
||||
},
|
||||
refreshIcon:
|
||||
{
|
||||
animationName: "spin",
|
||||
animationDuration: tokens.durationSlow,
|
||||
animationTimingFunction: tokens.curveEasyEaseMax,
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,105 @@
|
||||
import { Button, Checkbox, Input, Slider, Text, Tooltip, mergeClasses } from "@fluentui/react-components";
|
||||
import { Alert, InfoLabel } from "@fluentui/react-components/unstable";
|
||||
import { ArrowClockwiseRegular, ArrowUndoRegular, CheckmarkRegular, CopyRegular } from "@fluentui/react-icons";
|
||||
import { useEffect, useState } from "react";
|
||||
import GeneratorOptions from "../Models/GeneratorOptions";
|
||||
import { GetLocaleString as loc } from "../Utils/Localization";
|
||||
import { GeneratePassword } from "../Utils/PasswordGenerator";
|
||||
import { useStorage } from "../Utils/Storage";
|
||||
import { useTimeout } from "../Utils/Timeout";
|
||||
import { useStyles } from "./GeneratorView.styles";
|
||||
|
||||
type QuickOptions = Pick<GeneratorOptions, "Length" | "Special" | "ExcludeAmbiguous">;
|
||||
|
||||
export default function GeneratorView(): JSX.Element
|
||||
{
|
||||
const { generatorOptions } = useStorage();
|
||||
const [password, setPassword] = useState<string>("");
|
||||
const [quickOpts, _setOpts] = useState<QuickOptions>(generatorOptions);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const [refreshTimer, copyTimer] = [useTimeout(310), useTimeout(1000)];
|
||||
const cls = useStyles();
|
||||
|
||||
const resetOptions = (): void =>
|
||||
_setOpts(generatorOptions);
|
||||
|
||||
const setOptions = (opts: Partial<QuickOptions>) =>
|
||||
_setOpts({ ...quickOpts, ...opts });
|
||||
|
||||
function RefreshPassword(): void
|
||||
{
|
||||
setError(null);
|
||||
const options: GeneratorOptions = { ...generatorOptions, ...quickOpts };
|
||||
|
||||
try { setPassword(GeneratePassword(options)); }
|
||||
catch (e) { setError((e as Error).message); }
|
||||
}
|
||||
|
||||
async function CopyPassword(): Promise<void>
|
||||
{
|
||||
await window.navigator.clipboard.writeText(password);
|
||||
copyTimer.trigger();
|
||||
}
|
||||
|
||||
useEffect(resetOptions, [generatorOptions]);
|
||||
useEffect(RefreshPassword, [generatorOptions, quickOpts]);
|
||||
useEffect(refreshTimer.trigger, [password]);
|
||||
|
||||
const actionButtons: JSX.Element = <>
|
||||
<Tooltip content={ loc("generator@copy") } relationship="label">
|
||||
<Button
|
||||
appearance="subtle" onClick={ CopyPassword }
|
||||
icon={
|
||||
copyTimer.isActive ?
|
||||
<CheckmarkRegular className={ cls.copyIcon } /> :
|
||||
<CopyRegular className={ cls.copyIcon } />
|
||||
} />
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content={ loc("generator@refresh") } relationship="label">
|
||||
<Button
|
||||
appearance="subtle" onClick={ RefreshPassword }
|
||||
icon={
|
||||
<ArrowClockwiseRegular className={ mergeClasses(refreshTimer.isActive && cls.refreshIcon) } />
|
||||
} />
|
||||
</Tooltip>
|
||||
</>;
|
||||
|
||||
return (
|
||||
<section className={ cls.root }>
|
||||
{ error ?
|
||||
<Alert intent="warning">{ error }</Alert> :
|
||||
<Input readOnly contentAfter={ actionButtons } value={ password } className={ cls.input } />
|
||||
}
|
||||
|
||||
<div className={ mergeClasses(cls.root, cls.optionsSpacing) }>
|
||||
<InfoLabel info={ loc("generator@quickOptions__hint") } className={ cls.optionsLabel }>
|
||||
{ loc("generator@quickOptions") }
|
||||
</InfoLabel>
|
||||
|
||||
<div className={ cls.lengthContainer }>
|
||||
<Slider
|
||||
min={ 6 } max={ Math.max(32, generatorOptions.Length) }
|
||||
value={ quickOpts.Length } onChange={ (_, e) => setOptions({ Length: e.value }) } />
|
||||
<Text>{ quickOpts.Length }</Text>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Checkbox
|
||||
label={ loc("settings@special") }
|
||||
checked={ quickOpts.Special }
|
||||
onChange={ (_, e) => setOptions({ Special: e.checked as boolean }) } />
|
||||
<Checkbox
|
||||
label={ loc("settings@ambiguous") } disabled={ !quickOpts.Special }
|
||||
checked={ !quickOpts.ExcludeAmbiguous }
|
||||
onChange={ (_, e) => setOptions({ ExcludeAmbiguous: !e.checked }) } />
|
||||
|
||||
<Tooltip content={ loc("generator@reset") } relationship="label">
|
||||
<Button appearance="subtle" icon={ <ArrowUndoRegular /> } onClick={ resetOptions } />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
import { Input, Button, Link, Text, Tooltip } from "@fluentui/react-components";
|
||||
import { Alert } from "@fluentui/react-components/unstable";
|
||||
import { ArrowClockwiseRegular, CheckmarkRegular, CopyRegular } from "@fluentui/react-icons";
|
||||
import React from "react";
|
||||
import Generator from "../Utils/Generator";
|
||||
import GeneratorOptions from "../Utils/GeneratorOptions";
|
||||
import { loc } from "../Utils/Localization";
|
||||
import Settings from "../Utils/Settings";
|
||||
|
||||
interface IStates
|
||||
{
|
||||
password: string;
|
||||
error?: string;
|
||||
copyIcon: JSX.Element;
|
||||
}
|
||||
|
||||
interface IProps
|
||||
{
|
||||
generatorOptions: GeneratorOptions;
|
||||
settings: Settings;
|
||||
}
|
||||
|
||||
export default class PasswordView extends React.Component<IProps, IStates>
|
||||
{
|
||||
constructor(props: IProps)
|
||||
{
|
||||
super(props);
|
||||
this.state =
|
||||
{
|
||||
password: Generator.GeneratePassword(props.generatorOptions),
|
||||
error: Generator.ValidateProps(props.generatorOptions),
|
||||
copyIcon: <CopyRegular className="scaleUpIn" />,
|
||||
};
|
||||
}
|
||||
|
||||
private OnCopyPassword(password: string): void
|
||||
{
|
||||
console.log("PasswordView.OnCopyPassword");
|
||||
|
||||
if (!document.hasFocus())
|
||||
return;
|
||||
|
||||
window.navigator.clipboard.writeText(password);
|
||||
|
||||
this.setState({ copyIcon: <CheckmarkRegular className="scaleUpIn" /> });
|
||||
setTimeout(() => this.setState({ copyIcon: <CopyRegular className="scaleUpIn" /> }), 3000);
|
||||
}
|
||||
|
||||
private OnRefreshPassword(): void
|
||||
{
|
||||
console.log("PasswordView.OnRefreshPassword");
|
||||
|
||||
let password: string = Generator.GeneratePassword(this.props.generatorOptions);
|
||||
|
||||
this.setState({ password });
|
||||
|
||||
document.querySelector("svg#refresh-btn")?.classList.add("spin");
|
||||
setTimeout(() => document.querySelector("svg#refresh-btn")?.classList.remove("spin"), 600);
|
||||
|
||||
if (this.props.settings.Autocopy)
|
||||
this.OnCopyPassword(password);
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: Readonly<IProps>): void
|
||||
{
|
||||
console.log("PasswordView.componentDidUpdate");
|
||||
|
||||
// Converting to JSON is the easiest way to compare objects
|
||||
if (JSON.stringify(prevProps.generatorOptions) === JSON.stringify(this.props.generatorOptions))
|
||||
return;
|
||||
|
||||
let error: string = Generator.ValidateProps(this.props.generatorOptions);
|
||||
let password = Generator.GeneratePassword(this.props.generatorOptions);
|
||||
|
||||
this.setState({ password, error });
|
||||
|
||||
if (!error && this.props.settings.Autocopy)
|
||||
this.OnCopyPassword(password);
|
||||
}
|
||||
|
||||
private AlterSpecialsOnce(useSpecials: boolean): void
|
||||
{
|
||||
console.log("PasswordView.AlterSpecialsOnce", `useSpecials: ${useSpecials}`);
|
||||
|
||||
let options: GeneratorOptions = { ...this.props.generatorOptions, Special: useSpecials, ExcludeAmbiguous: true };
|
||||
|
||||
let error: string = Generator.ValidateProps(options);
|
||||
let password: string = Generator.GeneratePassword(options);
|
||||
|
||||
this.setState({ password, error });
|
||||
|
||||
if (error)
|
||||
setTimeout(() => this.OnRefreshPassword(), 3000);
|
||||
}
|
||||
|
||||
public render(): JSX.Element
|
||||
{
|
||||
return this.state.error ?
|
||||
<Alert intent="error" className="fadeIn">{ this.state.error }</Alert>
|
||||
:
|
||||
<section className="stack fadeIn">
|
||||
<Input
|
||||
value={ this.state.password } readOnly
|
||||
contentAfter={
|
||||
<>
|
||||
<Tooltip content={ loc("Copy") } relationship="label">
|
||||
<Button onClick={ () => this.OnCopyPassword(this.state.password) } appearance="subtle" icon={ this.state.copyIcon } />
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content={ loc("Generate new") } relationship="label">
|
||||
<Button onClick={ () => this.OnRefreshPassword() } appearance="subtle" icon={ <ArrowClockwiseRegular id="refresh-btn" /> } />
|
||||
</Tooltip>
|
||||
</>
|
||||
} />
|
||||
<Text>
|
||||
{ this.props.generatorOptions.Special ?
|
||||
<Link onClick={ () => this.AlterSpecialsOnce(false) }>
|
||||
{ loc("Exclude special symbols one time") }
|
||||
</Link>
|
||||
:
|
||||
<Link onClick={ () => this.AlterSpecialsOnce(true) }>
|
||||
{ loc("Include special symbols one time") }
|
||||
</Link>
|
||||
}
|
||||
</Text>
|
||||
</section>
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { makeStyles, shorthands, tokens } from "@fluentui/react-components";
|
||||
|
||||
export const useStyles = makeStyles({
|
||||
root:
|
||||
{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
...shorthands.gap(tokens.spacingVerticalS),
|
||||
},
|
||||
checkboxContainer:
|
||||
{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
},
|
||||
});
|
||||
@@ -1,83 +1,87 @@
|
||||
import { AccordionItem, AccordionHeader, AccordionPanel, Label, Text, Input, Divider, Checkbox, Tooltip } from "@fluentui/react-components";
|
||||
import { QuestionCircleRegular, SettingsRegular } from "@fluentui/react-icons";
|
||||
import React from "react";
|
||||
import GeneratorOptions from "../Utils/GeneratorOptions";
|
||||
import { loc } from "../Utils/Localization";
|
||||
import Settings from "../Utils/Settings";
|
||||
import CharacterHelpDialog from "./CharacterHelpDialog";
|
||||
import * as fui from "@fluentui/react-components";
|
||||
import { InfoLabel } from "@fluentui/react-components/unstable";
|
||||
import { SettingsRegular } from "@fluentui/react-icons";
|
||||
import ExtensionOptions from "../Models/ExtensionOptions";
|
||||
import GeneratorOptions from "../Models/GeneratorOptions";
|
||||
import { GetLocaleString as loc } from "../Utils/Localization";
|
||||
import { CharacterHints } from "../Utils/PasswordGenerator";
|
||||
import { useStorage } from "../Utils/Storage";
|
||||
import { useStyles } from "./SettingsSection.styles";
|
||||
|
||||
interface IProps
|
||||
// FIXME: Remove ts-ignore comments once slots override fix is released
|
||||
// Tracker: https://github.com/microsoft/fluentui/issues/27090
|
||||
|
||||
export default function SettingsSection(): JSX.Element
|
||||
{
|
||||
generatorOptions: GeneratorOptions;
|
||||
settings: Settings;
|
||||
}
|
||||
|
||||
export default class SettingsSection extends React.Component<IProps>
|
||||
{
|
||||
public render(): JSX.Element
|
||||
{
|
||||
let options: GeneratorOptions = this.props.generatorOptions;
|
||||
let settings: Settings = this.props.settings;
|
||||
|
||||
return (
|
||||
<AccordionItem value="settings">
|
||||
<AccordionHeader as="h2" icon={ <SettingsRegular /> }>{ loc("Settings") }</AccordionHeader>
|
||||
<AccordionPanel>
|
||||
<section className="stack gap fadeIn">
|
||||
<Label weight="semibold" htmlFor="pwd-length">{ loc("Password length") }</Label>
|
||||
<div className="stack">
|
||||
<Input
|
||||
id="pwd-length"
|
||||
value={ options.Length?.toString() }
|
||||
onChange={ (_, e) => GeneratorOptions.Update({ Length: parseInt(e.value) }) }
|
||||
type="number" min={ 4 } minLength={ 1 } />
|
||||
<Text size={ 200 }>{ loc("Recommended password length") } <b>16-32</b></Text>
|
||||
</div>
|
||||
<Divider />
|
||||
<div className="stack">
|
||||
<Text as="h3" weight="semibold">
|
||||
{ loc("Character options") }
|
||||
<CharacterHelpDialog />
|
||||
</Text>
|
||||
|
||||
<Text as="h4">{ loc("Include") }</Text>
|
||||
<div className="stack horizontal">
|
||||
<Checkbox label={ loc("Special symbols") }
|
||||
checked={ options.Special } onChange={ (_, e) => GeneratorOptions.Update({ Special: e.checked as boolean }) } />
|
||||
<Checkbox label={ loc("Numeric") }
|
||||
checked={ options.Numeric } onChange={ (_, e) => GeneratorOptions.Update({ Numeric: e.checked as boolean }) } />
|
||||
<Checkbox label={ loc("Uppercase") }
|
||||
checked={ options.Uppercase } onChange={ (_, e) => GeneratorOptions.Update({ Uppercase: e.checked as boolean }) } />
|
||||
<Checkbox label={ loc("Lowercase") }
|
||||
checked={ options.Lowercase } onChange={ (_, e) => GeneratorOptions.Update({ Lowercase: e.checked as boolean }) } />
|
||||
</div>
|
||||
|
||||
<Text as="h4">{ loc("Exclude") }</Text>
|
||||
<div className="stack horizontal">
|
||||
<Checkbox label={ loc("Similar") }
|
||||
checked={ options.ExcludeSimilar } onChange={ (_, e) => GeneratorOptions.Update({ ExcludeSimilar: e.checked as boolean }) } />
|
||||
<Checkbox label={ loc("Ambiguous") }
|
||||
checked={ options.ExcludeAmbiguous } onChange={ (_, e) => GeneratorOptions.Update({ ExcludeAmbiguous: e.checked as boolean }) } />
|
||||
<Checkbox label={ loc("Repeating") }
|
||||
checked={ options.ExcludeRepeating } onChange={ (_, e) => GeneratorOptions.Update({ ExcludeRepeating: e.checked as boolean }) } />
|
||||
</div>
|
||||
</div>
|
||||
<Divider />
|
||||
<div className="stack">
|
||||
<Checkbox
|
||||
checked={ settings.AddContext }
|
||||
onChange={ (_, e) => Settings.Update({ AddContext: e.checked as boolean }) }
|
||||
label={
|
||||
<Tooltip content={ loc("Right-click password field to quickly generate password") } relationship="description">
|
||||
<Text>{ loc("Add shortcut to context menu") } <QuestionCircleRegular /></Text>
|
||||
</Tooltip>
|
||||
} />
|
||||
<Checkbox label={ loc("Automatically copy to clipboard") }
|
||||
checked={ settings.Autocopy } onChange={ (_, e) => Settings.Update({ Autocopy: e.checked as boolean }) } />
|
||||
</div>
|
||||
</section>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
);
|
||||
}
|
||||
const { generatorOptions, updateStorage } = useStorage();
|
||||
const cls = useStyles();
|
||||
|
||||
const infoLabel = (content: string, hint: string) => ({
|
||||
children: (_: unknown, slotProps: fui.LabelProps) => (
|
||||
<InfoLabel { ...slotProps } info={ hint }>{ content }</InfoLabel>
|
||||
)
|
||||
});
|
||||
|
||||
const setOption = (option: keyof (GeneratorOptions & ExtensionOptions)) =>
|
||||
(_: unknown, args: fui.CheckboxOnChangeData) =>
|
||||
updateStorage({ [option]: args.checked } );
|
||||
|
||||
return (
|
||||
<fui.AccordionItem value="settings">
|
||||
<fui.AccordionHeader as="h2" icon={ <SettingsRegular /> }>{ loc("settings@title") }</fui.AccordionHeader>
|
||||
|
||||
<fui.AccordionPanel className={ cls.root }>
|
||||
|
||||
<fui.Field label={ loc("settings@length") } hint={ loc("settings@length__hint") }>
|
||||
<fui.Input
|
||||
type="number" min={ 6 }
|
||||
value={ generatorOptions.Length.toString() }
|
||||
onChange={ (_, e) => updateStorage({ Length: parseInt(e.value) }) } />
|
||||
</fui.Field>
|
||||
|
||||
<fui.Divider />
|
||||
|
||||
<fui.Text>{ loc("settings@include") }</fui.Text>
|
||||
<div className={ cls.checkboxContainer }>
|
||||
<fui.Checkbox label={ loc("settings@uppercase") }
|
||||
checked={ generatorOptions.Uppercase }
|
||||
onChange={ setOption("Uppercase") } />
|
||||
<fui.Checkbox
|
||||
label={ loc("settings@lowercase") }
|
||||
checked={ generatorOptions.Lowercase }
|
||||
onChange={ setOption("Lowercase") } />
|
||||
<fui.Checkbox
|
||||
label={ loc("settings@numeric") }
|
||||
checked={ generatorOptions.Numeric }
|
||||
onChange={ setOption("Numeric") } />
|
||||
<fui.Checkbox
|
||||
label={ loc("settings@special") }
|
||||
checked={ generatorOptions.Special }
|
||||
onChange={ setOption("Special") } />
|
||||
</div>
|
||||
|
||||
<fui.Text>{ loc("settings@exclude") }</fui.Text>
|
||||
<div className={ cls.checkboxContainer }>
|
||||
<fui.Checkbox
|
||||
// @ts-expect-error See line 11
|
||||
label={ infoLabel(loc("settings@similar"), CharacterHints.Similar) }
|
||||
checked={ generatorOptions.ExcludeSimilar }
|
||||
onChange={ setOption("ExcludeSimilar") } />
|
||||
<fui.Checkbox
|
||||
// @ts-expect-error See line 11
|
||||
label={ infoLabel(loc("settings@ambiguous"), CharacterHints.Ambiguous) }
|
||||
disabled={ !generatorOptions.Special }
|
||||
checked={ generatorOptions.ExcludeAmbiguous }
|
||||
onChange={ setOption("ExcludeAmbiguous") } />
|
||||
<fui.Checkbox
|
||||
// @ts-expect-error See line 11
|
||||
label={ infoLabel(loc("settings@repeating"), loc("settings@repeating__hint")) }
|
||||
checked={ generatorOptions.ExcludeRepeating }
|
||||
onChange={ setOption("ExcludeRepeating") } />
|
||||
</div>
|
||||
|
||||
</fui.AccordionPanel>
|
||||
|
||||
</fui.AccordionItem>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user