diff options
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/Footer.vue | 5 | ||||
-rw-r--r-- | src/components/Logo.vue | 9 | ||||
-rw-r--r-- | src/components/Navigation.vue | 6 | ||||
-rw-r--r-- | src/components/News.vue | 47 | ||||
-rw-r--r-- | src/components/ServerStatus.vue | 11 |
5 files changed, 48 insertions, 30 deletions
diff --git a/src/components/Footer.vue b/src/components/Footer.vue index 4d1df2e..076700e 100644 --- a/src/components/Footer.vue +++ b/src/components/Footer.vue @@ -13,10 +13,9 @@ </style> <script lang="ts"> -import Vue from "vue" -import Component from "vue-class-component" +import { Options, Vue } from "vue-class-component"; -@Component +@Options({}) export default class Copyright extends Vue { year = Reflect.construct(Date, []).getFullYear(); } diff --git a/src/components/Logo.vue b/src/components/Logo.vue index 20f071e..8b653e1 100644 --- a/src/components/Logo.vue +++ b/src/components/Logo.vue @@ -14,7 +14,6 @@ url("../assets/fonts/AlbertusTMW.woff") format("woff"), url("../assets/fonts/AlbertusTMW.ttf") format("truetype"); } - .logo { /* this is all relative because our mobile site has to be responsive */ background: url(../assets/logo.svg) no-repeat left top; /* FIXME: the -small logo is fugly */ @@ -23,21 +22,19 @@ font-family: "Albertus TMW", "Arial Black", "Times New Roman", fantasy; font-size: 7vw; text-shadow: 0.03ch 0.07ch #070905; + text-decoration: none; color: #34B039; height: 11vw; cursor: pointer; z-index: 200; max-width: calc(90% - 15vw); - &::selection { text-shadow: none; } - & span:last-of-type { display: none; } } - @media (min-width: 800px) { .logo { background-image: url(../assets/logo.svg); @@ -47,14 +44,12 @@ height: 100px; margin-top: 20px; position: relative; - & span:first-of-type { position: absolute; left: 125px; top: 0; font-size: 0.6em; } - & span:last-of-type { display: inline; position: absolute; @@ -69,7 +64,6 @@ } } } - @media (max-width: 300px) { .logo { background-image: url(../assets/logo-extrasmall.svg); @@ -78,7 +72,6 @@ text-shadow: none; } } - @media (min-width: 1100px) { .logo { max-width: 100%; diff --git a/src/components/Navigation.vue b/src/components/Navigation.vue index bb40e22..783e12a 100644 --- a/src/components/Navigation.vue +++ b/src/components/Navigation.vue @@ -58,6 +58,7 @@ border-radius: 5px; border: solid 1px #2f2e32; margin-bottom: 13px; + min-width: 17ch; & ul { list-style: none; @@ -149,11 +150,10 @@ </style> <script lang="ts"> -import { Component, Vue } from "vue-property-decorator"; -import RouteRecord from "vue-router"; +import { Options, Vue } from "vue-class-component"; import ServerStatus from "@/components/ServerStatus.vue"; -@Component({ +@Options({ components: { ServerStatus, }, diff --git a/src/components/News.vue b/src/components/News.vue index 7ad844f..3243b3b 100644 --- a/src/components/News.vue +++ b/src/components/News.vue @@ -2,12 +2,17 @@ <div class="news" v-if="count"> <span v-if="!entries.length">(no news entries)</span> - <article class="entry" v-for="entry in entries" :id="entry.id"> + <article class="entry" v-for="entry in entries" :id="entry.id" v-bind:key="entry.id"> <a :href="'#' + entry.id">{{ entry.title }}</a> <time :datetime="entry.date" class="date">{{ entry.date }}</time> <section class="body" v-html="entry.html"></section> <q>{{entry.author}}</q> </article> + <article v-if="count > 1 && !fullyLoaded" class="entry loading"> + <section class="body"> + Loading... Please wait. + </section> + </article> </div> </template> @@ -55,7 +60,7 @@ & .body { margin-top: 2ex; - &::v-deep > b { + &:deep(b) { display: block; margin-bottom: 1ex; } @@ -95,7 +100,7 @@ </style> <script lang="ts"> -import { Component, Prop, Vue } from "vue-property-decorator"; +import { Options, Vue } from "vue-class-component"; import newsEntries from "@/assets/news.json"; interface NewsEntry { @@ -106,36 +111,57 @@ interface NewsEntry { html: string; } -@Component +@Options({ + props: { + count: Number, + from: Number, + } +}) export default class News extends Vue { - @Prop({ default: Infinity }) private count!: number; - @Prop({ default: 0 }) private from!: number; + private count = Infinity; + private from = 0; private entries: NewsEntry[] = (newsEntries as NewsEntry[]).slice(this.from, this.count); + private fullyLoaded = false; beautify () { this.entries.forEach(entry => { // FIXME: weird Vue bug entry.html = entry.html.replace(/<br\/>/g,"<br></br>"); + + // compare the entry title with its first line: + const compare = `<b>${entry.title}</b><br></br>`; + + if (entry.html.startsWith(compare)) { + // duplicate title: remove the extra one + entry.html = entry.html.slice(compare.length); + } }); } mounted () { if (!process.env.VUE_APP_NEWS_JSON || !process.env.VUE_APP_NEWS_JSON.startsWith("https")) { - // TODO: allow arbitrary paths (no hardcoded news.json) this.beautify(); + // no extra news to load so end here return; } // restore from cache while we're loading a fresh copy if (Reflect.has(self, "localStorage")) { - let newsCache = localStorage.getItem("newsCache"); + const newsCache = localStorage.getItem("newsCache"); if (newsCache !== null) { this.entries = (JSON.parse(newsCache) as NewsEntry[]).slice(this.from, this.count); - this.beautify(); } } + // initial rendering + this.beautify(); + + if (this.count === 1 && this.entries.length >= 1) { + // don't loaad extra news unprompted + return; + } + const req = new Request(process.env.VUE_APP_NEWS_JSON, { method: "GET", cache: "default", // serve if fresh, else fetch @@ -145,13 +171,14 @@ export default class News extends Vue { .then(data => data.json()) .then(data => { this.entries = (data as NewsEntry[]).slice(this.from, this.count); + this.fullyLoaded = true; this.beautify(); if (Reflect.has(self, "localStorage")) { localStorage.setItem("newsCache", JSON.stringify(data)); } }) - .catch(e => { + .catch(() => { // no news (will use cache, if any) }) } diff --git a/src/components/ServerStatus.vue b/src/components/ServerStatus.vue index 68f7bd4..4b1e0d9 100644 --- a/src/components/ServerStatus.vue +++ b/src/components/ServerStatus.vue @@ -2,7 +2,7 @@ <aside> <a v-if="Online && Players" target="_blank" rel="noopener" href="https://server.themanaworld.org">Online: {{Players}} players</a> <a v-if="Online && !Players" target="_blank" rel="noopener" href="https://server.themanaworld.org">Online</a> - <a v-if="!Online" class="offline" target="_blank" rel="noopener" href="https://www.youtube.com/watch?v=ILVfzx5Pe-A">Offline</a> + <a v-if="!Online" class="offline" target="_blank" rel="noopener" href="https://www.youtube-nocookie.com/embed/ILVfzx5Pe-A?autoplay=1&modestbranding=1">Offline</a> </aside> </template> @@ -21,15 +21,14 @@ aside :any-link { </style> <script lang="ts"> -import Vue from "vue" -import Component from "vue-class-component" +import { Options, Vue } from "vue-class-component"; interface StatusResponse { serverStatus: string; playersOnline?: number; } -@Component +@Options({}) export default class ServerStatus extends Vue { Players = 0; Online = true; @@ -41,8 +40,8 @@ export default class ServerStatus extends Vue { }); try { - const raw_response = await fetch(req); - const data: StatusResponse = await raw_response.json(); + const rawResponse = await fetch(req); + const data: StatusResponse = await rawResponse.json(); this.Online = data.serverStatus === "Online"; this.Players = data.playersOnline || 0; |