aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBastien Wirtz <bastien.wirtz@gmail.com>2019-02-18 00:23:20 -0800
committerBastien Wirtz <bastien.wirtz@gmail.com>2019-02-18 00:23:20 -0800
commit9baec9aec294656fb632123906b4ac6a712267ba (patch)
tree0dff55723b3503bfc81f17bfe1d10a8f4298d56f
parente41196e76e6b184fc918fb44adc9af0ff3fdae30 (diff)
downloadhomer-9baec9aec294656fb632123906b4ac6a712267ba.tar.gz
homer-9baec9aec294656fb632123906b4ac6a712267ba.tar.zst
homer-9baec9aec294656fb632123906b4ac6a712267ba.zip
Add offline cache + improve layout
-rw-r--r--app.css17
-rw-r--r--app.js67
-rw-r--r--app.scss23
-rw-r--r--assets/logo.png (renamed from assets/homer.png)bin49200 -> 49200 bytes
-rw-r--r--config.yml2
-rw-r--r--index.html103
-rw-r--r--worker.js32
7 files changed, 160 insertions, 84 deletions
diff --git a/app.css b/app.css
index 98574af..60d2762 100644
--- a/app.css
+++ b/app.css
@@ -4,7 +4,7 @@ html {
4body { 4body {
5 font-family: 'Raleway', sans-serif; 5 font-family: 'Raleway', sans-serif;
6 background-color: #F5F5F5; 6 background-color: #F5F5F5;
7 height: 100%; } 7 min-height: 100%; }
8 body h1, body h2, body h3, body h4, body h5, body h6 { 8 body h1, body h2, body h3, body h4, body h5, body h6 {
9 font-family: 'Lato', sans-serif; } 9 font-family: 'Lato', sans-serif; }
10 body h1 { 10 body h1 {
@@ -66,7 +66,7 @@ body {
66 white-space: nowrap; 66 white-space: nowrap;
67 overflow: hidden; 67 overflow: hidden;
68 text-overflow: ellipsis; } 68 text-overflow: ellipsis; }
69 body #main-section .column { 69 body #main-section .container {
70 padding: 1.2rem .75rem; } 70 padding: 1.2rem .75rem; }
71 body #main-section .message { 71 body #main-section .message {
72 margin-top: 45px; 72 margin-top: 45px;
@@ -82,7 +82,7 @@ body {
82 background-color: #4285f4; 82 background-color: #4285f4;
83 position: absolute; 83 position: absolute;
84 top: 1rem; 84 top: 1rem;
85 right: -0.3rem; 85 right: -0.2rem;
86 width: 3px; 86 width: 3px;
87 overflow: hidden; 87 overflow: hidden;
88 transition: all 0.2s ease-out; 88 transition: all 0.2s ease-out;
@@ -137,3 +137,14 @@ body {
137 height: 20px; } 137 height: 20px; }
138 body .search-bar:focus-within .search-label::before { 138 body .search-bar:focus-within .search-label::before {
139 color: #4a4a4a; } 139 color: #4a4a4a; }
140 body .offline-message {
141 text-align: center;
142 margin: 35px 0; }
143 body .offline-message i {
144 font-size: 2rem; }
145 body .offline-message i.fa-redo-alt {
146 font-size: 1.3rem;
147 line-height: 1rem;
148 vertical-align: middle;
149 cursor: pointer;
150 color: #3273dc; }
diff --git a/app.js b/app.js
index fda16b2..6e20ea3 100644
--- a/app.js
+++ b/app.js
@@ -1,42 +1,53 @@
1var app = new Vue({ 1const app = new Vue({
2 el: '#app', 2 el: '#app',
3 data: { 3 data: {
4 config: null, 4 config: null,
5 filter: '' 5 offline: false,
6 filter: '',
6 }, 7 },
7 beforeCreate() { 8 created: function () {
8 let that = this; 9 let that = this;
9 10
10 return getConfig().then(function (config) { 11 this.checkOffline();
11 const size = 3; 12 that.getConfig().then(function (config) {
12 config.services.forEach(function (service) {
13 service.rows = [];
14 items = service.items;
15 while (items.length) {
16 service.rows.push(items.splice(0, size));
17 }
18
19 if (service.rows.length) {
20 let last = service.rows.length - 1;
21 service.rows[last] = service.rows[last].concat(Array(size - service.rows[last].length));
22 }
23 });
24 that.config = config; 13 that.config = config;
25 }).catch(function () { 14 }).catch(function () {
26 console.error('Fail to get config'); 15 that.offline = true;
27 }); 16 });
17
18 document.addEventListener('visibilitychange', function () {
19 if (document.visibilityState == "visible") {
20 that.checkOffline();
21 }
22 }, false);
23 },
24 methods: {
25 checkOffline: function () {
26 let that = this;
27 return fetch(window.location.href + "?alive", {
28 method: 'HEAD',
29 cache: 'no-store'
30 }).then(function () {
31 that.offline = false;
32 }).catch(function () {
33 that.offline = true;
34 });
35 },
36 getConfig: function (event) {
37 return fetch('config.yml').then(function (response) {
38 if (response.status != 200) {
39 return
40 }
41 return response.text().then(function (body) {
42 return jsyaml.load(body);
43 });
44 });
45 },
28 } 46 }
29}); 47});
30 48
31 49if ('serviceWorker' in navigator) {
32function getConfig() { 50 window.addEventListener('load', function () {
33 return fetch('config.yml').then(function (response) { 51 navigator.serviceWorker.register('/worker.js');
34 if (response.status !== 200) {
35 return;
36 }
37
38 return response.text().then(function (body) {
39 return jsyaml.load(body);
40 });
41 }); 52 });
42} 53}
diff --git a/app.scss b/app.scss
index e420c0e..da6271d 100644
--- a/app.scss
+++ b/app.scss
@@ -6,7 +6,7 @@ html { height: 100%; }
6body { 6body {
7 font-family: 'Raleway', sans-serif; 7 font-family: 'Raleway', sans-serif;
8 background-color: #F5F5F5; 8 background-color: #F5F5F5;
9 height: 100%; 9 min-height: 100%;
10 10
11 h1, h2, h3, h4, h5, h6 { 11 h1, h2, h3, h4, h5, h6 {
12 font-family: 'Lato', sans-serif; 12 font-family: 'Lato', sans-serif;
@@ -109,7 +109,7 @@ body {
109 text-overflow: ellipsis; 109 text-overflow: ellipsis;
110 } 110 }
111 111
112 .column { 112 .container {
113 padding: 1.2rem .75rem; 113 padding: 1.2rem .75rem;
114 } 114 }
115 115
@@ -136,7 +136,7 @@ body {
136 background-color: $secondary-color; 136 background-color: $secondary-color;
137 position: absolute; 137 position: absolute;
138 top: 1rem; 138 top: 1rem;
139 right: -0.3rem; 139 right: -0.2rem;
140 width: 3px; 140 width: 3px;
141 overflow: hidden; 141 overflow: hidden;
142 transition: all 0.2s ease-out; 142 transition: all 0.2s ease-out;
@@ -213,4 +213,21 @@ body {
213 } 213 }
214 } 214 }
215 215
216 .offline-message {
217 text-align: center;
218 margin: 35px 0;
219
220 i {
221 font-size: 2rem;
222 }
223
224 i.fa-redo-alt {
225 font-size: 1.3rem;
226 line-height: 1rem;
227 vertical-align: middle;
228 cursor: pointer;
229 color: #3273dc;
230 }
231 }
232
216} 233}
diff --git a/assets/homer.png b/assets/logo.png
index 6fa8ee8..6fa8ee8 100644
--- a/assets/homer.png
+++ b/assets/logo.png
Binary files differ
diff --git a/config.yml b/config.yml
index 3b0dec5..69c87ea 100644
--- a/config.yml
+++ b/config.yml
@@ -4,7 +4,7 @@
4 4
5title: "Simple homepage" 5title: "Simple homepage"
6subtitle: "Homer" 6subtitle: "Homer"
7logo: "assets/homer.png" 7logo: "assets/logo.png"
8icon: "fas fa-skull-crossbones" 8icon: "fas fa-skull-crossbones"
9 9
10# Optional message 10# Optional message
diff --git a/index.html b/index.html
index 24adcd4..3b83c8e 100644
--- a/index.html
+++ b/index.html
@@ -7,8 +7,8 @@
7 <meta name="robots" content="noindex"> 7 <meta name="robots" content="noindex">
8 <link rel="icon" type="image/png" href="assets/favicon.png"> 8 <link rel="icon" type="image/png" href="assets/favicon.png">
9 <title>Homer</title> 9 <title>Homer</title>
10 <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" 10 <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css"
11 crossorigin="anonymous"> 11 integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" crossorigin="anonymous">
12 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css"> 12 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css">
13 <link href="https://fonts.googleapis.com/css?family=Lato|Raleway" rel="stylesheet"> 13 <link href="https://fonts.googleapis.com/css?family=Lato|Raleway" rel="stylesheet">
14 <link rel="stylesheet" href="app.css"> 14 <link rel="stylesheet" href="app.css">
@@ -18,18 +18,18 @@
18 <div id="app" v-if="config"> 18 <div id="app" v-if="config">
19 <div id="bighead"> 19 <div id="bighead">
20 <section class="first-line"> 20 <section class="first-line">
21 <div class="container"> 21 <div v-cloak class="container">
22 <div class="logo"> 22 <div class="logo">
23 <img v-if="config.logo" :src="config.logo" /> 23 <img v-if="config.logo" :src="config.logo" />
24 <i v-if="config.icon" :class="config.icon"></i> 24 <i v-if="config.icon" :class="config.icon"></i>
25 </div> 25 </div>
26 <div class="dashboard-title"> 26 <div class="dashboard-title">
27 <span v-cloak class="headline">{{ config.subtitle }}</span> 27 <span class="headline">{{ config.subtitle }}</span>
28 <h1 v-cloak>{{ config.title }}</h1> 28 <h1>{{ config.title }}</h1>
29 </div> 29 </div>
30 </div> 30 </div>
31 </section> 31 </section>
32 <div v-if="config.links" class="container-fluid"> 32 <div v-cloak v-if="config.links" class="container-fluid">
33 <nav class="navbar" role="navigation" aria-label="main navigation"> 33 <nav class="navbar" role="navigation" aria-label="main navigation">
34 <div class="container"> 34 <div class="container">
35 <div class="navbar-menu"> 35 <div class="navbar-menu">
@@ -52,50 +52,53 @@
52 </div> 52 </div>
53 53
54 <section id="main-section" class="section"> 54 <section id="main-section" class="section">
55 <div class="container"> 55 <div v-cloak class="container">
56 <!-- Optional messages --> 56 <div v-if="offline" class="offline-message">
57 <article v-cloak v-if="config && config.message" class="message" :class="config.message.style"> 57 <i class="far fa-dizzy"></i>
58 <div v-if="config.message.title" class="message-header"> 58 <h1>You're offline bro. <i class="fas fa-redo-alt" v-on:click="checkOffline()"></i></h1>
59 <p>{{ config.message.title }}</p> 59 </div>
60 </div> 60 <div v-else>
61 <div v-if="config.message.content" class="message-body"> 61 <!-- Optional messages -->
62 {{ config.message.content }} 62 <article v-if="config && config.message" class="message" :class="config.message.style">
63 </div> 63 <div v-if="config.message.title" class="message-header">
64 </article> 64 <p>{{ config.message.title }}</p>
65 </div>
66 <div v-if="config.message.content" class="message-body">
67 {{ config.message.content }}
68 </div>
69 </article>
65 70
66 <h2 v-cloak v-if="filter"><i class="fas fa-search"></i> Search</h2> 71 <h2 v-if="filter"><i class="fas fa-search"></i> Search</h2>
67 72
68 <div v-for="(group, index) in config.services"> 73 <div v-for="(group, index) in config.services">
69 <h2 v-if="!filter && group.name"><i v-if="group.icon" :class="group.icon"></i><span v-else>#</span> 74 <h2 v-if="!filter && group.name"><i v-if="group.icon" :class="group.icon"></i><span v-else>#</span>
70 {{ group.name }}</h2> 75 {{ group.name }}</h2>
71 <div v-for="items in group.rows"> 76 <div class="columns is-multiline">
72 <div class="columns"> 77 <div v-for="item in group.items"
73 <div v-for="item in items" v-if="!filter || (item && (item.name.toLowerCase().includes(filter.toLowerCase()) || (item.tag && item.tag.toLowerCase().includes(filter.toLowerCase()))))" 78 v-if="!filter || (item && (item.name.toLowerCase().includes(filter.toLowerCase()) || (item.tag && item.tag.toLowerCase().includes(filter.toLowerCase()))))"
74 class="column"> 79 class="column is-one-third-widescreen">
75 <div> 80 <div v-if='item' class="card">
76 <div v-if='item' class="card"> 81 <a :href="item.url">
77 <a :href="item.url"> 82 <div class="card-content">
78 <div class="card-content"> 83 <div class="media">
79 <div class="media"> 84 <div v-if="item.logo" class="media-left">
80 <div v-if="item.logo" class="media-left"> 85 <figure class="image is-48x48">
81 <figure class="image is-48x48"> 86 <img :src="item.logo" />
82 <img :src="item.logo" /> 87 </figure>
83 </figure> 88 </div>
84 </div> 89 <div v-if="item.icon" class="media-left">
85 <div v-if="item.icon" class="media-left"> 90 <figure class="image is-48x48">
86 <figure class="image is-48x48"> 91 <i style="font-size: 35px" :class="item.icon"></i>
87 <i style="font-size: 35px" :class="item.icon"></i> 92 </figure>
88 </figure> 93 </div>
89 </div> 94 <div class="media-content">
90 <div class="media-content"> 95 <p class="title is-4">{{ item.name }}</p>
91 <p class="title is-4">{{ item.name }}</p> 96 <p class="subtitle is-6">{{ item.subtitle }}</p>
92 <p class="subtitle is-6">{{ item.subtitle }}</p>
93 </div>
94 </div> 97 </div>
95 <strong v-if="item.tag" class="tag">#{{ item.tag }}</strong>
96 </div> 98 </div>
97 </a> 99 <strong class="tag" v-if="item.tag">#{{ item.tag }}</strong>
98 </div> 100 </div>
101 </a>
99 </div> 102 </div>
100 </div> 103 </div>
101 </div> 104 </div>
@@ -108,13 +111,15 @@
108 <footer class="footer"> 111 <footer class="footer">
109 <div class="container"> 112 <div class="container">
110 <div class="content has-text-centered"> 113 <div class="content has-text-centered">
111 <p>Created with <span class="has-text-danger">❤️</span> with <a href="https://bulma.io/">bulma</a>, <a href="https://vuejs.org/">vuejs</a> 114 <p>Created with <span class="has-text-danger">❤️</span> with <a href="https://bulma.io/">bulma</a>, <a
112 & <a href="#">font awesome</a> // Fork me on <a href="https://github.com/bastienwirtz/homer"><i class="fab fa-github-alt"></i></a></p> 115 href="https://vuejs.org/">vuejs</a>
116 & <a href="#">font awesome</a> // Fork me on <a href="https://github.com/bastienwirtz/homer"><i
117 class="fab fa-github-alt"></i></a></p>
113 </div> 118 </div>
114 </div> 119 </div>
115 </footer> 120 </footer>
116 121
117 <script src="https://unpkg.com/vue"></script> 122 <script src="https://cdn.jsdelivr.net/npm/vue@2.6.2/dist/vue.min.js"></script>
118 <script src="vendors/js-yaml.min.js"></script> 123 <script src="vendors/js-yaml.min.js"></script>
119 <script src="app.js"></script> 124 <script src="app.js"></script>
120</body> 125</body>
diff --git a/worker.js b/worker.js
new file mode 100644
index 0000000..511c35a
--- /dev/null
+++ b/worker.js
@@ -0,0 +1,32 @@
1self.addEventListener('install', event => {
2 event.waitUntil(
3 caches
4 .open('homer')
5 .then(cache =>
6 cache.addAll([
7 '/',
8 '/index.html',
9 '/config.yml',
10 '/app.css',
11 '/app.js',
12 '/vendors/js-yaml.min.js',
13 '/assets/logo.png',
14 'https://use.fontawesome.com/releases/v5.5.0/css/all.css',
15 'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css',
16 'https://fonts.googleapis.com/css?family=Lato|Raleway',
17 'https://cdn.jsdelivr.net/npm/vue@2.6.2/dist/vue.min.js',
18 ])
19 )
20 )
21})
22
23self.addEventListener('fetch', event => {
24 event.respondWith(
25 caches.match(event.request).then(response => {
26 if (response) {
27 return response;
28 }
29 return fetch(event.request);
30 })
31 );
32});