mirror of
https://github.com/bastienwirtz/homer.git
synced 2024-12-27 09:09:08 +01:00
Add offline cache + improve layout
This commit is contained in:
parent
e41196e76e
commit
9baec9aec2
17
app.css
17
app.css
@ -4,7 +4,7 @@ html {
|
||||
body {
|
||||
font-family: 'Raleway', sans-serif;
|
||||
background-color: #F5F5F5;
|
||||
height: 100%; }
|
||||
min-height: 100%; }
|
||||
body h1, body h2, body h3, body h4, body h5, body h6 {
|
||||
font-family: 'Lato', sans-serif; }
|
||||
body h1 {
|
||||
@ -66,7 +66,7 @@ body {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis; }
|
||||
body #main-section .column {
|
||||
body #main-section .container {
|
||||
padding: 1.2rem .75rem; }
|
||||
body #main-section .message {
|
||||
margin-top: 45px;
|
||||
@ -82,7 +82,7 @@ body {
|
||||
background-color: #4285f4;
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: -0.3rem;
|
||||
right: -0.2rem;
|
||||
width: 3px;
|
||||
overflow: hidden;
|
||||
transition: all 0.2s ease-out;
|
||||
@ -137,3 +137,14 @@ body {
|
||||
height: 20px; }
|
||||
body .search-bar:focus-within .search-label::before {
|
||||
color: #4a4a4a; }
|
||||
body .offline-message {
|
||||
text-align: center;
|
||||
margin: 35px 0; }
|
||||
body .offline-message i {
|
||||
font-size: 2rem; }
|
||||
body .offline-message i.fa-redo-alt {
|
||||
font-size: 1.3rem;
|
||||
line-height: 1rem;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
color: #3273dc; }
|
||||
|
67
app.js
67
app.js
@ -1,42 +1,53 @@
|
||||
var app = new Vue({
|
||||
const app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
config: null,
|
||||
filter: ''
|
||||
offline: false,
|
||||
filter: '',
|
||||
},
|
||||
beforeCreate() {
|
||||
created: function () {
|
||||
let that = this;
|
||||
|
||||
return getConfig().then(function (config) {
|
||||
const size = 3;
|
||||
config.services.forEach(function (service) {
|
||||
service.rows = [];
|
||||
items = service.items;
|
||||
while (items.length) {
|
||||
service.rows.push(items.splice(0, size));
|
||||
}
|
||||
|
||||
if (service.rows.length) {
|
||||
let last = service.rows.length - 1;
|
||||
service.rows[last] = service.rows[last].concat(Array(size - service.rows[last].length));
|
||||
}
|
||||
});
|
||||
this.checkOffline();
|
||||
that.getConfig().then(function (config) {
|
||||
that.config = config;
|
||||
}).catch(function () {
|
||||
console.error('Fail to get config');
|
||||
that.offline = true;
|
||||
});
|
||||
|
||||
document.addEventListener('visibilitychange', function () {
|
||||
if (document.visibilityState == "visible") {
|
||||
that.checkOffline();
|
||||
}
|
||||
}, false);
|
||||
},
|
||||
methods: {
|
||||
checkOffline: function () {
|
||||
let that = this;
|
||||
return fetch(window.location.href + "?alive", {
|
||||
method: 'HEAD',
|
||||
cache: 'no-store'
|
||||
}).then(function () {
|
||||
that.offline = false;
|
||||
}).catch(function () {
|
||||
that.offline = true;
|
||||
});
|
||||
},
|
||||
getConfig: function (event) {
|
||||
return fetch('config.yml').then(function (response) {
|
||||
if (response.status != 200) {
|
||||
return
|
||||
}
|
||||
return response.text().then(function (body) {
|
||||
return jsyaml.load(body);
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function getConfig() {
|
||||
return fetch('config.yml').then(function (response) {
|
||||
if (response.status !== 200) {
|
||||
return;
|
||||
}
|
||||
|
||||
return response.text().then(function (body) {
|
||||
return jsyaml.load(body);
|
||||
});
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function () {
|
||||
navigator.serviceWorker.register('/worker.js');
|
||||
});
|
||||
}
|
||||
|
23
app.scss
23
app.scss
@ -6,7 +6,7 @@ html { height: 100%; }
|
||||
body {
|
||||
font-family: 'Raleway', sans-serif;
|
||||
background-color: #F5F5F5;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'Lato', sans-serif;
|
||||
@ -109,7 +109,7 @@ body {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.column {
|
||||
.container {
|
||||
padding: 1.2rem .75rem;
|
||||
}
|
||||
|
||||
@ -136,7 +136,7 @@ body {
|
||||
background-color: $secondary-color;
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: -0.3rem;
|
||||
right: -0.2rem;
|
||||
width: 3px;
|
||||
overflow: hidden;
|
||||
transition: all 0.2s ease-out;
|
||||
@ -213,4 +213,21 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.offline-message {
|
||||
text-align: center;
|
||||
margin: 35px 0;
|
||||
|
||||
i {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
i.fa-redo-alt {
|
||||
font-size: 1.3rem;
|
||||
line-height: 1rem;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
color: #3273dc;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
@ -4,7 +4,7 @@
|
||||
|
||||
title: "Simple homepage"
|
||||
subtitle: "Homer"
|
||||
logo: "assets/homer.png"
|
||||
logo: "assets/logo.png"
|
||||
icon: "fas fa-skull-crossbones"
|
||||
|
||||
# Optional message
|
||||
|
103
index.html
103
index.html
@ -7,8 +7,8 @@
|
||||
<meta name="robots" content="noindex">
|
||||
<link rel="icon" type="image/png" href="assets/favicon.png">
|
||||
<title>Homer</title>
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU"
|
||||
crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css"
|
||||
integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Lato|Raleway" rel="stylesheet">
|
||||
<link rel="stylesheet" href="app.css">
|
||||
@ -18,18 +18,18 @@
|
||||
<div id="app" v-if="config">
|
||||
<div id="bighead">
|
||||
<section class="first-line">
|
||||
<div class="container">
|
||||
<div v-cloak class="container">
|
||||
<div class="logo">
|
||||
<img v-if="config.logo" :src="config.logo" />
|
||||
<i v-if="config.icon" :class="config.icon"></i>
|
||||
</div>
|
||||
<div class="dashboard-title">
|
||||
<span v-cloak class="headline">{{ config.subtitle }}</span>
|
||||
<h1 v-cloak>{{ config.title }}</h1>
|
||||
<span class="headline">{{ config.subtitle }}</span>
|
||||
<h1>{{ config.title }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div v-if="config.links" class="container-fluid">
|
||||
<div v-cloak v-if="config.links" class="container-fluid">
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-menu">
|
||||
@ -52,50 +52,53 @@
|
||||
</div>
|
||||
|
||||
<section id="main-section" class="section">
|
||||
<div class="container">
|
||||
<!-- Optional messages -->
|
||||
<article v-cloak v-if="config && config.message" class="message" :class="config.message.style">
|
||||
<div v-if="config.message.title" class="message-header">
|
||||
<p>{{ config.message.title }}</p>
|
||||
</div>
|
||||
<div v-if="config.message.content" class="message-body">
|
||||
{{ config.message.content }}
|
||||
</div>
|
||||
</article>
|
||||
<div v-cloak class="container">
|
||||
<div v-if="offline" class="offline-message">
|
||||
<i class="far fa-dizzy"></i>
|
||||
<h1>You're offline bro. <i class="fas fa-redo-alt" v-on:click="checkOffline()"></i></h1>
|
||||
</div>
|
||||
<div v-else>
|
||||
<!-- Optional messages -->
|
||||
<article v-if="config && config.message" class="message" :class="config.message.style">
|
||||
<div v-if="config.message.title" class="message-header">
|
||||
<p>{{ config.message.title }}</p>
|
||||
</div>
|
||||
<div v-if="config.message.content" class="message-body">
|
||||
{{ config.message.content }}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<h2 v-cloak v-if="filter"><i class="fas fa-search"></i> Search</h2>
|
||||
<h2 v-if="filter"><i class="fas fa-search"></i> Search</h2>
|
||||
|
||||
<div v-for="(group, index) in config.services">
|
||||
<h2 v-if="!filter && group.name"><i v-if="group.icon" :class="group.icon"></i><span v-else>#</span>
|
||||
{{ group.name }}</h2>
|
||||
<div v-for="items in group.rows">
|
||||
<div class="columns">
|
||||
<div v-for="item in items" v-if="!filter || (item && (item.name.toLowerCase().includes(filter.toLowerCase()) || (item.tag && item.tag.toLowerCase().includes(filter.toLowerCase()))))"
|
||||
class="column">
|
||||
<div>
|
||||
<div v-if='item' class="card">
|
||||
<a :href="item.url">
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<div v-if="item.logo" class="media-left">
|
||||
<figure class="image is-48x48">
|
||||
<img :src="item.logo" />
|
||||
</figure>
|
||||
</div>
|
||||
<div v-if="item.icon" class="media-left">
|
||||
<figure class="image is-48x48">
|
||||
<i style="font-size: 35px" :class="item.icon"></i>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="media-content">
|
||||
<p class="title is-4">{{ item.name }}</p>
|
||||
<p class="subtitle is-6">{{ item.subtitle }}</p>
|
||||
</div>
|
||||
<div v-for="(group, index) in config.services">
|
||||
<h2 v-if="!filter && group.name"><i v-if="group.icon" :class="group.icon"></i><span v-else>#</span>
|
||||
{{ group.name }}</h2>
|
||||
<div class="columns is-multiline">
|
||||
<div v-for="item in group.items"
|
||||
v-if="!filter || (item && (item.name.toLowerCase().includes(filter.toLowerCase()) || (item.tag && item.tag.toLowerCase().includes(filter.toLowerCase()))))"
|
||||
class="column is-one-third-widescreen">
|
||||
<div v-if='item' class="card">
|
||||
<a :href="item.url">
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<div v-if="item.logo" class="media-left">
|
||||
<figure class="image is-48x48">
|
||||
<img :src="item.logo" />
|
||||
</figure>
|
||||
</div>
|
||||
<div v-if="item.icon" class="media-left">
|
||||
<figure class="image is-48x48">
|
||||
<i style="font-size: 35px" :class="item.icon"></i>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="media-content">
|
||||
<p class="title is-4">{{ item.name }}</p>
|
||||
<p class="subtitle is-6">{{ item.subtitle }}</p>
|
||||
</div>
|
||||
<strong v-if="item.tag" class="tag">#{{ item.tag }}</strong>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<strong class="tag" v-if="item.tag">#{{ item.tag }}</strong>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -108,13 +111,15 @@
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="content has-text-centered">
|
||||
<p>Created with <span class="has-text-danger">❤️</span> with <a href="https://bulma.io/">bulma</a>, <a href="https://vuejs.org/">vuejs</a>
|
||||
& <a href="#">font awesome</a> // Fork me on <a href="https://github.com/bastienwirtz/homer"><i class="fab fa-github-alt"></i></a></p>
|
||||
<p>Created with <span class="has-text-danger">❤️</span> with <a href="https://bulma.io/">bulma</a>, <a
|
||||
href="https://vuejs.org/">vuejs</a>
|
||||
& <a href="#">font awesome</a> // Fork me on <a href="https://github.com/bastienwirtz/homer"><i
|
||||
class="fab fa-github-alt"></i></a></p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://unpkg.com/vue"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.2/dist/vue.min.js"></script>
|
||||
<script src="vendors/js-yaml.min.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
|
32
worker.js
Normal file
32
worker.js
Normal file
@ -0,0 +1,32 @@
|
||||
self.addEventListener('install', event => {
|
||||
event.waitUntil(
|
||||
caches
|
||||
.open('homer')
|
||||
.then(cache =>
|
||||
cache.addAll([
|
||||
'/',
|
||||
'/index.html',
|
||||
'/config.yml',
|
||||
'/app.css',
|
||||
'/app.js',
|
||||
'/vendors/js-yaml.min.js',
|
||||
'/assets/logo.png',
|
||||
'https://use.fontawesome.com/releases/v5.5.0/css/all.css',
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css',
|
||||
'https://fonts.googleapis.com/css?family=Lato|Raleway',
|
||||
'https://cdn.jsdelivr.net/npm/vue@2.6.2/dist/vue.min.js',
|
||||
])
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
self.addEventListener('fetch', event => {
|
||||
event.respondWith(
|
||||
caches.match(event.request).then(response => {
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
return fetch(event.request);
|
||||
})
|
||||
);
|
||||
});
|
Loading…
Reference in New Issue
Block a user