stash
This commit is contained in:
parent
08f723e9da
commit
e2cf04cf18
9 changed files with 25270 additions and 78 deletions
25141
frontend/src/assets/js/app.js
Normal file
25141
frontend/src/assets/js/app.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -3,10 +3,10 @@
|
||||||
<Sidebar/>
|
<Sidebar/>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<nav class="navbar navbar-expand navbar-light navbar-bg">
|
<nav class="navbar navbar-expand navbar-light navbar-bg">
|
||||||
<a class="sidebar-toggle d-flex">
|
<a class="sidebar-toggle d-flex" @click="toggleSidebar">
|
||||||
<i class="hamburger align-self-center"></i>
|
<i class="hamburger align-self-center"></i>
|
||||||
</a>
|
</a>
|
||||||
<SearchBox v-if="!hideSearch"/>
|
<SearchBox/>
|
||||||
<div class="navbar-collapse collapse">
|
<div class="navbar-collapse collapse">
|
||||||
<ul class="navbar-nav navbar-align">
|
<ul class="navbar-nav navbar-align">
|
||||||
<Notifications :notifications="notifications"/>
|
<Notifications :notifications="notifications"/>
|
||||||
|
@ -51,8 +51,12 @@ export default {
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
methods: {
|
||||||
}
|
toggleSidebar() {
|
||||||
|
closeAllDropdowns();
|
||||||
|
document.getElementById("sidebar").classList.toggle("collapsed");
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -8,18 +8,25 @@
|
||||||
<ul>
|
<ul>
|
||||||
<img v-for="file in files" :key="file.id" :src="file.name" :alt="file.name" class="img-thumbnail">
|
<img v-for="file in files" :key="file.id" :src="file.name" :alt="file.name" class="img-thumbnail">
|
||||||
</ul>
|
</ul>
|
||||||
<fieldset>
|
<legend>Files</legend>
|
||||||
<legend>Files</legend>
|
<div class="input-group">
|
||||||
<div class="input-group">
|
<input type="file" class="form-control" multiple id="files">
|
||||||
<input type="file" class="form-control" multiple id="files">
|
<button class="btn btn-outline-secondary" type="button">
|
||||||
<button class="btn btn-outline-secondary" type="button">
|
<b-icon-trash></b-icon-trash>
|
||||||
<b-icon-trash></b-icon-trash>
|
</button>
|
||||||
</button>
|
<button class="btn btn-outline-secondary" type="button" @click="uploadFiles">
|
||||||
<button class="btn btn-outline-secondary" type="button" @click="uploadFiles">
|
<b-icon-upload></b-icon-upload>
|
||||||
<b-icon-upload></b-icon-upload>
|
</button>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
<div class="input-group" v-if="show_camera">
|
||||||
</fieldset>
|
<input type="file" class="form-control" accept="image/*" capture="camera" id="photos">
|
||||||
|
<button class="btn btn-outline-secondary" type="button">
|
||||||
|
<b-icon-trash></b-icon-trash>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary" type="button" @click="uploadPhoto">
|
||||||
|
<b-icon-upload></b-icon-upload>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -38,7 +45,9 @@ import {mapActions, mapState} from "vuex";
|
||||||
export default {
|
export default {
|
||||||
name: "CombinedFileField",
|
name: "CombinedFileField",
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {
|
||||||
|
show_camera: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
...BIcons
|
...BIcons
|
||||||
|
@ -55,15 +64,6 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["files"]),
|
...mapState(["files"]),
|
||||||
/*localValue: {
|
|
||||||
get() {
|
|
||||||
return this.value;
|
|
||||||
},
|
|
||||||
set(value) {
|
|
||||||
this.$emit("input", value);
|
|
||||||
console.log("set", value);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(["fetchFiles", "pushFile"]),
|
...mapActions(["fetchFiles", "pushFile"]),
|
||||||
|
@ -84,20 +84,9 @@ export default {
|
||||||
});
|
});
|
||||||
const data = new Uint8Array(buffer);
|
const data = new Uint8Array(buffer);
|
||||||
const hash = nacl.crypto_hash(data);
|
const hash = nacl.crypto_hash(data);
|
||||||
//const hex_hash = Array.from(hash)
|
|
||||||
// .map(b => b.toString(16).padStart(2, "0"))
|
|
||||||
// .join("");
|
|
||||||
//console.log("name", file.name);
|
|
||||||
//console.log("size", file.size);
|
|
||||||
//console.log("type", file.type);
|
|
||||||
//console.log("hash", hex_hash);
|
|
||||||
var base64 = btoa(
|
var base64 = btoa(
|
||||||
data.reduce((a, b) => a + String.fromCharCode(b), '')
|
data.reduce((a, b) => a + String.fromCharCode(b), '')
|
||||||
);
|
);
|
||||||
//console.log("base64", base64.length);
|
|
||||||
//console.log("base64", base64);
|
|
||||||
//console.log("base64", typeof base64);
|
|
||||||
//formData.append("files[]", files[i]);
|
|
||||||
await this.pushFile({
|
await this.pushFile({
|
||||||
file: {
|
file: {
|
||||||
mime_type: file.type,
|
mime_type: file.type,
|
||||||
|
@ -108,10 +97,51 @@ export default {
|
||||||
await this.fetchFiles();
|
await this.fetchFiles();
|
||||||
}
|
}
|
||||||
//console.log("formData", formData);
|
//console.log("formData", formData);
|
||||||
}
|
},
|
||||||
|
async uploadPhoto() {
|
||||||
|
//const formData = new FormData();
|
||||||
|
const files = document.getElementById("photos").files;
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
const file = files[i];
|
||||||
|
const buffer = await new Promise((resolve, reject) => {
|
||||||
|
reader.onload = (e) => {
|
||||||
|
resolve(e.target.result);
|
||||||
|
};
|
||||||
|
reader.onerror = (e) => {
|
||||||
|
reject(e);
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file)
|
||||||
|
});
|
||||||
|
const data = new Uint8Array(buffer);
|
||||||
|
const hash = nacl.crypto_hash(data);
|
||||||
|
var base64 = btoa(
|
||||||
|
data.reduce((a, b) => a + String.fromCharCode(b), '')
|
||||||
|
);
|
||||||
|
await this.pushFile({
|
||||||
|
file: {
|
||||||
|
mime_type: file.type,
|
||||||
|
data: base64,
|
||||||
|
},
|
||||||
|
item_id: 1
|
||||||
|
});
|
||||||
|
await this.fetchFiles();
|
||||||
|
}
|
||||||
|
//console.log("formData", formData);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.fetchFiles();
|
this.fetchFiles();
|
||||||
|
//detect media api
|
||||||
|
if ('capture' in document.createElement('input')) {
|
||||||
|
console.log('capture supported.');
|
||||||
|
this.show_camera = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navigator.getUserMedia) {
|
||||||
|
console.log('getUserMedia supported.');
|
||||||
|
this.show_camera = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-icon dropdown-toggle" href="#" id="messagesDropdown" @click.prevent="toggleDropdown">
|
<a class="nav-icon dropdown-toggle" href="#" @click="toggleDropdown">
|
||||||
<div class="position-relative">
|
<div class="position-relative">
|
||||||
<b-icon-chat-left class="bi-valign-middle"></b-icon-chat-left>
|
<b-icon-chat-left class="bi-valign-middle"></b-icon-chat-left>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<div :class="['dropdown-menu','dropdown-menu-right', 'dropdown-menu-lg','py-0', {'show': show_dropdown}]">
|
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-right py-0" id="messagesDropdown">
|
||||||
<div class="dropdown-menu-header">
|
<div class="dropdown-menu-header">
|
||||||
<div class="position-relative">
|
<div class="position-relative">
|
||||||
4 New Messages
|
4 New Messages
|
||||||
|
@ -94,11 +94,6 @@ export default {
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
show_dropdown: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
top_messages() {
|
top_messages() {
|
||||||
return this.messages.slice(0, 4);
|
return this.messages.slice(0, 4);
|
||||||
|
@ -106,12 +101,12 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleDropdown() {
|
toggleDropdown() {
|
||||||
this.show_dropdown = !this.show_dropdown;
|
closeAllDropdowns();
|
||||||
|
document.getElementById("messagesDropdown").classList.toggle("show");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-icon dropdown-toggle" href="#" id="alertsDropdown" @click.prevent="toggleDropdown">
|
<a class="nav-icon dropdown-toggle" href="#" @click="toggleDropdown">
|
||||||
<div class="position-relative">
|
<div class="position-relative">
|
||||||
<b-icon-bell class="bi-valign-middle"></b-icon-bell>
|
<b-icon-bell class="bi-valign-middle"></b-icon-bell>
|
||||||
<span :class="['indicator', notificationsColor(top_notifications)]">{{
|
<span :class="['indicator', notificationsColor(top_notifications)]">{{
|
||||||
top_notifications.length
|
top_notifications.length
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<div :class="['dropdown-menu','dropdown-menu-right', 'dropdown-menu-lg','py-0', {'show': show_dropdown}]">
|
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-right py-0" id="notificationsDropdown">
|
||||||
<div class="dropdown-menu-header">
|
<div class="dropdown-menu-header">
|
||||||
{{ top_notifications.length }} New Notifications
|
{{ top_notifications.length }} New Notifications
|
||||||
</div>
|
</div>
|
||||||
|
@ -60,11 +60,6 @@ export default {
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
show_dropdown: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
top_notifications() {
|
top_notifications() {
|
||||||
return this.notifications.sort((a, b) => {
|
return this.notifications.sort((a, b) => {
|
||||||
|
@ -90,7 +85,8 @@ export default {
|
||||||
return 'bg-primary';
|
return 'bg-primary';
|
||||||
},
|
},
|
||||||
toggleDropdown() {
|
toggleDropdown() {
|
||||||
this.show_dropdown = !this.show_dropdown
|
closeAllDropdowns();
|
||||||
|
document.getElementById('notificationsDropdown').classList.toggle('show');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<nav id="sidebar" class="sidebar">
|
<nav id="sidebar" class="sidebar">
|
||||||
<div class="sidebar-content js-simplebar">
|
<div class="sidebar-content">
|
||||||
<router-link to="/" class="sidebar-brand">
|
<router-link to="/" class="sidebar-brand">
|
||||||
<!--img src="/src/assets/icons/toolshed-48x48.png" alt="Toolshed logo"-->
|
<img src="/src/assets/icons/toolshed-48x48.png" alt="Toolshed Logo" class="align-middle logo mr-2 h-75">
|
||||||
<span class="align-middle">Toolshed</span>
|
<span class="align-middle">Toolshed</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
<ul class="sidebar-nav">
|
<ul class="sidebar-nav">
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-icon dropdown-toggle d-inline-block d-sm-none" href="#"
|
<a class="nav-icon dropdown-toggle d-inline-block d-sm-none" href="#" @click="toggleDropdown">
|
||||||
data-toggle="dropdown">
|
|
||||||
<i class="align-middle" data-feather="settings"></i>
|
<i class="align-middle" data-feather="settings"></i>
|
||||||
<b-icon-chat-left class="bi-valign-middle"></b-icon-chat-left>
|
<b-icon-person class="bi-valign-middle"></b-icon-person>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="nav-link dropdown-toggle d-none d-sm-inline-block" href="#"
|
<a class="nav-link dropdown-toggle d-none d-sm-inline-block" href="#" @click="toggleDropdown">
|
||||||
@click.prevent="toggleDropdown">
|
|
||||||
<img src="/assets/img/avatars/avatar.png" class="avatar img-fluid rounded mr-1"
|
<img src="/assets/img/avatars/avatar.png" class="avatar img-fluid rounded mr-1"
|
||||||
alt="Charles Hall"/>
|
alt="Charles Hall"/>
|
||||||
<span class="text-dark">
|
<span class="text-dark">
|
||||||
{{ username }}
|
{{ username }}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div :class="['dropdown-menu','dropdown-menu-right', {'show': show_dropdown}]">
|
<div class="dropdown-menu dropdown-menu-right" id="userDropdown">
|
||||||
<router-link to="/profile" class="dropdown-item">
|
<router-link to="/profile" class="dropdown-item">
|
||||||
<b-icon-person class="bi-valign-middle mr-1"></b-icon-person>
|
<b-icon-person class="bi-valign-middle mr-1"></b-icon-person>
|
||||||
Profile
|
Profile
|
||||||
|
@ -40,15 +38,11 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
...BIcons
|
...BIcons
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
show_dropdown: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['logout']),
|
...mapMutations(['logout']),
|
||||||
toggleDropdown() {
|
toggleDropdown() {
|
||||||
this.show_dropdown = !this.show_dropdown
|
closeAllDropdowns();
|
||||||
|
document.getElementById("userDropdown").classList.toggle("show");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -31,7 +31,7 @@ class ServerSet {
|
||||||
if (this.unreachable_neighbors.queryUnreachable(server)) {
|
if (this.unreachable_neighbors.queryUnreachable(server)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const url = "https://" + server + target // TODO https
|
const url = "http://" + server + target // TODO https
|
||||||
return await fetch(url, {
|
return await fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -59,7 +59,7 @@ class ServerSet {
|
||||||
if (this.unreachable_neighbors.queryUnreachable(server)) {
|
if (this.unreachable_neighbors.queryUnreachable(server)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const url = "https://" + server + target // TODO https
|
const url = "http://" + server + target // TODO https
|
||||||
return await fetch(url, {
|
return await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -89,7 +89,7 @@ class ServerSet {
|
||||||
if (this.unreachable_neighbors.queryUnreachable(server)) {
|
if (this.unreachable_neighbors.queryUnreachable(server)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const url = "https://" + server + target // TODO https
|
const url = "http://" + server + target // TODO https
|
||||||
return await fetch(url, {
|
return await fetch(url, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -119,7 +119,7 @@ class ServerSet {
|
||||||
if (this.unreachable_neighbors.queryUnreachable(server)) {
|
if (this.unreachable_neighbors.queryUnreachable(server)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const url = "https://" + server + target // TODO https
|
const url = "http://" + server + target // TODO https
|
||||||
return await fetch(url, {
|
return await fetch(url, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -149,7 +149,7 @@ class ServerSet {
|
||||||
if (this.unreachable_neighbors.queryUnreachable(server)) {
|
if (this.unreachable_neighbors.queryUnreachable(server)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const url = "https://" + server + target // TODO https
|
const url = "http://" + server + target // TODO https
|
||||||
return await fetch(url, {
|
return await fetch(url, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import {createApp} from 'vue'
|
import {createApp} from 'vue'
|
||||||
//import { BootstrapVue, BootstrapVueIcons } from 'bootstrap-vue'
|
//import { BootstrapVue, BootstrapVueIcons } from 'bootstrap-vue'
|
||||||
import { BootstrapIconsPlugin } from 'bootstrap-icons-vue';
|
import {BootstrapIconsPlugin} from 'bootstrap-icons-vue';
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
|
||||||
import './assets/css/toolshed.css'
|
import './assets/css/toolshed.css'
|
||||||
|
//import './assets/js/app.js'
|
||||||
|
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import store from './store';
|
import store from './store';
|
||||||
|
@ -16,3 +17,34 @@ _nacl.instantiate((nacl) => {
|
||||||
window.nacl = nacl
|
window.nacl = nacl
|
||||||
app.use(router).mount('#app')
|
app.use(router).mount('#app')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
window.closeAllDropdowns = function () {
|
||||||
|
const dropdowns = document.getElementsByClassName("dropdown-menu");
|
||||||
|
let i;
|
||||||
|
for (i = 0; i < dropdowns.length; i++) {
|
||||||
|
const openDropdown = dropdowns[i];
|
||||||
|
if (openDropdown.classList.contains('show')) {
|
||||||
|
openDropdown.classList.remove('show');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onclick = function (event) {
|
||||||
|
if (!event.target.matches('.dropdown-toggle *')
|
||||||
|
&& !event.target.matches('.dropdown-toggle')
|
||||||
|
&& !event.target.matches('.dropdown-menu *')
|
||||||
|
&& !event.target.matches('.dropdown-menu')) {
|
||||||
|
closeAllDropdowns();
|
||||||
|
}
|
||||||
|
if (!event.target.matches('.sidebar-toggle *')
|
||||||
|
&& !event.target.matches('.sidebar-toggle')
|
||||||
|
&& !event.target.matches('.sidebar *')
|
||||||
|
&& !event.target.matches('.sidebar')) {
|
||||||
|
const sidebar = document.getElementById("sidebar");
|
||||||
|
const marginLeft = parseInt(getComputedStyle(sidebar).marginLeft);
|
||||||
|
if (sidebar.classList.contains('collapsed') && marginLeft === 0) {
|
||||||
|
sidebar.classList.remove('collapsed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue