Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat websocket #10

Merged
merged 2 commits into from
Jun 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module.exports = {
semi: false,
},
],
'no-case-declarations': 'off',
},
parser: 'vue-eslint-parser',
parserOptions: {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"build:prod": "export NODE_ENV=production && vue-tsc && vite build",
"preview": "vite preview",
"lint": "eslint src --ext .js,.ts,.vue",
"gen-api": "node ./scripts/gen-api.js",
Expand Down
2 changes: 1 addition & 1 deletion src/lib/apis/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Apis, Configuration } from './generated'
const api = new Apis(
new Configuration({
basePath:
import.meta.env.MODE === 'production'
process.env.NODE_ENV === 'production'
? 'https://trap.show/h24s_19_server'
: 'http://localhost:3000',
}),
Expand Down
154 changes: 154 additions & 0 deletions src/stores/individualRoom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { ref } from 'vue'
import { defineStore } from 'pinia'

type IndividualRoomState =
| {
ws: null
}
| {
ws: WebSocket
roomId: string
posts: Post[]
rejectedPosts: RejectedPostResponse[]
}

interface BasicPost {
wordId: number
word: string
reading: string
basicScore: number
senderId: string
senderName: string
}

export interface Post extends BasicPost {
additionalScore: number
totalScore: number
isInvalid: boolean
}

interface RejectedPostResponse {
type: 'post_word_rejected'
word: string
reading: string
}

interface PostedWord extends BasicPost {
type: 'posted_word'
}

interface ScoreChangeResponse {
// いいねや通報で得点が変動した時
type: 'score_change'
wordId: number
additionalScore: number
basicScore: number
totalScore: number
isInvalid: boolean
}

type WebSocketResponse = PostedWord | RejectedPostResponse | ScoreChangeResponse

const BASE_URL =
process.env.NODE_ENV === 'production'
? 'wss://trap.show/h24s_19_server'
: 'ws://localhost:3000'
const websocketUrl = (roomId: string) => `${BASE_URL}/api/ws/${roomId}`

export const useIndividualRoom = defineStore('individualRoom', () => {
const state = ref<IndividualRoomState>({
ws: null,
})

const connect = (roomId: string) => {
const ws = new WebSocket(websocketUrl(roomId))
ws.onopen = () => {
console.log('connected')
state.value = { ws, roomId, posts: [], rejectedPosts: [] }
}

ws.onclose = () => {
console.log('disconnected')
state.value = { ws: null }
}

ws.onmessage = (event) => {
if (state.value.ws === null) {
throw new Error('WebSocket is not connected')
}
console.log('message', event.data)

const data: WebSocketResponse = JSON.parse(event.data)
switch (data.type) {
case 'posted_word':
state.value.posts.push({
...data,
additionalScore: 0,
totalScore: data.basicScore,
isInvalid: false,
})
break
case 'post_word_rejected':
state.value.rejectedPosts.push(data)
break
case 'score_change':
const post = state.value.posts.find(
(post) => post.wordId === data.wordId,
)
if (post === undefined) {
throw new Error('Post not found')
}
post.additionalScore = data.additionalScore
post.basicScore = data.basicScore
post.totalScore = data.totalScore
post.isInvalid = data.isInvalid
break
default:
throw new Error('Unknown message type')
}
}
}

const close = () => {
if (state.value.ws === null) {
throw new Error('WebSocket is not connected')
}
state.value.ws.close()
}

const sendPost = (word: string, reading: string) => {
if (state.value.ws === null) {
throw new Error('WebSocket is not connected')
}
state.value.ws.send(
JSON.stringify({ type: 'postWord', args: { word, reading } }),
)
}

const reportPost = (wordId: number) => {
if (state.value.ws === null) {
throw new Error('WebSocket is not connected')
}
state.value.ws.send(
JSON.stringify({ type: 'reportWord', args: { wordId } }),
)
}

const goodPost = (wordId: number, score: number) => {
if (state.value.ws === null) {
throw new Error('WebSocket is not connected')
}
state.value.ws.send(
JSON.stringify({ type: 'goodWord', args: { wordId, score } }),
)
}

return {
state,
connect,
close,
sendPost,
reportPost,
goodPost,
}
})
Loading