fix https://github.com/lesspass/lesspass/issues/206pull/342/head
@@ -28,9 +28,6 @@ | |||||
v-if="!saved && isAuthenticated && $store.state.password.site !== ''"> | v-if="!saved && isAuthenticated && $store.state.password.site !== ''"> | ||||
<i class="fa fa-lg fa-save pointer"></i> | <i class="fa fa-lg fa-save pointer"></i> | ||||
</span> | </span> | ||||
<router-link class="white-link pl-3" :to="{ name: 'configureOptions'}"> | |||||
<i class="fa fa-lg fa-cog" aria-hidden="true"></i> | |||||
</router-link> | |||||
<router-link class="white-link pl-3" :to="{ name: 'passwords'}" v-if="isAuthenticated"> | <router-link class="white-link pl-3" :to="{ name: 'passwords'}" v-if="isAuthenticated"> | ||||
<i class="fa fa-lg fa-key" aria-hidden="true"></i> | <i class="fa fa-lg fa-key" aria-hidden="true"></i> | ||||
</router-link> | </router-link> | ||||
@@ -50,12 +50,12 @@ | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="form-group row mb-0"> | |||||
<div class="col-6 col-sm-4 mb-3 mb-sm-0"> | |||||
<div class="form-group row"> | |||||
<div class="col"> | |||||
<label for="passwordLength">{{ $t('Length') }}</label> | <label for="passwordLength">{{ $t('Length') }}</label> | ||||
<div class="input-group input-group-sm"> | <div class="input-group input-group-sm"> | ||||
<span class="input-group-btn" v-on:click="options.length=decrement(options.length, {min: 5, max: 35})"> | <span class="input-group-btn" v-on:click="options.length=decrement(options.length, {min: 5, max: 35})"> | ||||
<button class="btn btn-secondary" type="button"> | |||||
<button class="btn btn-secondary p-1" type="button"> | |||||
<i class="fa fa-minus"></i> | <i class="fa fa-minus"></i> | ||||
</button> | </button> | ||||
</span> | </span> | ||||
@@ -66,14 +66,14 @@ | |||||
max="35" | max="35" | ||||
v-model="options.length"> | v-model="options.length"> | ||||
<span class="input-group-btn" | <span class="input-group-btn" | ||||
v-on:click="options.length=increment(options.length, {min: 5, max: 35})"> | |||||
<button class="btn btn-secondary" type="button"> | |||||
v-on:click="options.length=increment(options.length, {min: 5, max: 35})"> | |||||
<button class="btn btn-secondary p-1" type="button"> | |||||
<i class="fa fa-plus"></i> | <i class="fa fa-plus"></i> | ||||
</button> | </button> | ||||
</span> | </span> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="col-6 col-sm-4 mb-3 mb-sm-0"> | |||||
<div class="col"> | |||||
<label for="passwordCounter" | <label for="passwordCounter" | ||||
class="hint--top hint--medium" | class="hint--top hint--medium" | ||||
v-bind:aria-label="$t('CounterFieldHelp','Increment this value to change the generated password without changing your master password.')"> | v-bind:aria-label="$t('CounterFieldHelp','Increment this value to change the generated password without changing your master password.')"> | ||||
@@ -81,7 +81,7 @@ | |||||
</label> | </label> | ||||
<div class="input-group input-group-sm"> | <div class="input-group input-group-sm"> | ||||
<span class="input-group-btn" v-on:click="options.counter=decrement(options.counter, {min: 1})"> | <span class="input-group-btn" v-on:click="options.counter=decrement(options.counter, {min: 1})"> | ||||
<button class="btn btn-secondary" type="button"> | |||||
<button class="btn btn-secondary p-1" type="button"> | |||||
<i class="fa fa-minus"></i> | <i class="fa fa-minus"></i> | ||||
</button> | </button> | ||||
</span> | </span> | ||||
@@ -90,41 +90,50 @@ | |||||
type="number" | type="number" | ||||
min="1" | min="1" | ||||
v-model="options.counter"> | v-model="options.counter"> | ||||
<span class="input-group-btn" v-on:click="options.counter=increment(options.counter, {min: 1})"> | |||||
<button class="btn btn-secondary" type="button"> | |||||
<span class="input-group-btn" v-on:click="options.counter=increment(options.counter, {min: 1})"> | |||||
<button class="btn btn-secondary p-1" type="button"> | |||||
<i class="fa fa-plus"></i> | <i class="fa fa-plus"></i> | ||||
</button> | </button> | ||||
</span> | </span> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="clearfix hidden-sm-up"></div> | |||||
<div class="col-12 col-sm-4"> | |||||
<div class="row hidden-sm-down"> | |||||
<div class="col"> | |||||
<div class="row"> | |||||
<div class="col"> | <div class="col"> | ||||
<label>{{ $t('Version') }}</label> | <label>{{ $t('Version') }}</label> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="row no-gutters"> | <div class="row no-gutters"> | ||||
<div class="col-6"> | |||||
<div class="col"> | |||||
<button type="button" class="btn btn-block btn-sm border-right-0" | <button type="button" class="btn btn-block btn-sm border-right-0" | ||||
v-bind:class="{'btn-primary':options.version===2,'btn-secondary':options.version!==2}" | v-bind:class="{'btn-primary':options.version===2,'btn-secondary':options.version!==2}" | ||||
v-on:click="setVersion(2)"> | v-on:click="setVersion(2)"> | ||||
<span class="hidden-sm-up">{{$t('version')}} </span> | |||||
<span class="hidden-sm-down">{{$t('versionShortcut', 'v')}}</span>2 | |||||
<span class="hidden-xs-up">{{$t('version')}} </span> | |||||
<span class="hidden-xs-down">{{$t('versionShortcut', 'v')}}</span>2 | |||||
</button> | </button> | ||||
</div> | </div> | ||||
<div class="col-6"> | |||||
<div class="col"> | |||||
<button type="button" | <button type="button" | ||||
class="btn btn-block btn-sm border-left-0" | class="btn btn-block btn-sm border-left-0" | ||||
v-bind:class="{'btn-warning':options.version===1,'btn-secondary':options.version!==1}" | v-bind:class="{'btn-warning':options.version===1,'btn-secondary':options.version!==1}" | ||||
v-on:click="setVersion(1)"> | v-on:click="setVersion(1)"> | ||||
<span class="hidden-sm-up">{{$t('version')}} </span> | |||||
<span class="hidden-sm-down">{{$t('versionShortcut', 'v')}}</span>1 | |||||
<span class="hidden-xs-up">{{$t('version')}} </span> | |||||
<span class="hidden-xs-down">{{$t('versionShortcut', 'v')}}</span>1 | |||||
</button> | </button> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="form-group row mb-0"> | |||||
<div class="col"> | |||||
<button type="button" class="btn btn-sm hint--top-right hint--medium" | |||||
v-bind:aria-label="$t('DefaultOptionLocalStorage', 'We use local storage to save default options locally. Each time you open the app, these options will be loaded by default.')" | |||||
v-bind:class="{'btn-outline-warning':options.version===1,'btn-outline-primary':options.version!==1}" | |||||
v-on:click="saveDefaultOptions()"> | |||||
<i class="fa fa-floppy-o" aria-hidden="true"></i> {{$t('Save options as default')}} | |||||
</button> | |||||
</div> | |||||
</div> | |||||
</div> | </div> | ||||
</template> | </template> | ||||
@@ -184,6 +193,10 @@ | |||||
const onlyV2DefaultDate = new Date(2017, 4, 10); | const onlyV2DefaultDate = new Date(2017, 4, 10); | ||||
return Math.round(Math.abs((now.getTime() - onlyV2DefaultDate.getTime()) / (oneDay))); | return Math.round(Math.abs((now.getTime() - onlyV2DefaultDate.getTime()) / (oneDay))); | ||||
}, | }, | ||||
saveDefaultOptions(){ | |||||
this.$store.dispatch('saveDefaultOptions', {options: this.options}); | |||||
Message.success(this.$t('Your options have been saved successfully')); | |||||
}, | |||||
} | } | ||||
} | } | ||||
</script> | </script> |
@@ -1,7 +1,6 @@ | |||||
import Vue from 'vue'; | import Vue from 'vue'; | ||||
import VueRouter from 'vue-router'; | import VueRouter from 'vue-router'; | ||||
import ConfigureOptions from './views/ConfigureOptions.vue'; | |||||
import Login from './views/Login.vue'; | import Login from './views/Login.vue'; | ||||
import PasswordGenerator from './views/PasswordGenerator.vue'; | import PasswordGenerator from './views/PasswordGenerator.vue'; | ||||
import PasswordReset from './views/PasswordReset.vue'; | import PasswordReset from './views/PasswordReset.vue'; | ||||
@@ -14,7 +13,6 @@ const routes = [ | |||||
{path: '/', name: 'home', component: PasswordGenerator}, | {path: '/', name: 'home', component: PasswordGenerator}, | ||||
{path: '/login', name: 'login', component: Login}, | {path: '/login', name: 'login', component: Login}, | ||||
{path: '/passwords/', name: 'passwords', component: Passwords}, | {path: '/passwords/', name: 'passwords', component: Passwords}, | ||||
{path: '/options/default/', name: 'configureOptions', component: ConfigureOptions}, | |||||
{path: '/passwords/:id', name: 'password', component: PasswordGenerator}, | {path: '/passwords/:id', name: 'password', component: PasswordGenerator}, | ||||
{path: '/password/reset', name: 'passwordReset', component: PasswordReset}, | {path: '/password/reset', name: 'passwordReset', component: PasswordReset}, | ||||
{path: '/password/reset/confirm/:uid/:token', name: 'passwordResetConfirm', component: PasswordResetConfirm}, | {path: '/password/reset/confirm/:uid/:token', name: 'passwordResetConfirm', component: PasswordResetConfirm}, | ||||
@@ -1,6 +1,6 @@ | |||||
export function showTooltip(elem, msg) { | export function showTooltip(elem, msg) { | ||||
var classNames = elem.className; | var classNames = elem.className; | ||||
elem.setAttribute('class', classNames + ' hint--right'); | |||||
elem.setAttribute('class', classNames + ' hint--top'); | |||||
elem.setAttribute('aria-label', msg); | elem.setAttribute('aria-label', msg); | ||||
setTimeout(function() { | setTimeout(function() { | ||||
elem.setAttribute('class', classNames); | elem.setAttribute('class', classNames); | ||||
@@ -20,8 +20,8 @@ export const loadPasswordForSite = ({commit}, payload) => { | |||||
commit(types.LOAD_PASSWORD_FOR_SITE, payload); | commit(types.LOAD_PASSWORD_FOR_SITE, payload); | ||||
}; | }; | ||||
export const saveDefaultPassword = ({commit}, payload) => { | |||||
commit(types.SET_DEFAULT_PASSWORD, payload); | |||||
export const saveDefaultOptions = ({commit}, payload) => { | |||||
commit(types.SET_DEFAULT_OPTIONS, payload); | |||||
}; | }; | ||||
export const passwordGenerated = ({commit}) => { | export const passwordGenerated = ({commit}) => { | ||||
@@ -3,7 +3,7 @@ export const LOGIN = 'LOGIN'; | |||||
export const SET_TOKEN = 'SET_TOKEN'; | export const SET_TOKEN = 'SET_TOKEN'; | ||||
export const PASSWORD_GENERATED = 'PASSWORD_GENERATED'; | export const PASSWORD_GENERATED = 'PASSWORD_GENERATED'; | ||||
export const SET_PASSWORD = 'SET_PASSWORD'; | export const SET_PASSWORD = 'SET_PASSWORD'; | ||||
export const SET_DEFAULT_PASSWORD = 'SET_DEFAULT_PASSWORD'; | |||||
export const SET_DEFAULT_OPTIONS = 'SET_DEFAULT_OPTIONS'; | |||||
export const SET_PASSWORDS = 'SET_PASSWORDS'; | export const SET_PASSWORDS = 'SET_PASSWORDS'; | ||||
export const DELETE_PASSWORD = 'DELETE_PASSWORD'; | export const DELETE_PASSWORD = 'DELETE_PASSWORD'; | ||||
export const SET_BASE_URL = 'SET_BASE_URL'; | export const SET_BASE_URL = 'SET_BASE_URL'; | ||||
@@ -19,8 +19,8 @@ export default { | |||||
[types.PASSWORD_GENERATED](state){ | [types.PASSWORD_GENERATED](state){ | ||||
state.lastUse = new Date().getTime(); | state.lastUse = new Date().getTime(); | ||||
}, | }, | ||||
[types.SET_DEFAULT_PASSWORD](state, {password}){ | |||||
state.defaultPassword = {...password}; | |||||
[types.SET_DEFAULT_OPTIONS](state, {options}){ | |||||
state.defaultPassword = Object.assign({}, state.defaultPassword, options); | |||||
}, | }, | ||||
[types.SET_PASSWORDS](state, {passwords}){ | [types.SET_PASSWORDS](state, {passwords}){ | ||||
state.passwords = passwords; | state.passwords = passwords; | ||||
@@ -1,55 +0,0 @@ | |||||
<template> | |||||
<div> | |||||
<div class="form-group"> | |||||
<label for="login">Login</label> | |||||
<div class="inner-addon left-addon"> | |||||
<i class="fa fa-user"></i> | |||||
<input id="login" | |||||
name="login" | |||||
type="text" | |||||
class="form-control" | |||||
autocomplete="off" | |||||
autocorrect="off" | |||||
autocapitalize="none" | |||||
v-bind:placeholder="$t('Login')" | |||||
v-model="defaultOptions.login"> | |||||
</div> | |||||
</div> | |||||
<options v-bind:password="defaultOptions" v-on:optionsUpdated="optionsUpdated"></options> | |||||
<div class="form-group pt-3"> | |||||
<button type="button" class="btn btn-sm btn-block hint--top hint--medium" | |||||
v-bind:aria-label="$t('DefaultOptionLocalStorage', 'We use local storage to save default options locally. Each time you open the app, these options will be loaded by default.')" | |||||
v-bind:class="{'btn-warning':defaultOptions.version===1,'btn-primary':defaultOptions.version!==1}" | |||||
v-on:click="saveOptionsAsDefault"> | |||||
{{$t('Save default options locally')}} | |||||
</button> | |||||
</div> | |||||
</div> | |||||
</template> | |||||
<script type="text/ecmascript-6"> | |||||
import Options from '../components/Options.vue'; | |||||
export default { | |||||
name: 'configure-options-view', | |||||
components: { | |||||
Options | |||||
}, | |||||
data(){ | |||||
return { | |||||
defaultOptions: {} | |||||
} | |||||
}, | |||||
created(){ | |||||
this.defaultOptions = Object.assign({}, this.$store.state.defaultPassword); | |||||
}, | |||||
methods: { | |||||
optionsUpdated(options){ | |||||
this.defaultOptions = Object.assign({}, this.defaultOptions, options); | |||||
}, | |||||
saveOptionsAsDefault(){ | |||||
this.$store.dispatch('saveDefaultPassword', {password: this.defaultOptions}); | |||||
}, | |||||
} | |||||
} | |||||
</script> |
@@ -115,11 +115,12 @@ | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="col col-auto"> | <div class="col col-auto"> | ||||
<button class="btn btn-copy btn-secondary" | |||||
<button class="btn btn-copy btn-secondary hint--top" | |||||
type="button" | type="button" | ||||
v-bind:aria-label="$t('Share this password')" | |||||
v-bind:data-clipboard-text="passwordURL" | v-bind:data-clipboard-text="passwordURL" | ||||
v-bind:disabled="password.site === ''"> | |||||
<i class="fa fa-lg fa-share-alt pointer" aria-hidden="true"></i> | |||||
v-if="password.site !== ''"> | |||||
<i class="fa fa-share-alt pointer" aria-hidden="true"></i> | |||||
</button> | </button> | ||||
<button type="button" class="btn btn-secondary" v-on:click="showOptions=!showOptions"> | <button type="button" class="btn btn-secondary" v-on:click="showOptions=!showOptions"> | ||||
<i class="fa fa-sliders" aria-hidden="true"></i> | <i class="fa fa-sliders" aria-hidden="true"></i> | ||||
@@ -80,8 +80,8 @@ test('SET_PASSWORD immutable', t => { | |||||
t.is(state.password.version, 2); | t.is(state.password.version, 2); | ||||
}); | }); | ||||
test('SET_DEFAULT_PASSWORD', t => { | |||||
const SET_DEFAULT_PASSWORD = mutations[types.SET_DEFAULT_PASSWORD]; | |||||
test('SET_DEFAULT_OPTIONS', t => { | |||||
const SET_DEFAULT_OPTIONS = mutations[types.SET_DEFAULT_OPTIONS]; | |||||
const state = { | const state = { | ||||
defaultPassword: { | defaultPassword: { | ||||
site: '', | site: '', | ||||
@@ -95,7 +95,7 @@ test('SET_DEFAULT_PASSWORD', t => { | |||||
version: 2 | version: 2 | ||||
} | } | ||||
}; | }; | ||||
SET_DEFAULT_PASSWORD(state, {password: {symbols: false, length: 30}}); | |||||
SET_DEFAULT_OPTIONS(state, {options: {symbols: false, length: 30}}); | |||||
t.is(state.defaultPassword.length, 30); | t.is(state.defaultPassword.length, 30); | ||||
t.false(state.defaultPassword.symbols); | t.false(state.defaultPassword.symbols); | ||||
}); | }); | ||||