mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-06 03:20:13 +08:00
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:
1 parent
10e9d89349
commit
7d7ad154ab
10 files changed
+179
-168
No files matched your search
@@ -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>
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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">
|
||||
|
||||
Reference in new issue
Block a user