Compare commits
2 commits
f45c77ba64
...
db117dab29
Author | SHA1 | Date | |
---|---|---|---|
|
db117dab29 | ||
|
1c8d6db391 |
7 changed files with 43 additions and 15 deletions
|
@ -14,8 +14,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Possibility to set a default account
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- The style has been entirely reworked using Bootstrap instead of Bulma
|
- The style has been entirely reworked using Bootstrap instead of Bulma
|
||||||
|
- It is now impossible to include the separator in the dedicated name
|
||||||
|
|
||||||
|
|
||||||
## [0.3.0] - 2023-08-25
|
## [0.3.0] - 2023-08-25
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export function sortAccounts(accounts) {
|
export function sortAccounts(accounts) {
|
||||||
accounts.value.sort((a, b) => {
|
accounts.sort((a, b) => {
|
||||||
const va = `${a.localPart}@${a.domain}`;
|
const va = `${a.localPart}@${a.domain}`;
|
||||||
const vb = `${b.localPart}@${b.domain}`;
|
const vb = `${b.localPart}@${b.domain}`;
|
||||||
return va.localeCompare(vb);
|
return va.localeCompare(vb);
|
||||||
|
|
|
@ -71,6 +71,8 @@
|
||||||
},
|
},
|
||||||
"manageAccounts": {
|
"manageAccounts": {
|
||||||
"title": "Accounts",
|
"title": "Accounts",
|
||||||
|
"isDefault": "default",
|
||||||
|
"setDefault": "Set default",
|
||||||
"delete": "@:invariants.controls.delete",
|
"delete": "@:invariants.controls.delete",
|
||||||
"close": "@:invariants.controls.close"
|
"close": "@:invariants.controls.close"
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,8 @@
|
||||||
},
|
},
|
||||||
"manageAccounts": {
|
"manageAccounts": {
|
||||||
"title": "Comptes",
|
"title": "Comptes",
|
||||||
|
"isDefault": "défaut",
|
||||||
|
"setDefault": "Définir par défaut",
|
||||||
"delete": "@:invariants.controls.delete",
|
"delete": "@:invariants.controls.delete",
|
||||||
"close": "@:invariants.controls.close"
|
"close": "@:invariants.controls.close"
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@ const addAccount = () => {
|
||||||
separator: separator.value,
|
separator: separator.value,
|
||||||
domain: domainName.value,
|
domain: domainName.value,
|
||||||
key: key,
|
key: key,
|
||||||
|
isDefault: false,
|
||||||
};
|
};
|
||||||
accounts.value.push(newAccount);
|
accounts.value.push(newAccount);
|
||||||
return toMainView();
|
return toMainView();
|
||||||
|
|
|
@ -11,8 +11,12 @@ import LayoutComponent from '../components/LayoutComponent.vue';
|
||||||
import NavBarComponent from '../components/NavBarComponent.vue';
|
import NavBarComponent from '../components/NavBarComponent.vue';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const accounts = sortAccounts(useStorage('sake-accounts', []));
|
const accounts = useStorage('sake-accounts', []);
|
||||||
const selectedAccountId = ref(accounts.value[0].id);
|
const sortedAccounts = computed(() => sortAccounts(accounts.value));
|
||||||
|
const selectedAccountId = ref((() => {
|
||||||
|
const def = accounts.value.find((a) => a.isDefault);
|
||||||
|
return def ? def.id : accounts.value[0].id;
|
||||||
|
})());
|
||||||
const subAddrName = ref('');
|
const subAddrName = ref('');
|
||||||
|
|
||||||
const fromRawAccount = (raw_account) => {
|
const fromRawAccount = (raw_account) => {
|
||||||
|
@ -29,15 +33,20 @@ const generatedAddr = computed(() => {
|
||||||
const raw_account = accounts.value.find((e) => e.id == selectedAccountId.value);
|
const raw_account = accounts.value.find((e) => e.id == selectedAccountId.value);
|
||||||
if (raw_account) {
|
if (raw_account) {
|
||||||
const account = fromRawAccount(raw_account);
|
const account = fromRawAccount(raw_account);
|
||||||
var hasher = hmac.create(sha256, account.key);
|
if (subAddrName.value.indexOf(account.separator) != -1) {
|
||||||
hasher.update(account.localPart);
|
subAddrName.value = subAddrName.value.replaceAll(account.separator, '');
|
||||||
hasher.update(account.separator);
|
}
|
||||||
hasher.update(subAddrName.value);
|
if (subAddrName.value) {
|
||||||
const mac = hasher.digest();
|
var hasher = hmac.create(sha256, account.key);
|
||||||
const offset = mac[mac.length - 1] & 0xf;
|
hasher.update(account.localPart);
|
||||||
const reduced_mac = mac.slice(offset, offset + 5);
|
hasher.update(account.separator);
|
||||||
const code = base32Encode(reduced_mac, 'RFC4648', { padding: false }).toLowerCase();
|
hasher.update(subAddrName.value);
|
||||||
return `${account.localPart}${account.separator}${subAddrName.value}${account.separator}${code}@${account.domain}`;
|
const mac = hasher.digest();
|
||||||
|
const offset = mac[mac.length - 1] & 0xf;
|
||||||
|
const reduced_mac = mac.slice(offset, offset + 5);
|
||||||
|
const code = base32Encode(reduced_mac, 'RFC4648', { padding: false }).toLowerCase();
|
||||||
|
return `${account.localPart}${account.separator}${subAddrName.value}${account.separator}${code}@${account.domain}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
|
@ -58,7 +67,7 @@ const resetForm = () => {
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label" for="account-name">{{ $t("main.account") }}</label>
|
<label class="form-label" for="account-name">{{ $t("main.account") }}</label>
|
||||||
<select class="form-select" id="account-name" v-model="selectedAccountId">
|
<select class="form-select" id="account-name" v-model="selectedAccountId">
|
||||||
<option v-for="account in accounts" :key="account.id" :value="account.id">{{ account.localPart }}@{{ account.domain }}</option>
|
<option v-for="account in sortedAccounts" :key="account.id" :value="account.id">{{ account.localPart }}@{{ account.domain }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,24 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { sortAccounts } from '../accounts';
|
import { sortAccounts } from '../accounts';
|
||||||
|
import { computed } from 'vue';
|
||||||
import { RouterLink, useRouter } from 'vue-router';
|
import { RouterLink, useRouter } from 'vue-router';
|
||||||
import { useStorage } from '@vueuse/core';
|
import { useStorage } from '@vueuse/core';
|
||||||
import ButtonGroupComponent from '../components/ButtonGroupComponent.vue';
|
import ButtonGroupComponent from '../components/ButtonGroupComponent.vue';
|
||||||
import LayoutComponent from '../components/LayoutComponent.vue';
|
import LayoutComponent from '../components/LayoutComponent.vue';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const accounts = sortAccounts(useStorage('sake-accounts', []));
|
const accounts = useStorage('sake-accounts', []);
|
||||||
|
const sortedAccounts = computed(() => sortAccounts(accounts.value));
|
||||||
|
|
||||||
const deleteAccount = (accountId) => {
|
const deleteAccount = (accountId) => {
|
||||||
return router.push(`/delete-account/${accountId}`);
|
return router.push(`/delete-account/${accountId}`);
|
||||||
};
|
};
|
||||||
|
const setDefaultAccount = (accountId) => {
|
||||||
|
accounts.value = accounts.value.map((a) => {
|
||||||
|
a.isDefault = a.id === accountId;
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
};
|
||||||
const toMainView = () => {
|
const toMainView = () => {
|
||||||
return router.push('/');
|
return router.push('/');
|
||||||
};
|
};
|
||||||
|
@ -22,11 +30,13 @@ const toMainView = () => {
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="account in accounts">
|
<tr v-for="account in sortedAccounts">
|
||||||
<th class="text-end align-middle">
|
<th class="text-end align-middle">
|
||||||
|
<span class="badge text-bg-primary" v-if="account.isDefault">{{ $t("manageAccounts.isDefault") }}</span>
|
||||||
{{ account.localPart }}@{{ account.domain }}
|
{{ account.localPart }}@{{ account.domain }}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
|
<button type="button" class="btn btn-primary me-2" @click="setDefaultAccount(account.id)" v-if="!account.isDefault">{{ $t("manageAccounts.setDefault") }}</button>
|
||||||
<button type="button" class="btn btn-danger" @click="deleteAccount(account.id)">{{ $t("manageAccounts.delete") }}</button>
|
<button type="button" class="btn btn-danger" @click="deleteAccount(account.id)">{{ $t("manageAccounts.delete") }}</button>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
Loading…
Reference in a new issue