This commit is contained in:
j3d1 2023-05-11 04:09:26 +02:00
parent e89c261f56
commit a5ca19ddf8
5 changed files with 20968 additions and 13 deletions

File diff suppressed because it is too large Load diff

View file

@ -6,9 +6,129 @@
<a class="sidebar-toggle d-flex" @click="toggleSidebar">
<i class="hamburger align-self-center"></i>
</a>
<form class="d-none d-sm-inline-block">
<div class="input-group input-group-navbar">
<input type="text" class="form-control" placeholder="Search…" aria-label="Search">
<button class="btn" type="button">
<b-icon-search class="bi-valign-middle"></b-icon-search>
</button>
</div>
</form>
<div class="navbar-collapse collapse">
<ul class="navbar-nav navbar-align">
<UserDropdown/>
<Notifications :notifications="notifications"/>
<li class="nav-item dropdown">
<a class="nav-icon dropdown-toggle" href="#" id="messagesDropdown" data-toggle="dropdown">
<div class="position-relative">
<b-icon-chat-left class="bi-valign-middle"></b-icon-chat-left>
</div>
</a>
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-right py-0"
aria-labelledby="messagesDropdown">
<div class="dropdown-menu-header">
<div class="position-relative">
4 New Messages
</div>
</div>
<div class="list-group">
<a href="#" class="list-group-item">
<div class="row g-0 align-items-center">
<div class="col-2">
<!--<img src="/static/assets/img/avatars/avatar-5.png"
class="avatar img-fluid rounded-circle" alt="Vanessa Tucker">-->
</div>
<div class="col-10 pl-2">
<div class="text-dark">Vanessa Tucker</div>
<div class="text-muted small mt-1">Nam pretium turpis et arcu. Duis arcu
tortor.
</div>
<div class="text-muted small mt-1">15m ago</div>
</div>
</div>
</a>
<a href="#" class="list-group-item">
<div class="row g-0 align-items-center">
<div class="col-2">
<!--<img src="/static/assets/img/avatars/avatar-2.png"
class="avatar img-fluid rounded-circle" alt="William Harris">-->
</div>
<div class="col-10 pl-2">
<div class="text-dark">William Harris</div>
<div class="text-muted small mt-1">Curabitur ligula sapien euismod
vitae.
</div>
<div class="text-muted small mt-1">2h ago</div>
</div>
</div>
</a>
<a href="#" class="list-group-item">
<div class="row g-0 align-items-center">
<div class="col-2">
<!--<img src="/static/assets/img/avatars/avatar-4.png"
class="avatar img-fluid rounded-circle" alt="Christina Mason">-->
</div>
<div class="col-10 pl-2">
<div class="text-dark">Christina Mason</div>
<div class="text-muted small mt-1">Pellentesque auctor neque nec urna.
</div>
<div class="text-muted small mt-1">4h ago</div>
</div>
</div>
</a>
<!-- {% for messege in top_messages %}-->
<a href="#" class="list-group-item">
<div class="row g-0 align-items-center">
<div class="col-2">
<!--<img src="/static/assets/img/avatars/avatar-3.png"
class="avatar img-fluid rounded-circle" alt="Sharon Lessman">-->
</div>
<div class="col-10 pl-2">
<div class="text-dark">Sharon Lessman</div>
<div class="text-muted small mt-1">Aenean tellus metus, bibendum sed,
posuere ac,
mattis non.
</div>
<div class="text-muted small mt-1">5h ago</div>
</div>
</div>
</a>
<!--{% endfor %}-->
</div>
<div class="dropdown-menu-footer">
<a href="#" class="text-muted">Show all messages</a>
</div>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-icon dropdown-toggle d-inline-block d-sm-none" href="#"
data-toggle="dropdown">
<i class="align-middle" data-feather="settings"></i>
<b-icon-chat-left class="bi-valign-middle"></b-icon-chat-left>
</a>
<a class="nav-link dropdown-toggle d-none d-sm-inline-block" href="#"
data-toggle="dropdown">
<!--<img src="/static/assets/img/avatars/avatar.png" class="avatar img-fluid rounded mr-1"
alt="Charles Hall"/>-->
<span class="text-dark">
<!--{{ request.user.username }}-->
</span>
</a>
<div class="dropdown-menu dropdown-menu-right">
<router-link to="/profile" class="dropdown-item">
<b-icon-person class="bi-valign-middle mr-1"></b-icon-person>
Profile
</router-link>
<router-link to="/settings" class="dropdown-item">
<b-icon-sliders class="bi-valign-middle mr-1"></b-icon-sliders>
Settings &
Privacy
</router-link>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#" @click="logout"> Log out</a>
</div>
</li>
</ul>
</div>
</nav>
@ -19,32 +139,49 @@
</template>
<script>
/* global closeAllDropdowns */
import {mapGetters, mapMutations, mapState} from 'vuex';
import * as BIcons from "bootstrap-icons-vue";
import Footer from "@/components/Footer.vue";
import Sidebar from "@/components/Sidebar.vue";
import UserDropdown from "@/components/UserDropdown.vue";
import Notifications from "@/components/Notifications.vue";
export default {
name: 'BaseLayout',
components: {
UserDropdown,
Footer,
Sidebar
Sidebar,
Notifications,
...BIcons
},
props: {
hideSearch: {
type: Boolean,
required: false,
default: false
}
},
},
computed: {
//...mapState(['notifications']),
...mapGetters(['notifications']),
username() {
return this.$route.params.username
},
},
top_notifications() {
return this.notifications.slice(0, 5)
},
},
methods: {
toggleSidebar() {
closeAllDropdowns();
document.getElementById("sidebar").classList.toggle("collapsed");
},
...mapMutations(['logout'])
},
async mounted() {
}
}
</script>
<style scoped>
@ -76,10 +213,6 @@ export default {
box-shadow: 0 0 2rem var(--bs-shadow)
}
.navbar-align {
margin-left: auto;
}
.navbar-bg {
background: var(--bs-white);
}

