stash
This commit is contained in:
parent
17bfb84a94
commit
2fe8d14c2c
35 changed files with 1976 additions and 2134 deletions
|
|
@ -55,6 +55,10 @@
|
|||
Don’t have an account?
|
||||
<router-link to="/register">Sign up</router-link>
|
||||
</p>
|
||||
<p class="mb-0 text-muted">
|
||||
Connect with existing account?
|
||||
<router-link to="/pairing">Pairing</router-link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
236
frontend/src/views/Pairing.vue
Normal file
236
frontend/src/views/Pairing.vue
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
<template>
|
||||
<div class="main d-flex w-100">
|
||||
<div class="container d-flex flex-column">
|
||||
<div class="row vh-100">
|
||||
<div class="col-sm-10 col-md-8 col-lg-6 mx-auto d-table h-100">
|
||||
<div class="d-table-cell align-middle">
|
||||
<div class="text-center mt-4">
|
||||
<h1 class="h2">
|
||||
Toolshed
|
||||
</h1>
|
||||
|
||||
<p class="lead" v-if="msg">
|
||||
{{ msg }}
|
||||
</p>
|
||||
<p class="lead" v-else>
|
||||
Create an account to get started
|
||||
</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="m-sm-4">
|
||||
<div :class="errors.raw_record?['mb-3','is-invalid']:['mb-3']">
|
||||
<label class="form-label">Identity Record</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control form-control-lg" type="text"
|
||||
:class="errors.raw_record?['is-invalid']:[]"
|
||||
v-model="raw_record" placeholder="Paste identity record"
|
||||
@input.prevent="try_parsing">
|
||||
<span class="input-group-text form-control-lg" v-if="raw_record == ''"
|
||||
@click="pasteClipboard()">
|
||||
<b-icon-clipboard></b-icon-clipboard>
|
||||
</span>
|
||||
<span class="input-group-text form-control-lg" v-else
|
||||
@click="raw_record = ''">
|
||||
<b-icon-x-circle></b-icon-x-circle>
|
||||
</span>
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ errors.raw_record }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" v-if="is_unlocked || is_locked">
|
||||
<label class="form-label">Username</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control form-control-lg" type="email"
|
||||
disabled v-model="username">
|
||||
<span class="input-group-text form-control-lg bg-success"
|
||||
v-if="is_unlocked">
|
||||
<b-icon-unlock></b-icon-unlock>
|
||||
</span>
|
||||
<span class="input-group-text form-control-lg bg-warning"
|
||||
v-if="is_locked">
|
||||
<b-icon-lock></b-icon-lock>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" v-if="is_locked" :class="errors.password?['is-invalid']:[]">
|
||||
<form role="form" method="post" @submit.prevent="try_unlock">
|
||||
<label class="form-label">Password</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control form-control-lg" type="password"
|
||||
name="password" placeholder="Enter your password"
|
||||
v-model="password" :class="errors.password?['is-invalid']:[]">
|
||||
<button class="btn btn-lg btn-primary" type="button"
|
||||
@click="try_unlock">
|
||||
<b-icon-unlock></b-icon-unlock>
|
||||
unlock
|
||||
</button>
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ errors.password }}
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="remember-me"
|
||||
name="remember-me" checked v-model="remember"
|
||||
@change="setRemember(remember)">
|
||||
<span class="form-check-label">
|
||||
Remember me next time
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-3">
|
||||
<button type="submit" class="btn btn-lg btn-primary" :disabled="!is_unlocked"
|
||||
@click="try_pairing">
|
||||
Pair
|
||||
</button>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="text-center">
|
||||
<p class="mb-0 text-muted">
|
||||
Already have an account?
|
||||
<router-link to="/login">Login</router-link>
|
||||
</p>
|
||||
<p class="mb-0 text-muted">
|
||||
Don’t have an account?
|
||||
<router-link to="/register">Sign up</router-link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapActions, mapMutations} from 'vuex';
|
||||
import {ServerSet, createSignAuth} from '../federation';
|
||||
import NeighborsCache from '../neigbors';
|
||||
import {decryptIdentityRecord, parseIdentityRecord, serializeIdentityRecord} from "@/identity";
|
||||
import router from "@/router";
|
||||
|
||||
export default {
|
||||
name: 'Pairing',
|
||||
data() {
|
||||
return {
|
||||
msg: 'Pair with existing Account',
|
||||
//raw_record: 'test_a@a.localhost:4ab79601edc400fd0263c7d5fd045ea7f5de28f1dd8905e2479f96893f3257d8:7f0ee42b',
|
||||
//raw_record: 'test_a@a.localhost:$pbkdf2$100000$67da1c2a055dd41fc9707c37d2cdecea$8173b4d626352bcafab626b2$60027066142f55ecfd6f91b587f99c5f02ee536ce08c31e3f69148a51711489220a2b10d5450166ee09a781f891be8ab3a418e31a8e71f6468f189ba0baf31ece21166b63ba608683bdbea8afa7dfc33b1a994d8be4e4b2123',
|
||||
raw_record: '',
|
||||
crypted_record: null,
|
||||
username: '',
|
||||
password: '',
|
||||
keypair: '',
|
||||
remember: false,
|
||||
errors: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
is_unlocked() {
|
||||
return !!this.keypair && !!this.username;
|
||||
},
|
||||
is_locked() {
|
||||
return !!this.username && !!this.crypted_record && !this.keypair;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['lookupServer']),
|
||||
...mapMutations(['setRemember', 'setUserFromIdentityRecord']),
|
||||
async try_pairing() {
|
||||
const unreachable = new NeighborsCache();
|
||||
const servers = await this.lookupServer({username: this.username}).then(servers => new ServerSet(servers, unreachable))
|
||||
const auth = createSignAuth(this.username, this.keypair.signSk);
|
||||
const reply = await servers.getRaw(auth, '/api/friends/');
|
||||
if (reply.status === 200) {
|
||||
this.setUserFromIdentityRecord({username: this.username, keypair: this.keypair});
|
||||
router.push({path: '/'});
|
||||
} else {
|
||||
console.error('Error:', reply);
|
||||
}
|
||||
},
|
||||
try_parsing() {
|
||||
if (!this.raw_record) {
|
||||
this.username = '';
|
||||
this.keypair = '';
|
||||
this.errors = {};
|
||||
return;
|
||||
}
|
||||
const parsed = parseIdentityRecord(this.raw_record)
|
||||
const {user, keypair, errors} = parsed;
|
||||
if (user) {
|
||||
this.username = user;
|
||||
}
|
||||
if (errors) {
|
||||
this.errors.raw_record = errors;
|
||||
}
|
||||
if (keypair) {
|
||||
this.keypair = keypair;
|
||||
}
|
||||
if (parsed.crypted) {
|
||||
this.crypted_record = parsed;
|
||||
}
|
||||
},
|
||||
async try_unlock() {
|
||||
const {
|
||||
user,
|
||||
keypair,
|
||||
errors
|
||||
} = await decryptIdentityRecord(this.crypted_record, this.username, this.password);
|
||||
if (user) {
|
||||
this.username = user;
|
||||
}
|
||||
if (errors) {
|
||||
this.errors.password = errors;
|
||||
}
|
||||
if (keypair) {
|
||||
this.keypair = keypair;
|
||||
}
|
||||
},
|
||||
async pasteClipboard() {
|
||||
try {
|
||||
this.raw_record = await navigator.clipboard.readText();
|
||||
this.try_parsing()
|
||||
} catch (e) {
|
||||
console.error('Error:', e);
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
try {
|
||||
fetch('/local/domains')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
this.domains = data;
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Error:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.is-invalid input, .is-invalid select {
|
||||
border: 1px solid var(--bs-danger);
|
||||
}
|
||||
|
||||
.is-invalid .invalid-feedback {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.input-group-text svg {
|
||||
overflow: visible;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -27,9 +27,7 @@
|
|||
type="text" v-model="form.username" id="validationCustomUsername"
|
||||
placeholder="Enter your username" required/>
|
||||
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text form-control form-control-lg">@</span>
|
||||
</div>
|
||||
<span class="input-group-text form-control-lg">@</span>
|
||||
<select class="form-control form-control-lg"
|
||||
id="exampleFormControlSelect1"
|
||||
placeholder="Domain" v-model="form.domain" required>
|
||||
|
|
@ -77,6 +75,10 @@
|
|||
Already have an account?
|
||||
<router-link to="/login">Login</router-link>
|
||||
</p>
|
||||
<p class="mb-0 text-muted">
|
||||
Connect with existing account?
|
||||
<router-link to="/pairing">Pairing</router-link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,43 +2,32 @@
|
|||
<BaseLayout>
|
||||
<main class="content">
|
||||
<div class="container-fluid p-0">
|
||||
|
||||
<h1 class="h3 mb-3">Settings</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xl-2">
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Profile Settings</h5>
|
||||
</div>
|
||||
|
||||
<div class="list-group list-group-flush" role="tablist">
|
||||
<a class="list-group-item list-group-item-action" @click="active_tab = 'account'"
|
||||
:class="{ active: active_tab === 'account' }" href="#" role="tab">
|
||||
Account
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action" @click="active_tab = 'password'"
|
||||
:class="{ active: active_tab === 'password' }" href="#"
|
||||
role="tab">
|
||||
Password
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action" @click="active_tab = 'privacy'"
|
||||
:class="{ active: active_tab === 'privacy' }" href="#" role="tab">
|
||||
Privacy and safety
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action" @click="active_tab = 'email'"
|
||||
:class="{ active: active_tab === 'email' }" href="#" role="tab">
|
||||
Email notifications
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action" @click="active_tab = 'notifications'"
|
||||
:class="{ active: active_tab === 'notifications' }" href="#">
|
||||
Web notifications
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action" @click="active_tab = 'data'"
|
||||
:class="{ active: active_tab === 'data' }" href="#">
|
||||
Your data
|
||||
</a>
|
||||
<router-link class="list-group-item list-group-item-action" :to="{name: 'account'}"
|
||||
active-class="dummy" exact-active-class="active">Account
|
||||
</router-link>
|
||||
<router-link class="list-group-item list-group-item-action" :to="{name: 'password'}"
|
||||
active-class="active">Password
|
||||
</router-link>
|
||||
<router-link class="list-group-item list-group-item-action" :to="{name: 'privacy'}"
|
||||
active-class="active">Privacy and safety
|
||||
</router-link>
|
||||
<router-link class="list-group-item list-group-item-action" :to="{name: 'email'}"
|
||||
active-class="active">Email notifications
|
||||
</router-link>
|
||||
<router-link class="list-group-item list-group-item-action"
|
||||
:to="{name: 'notifications'}" active-class="active">Web notifications
|
||||
</router-link>
|
||||
<router-link class="list-group-item list-group-item-action" :to="{name: 'data'}"
|
||||
active-class="active">Your data
|
||||
</router-link>
|
||||
<a class="list-group-item list-group-item-action delete" @click="deleteAccount"
|
||||
href="#">
|
||||
Delete account
|
||||
|
|
@ -46,306 +35,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-9 col-xl-10">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade" :class="{ 'show active': active_tab === 'account' }" id="account"
|
||||
role="tabpanel">
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
|
||||
<h5 class="card-title mb-0">Public info</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="mb-3">
|
||||
<label class="form-label"
|
||||
for="inputUsername">Username</label>
|
||||
<input type="text" class="form-control" id="inputUsername"
|
||||
placeholder="Username">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"
|
||||
for="inputUsername">Biography</label>
|
||||
<textarea rows="2" class="form-control" id="inputBio"
|
||||
placeholder="Tell something about yourself"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="text-center">
|
||||
<img alt="Charles Hall"
|
||||
src="/assets/img/avatars/avatar.png"
|
||||
class="rounded-circle img-responsive mt-2" width="128"
|
||||
height="128"/>
|
||||
<div class="mt-2">
|
||||
<span class="btn btn-primary"><i
|
||||
class="fas fa-upload"></i> Upload</span>
|
||||
</div>
|
||||
<small>For best results, use an image at least 128px by
|
||||
128px in .jpg format</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
|
||||
<h5 class="card-title mb-0">Private info</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="row">
|
||||
<div class="mb-3 col-md-6">
|
||||
<label class="form-label" for="inputFirstName">First
|
||||
name</label>
|
||||
<input type="text" class="form-control" id="inputFirstName"
|
||||
placeholder="First name">
|
||||
</div>
|
||||
<div class="mb-3 col-md-6">
|
||||
<label class="form-label" for="inputLastName">Last name</label>
|
||||
<input type="text" class="form-control" id="inputLastName"
|
||||
placeholder="Last name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="inputEmail4">Email</label>
|
||||
<input type="email" class="form-control" id="inputEmail4"
|
||||
placeholder="Email">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="inputAddress">Address</label>
|
||||
<input type="text" class="form-control" id="inputAddress"
|
||||
placeholder="1234 Main St">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="inputAddress2">Address 2</label>
|
||||
<input type="text" class="form-control" id="inputAddress2"
|
||||
placeholder="Apartment, studio, or floor">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="mb-3 col-md-6">
|
||||
<label class="form-label" for="inputCity">City</label>
|
||||
<input type="text" class="form-control" id="inputCity">
|
||||
</div>
|
||||
<div class="mb-3 col-md-4">
|
||||
<label class="form-label" for="inputState">State</label>
|
||||
<select id="inputState" class="form-control">
|
||||
<option selected>Choose...</option>
|
||||
<option>...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3 col-md-2">
|
||||
<label class="form-label" for="inputZip">Zip</label>
|
||||
<input type="text" class="form-control" id="inputZip">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="tab-pane fade" :class="{ 'show active': active_tab === 'password' }"
|
||||
id="password" role="tabpanel">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Password</h5>
|
||||
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="inputPasswordCurrent">Current
|
||||
password</label>
|
||||
<input type="password" class="form-control"
|
||||
id="inputPasswordCurrent">
|
||||
<small><a href="#">Forgot your password?</a></small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="inputPasswordNew">New
|
||||
password</label>
|
||||
<input type="password" class="form-control" id="inputPasswordNew">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="inputPasswordNew2">Verify
|
||||
password</label>
|
||||
<input type="password" class="form-control" id="inputPasswordNew2">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" :class="{ 'show active': active_tab === 'privacy' }" id="privacy"
|
||||
role="tabpanel">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Privacy and safety</h5>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label d-block">Profile visibility</label>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check input-switch" type="checkbox"
|
||||
id="flexSwitchCheckDefault">
|
||||
<label class="form-check
|
||||
label-switch" for="flexSwitchCheckDefault">Visible to
|
||||
everyone</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label
|
||||
d-block">Photo visibility</label>
|
||||
<div class="form-check
|
||||
form-switch">
|
||||
<input class="form-check
|
||||
input-switch" type="checkbox"
|
||||
id="flexSwitchCheckDefault">
|
||||
<label class="form-check
|
||||
label-switch" for="flexSwitchCheckDefault">Visible to
|
||||
everyone</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label
|
||||
d-block">Last seen</label>
|
||||
<div class="form-check
|
||||
form-switch">
|
||||
<input class="form-check
|
||||
input-switch" type="checkbox"
|
||||
id="flexSwitchCheckDefault">
|
||||
<label class="form-check
|
||||
label-switch" for="flexSwitchCheckDefault">Visible to
|
||||
everyone</label>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save
|
||||
changes
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<small>Changes will be reflected to all your devices</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="tab-pane fade" :class="{ 'show active': active_tab === 'email' }" id="email"
|
||||
role="tabpanel">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title
|
||||
mb-0">Email notifications</h5>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label
|
||||
d-block">Email notifications</label>
|
||||
<div class="form-check
|
||||
form-switch">
|
||||
<input class="form-check
|
||||
input-switch" type="checkbox"
|
||||
id="flexSwitchCheckDefault">
|
||||
<label class="form-check
|
||||
label-switch" for="flexSwitchCheckDefault">Receive
|
||||
email notifications</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label
|
||||
d-block">Send email notifications to</label>
|
||||
<input type="email" class="form-control"
|
||||
id="inputEmail">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save
|
||||
changes
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" :class="{ 'show active': active_tab === 'notifications' }"
|
||||
id="notifications" role="tabpanel">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-0">Web notifications</h5>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label d-block">Web notifications</label>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check input-switch" type="checkbox"
|
||||
id="flexSwitchCheckDefault">
|
||||
<label class="form-check label-switch" for="flexSwitchCheckDefault">Receive
|
||||
web notifications</label>
|
||||
<small>These notifications can be sent to your email or
|
||||
phone</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label d-block">Send web notifications to</label>
|
||||
<input type="email" class="form-control"
|
||||
id="inputEmail">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" :class="{ 'show active': active_tab === 'data' }" id="data"
|
||||
role="tabpanel">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-0">Export</h5>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label
|
||||
d-block">Download your data</label>
|
||||
<button type="button" class="btn btn-primary">Download</button>
|
||||
<small>Download all your data including photos, videos,
|
||||
comments, profile information and more</small>
|
||||
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label
|
||||
d-block">Delete your data</label>
|
||||
<button type="button" class="btn btn-danger">Delete</button>
|
||||
<small>Delete all your data including photos, videos,
|
||||
comments, profile information and more</small>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-0">Import</h5>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label
|
||||
d-block">Import data</label>
|
||||
<button type="button" class="btn btn-primary">Import</button>
|
||||
<small>Import data from backup or other instances</small>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
</BaseLayout>
|
||||
|
|
@ -357,15 +52,12 @@ import BaseLayout from "@/components/BaseLayout.vue";
|
|||
export default {
|
||||
name: 'Settings',
|
||||
components: {BaseLayout},
|
||||
data: () => ({
|
||||
active_tab: "account"
|
||||
}),
|
||||
methods: {
|
||||
deleteAccount() {
|
||||
if (confirm('Are you sure you want to delete your account?')) {
|
||||
alert('Account deleted');
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
111
frontend/src/views/settings/Account.vue
Normal file
111
frontend/src/views/settings/Account.vue
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Public info</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="mb-3">
|
||||
<label class="form-label"
|
||||
for="inputUsername">Username</label>
|
||||
<input type="text" class="form-control" id="inputUsername"
|
||||
placeholder="Username">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"
|
||||
for="inputUsername">Biography</label>
|
||||
<textarea rows="2" class="form-control" id="inputBio"
|
||||
placeholder="Tell something about yourself"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="text-center">
|
||||
<img alt="Charles Hall"
|
||||
src="/assets/img/avatars/avatar.png"
|
||||
class="rounded-circle img-responsive mt-2" width="128"
|
||||
height="128"/>
|
||||
<div class="mt-2">
|
||||
<span class="btn btn-primary"><i
|
||||
class="fas fa-upload"></i> Upload</span>
|
||||
</div>
|
||||
<small>For best results, use an image at least 128px by
|
||||
128px in .jpg format</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Private info</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="row">
|
||||
<div class="mb-3 col-md-6">
|
||||
<label class="form-label" for="inputFirstName">First
|
||||
name</label>
|
||||
<input type="text" class="form-control" id="inputFirstName"
|
||||
placeholder="First name">
|
||||
</div>
|
||||
<div class="mb-3 col-md-6">
|
||||
<label class="form-label" for="inputLastName">Last name</label>
|
||||
<input type="text" class="form-control" id="inputLastName"
|
||||
placeholder="Last name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="inputEmail4">Email</label>
|
||||
<input type="email" class="form-control" id="inputEmail4"
|
||||
placeholder="Email">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="inputAddress">Address</label>
|
||||
<input type="text" class="form-control" id="inputAddress"
|
||||
placeholder="1234 Main St">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="inputAddress2">Address 2</label>
|
||||
<input type="text" class="form-control" id="inputAddress2"
|
||||
placeholder="Apartment, studio, or floor">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="mb-3 col-md-6">
|
||||
<label class="form-label" for="inputCity">City</label>
|
||||
<input type="text" class="form-control" id="inputCity">
|
||||
</div>
|
||||
<div class="mb-3 col-md-4">
|
||||
<label class="form-label" for="inputState">State</label>
|
||||
<select id="inputState" class="form-control">
|
||||
<option selected>Choose...</option>
|
||||
<option>...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3 col-md-2">
|
||||
<label class="form-label" for="inputZip">Zip</label>
|
||||
<input type="text" class="form-control" id="inputZip">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Account',
|
||||
components: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
147
frontend/src/views/settings/Data.vue
Normal file
147
frontend/src/views/settings/Data.vue
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-0">Backup Account Key</h5>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label d-block">Backup your account key</label>
|
||||
<button type="button" class="btn btn-primary" @click="exportKey">
|
||||
Backup
|
||||
</button>
|
||||
<br>
|
||||
<small>Backup your account key to restore your account in case
|
||||
of loss</small>
|
||||
<br>
|
||||
<p>{{ localUserIdentityRecord }}</p>
|
||||
|
||||
<vue-qrcode :value="'foo'" tag="svg" :size="200" :options="{errorCorrectionLevel: 'H'}"></vue-qrcode>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-0">Export</h5>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label
|
||||
d-block">Download your data</label>
|
||||
<button type="button" class="btn btn-primary" @click="exportData">
|
||||
Download
|
||||
</button>
|
||||
<br>
|
||||
<small>Download all your data including photos, videos,
|
||||
comments, profile information and more</small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label
|
||||
d-block">Delete your data</label>
|
||||
<button type="button" class="btn btn-danger" @click="deleteData">
|
||||
Delete
|
||||
</button>
|
||||
<br>
|
||||
<small>Delete all your data including photos, videos,
|
||||
comments, profile information and more</small>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-0">Import</h5>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label
|
||||
d-block">Import data</label>
|
||||
<div class="btn-group">
|
||||
<input type="file" class="form-control" id="inputFile"
|
||||
accept=".zip" @change="chooseFile">
|
||||
<button type="button" class="btn btn-primary" @click="importData"
|
||||
:disabled="!selectedFile">
|
||||
Import
|
||||
</button>
|
||||
</div>
|
||||
<br>
|
||||
<small>Import data from backup or other instances</small>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapActions, mapGetters, mapMutations} from 'vuex';
|
||||
import router from "@/router";
|
||||
|
||||
//import VueQrcode from '@chenfengyuan/vue-qrcode';
|
||||
|
||||
export default {
|
||||
name: 'Data',
|
||||
components: {},
|
||||
data: () => ({
|
||||
selectedFile: null,
|
||||
localUserIdentityRecord: null
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters(['signAuth'])
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['getHomeServers', 'userIdentityRecord']),
|
||||
chooseFile(event) {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
this.selectedFile = file;
|
||||
}
|
||||
},
|
||||
deleteData() {
|
||||
if (confirm('Are you sure you want to delete your data?')) {
|
||||
alert('Data deleted');
|
||||
}
|
||||
},
|
||||
exportKey() {
|
||||
const key = this.userIdentityRecord;
|
||||
},
|
||||
async importData() {
|
||||
if (!this.selectedFile) {
|
||||
alert('Please select a file to import');
|
||||
return;
|
||||
}
|
||||
alert('Data import not implemented');
|
||||
return;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', this.selectedFile);
|
||||
console.log(formData);
|
||||
const servers = await this.getHomeServers();
|
||||
const data = await servers.postRaw(this.signHashedAuth, '/api/import/', formData);
|
||||
console.log(data);
|
||||
},
|
||||
async exportData() {
|
||||
const servers = await this.getHomeServers();
|
||||
const data = await servers.getRaw(this.signAuth, '/api/export/');
|
||||
const blob = new Blob([await data.blob()], {type: 'application/zip'});
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
const date_str = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '').replace(/:/g, '-');
|
||||
a.download = 'toolshed_data_' + date_str + '.zip';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.userIdentityRecord({}).then((data) => {
|
||||
this.localUserIdentityRecord = data;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
41
frontend/src/views/settings/Email.vue
Normal file
41
frontend/src/views/settings/Email.vue
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-0">Email notifications</h5>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label d-block">Email notifications</label>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check input-switch" type="checkbox"
|
||||
id="flexSwitchCheckDefault">
|
||||
<label class="form-check label-switch" for="flexSwitchCheckDefault">Receive
|
||||
email notifications</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label d-block">Send email notifications to</label>
|
||||
<input type="email" class="form-control"
|
||||
id="inputEmail">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save
|
||||
changes
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Email',
|
||||
components: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
42
frontend/src/views/settings/Notifications.vue
Normal file
42
frontend/src/views/settings/Notifications.vue
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-0">Web notifications</h5>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label d-block">Web notifications</label>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check input-switch" type="checkbox"
|
||||
id="flexSwitchCheckDefault">
|
||||
<label class="form-check label-switch" for="flexSwitchCheckDefault">Receive
|
||||
web notifications</label>
|
||||
<small>These notifications can be sent to your email or
|
||||
phone</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label d-block">Send web notifications to</label>
|
||||
<input type="email" class="form-control"
|
||||
id="inputEmail">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Notifications',
|
||||
components: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
41
frontend/src/views/settings/Password.vue
Normal file
41
frontend/src/views/settings/Password.vue
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Password</h5>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="inputPasswordCurrent">Current
|
||||
password</label>
|
||||
<input type="password" class="form-control"
|
||||
id="inputPasswordCurrent">
|
||||
<small><a href="#">Forgot your password?</a></small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="inputPasswordNew">New
|
||||
password</label>
|
||||
<input type="password" class="form-control" id="inputPasswordNew">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="inputPasswordNew2">Verify
|
||||
password</label>
|
||||
<input type="password" class="form-control" id="inputPasswordNew2">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Password',
|
||||
components: {},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
68
frontend/src/views/settings/Privacy.vue
Normal file
68
frontend/src/views/settings/Privacy.vue
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Privacy and safety</h5>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label d-block">Profile visibility</label>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check input-switch" type="checkbox"
|
||||
id="flexSwitchCheckDefault">
|
||||
<label class="form-check
|
||||
label-switch" for="flexSwitchCheckDefault">Visible to
|
||||
everyone</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label
|
||||
d-block">Photo visibility</label>
|
||||
<div class="form-check
|
||||
form-switch">
|
||||
<input class="form-check
|
||||
input-switch" type="checkbox"
|
||||
id="flexSwitchCheckDefault">
|
||||
<label class="form-check
|
||||
label-switch" for="flexSwitchCheckDefault">Visible to
|
||||
everyone</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label
|
||||
d-block">Last seen</label>
|
||||
<div class="form-check
|
||||
form-switch">
|
||||
<input class="form-check
|
||||
input-switch" type="checkbox"
|
||||
id="flexSwitchCheckDefault">
|
||||
<label class="form-check
|
||||
label-switch" for="flexSwitchCheckDefault">Visible to
|
||||
everyone</label>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save
|
||||
changes
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<small>Changes will be reflected to all your devices</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapActions, mapGetters, mapMutations} from 'vuex';
|
||||
import router from "@/router";
|
||||
|
||||
export default {
|
||||
name: 'Privacy',
|
||||
components: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue