Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions frontend/assets/style/_mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ $payment-table-desktop-bp: 1290px;
width: $dimensions;
}

@mixin tooltip-style-common {
position: absolute;
top: 0;
left: 0;
min-width: 3rem;
max-width: 12rem;
border-radius: 3px;
padding: 0.5rem;
z-index: 50; // $zindex-tooltip: 50;
pointer-events: none;
background-color: var(--text_0);
opacity: 0.95;
color: var(--background_0);
}

@mixin overflow-touch {
-webkit-overflow-scrolling: touch;
}
Expand Down
13 changes: 1 addition & 12 deletions frontend/views/components/Tooltip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -375,18 +375,7 @@ export default ({

.c-tooltip,
.c-anchored-tooltip {
position: absolute;
top: 0;
left: 0;
min-width: 3rem;
max-width: 12rem;
border-radius: $radius;
padding: 0.5rem;
z-index: $zindex-tooltip;
pointer-events: none;
background-color: $text_0;
opacity: 0.95;
color: $background_0;
@include tooltip-style-common;

&.has-text-center {
text-align: center;
Expand Down
130 changes: 121 additions & 9 deletions frontend/views/containers/access/PasswordForm.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
<template lang='pug'>
label.field
component.field(:is='mode === "manual" ? "label" : "div"')
.label(v-if='label') {{ label }}

.inputgroup.c-mode-auto(
v-if='mode === "auto"'
v-error:[name]=''
)
.input.width-with-single-addon.has-ellipsis.c-auto-password(
:data-test='name'
) {{ ephemeral.randomPassword }}

.addons
button.is-success.c-copy-btn(
type='button'
@click.stop='copyPassword'
)
i18n Copy

i18n.c-feedback(
v-if='ephemeral.showCopyFeedback'
) Copied to clipboard!

.inputgroup(
v-error:[name]='{ attrs: { "data-test": "badPassword" }}'
v-else
v-error:[name]=''
)
input.input.with-single-addon(
:type='isLock ? "password" : "text"'
Expand All @@ -17,7 +38,6 @@ label.field
.addons
button.is-icon(
type='button'
v-if='hasIconRight'
:aria-label='L("Toggle password visibility")'
:aria-pressed='!isLock'
@click.prevent='isLock = !isLock'
Expand All @@ -26,13 +46,31 @@ label.field
</template>

<script>
import { base58btc } from '@chelonia/multiformats/bases/base58'
import Tooltip from '@components/Tooltip.vue'
import validationsDebouncedMixins from '@view-utils/validationsDebouncedMixins.js'
import { L } from '@common/common.js'

function generateBase58Password (length = 32) {
const bytes = crypto.getRandomValues(new Uint8Array((length)))
const encoded = base58btc.baseEncode(bytes)

// Truncate to desired length
return encoded.slice(0, length)
}

export default ({
name: 'PasswordForm',
components: {
Tooltip
},
data () {
return {
isLock: true
isLock: true,
ephemeral: {
randomPassword: '',
showCopyFeedback: false
}
}
},
mixins: [validationsDebouncedMixins],
Expand All @@ -42,6 +80,11 @@ export default ({
required: false,
default: 'password'
},
mode: {
type: String,
requried: false,
default: 'manual' // 'manual' | 'auto'
},
label: {
type: String,
required: false
Expand All @@ -50,10 +93,6 @@ export default ({
type: Object,
required: true
},
hasIconRight: {
type: Boolean,
default: true
},
showPlaceholder: {
type: Boolean,
default: false
Expand All @@ -67,15 +106,88 @@ export default ({
required: false
}
},
methods: {
generateRandomPassword (pwLen = 32) {
let genPassword = ''

if (process.env.CI || (Math.random() > 1 && process.env.NODE_ENV !== 'production')) {
// For easier debugging/development, use the common default password when running in non-production environments.
// Comment out process.env.NODE_ENV !== 'production' though if you need to use/test the safe auto-generated password feature in local development.
genPassword = '123456789'
} else {
genPassword = generateBase58Password(pwLen)
}

this.ephemeral.randomPassword = genPassword
this.$v.form[this.name].$model = genPassword
},
copyPassword () {
const pw = this.ephemeral.randomPassword
const copyToClipBoard = () => {
navigator.clipboard.writeText(pw)
this.ephemeral.showCopyFeedback = true

setTimeout(() => {
this.ephemeral.showCopyFeedback = false
}, 1500)
}

if (navigator.share) {
navigator.share({
title: L('Your password'),
text: pw
}).catch((error) => {
console.error('navigator.share failed with:', error)
copyToClipBoard()
})
} else {
copyToClipBoard()
}
}
},
created () {
this.isLock = !this.showPassword
if (this.mode === 'auto') {
this.generateRandomPassword()
} else {
this.isLock = !this.showPassword
}
}
}: Object)
</script>

<style lang="scss" scoped>
@import "@assets/style/_variables.scss";

.c-mode-auto {
.c-auto-password {
display: block;
line-height: 2.75rem;
padding-right: 5.5rem;
}

.addons {
align-items: center;
right: 0.5rem;
}
}

.icon {
cursor: pointer;
pointer-events: initial !important;
}

button.c-copy-btn {
min-height: unset;
height: 1.75rem;
border-radius: 3px;
padding-left: 1rem;
padding-right: 1rem;
}

.c-feedback {
@include tooltip-style-common;
top: calc(100% + 0.5rem);
left: 50%;
transform: translateX(-50%);
}
</style>
3 changes: 0 additions & 3 deletions frontend/views/containers/access/PasswordModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ modal-template(class='is-centered is-left-aligned' back-on-mobile=true ref='moda
:value='form'
:$v='$v'
@enter='changePassword'
:hasIconRight='true'
:showPlaceholder='false'
:showPassword='false'
size='is-large'
Expand All @@ -26,7 +25,6 @@ modal-template(class='is-centered is-left-aligned' back-on-mobile=true ref='moda
:value='form'
:$v='$v'
@enter='changePassword'
:hasIconRight='true'
:showPlaceholder='false'
:showPassword='false'
size='is-large'
Expand All @@ -38,7 +36,6 @@ modal-template(class='is-centered is-left-aligned' back-on-mobile=true ref='moda
:value='form'
:$v='$v'
@enter='changePassword'
:hasIconRight='true'
:showPlaceholder='false'
:showPassword='false'
size='is-large'
Expand Down
66 changes: 45 additions & 21 deletions frontend/views/containers/access/SignupForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,36 @@ form(data-test='signup' @submit.prevent='')
v-error:username='{ attrs: { "data-test": "badUsername" } }'
)

.c-password-fields-container
password-form(:label='L("Password")' name='password' :$v='$v')
.c-auto-password-field-container
password-form(
mode='auto'
:label='L("This is your password. Save it now.")'
name='password'
:$v='$v'
)

password-form(:label='L("Confirm Password")' name='passwordConfirm' :$v='$v')
.c-confirm-check-container
label.checkbox
input.input(
type='checkbox'
name='savedPassword'
v-model='form.savedPassword'
data-test='savedPassword'
@click.stop=''
)
i18n I have saved the password

label.checkbox
input.input(
type='checkbox'
name='terms'
v-model='form.terms'
data-test='signTerms'
@click.stop=''
)
i18n(
:args='{ a_: `<a class="link" target="_blank" href="${linkToTerms}">`, _a: "</a>"}'
) I agree to the {a_}terms and conditions{_a}
label.checkbox
input.input(
type='checkbox'
name='terms'
v-model='form.terms'
data-test='signTerms'
@click.stop=''
)
i18n(
:args='{ a_: `<a class="link" target="_blank" href="${linkToTerms}">`, _a: "</a>"}'
) I agree to the {a_}terms and conditions{_a}

banner-scoped(ref='formMsg' allow-a)

Expand All @@ -44,7 +58,7 @@ form(data-test='signup' @submit.prevent='')
<script>
import sbp from '@sbp/sbp'
import { L } from '@common/common.js'
import { maxLength, minLength, required, sameAs } from 'vuelidate/lib/validators'
import { maxLength, minLength, required } from 'vuelidate/lib/validators'
import { validationMixin } from 'vuelidate'
import PasswordForm from '@containers/access/PasswordForm.vue'
import BannerScoped from '@components/banners/BannerScoped.vue'
Expand Down Expand Up @@ -107,7 +121,7 @@ export default ({
form: {
username: '',
password: '',
passwordConfirm: '',
savedPassword: false,
terms: false,
pictureBase64: ''
},
Expand Down Expand Up @@ -175,13 +189,11 @@ export default ({
[L('A password is required.')]: required,
[L('Your password must be at least {minChars} characters long.', { minChars: passwordMinChars })]: minLength(passwordMinChars)
},
passwordConfirm: {
[L('Passwords do not match.')]: sameAs('password')
savedPassword: {
[L('Please save the password.')]: (value) => Boolean(value)
},
terms: {
[L('You need to agree to the terms and conditions.')]: (value) => {
return Boolean(value)
}
[L('You need to agree to the terms and conditions.')]: (value) => Boolean(value)
}
}
}
Expand All @@ -208,4 +220,16 @@ export default ({
margin-bottom: 1.5rem;
}
}

.c-auto-password-field-container {
margin: 1.5rem 0;
}

.c-confirm-check-container {
position: relative;
display: flex;
flex-direction: column;
row-gap: 0.5rem;
margin-bottom: 2rem;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
:label='L("Password")'
:value='form'
:$v='$v'
:hasIconRight='true'
:showPlaceholder='false'
:showPassword='false'
size='is-large'
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading