@@ -28,9 +28,6 @@ | |||
v-if="!saved && isAuthenticated && $store.state.password.site !== ''"> | |||
<i class="fa fa-lg fa-save pointer"></i> | |||
</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"> | |||
<i class="fa fa-lg fa-key" aria-hidden="true"></i> | |||
</router-link> | |||
@@ -50,12 +50,12 @@ | |||
</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> | |||
<div class="input-group input-group-sm"> | |||
<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> | |||
</button> | |||
</span> | |||
@@ -66,14 +66,14 @@ | |||
max="35" | |||
v-model="options.length"> | |||
<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> | |||
</button> | |||
</span> | |||
</div> | |||
</div> | |||
<div class="col-6 col-sm-4 mb-3 mb-sm-0"> | |||
<div class="col"> | |||
<label for="passwordCounter" | |||
class="hint--top hint--medium" | |||
v-bind:aria-label="$t('CounterFieldHelp','Increment this value to change the generated password without changing your master password.')"> | |||
@@ -81,7 +81,7 @@ | |||
</label> | |||
<div class="input-group input-group-sm"> | |||
<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> | |||
</button> | |||
</span> | |||
@@ -90,41 +90,50 @@ | |||
type="number" | |||
min="1" | |||
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> | |||
</button> | |||
</span> | |||
</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"> | |||
<label>{{ $t('Version') }}</label> | |||
</div> | |||
</div> | |||
<div class="row no-gutters"> | |||
<div class="col-6"> | |||
<div class="col"> | |||
<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-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> | |||
</div> | |||
<div class="col-6"> | |||
<div class="col"> | |||
<button type="button" | |||
class="btn btn-block btn-sm border-left-0" | |||
v-bind:class="{'btn-warning':options.version===1,'btn-secondary':options.version!==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> | |||
</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> | |||
</template> | |||
@@ -184,6 +193,10 @@ | |||
const onlyV2DefaultDate = new Date(2017, 4, 10); | |||
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> |
@@ -1,7 +1,6 @@ | |||
import Vue from 'vue'; | |||
import VueRouter from 'vue-router'; | |||
import ConfigureOptions from './views/ConfigureOptions.vue'; | |||
import Login from './views/Login.vue'; | |||
import PasswordGenerator from './views/PasswordGenerator.vue'; | |||
import PasswordReset from './views/PasswordReset.vue'; | |||
@@ -14,7 +13,6 @@ const routes = [ | |||
{path: '/', name: 'home', component: PasswordGenerator}, | |||
{path: '/login', name: 'login', component: Login}, | |||
{path: '/passwords/', name: 'passwords', component: Passwords}, | |||
{path: '/options/default/', name: 'configureOptions', component: ConfigureOptions}, | |||
{path: '/passwords/:id', name: 'password', component: PasswordGenerator}, | |||
{path: '/password/reset', name: 'passwordReset', component: PasswordReset}, | |||
{path: '/password/reset/confirm/:uid/:token', name: 'passwordResetConfirm', component: PasswordResetConfirm}, | |||
@@ -1,6 +1,6 @@ | |||
export function showTooltip(elem, msg) { | |||
var classNames = elem.className; | |||
elem.setAttribute('class', classNames + ' hint--right'); | |||
elem.setAttribute('class', classNames + ' hint--top'); | |||
elem.setAttribute('aria-label', msg); | |||
setTimeout(function() { | |||
elem.setAttribute('class', classNames); | |||
@@ -20,8 +20,8 @@ export const loadPasswordForSite = ({commit}, 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}) => { | |||
@@ -3,7 +3,7 @@ export const LOGIN = 'LOGIN'; | |||
export const SET_TOKEN = 'SET_TOKEN'; | |||
export const PASSWORD_GENERATED = 'PASSWORD_GENERATED'; | |||
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 DELETE_PASSWORD = 'DELETE_PASSWORD'; | |||
export const SET_BASE_URL = 'SET_BASE_URL'; | |||
@@ -19,8 +19,8 @@ export default { | |||
[types.PASSWORD_GENERATED](state){ | |||
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}){ | |||
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 class="col col-auto"> | |||
<button class="btn btn-copy btn-secondary" | |||
<button class="btn btn-copy btn-secondary hint--top" | |||
type="button" | |||
v-bind:aria-label="$t('Share this password')" | |||
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 type="button" class="btn btn-secondary" v-on:click="showOptions=!showOptions"> | |||
<i class="fa fa-sliders" aria-hidden="true"></i> | |||
@@ -80,8 +80,8 @@ test('SET_PASSWORD immutable', t => { | |||
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 = { | |||
defaultPassword: { | |||
site: '', | |||
@@ -95,7 +95,7 @@ test('SET_DEFAULT_PASSWORD', t => { | |||
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.false(state.defaultPassword.symbols); | |||
}); | |||