Add offline cache + improve layout

This commit is contained in:
Bastien Wirtz 2019-02-18 00:23:20 -08:00
parent e41196e76e
commit 9baec9aec2
7 changed files with 160 additions and 84 deletions

17
app.css
View File

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

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

View File

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

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -4,7 +4,7 @@
title: "Simple homepage"
subtitle: "Homer"
logo: "assets/homer.png"
logo: "assets/logo.png"
icon: "fas fa-skull-crossbones"
# Optional message

View File

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