]> git.immae.eu Git - github/bastienwirtz/homer.git/blame - app.js
Merge pull request #34 from Sajfer/dockerignore
[github/bastienwirtz/homer.git] / app.js
CommitLineData
9baec9ae 1const app = new Vue({
09763dbf
BW
2 el: '#app',
3 data: {
4 config: null,
9baec9ae
BW
5 offline: false,
6 filter: '',
5323df4a 7 vlayout: true,
7cc525b2
T
8 isDark: null,
9 showMenu: false
09763dbf 10 },
7fd9dc6f 11 created: async function () {
9ca12a40 12 let that = this;
7fd9dc6f 13
a489a0a3 14 this.isDark = 'overrideDark' in localStorage ?
d6adb2db 15 JSON.parse(localStorage.overrideDark) : matchMedia("(prefers-color-scheme: dark)").matches;
9ca12a40 16
d6adb2db
BW
17 if ('vlayout' in localStorage) {
18 this.vlayout = JSON.parse(localStorage.vlayout)
19 }
a489a0a3 20
9baec9ae 21 this.checkOffline();
7fd9dc6f
BW
22 try {
23 this.config = await this.getConfig();
26221ad3 24 document.title = this.config.title + ' | Homer';
7fd9dc6f
BW
25 } catch (error) {
26 this.offline = true;
27 }
28
29 // Look for a new message if an endpoint is provided.
d5ef84f5 30 if (this.config.message && this.config.message.url) {
7fd9dc6f
BW
31 this.getMessage(this.config.message.url).then(function(message){
32 // keep the original config value if no value is provided by the endpoint
33 for (const prop of ['title','style','content']) {
34 if (prop in message && message[prop] !== null) {
35 that.config.message[prop] = message[prop];
36 }
37 }
38 });
39 }
9baec9ae
BW
40
41 document.addEventListener('visibilitychange', function () {
42 if (document.visibilityState == "visible") {
43 that.checkOffline();
44 }
45 }, false);
46 },
47 methods: {
48 checkOffline: function () {
49 let that = this;
50 return fetch(window.location.href + "?alive", {
51 method: 'HEAD',
52 cache: 'no-store'
53 }).then(function () {
54 that.offline = false;
55 }).catch(function () {
56 that.offline = true;
57 });
58 },
59 getConfig: function (event) {
60 return fetch('config.yml').then(function (response) {
61 if (response.status != 200) {
62 return
63 }
64 return response.text().then(function (body) {
65 return jsyaml.load(body);
66 });
67 });
68 },
7fd9dc6f
BW
69 getMessage: function (url) {
70 return fetch(url).then(function (response) {
71 if (response.status != 200) {
72 return;
73 }
74 return response.json();
75 });
76 },
5323df4a 77 toggleTheme: function() {
d6adb2db
BW
78 this.isDark = !this.isDark;
79 localStorage.overrideDark = this.isDark;
80 },
81 toggleLayout: function() {
82 this.vlayout = !this.vlayout;
a489a0a3
FB
83 localStorage.vlayout = this.vlayout;
84 },
7cc525b2
T
85 toggleMenu: function() {
86 this.showMenu = !this.showMenu;
a4de4a3a
JS
87 },
88 matchesFilter: function(item) {
89 return (item.name.toLowerCase().includes(this.filter.toLowerCase())
90 || (item.tag && item.tag.toLowerCase().includes(this.filter.toLowerCase())))
91 },
92 firstMatchingService: function() {
93 for (group of this.config.services) {
94 for (item of group.items) {
95 if (this.matchesFilter(item)) {
96 return item;
97 }
98 }
99 }
100 return null;
101 },
102 navigateToFirstService: function(target) {
103 service = this.firstMatchingService();
104 if (service) {
105 window.open(service.url, target || service.target || '_self');
106 }
7cc525b2 107 }
6eba37a3
JS
108 },
109 mounted() {
110 function isSmallScreen() {
111 return window.matchMedia('screen and (max-width: 1023px)').matches;
112 }
113 this._keyListener = function(e) {
114 if (e.key === '/') {
115 if (isSmallScreen()) {
116 this.showMenu = true;
117 }
118 Vue.nextTick(() => {
119 this.$refs.search.focus();
120 });
121
122 e.preventDefault();
123 }
124 if (e.key === 'Escape') {
125 this.filter = '';
126 this.$refs.search.blur();
127 if (isSmallScreen()) {
128 this.showMenu = false;
129 }
130 }
131 }
132
133 document.addEventListener('keydown', this._keyListener.bind(this));
134 },
135 beforeDestroy() {
136 document.removeEventListener('keydown', this._keyListener);
09763dbf
BW
137 }
138});
139
4877ec98
BW
140Vue.component('service', {
141 props: ['item'],
142 template: `<div>
143 <div class="card">
56c69e0d 144 <a :href="item.url" :target="item.target">
4877ec98
BW
145 <div class="card-content">
146 <div class="media">
147 <div v-if="item.logo" class="media-left">
148 <figure class="image is-48x48">
149 <img :src="item.logo" />
150 </figure>
151 </div>
152 <div v-if="item.icon" class="media-left">
153 <figure class="image is-48x48">
154 <i style="font-size: 35px" :class="item.icon"></i>
155 </figure>
156 </div>
157 <div class="media-content">
158 <p class="title is-4">{{ item.name }}</p>
159 <p class="subtitle is-6">{{ item.subtitle }}</p>
160 </div>
161 </div>
a2fdb8a9 162 <div class="tag" :class="item.tagstyle" v-if="item.tag">
163 <strong class="tag-text">#{{ item.tag }}</strong>
164 </div>
4877ec98
BW
165 </div>
166 </a>
167 </div></div>`
168});
169
9baec9ae
BW
170if ('serviceWorker' in navigator) {
171 window.addEventListener('load', function () {
a489a0a3 172 navigator.serviceWorker.register('worker.js');
09763dbf 173 });
e41196e7 174}