]> git.immae.eu Git - github/bastienwirtz/homer.git/blobdiff - app.js
Merge pull request #36 from jozefs/keyboard-shortcuts
[github/bastienwirtz/homer.git] / app.js
diff --git a/app.js b/app.js
index 26979755f3acb2bb43eac226bf92207f271c9181..36508abb694ef108fc0ecbfd552f3cda62a71f81 100644 (file)
--- a/app.js
+++ b/app.js
-var app = new Vue({
+const app = new Vue({
     el: '#app',
     data: {
         config: null,
-        filter: ''
+        offline: false,
+        filter: '',
+        vlayout: true,
+        isDark: null,
+        showMenu: false
     },
-    beforeCreate () {
-        var that = this;
-        return getConfig().then(function (config) {
-            // Splice services list into groups of 3 for flex column display
-            var size = 3;
-            config.services.forEach(function(service) {
-                service.rows = [];
-                items = service.items;
-                while (items.length) {
-                    service.rows.push(items.splice(0, size));
+    created: async function () {
+        let that = this;
+
+        this.isDark = 'overrideDark' in localStorage ?
+            JSON.parse(localStorage.overrideDark) : matchMedia("(prefers-color-scheme: dark)").matches;
+
+        if ('vlayout' in localStorage) {
+            this.vlayout = JSON.parse(localStorage.vlayout)
+        }
+
+        this.checkOffline();
+        try {
+            this.config =  await this.getConfig();
+            document.title = this.config.title + ' | Homer';
+        } catch (error) {
+            this.offline = true;
+        }
+
+        // Look for a new message if an endpoint is provided.
+        if (this.config.message && this.config.message.url) {
+            this.getMessage(this.config.message.url).then(function(message){
+                // keep the original config value if no value is provided by the endpoint
+                for (const prop of ['title','style','content']) {
+                    if (prop in message && message[prop] !== null) {
+                        that.config.message[prop] = message[prop];
+                    }    
                 }
+            });
+        }
 
-                if (service.rows.length) {
-                    var last = service.rows.length-1;
-                    service.rows[last] = service.rows[last].concat(Array(size - service.rows[last].length));
+        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);
+                });
+            });
+        },
+        getMessage: function (url) {
+            return fetch(url).then(function (response) {
+                if (response.status != 200) {
+                    return;
                 }
+                return response.json();
             });
-            that.config = config;
-        });
+        },
+        toggleTheme: function() {
+            this.isDark = !this.isDark;
+            localStorage.overrideDark = this.isDark; 
+        }, 
+        toggleLayout: function() {
+            this.vlayout = !this.vlayout;
+            localStorage.vlayout = this.vlayout;
+        },
+        toggleMenu: function() {
+            this.showMenu = !this.showMenu;
+        }
+    },
+    mounted() {
+        function isSmallScreen() {
+            return window.matchMedia('screen and (max-width: 1023px)').matches;
+        }
+        this._keyListener = function(e) {
+            if (e.key === '/') {
+                if (isSmallScreen()) {
+                    this.showMenu = true;
+                }
+                Vue.nextTick(() => {
+                    this.$refs.search.focus();
+                });
+
+                e.preventDefault();
+            }
+            if (e.key === 'Escape') {
+                this.filter = '';
+                this.$refs.search.blur();
+                if (isSmallScreen()) {
+                    this.showMenu = false;
+                }
+            }
+        }
+
+        document.addEventListener('keydown', this._keyListener.bind(this));
+    },
+    beforeDestroy() {
+        document.removeEventListener('keydown', this._keyListener);
     }
 });
 
+Vue.component('service', {
+    props: ['item'],
+    template: `<div>
+    <div class="card">
+        <a :href="item.url" :target="item.target">
+            <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>
+                <div class="tag" :class="item.tagstyle" v-if="item.tag">
+                    <strong class="tag-text">#{{ item.tag }}</strong>
+                </div>
+            </div>
+        </a>
+    </div></div>`
+});
 
-function getConfig() {
-    return new Promise(function (resolve, reject) {
-        var xhr = new XMLHttpRequest();
-        xhr.open('GET', 'config.yml');
-        xhr.onload = function () {
-            if (this.status >= 200 && this.status < 300) {
-                try {
-                    var data = jsyaml.load(xhr.response);
-                    resolve(data);
-                } catch (e) {
-                    console.error('fail to parse config file');
-                    reject();
-                }
-            } else {
-                reject({
-                    status: this.status,
-                    statusText: xhr.statusText
-                });
-            }
-        };
-        xhr.onerror = function () {
-            reject({
-                status: this.status,
-                statusText: xhr.statusText
-            });
-        };
-        xhr.send();
+if ('serviceWorker' in navigator) {
+    window.addEventListener('load', function () {
+        navigator.serviceWorker.register('worker.js');
     });
-}
\ No newline at end of file
+}