diff options
| author | mo khan <mo@mokhan.ca> | 2025-04-22 11:34:14 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-04-22 11:34:14 -0600 |
| commit | c11693668f7e230a21f806664b411a598bee9b10 (patch) | |
| tree | 0fecffe3b5b7a98b2134cc86402370a777fdcd4c /public | |
| parent | 699ef9cc2a910774d1f210ef0562496d08f04e03 (diff) | |
feat: add tiny vue.js app to list and add new sparkles
Diffstat (limited to 'public')
| -rw-r--r-- | public/application.js | 76 | ||||
| -rw-r--r-- | public/index.html | 17 |
2 files changed, 91 insertions, 2 deletions
diff --git a/public/application.js b/public/application.js new file mode 100644 index 0000000..dcc45f8 --- /dev/null +++ b/public/application.js @@ -0,0 +1,76 @@ +document.addEventListener('DOMContentLoaded', (event) => { + const { createApp, ref } = Vue + const regex = /\s*(?<sparklee>@\w+)\s+(?<reason>.+)/ + + const app = createApp({ + created: function() { + this.reload(); + this.intervalId = setInterval(() => this.reload(), 30000); + }, + destroyed: function() { + if (this.intervalId) + clearInterval(this.intervalId); + this.intervalId = null; + }, + computed: { + heading: function() { + return this.sparkles.length == 0 ? "No Sparkles Sent" : "Recent Sparkles"; + }, + recentSparkles: function() { + return this.sparkles.reverse(); + }, + isDisabled: function() { + return this.isSending || !this.isValid(); + }, + }, + data() { + return { + intervalId: null, + errorMessage: "", + isSending: false, + sparkle: "", + sparkles: [], + } + }, + methods: { + reload: function() { + fetch("/sparkles") + .then((response) => response.json()) + .then((json) => this.sparkles = json) + .catch((json) => console.dir(json)); + }, + isValid: function() { + return this.sparkle.length > 0; + }, + submitSparkle: function() { + this.isSending = true; + + let matches = regex.exec(this.sparkle) + let sparklee = matches.groups.sparklee + let reason = matches.groups.reason + + fetch("/sparkles", { + method: "POST", + mode: "cors", + cache: "no-cache", + headers: { "Content-Type": "application/json" }, + redirect: "follow", + body: JSON.stringify({ sparklee: sparklee, reason: reason }) + }).then((response) => { + response.json().then((json) => { + this.isSending = false; + if (response.ok) { + this.sparkles.push(json); + this.sparkle = ""; + } else { + this.errorMessage = json["error"]; + } + }) + }).catch((error) => console.error(error)); + }, + } + }) + + app.mount('#app') +}) + diff --git a/public/index.html b/public/index.html index 6231d7b..d9033a5 100644 --- a/public/index.html +++ b/public/index.html @@ -6,17 +6,30 @@ <meta name="color-scheme" content="light dark"> <title>SparkleLab</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"> + <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> + <script src="/application.js"></script> </head> <body> - <main class="container"> + <main id="app" class="container"> <nav> <ul> - <li><strong>SparkleLab</strong></li> + <li><strong>SparkleLab✨</strong></li> </ul> <ul> <li><a href="/session/new">Login</a></li> </ul> </nav> + + <form v-on:submit.prevent="submitSparkle"> + <label>/sparkle <input type="text" placeholder="@tanuki for helping me with my homework!" v-model="sparkle" /> </label> + <button type="submit" v-bind:disabled="isDisabled">✨ Sparkle</button> + </form> + <span class="error">{{ errorMessage }}</span> + + <h1>{{ heading }}</h1> + <div v-for="sparkle in recentSparkles"> + <p> <strong>{{ sparkle.sparklee }}</strong> {{ sparkle.reason }} </p> + </div> </main> </body> </html> |
