summaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/Footer.vue23
-rw-r--r--src/components/Logo.vue64
-rw-r--r--src/components/Navigation.vue121
-rw-r--r--src/components/News.vue54
-rw-r--r--src/components/ServerStatus.vue73
5 files changed, 335 insertions, 0 deletions
diff --git a/src/components/Footer.vue b/src/components/Footer.vue
new file mode 100644
index 0000000..c454e3a
--- /dev/null
+++ b/src/components/Footer.vue
@@ -0,0 +1,23 @@
+<template>
+ <footer class="footer">
+ &copy; 2004&mdash;{{ year }} The Mana World
+ </footer>
+</template>
+
+<style scoped>
+.footer {
+ text-align: right;
+ font-size: 8pt;
+ padding: 5px;
+}
+</style>
+
+<script lang="ts">
+import Vue from "vue"
+import Component from "vue-class-component"
+
+@Component
+export default class Copyright extends Vue {
+ year = Reflect.construct(Date, []).getFullYear();
+}
+</script>
diff --git a/src/components/Logo.vue b/src/components/Logo.vue
new file mode 100644
index 0000000..ff81161
--- /dev/null
+++ b/src/components/Logo.vue
@@ -0,0 +1,64 @@
+<template>
+ <router-link tag="div" :to="{ name: 'home' }" class="logo">
+ The Mana World
+ <!--<span>Feel the mana power growing inside you</span>-->
+ <span>A free open source 2D MMORPG in development</span>
+ </router-link>
+</template>
+
+<style scoped>
+/*
+ XXX: I couldn't find the font usued in the original PNG logo so I used something
+ similar-ish
+*/
+@import url('https://fonts.googleapis.com/css?family=Carter+One&display=swap');
+
+.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 */
+ background-size: 12vw 12vw;
+ padding: 2vw 0 0 12vw;
+ font-family: 'Carter One', cursive;
+ font-size: 7vw;
+ text-shadow: 0.03ch 0.06ch #070905;
+ color: #34B039;
+ height: 11vw;
+ cursor: pointer;
+
+ & span {
+ display: none;
+ }
+}
+
+@media (min-width: 800px) {
+ .logo {
+ background-image: url(../assets/logo.svg);
+ background-size: 100px 100px;
+ padding: 10px 0 0 100px;
+ font-size: 3em;
+ height: 100px;
+ position: relative;
+
+ & span {
+ display: inline;
+ position: absolute;
+ font-family: Helvetica;
+ font-size: 0.3em;
+ top: 72px;
+ left: 110px;
+ text-shadow: none;
+ color: #616260;
+ font-style: oblique;
+ }
+ }
+}
+
+@media (max-width: 300px) {
+ .logo {
+ background: url(../assets/logo-extrasmall.svg) no-repeat left top;
+ background-size: 12vw 12vw;
+ font-weight: bold;
+ text-shadow: none;
+ }
+}
+</style>
diff --git a/src/components/Navigation.vue b/src/components/Navigation.vue
new file mode 100644
index 0000000..199290b
--- /dev/null
+++ b/src/components/Navigation.vue
@@ -0,0 +1,121 @@
+<template>
+ <nav class="nav">
+ <ul>
+ <li><router-link :class="{ 'custom-active': isHome }" :to="{ name: 'home' }">Home</router-link></li>
+ <li><router-link :to="{ name: 'registration' }">Create Account</router-link></li>
+ <li><a href="https://wiki.themanaworld.org/index.php/Downloads">Download</a></li>
+ <li><router-link :to="{ name: 'about' }">About</router-link></li>
+ <li><a href="https://wiki.themanaworld.org/index.php/FAQ">FAQ</a></li> <!-- we might want to put FAQ under About, or put About on the wiki -->
+ <li><router-link :class="{ 'custom-active': isSupport }" :to="{ name: 'support' }">Support</router-link></li>
+ <li><a href="https://wiki.themanaworld.org/">Wiki</a></li>
+ <li><a href="https://forums.themanaworld.org/">Forums</a></li>
+ </ul>
+ <!-- TODO: we want a server status component: https://api.themanaworld.org/api/tmwa/server -->
+ <div class="server">
+ <span>Server Status</span>
+ <ServerStatus class="status"/>
+ </div>
+ <ul>
+ <span>Source Code</span>
+ <li><a href="https://github.com/themanaworld">The Mana World</a></li>
+ <li><a href="https://gitlab.com/evol">Evol Online</a></li>
+ <li><a href="https://gitlab.com/manaplus">ManaPlus</a></li>
+ <li><a href="https://github.com/bjorn/tiled">Tiled</a></li>
+ </ul>
+ </nav>
+</template>
+
+<style scoped>
+.nav {
+ background: #BA7A58;
+ color: #2f2e32;
+ border-radius: 0 0 15px 15px;
+ padding: 15px;
+ font-size: 14px;
+
+ & span {
+ text-align: center;
+ display: block;
+ padding: 5px;
+ border-bottom: solid 1px #2f2e32;
+ }
+
+ & a, & a:visited {
+ color: #2f2e32;
+ text-decoration: none;
+ display: block;
+ border: solid 1px #CBA083;
+ padding: 1ch;
+
+ &:hover, &.router-link-exact-active, &.custom-active {
+ background: rgba(255,255,255,0.4);
+ border: solid 1px #2f2e32;
+ font-weight: bold;
+ }
+ }
+
+ & ul, & div {
+ background: #CBA083;
+ margin: 0px;
+ padding: 0px;
+ border-radius: 5px;
+ border: solid 1px #2f2e32;
+ list-style: none;
+ margin-bottom: 13px;
+ }
+
+ & ul li {
+ margin-left: 0.8ch;
+ margin-right: 0.8ch;
+
+ &:first-of-type {
+ margin-top: 0.8ch;
+ }
+
+ &:last-of-type {
+ margin-bottom: 0.8ch;
+ }
+ }
+
+
+ & .server > .status {
+ text-align: center;
+ font-weight: bolder;
+ border: 0;
+ border-radius: 0 0 5px 5px;
+
+ &:hover {
+ background: rgba(255,255,255,0.4);
+ }
+ }
+}
+
+@media (min-width: 1100px) {
+ .nav {
+ border-radius: 0 15px 15px 0;
+ }
+}
+</style>
+
+<script lang="ts">
+import { Component, Vue } from "vue-property-decorator";
+import RouteRecord from "vue-router";
+import ServerStatus from "@/components/ServerStatus.vue";
+
+@Component({
+ components: {
+ ServerStatus,
+ },
+ computed: {
+ // XXX: find a better way to do this
+
+ isSupport() {
+ return this.$route.path.startsWith("/recover");
+ },
+ isHome() {
+ return this.$route.path.startsWith("/news");
+ }
+ }
+})
+export default class NavigationV extends Vue {}
+</script>
diff --git a/src/components/News.vue b/src/components/News.vue
new file mode 100644
index 0000000..8a58514
--- /dev/null
+++ b/src/components/News.vue
@@ -0,0 +1,54 @@
+<template>
+ <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.date">
+ <a :href="'#' + entry.date">{{ entry.title }}</a>
+ <time :datetime="entry.date" class="date">{{ entry.date }}</time>
+ <section class="body" v-html="entry.html"></section>
+ </article>
+ </div>
+</template>
+
+<style scoped>
+.news .entry {
+ margin-bottom: 1em;
+
+ &:nth-of-type(1n + 2) {
+ margin-top: 2em;
+ }
+
+ & > a {
+ text-decoration: none;
+ color: inherit;
+ font-weight: bold;
+ }
+
+ & > .date {
+ float: right;
+ }
+
+ & .body {
+ margin-top: 5px;
+ }
+}
+</style>
+
+<script lang="ts">
+import { Component, Prop, Vue } from "vue-property-decorator";
+import newsEntries from "@/assets/news.json";
+
+interface NewsEntry {
+ title: string;
+ date: string;
+ author: string;
+ html: string;
+}
+
+@Component
+export default class News extends Vue {
+ @Prop({ default: Infinity }) private count!: number;
+ @Prop({ default: 0 }) private from!: number;
+ private entries: NewsEntry[] = (newsEntries as NewsEntry[]).slice(this.from, this.count);
+}
+</script>
diff --git a/src/components/ServerStatus.vue b/src/components/ServerStatus.vue
new file mode 100644
index 0000000..c35e916
--- /dev/null
+++ b/src/components/ServerStatus.vue
@@ -0,0 +1,73 @@
+<template>
+ <aside>
+ <a v-if="Online && Players" target="_blank" href="https://server.themanaworld.org">Online: {{Players}} players</a>
+ <a v-if="Online && !Players" target="_blank" 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>
+ </aside>
+</template>
+
+<style scoped>
+aside :any-link {
+ text-decoration: none;
+ color: green;
+ display: block;
+ padding: 8px;
+
+ &.offline {
+ color: #d42424;
+ }
+}
+</style>
+
+<script lang="ts">
+import Vue from "vue"
+import Component from "vue-class-component"
+
+interface StatusResponse {
+ serverStatus: string;
+ playersOnline?: number;
+}
+
+@Component
+export default class ServerStatus extends Vue {
+ Players = 0;
+ Online = true;
+
+ private async getStatus () {
+ const req = new Request(`${process.env.VUE_APP_API}/tmwa/server`, {
+ mode: "cors",
+ referrer: "no-referrer",
+ });
+
+ try {
+ const raw_response = await fetch(req);
+ const data: StatusResponse = await raw_response.json();
+
+ this.Online = data.serverStatus === "Online";
+ this.Players = data.playersOnline || 0;
+
+ if (Reflect.has(self, "localStorage")) {
+ localStorage.setItem("onlinePlayers", `${this.Players}`);
+ localStorage.setItem("serverOnline", this.Online ? "true": "false");
+ }
+ } catch (err) {
+ // API unreachable (assume it's online anyway)
+ this.Online = true;
+ }
+
+ setTimeout(this.getStatus, 8000);
+ }
+
+ mounted () {
+ // use the last cached value to populate prior to first fetch:
+ if (Reflect.has(self, "localStorage")) {
+ this.Players = +(localStorage.getItem("onlinePlayers") || 99);
+ this.Online = !!(localStorage.getItem("serverOnline") || true);
+ } else {
+ this.Players = 99;
+ }
+
+ this.getStatus();
+ }
+}
+</script>