View file

@ -0,0 +1,90 @@
<template>
<li class="nav-item dropdown">
<a class="nav-icon dropdown-toggle" href="#" id="alertsDropdown" data-toggle="dropdown">
<div class="position-relative">
<b-icon-bell class="bi-valign-middle"></b-icon-bell>
<span :class="['indicator', notificationsColor(top_notifications)]">{{
top_notifications.length
}}</span>
</div>
</a>
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-right py-0"
aria-labelledby="alertsDropdown">
<div class="dropdown-menu-header">
{{ top_notifications.length }} New Notifications
</div>
<div class="list-group">
<a href="#" class="list-group-item" v-for="notification in top_notifications" :key="notification.id">
<div class="row g-0 align-items-center">
<div class="col-2">
<b-icon-exclamation-circle class="bi-valign-middle text-danger"
v-if="notification.type === 'error'"></b-icon-exclamation-circle>
<b-icon-info-circle class="bi-valign-middle text-info"
v-else-if="notification.type === 'info'"></b-icon-info-circle>
<b-icon-check-circle class="bi-valign-middle text-success"
v-else-if="notification.type === 'success'"></b-icon-check-circle>
<b-icon-bell class="bi-valign-middle text-warning"
v-else-if="notification.type === 'warning'"></b-icon-bell>
<b-icon-house class="bi-valign-middle text-primary"
v-else-if="notification.type === 'login'"></b-icon-house>
<b-icon-person-plus class="bi-valign-middle text-success"
v-else-if="notification.type === 'friend'"></b-icon-person-plus>
</div>
<div class="col-10">
<div class="text-dark">{{ notification.title }}</div>
<div class="text-muted small mt-1" v-if="notification.msg">{{ notification.msg }}</div>
<div class="text-muted small mt-1">{{ humanizeTime(notification.time) }}</div>
</div>
</div>
</a>
</div>
<div class="dropdown-menu-footer">
<a href="#" class="text-muted">Show all notifications</a>
</div>
</div>
</li>
</template>
<script>
import {mapGetters, mapMutations} from 'vuex';
import * as BIcons from "bootstrap-icons-vue";
export default {
name: 'Notifications',
components: {
...BIcons
},
props: ['notifications'],
computed: {
top_notifications() {
return this.notifications.sort((a, b) => {
return new Date(b.time) - new Date(a.time);
}).slice(0, 8);
},
},
methods: {
...mapMutations([]),
humanizeTime(time) {
return moment(time).fromNow();
},
notificationsColor(notifications) {
if (notifications.length === 0) {
return 'invisible';
}
if (notifications.filter(n => n.type === 'error').length > 0) {
return 'bg-danger';
}
if (notifications.filter(n => n.type === 'warning').length > 0) {
return 'bg-warning';
}
return 'bg-primary';
}
},
async mounted() {
}
}
</script>
<style scoped>
</style>

View file

@ -120,7 +120,6 @@ export default createStore({
} else {
return false;
}
},
async lookupServer({state}, {username}) {
const domain = username.split('@')[1]
@ -338,5 +337,41 @@ export default createStore({
return acc.concat(items)
}, [])
},
notifications(state) {
// supported types: error, warning, info, login, success, friend
const u = state.unreachable_neighbors.list().map(elem => {
return {
type: 'error',
title: elem.domain + ' unreachable',
msg: 'The neighbor ' + elem.domain + ' is currently unreachable. Please try again later.',
time: elem.time
}
})
return [...u, {
type: 'info',
title: 'Welcome to the Toolshed',
msg: 'This is a federated social network. You can add friends from other servers and share items with them.',
time: Date.now()
}, {
type: 'warning',
title: 'Lorem ipsum',
msg: 'Aliquam ex eros, imperdiet vulputate hendrerit et.',
time: Date.now() - 1000 * 60 * 60 * 2
}, {
type: 'login',
title: 'Login from 192.186.1.8',
time: Date.now() - 1000 * 60 * 60 * 5
}, {
type: 'friend',
title: 'New connection',
msg: 'Christina accepted your request.',
time: Date.now() - 1000 * 60 * 60 * 14
}, {
type: 'success',
title: 'Lorem ipsum',
msg: 'Aliquam ex eros, imperdiet vulputate hendrerit et.',
time: Date.now() - 1000 * 60 * 60 * 24
}]
},
}
})

View file

@ -115,9 +115,11 @@ export default {
async getInventoryItems() {
try {
const servers = await this.getFriends().then(friends => friends.map(friend => this.getFriendServer({username: friend})))
const urls = servers.map(server => server.then(s => `http://${s}/api/inventory_items/`))
const urls = servers.map(server => server.then(s => {
return {host: `http://${s}`, target: "/api/inventory_items/"}
}))
urls.map(url => url.then(u => this.apiFederatedGet(u).then(items => {
this.setInventoryItems({url: u, items})
this.setInventoryItems({url: u.domain, items})
}).catch(e => {
}))) // TODO: handle error
} catch (e) {