App.vue (4031B)
1 <template> 2 <v-app> 3 <v-app-bar app> 4 <v-tabs v-model="tab"> 5 <v-tab v-for="filename in filenames" 6 :key="filename" 7 style="text-transform: none !important"> 8 {{filename}} 9 </v-tab> 10 </v-tabs> 11 <v-spacer/> 12 <v-icon title="Download ZIP" 13 @click="download">mdi-zip-box</v-icon> 14 </v-app-bar> 15 <v-main> 16 <v-alert dense 17 v-if="!connected" 18 :type="connected === null ? 'info' : 'error'" 19 :style="connected ? '' : 'cursor: pointer;'" 20 @click="connect"> 21 {{connected === null ? 'Connecting...' : 'No connection - click to retry'}} 22 </v-alert> 23 <v-tabs-items v-model="tab"> 24 <v-tab-item v-for="filename in filenames" 25 :transition="false" :reverse-transition="false" 26 style="font-family: monospace; margin: 1em; line-height: 1.2" :key="filename"> 27 <pre><code style="background-color: white; padding: 0">{{files[filename]}}</code></pre> 28 </v-tab-item> 29 </v-tabs-items> 30 </v-main> 31 </v-app> 32 </template> 33 34 <script> 35 import JSZip from "jszip" 36 import { saveAs } from "file-saver" 37 export default { 38 name: "App", 39 data() { 40 return { 41 tab: null, 42 files: {}, 43 filenames: [], 44 connected: null, 45 ws: null, 46 follow: true, 47 zip: new JSZip() 48 } 49 }, 50 methods: { 51 connect() { 52 this.connected = null 53 if (!this.roomCode) { 54 //this.status = "No room specified" // TODO: Use? 55 this.connected = false 56 return 57 } 58 // Quick fix to determine whether to use WSS or not 59 let proto = window.location.href.startsWith("https://") ? "wss" : "ws" 60 let url = `${proto}://${document.location.host}/ws/view/${this.roomCode}` 61 this.ws = new WebSocket(url) 62 this.ws.onclose = () => { 63 this.connected = false 64 } 65 this.ws.onopen = () => { 66 this.files = {} 67 this.filenames = [] 68 this.connected = true 69 } 70 this.ws.onmessage = (msg) => { 71 let fileReceived = JSON.parse(msg.data) 72 if (!fileReceived.contents || !fileReceived.contents.length) { 73 // null file contents: Remove existing file 74 if (this.filenames.includes(fileReceived.name)) { 75 this.filenames = this.filenames.filter(fn => fn !== fileReceived.name) 76 delete this.files[fileReceived.name] 77 } 78 } else { 79 this.$set(this.files, fileReceived.name, fileReceived.contents) 80 if (!this.filenames.includes(fileReceived.name)) { 81 this.filenames.push(fileReceived.name) 82 hljs.initHighlightingOnLoad() 83 } 84 if (this.tab === null || this.follow) { 85 this.tab = this.filenames.findIndex(fn => fn === fileReceived.name) 86 } 87 } 88 //hljs.highlightBlock(document.getElementById("viewer")) 89 } 90 }, 91 download() { 92 for (let fn in this.files) { 93 this.zip.file(fn, this.files[fn]) 94 } 95 this.zip.generateAsync({type:"blob"}).then(content => { 96 saveAs(content, this.roomCode + ".zip") 97 }) 98 } 99 }, 100 computed: { 101 roomCode() { 102 let qParams = new URLSearchParams(window.location.search) 103 return qParams.get("r") 104 } 105 }, 106 mounted() { 107 this.connect() 108 } 109 } 110 </script> 111 112 <style> 113 114 </style>