This commit is contained in:
j3d1 2023-07-02 20:30:42 +02:00
parent 08f723e9da
commit e2cf04cf18
9 changed files with 25270 additions and 78 deletions

25141
frontend/src/assets/js/app.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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() {

View file

@ -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">

View file

@ -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: {

View file

@ -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: {

View file

@ -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');
}
}
}