Fix re-login error (#60)

* refactor: put all session ref into one reactive store

* fix relogin duplicate signal

* fix: put quest data into store, fix hint reactivity

* fix: duplicate after relogin

* only register listener once
This commit is contained in:
Lin, Hung-Shin authored and Annie Huang committed 2023-11-12 21:24:01 +08:00
1 parent 96cdd2b157
commit acff4a3f2c
10 files changed
+179 -168

No files matched your search

+2 -2
View File
@@ -25,10 +25,10 @@
</template>
<script setup>
import sessionManager from '../utils/session'
import { createSession } from '../store/session'
const reset = async () => {
console.log('resetting...')
await sessionManager.createSession()
await createSession()
}
</script>
+8 -2
View File
@@ -4,12 +4,18 @@ import { useRouter } from 'vue-router'
import { logout } from '../utils/auth'
import { bugReportUrl } from '../config'
import api from '../utils/api'
import { reset } from '../store/session'
const router = useRouter()
const handleLogout = async () => {
await logout()
router.push({ name: 'login' })
try {
await logout()
reset()
router.push({ name: 'login' })
} catch (err) {
console.error(err)
}
}
const questData = ref(null)
+12 -8
View File
@@ -1,11 +1,15 @@
<script setup>
import { ref, watch } from 'vue'
import sessionManager from '../utils/session'
import sessionStore from '../store/session'
const current = ref(-1)
watch(sessionManager.hints, (hints) => {
current.value = hints.length - 1
})
watch(
() => sessionStore.session.hints,
(hints) => {
current.value = hints.length - 1
},
{ deep: true }
)
const left = () => {
if (current.value > 0) {
@@ -14,7 +18,7 @@ const left = () => {
}
const right = () => {
if (current.value < sessionManager.hints.value.length - 1) {
if (current.value < sessionStore.session.hints.length - 1) {
current.value += 1
}
}
@@ -43,13 +47,13 @@ const right = () => {
<p class="inline text-text font-xl w-1/14 p-2 m-1">{{ current + 1 }}</p>
<p class="inline text-text font-xl w-1/14 p-2 m-1">/</p>
<p class="inline text-text font-xl w-1/14 p-2 m-1">
{{ sessionManager.hints.value.length }}
{{ sessionStore.session.hints.length }}
</p>
<button
class="p-2 m-1 w-1/8"
id="Rbutton"
@click="right"
:disabled="current === sessionManager.hints.value.length - 1"
:disabled="current === sessionStore.session.hints.length - 1"
>
<font-awesome-icon :icon="['fas', 'arrow-right']" class="text-text" />
</button>
@@ -57,7 +61,7 @@ const right = () => {
</div>
<div id="hint" class="bg-bg flex flex-wrap p-8">
<p class="text-text font-xl whitespace-pre-wrap">
{{ sessionManager.hints.value[current] }}
{{ sessionStore.session.hints[current] }}
</p>
<br />
</div>
+7 -25
View File
@@ -1,37 +1,20 @@
<script setup>
import { onMounted, ref } from 'vue'
import api from '../utils/api'
import sessionManager from '../utils/session'
const questData = ref(null)
const questErr = ref(null)
onMounted(async () => {
try {
const res = await api.get('/quests/helloworld')
questData.value = res.data
} catch (err) {
questErr.value = err
}
})
import sessionStore from '../store/session'
</script>
<template>
<div class="h-full w-full max-h-full">
<h1 id="topic" class="text-red-500" v-if="questErr">
Failed to get quest infomation
</h1>
<h1 id="topic" class="text-text" v-else-if="questData">
{{ questData.title }}
<h1 id="topic" class="text-text" v-if="sessionStore.quest">
{{ sessionStore.quest.title }}
</h1>
<hr class="border-text-tertiary border-2 my-2" />
<div>
<div v-if="questData" class="whitespace-pre-wrap">
<p class="text-text">{{ questData.instruction }}</p>
<div v-if="sessionStore.quest" class="whitespace-pre-wrap">
<p class="text-text">{{ sessionStore.quest.instruction }}</p>
<br />
<p class="text-text">Tasks:</p>
<ul v-if="sessionManager.session.value">
<li v-for="task in sessionManager.session.value.tasks" :key="task.id">
<ul v-if="sessionStore.session">
<li v-for="task in sessionStore.session.tasks" :key="task.id">
<p v-if="task.completed" class="text-text-primary">
<span class="">✓</span>
{{ task.name }}
@@ -43,7 +26,6 @@ onMounted(async () => {
</li>
</ul>
</div>
<p class="text-red-500" v-else-if="questErr">{{ questErr.message }}</p>
<p class="text-text" v-else>Loading...</p>
</div>
</div>
+4 -3
View File
@@ -10,12 +10,13 @@
<script setup>
import { onMounted, ref } from 'vue'
import sessionManager from '../utils/session'
import { useTerminal } from '../store/session'
const terminal = ref(null)
onMounted(() => {
sessionManager.term.mount(terminal.value)
window.addEventListener('resize', () => sessionManager.term.resizeScreen())
const term = useTerminal()
term.mount(terminal.value)
window.addEventListener('resize', () => term.resizeScreen())
})
</script>
+3 -3
View File
@@ -1,6 +1,6 @@
<script setup>
import FileNode from './FileNode.vue'
import sessionManager from '../utils/session.js'
import sessionStore from '../store/session.js'
</script>
<template>
@@ -8,8 +8,8 @@ import sessionManager from '../utils/session.js'
<ul>
<li>
<FileNode
:node="sessionManager.graph.value"
:pwd="sessionManager.pwd.value"
:node="sessionStore.session.graph"
:pwd="sessionStore.session.pwd"
/>
</li>
</ul>
+127
View File
@@ -0,0 +1,127 @@
import { reactive } from 'vue'
import { FileGraph } from '@linux-odyssey/file-graph'
import api from '../utils/api'
import Socket from '../utils/socket'
import SocketTerminal from '../utils/terminal'
function newSession() {
const graph = new FileGraph({
path: '/',
type: 'folder',
discovered: false,
})
return {
status: 'inactive',
graph,
pwd: '',
hints: [],
tasks: [],
}
}
const store = reactive({
session: newSession(),
questId: '',
quest: null,
})
const socket = new Socket()
const term = new SocketTerminal(40, 80)
function setQuest(questId) {
api.get(`/quests/${questId}`).then((res) => {
store.questId = res.data._id
store.quest = res.data
})
}
async function setSession(session) {
store.session = session
store.session.graph = new FileGraph(session.graph)
term.reset()
await socket.connect(session)
term.focus()
}
export async function createSession() {
console.log('Creating a new session...')
const res = await api.post('/sessions', { quest_id: store.questId })
const { data } = res
await setSession(data)
}
async function getActiveSession(questId) {
const res = await api.post('/sessions/active', {
quest_id: questId,
})
const session = res.data
try {
await setSession(session)
} catch (err) {
console.log(err)
await createSession().catch(console.error)
}
}
function updateGraph(event) {
if (event.discover) {
store.session.graph.discover(event.discover)
}
if (event.pwd) {
store.session.pwd = event.pwd
}
}
function updateHints(hints) {
store.session.hints.push(...hints)
}
function updateStatus(status) {
store.session.status = status
}
function setTasks(tasks) {
store.session.tasks = tasks
}
export function useTerminal() {
return term
}
export function reset() {
socket.reset()
term.reset()
store.session = newSession()
store.quest = null
}
export async function init(questId) {
reset()
await setQuest(questId)
await getActiveSession(questId)
}
function setup() {
socket.on('terminal', (data) => {
term.write(data)
})
term.onData((data) => {
socket.emit('terminal', data)
})
socket.on('graph', (event) => {
updateGraph(event)
})
socket.on('hints', (event) => {
updateHints(event)
})
socket.on('tasks', (tasks) => {
setTasks(tasks)
})
socket.on('status', (event) => {
updateStatus(event)
})
}
setup()
export default store
-107
View File
@@ -1,107 +0,0 @@
import { ref } from 'vue'
import { FileGraph } from '@linux-odyssey/file-graph'
import api from './api'
import Socket from './socket'
import SocketTerminal from './terminal'
class SessionManager {
constructor(questId) {
this.questId = questId
this.session = ref(null)
this.hints = ref([])
this.graph = ref(
new FileGraph({
path: '/',
type: 'folder',
discovered: false,
})
)
this.pwd = ref('')
this.status = ref('inactive')
this.socket = new Socket()
this.term = new SocketTerminal(40, 80)
}
init() {
this.socket.on('terminal', (data) => {
this.term.write(data)
})
this.term.onData((data) => {
this.socket.emit('terminal', data)
})
this.socket.on('graph', (event) => {
this.handleGraphUpdate(event)
})
this.socket.on('hints', (event) => {
this.handleHintUpdate(event)
})
this.socket.on('tasks', (tasks) => {
this.setTasks(tasks)
})
this.socket.on('status', (event) => {
this.handleStatusUpdate(event)
})
return this.getActiveSession()
}
getSession() {
return this.session.value
}
async setSession(session) {
this.session.value = session
this.graph.value = new FileGraph(session.graph)
this.hints.value = session.hints
this.status.value = session.status
this.term.reset()
await this.socket.connect(session)
this.term.focus()
}
handleGraphUpdate(event) {
if (event.discover) {
this.graph.value.discover(event.discover)
}
if (event.pwd) {
this.pwd.value = event.pwd
}
}
handleHintUpdate(hints) {
this.hints.value = [...this.hints.value, ...hints]
}
handleStatusUpdate(status) {
this.status.value = status
}
setTasks(tasks) {
this.session.value.tasks = tasks
}
async createSession() {
console.log('Creating a new session...')
const res = await api.post('/sessions', { quest_id: this.questId })
const { data } = res
console.log('Created Session ID:', data._id)
this.setSession(data)
}
async getActiveSession() {
const res = await api.post('/sessions/active', {
quest_id: this.questId,
})
const session = res.data
console.log(session)
try {
await this.setSession(session)
} catch (err) {
console.log(err)
this.createSession().catch(console.error)
}
}
}
const sessionManager = new SessionManager('helloworld')
export default sessionManager
+12 -14
View File
@@ -8,10 +8,7 @@ class SocketWrapper {
connect(session) {
return new Promise((resolve, reject) => {
if (this.socket) {
this.socket.off()
this.socket.disconnect()
}
this.disconnect()
this.socket = io('', {
query: {
@@ -38,25 +35,26 @@ class SocketWrapper {
}
}
once(event, callback) {
if (this.socket) {
this.socket.once(event, callback)
}
}
bindListeners() {
this.listeners.forEach(({ event, callback }) => {
this.socket.on(event, callback)
})
}
send(data) {
this.socket.send(data)
}
emit(event, data) {
this.socket.emit(event, data)
}
disconnect() {
if (this.socket) {
this.socket.off()
this.socket.disconnect()
}
}
reset() {
this.disconnect()
}
}
export default SocketWrapper
+4 -4
View File
@@ -1,6 +1,6 @@
<script setup>
import { computed, onMounted } from 'vue'
import sessionManager from '../utils/session'
import sessionStore, { init } from '../store/session'
import GameHeaderPart from '../components/GameHeaderPart.vue'
import CommandlistPart from '../components/CommandlistPart.vue'
import HintPart from '../components/HintPart.vue'
@@ -11,11 +11,11 @@ import ControlPalette from '../components/ControlPalette.vue'
import CompleteModal from '../components/CompleteModal.vue'
const completed = computed(() => {
return sessionManager.status.value === 'finished'
return sessionStore.session.status === 'finished'
})
onMounted(async () => {
await sessionManager.init()
await init('helloworld')
})
</script>
@@ -28,7 +28,7 @@ onMounted(async () => {
<input
type="hidden"
id="currentStatus"
:value="sessionManager.status.value"
:value="sessionStore.session.status"
/>
<!-- main -->
<div id="main" class="h-[94vh] w-full flex p-3 space-x-3">