diff --git a/src/api/nitro/room/widgets/events/RoomWidgetPollUpdateEvent.ts b/src/api/nitro/room/widgets/events/RoomWidgetPollUpdateEvent.ts new file mode 100644 index 00000000..58e8d644 --- /dev/null +++ b/src/api/nitro/room/widgets/events/RoomWidgetPollUpdateEvent.ts @@ -0,0 +1,110 @@ +import { PollQuestion } from '@nitrots/nitro-renderer'; +import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent'; + +export class RoomWidgetPollUpdateEvent extends RoomWidgetUpdateEvent +{ + public static readonly OFFER = 'RWPUW_OFFER'; + public static readonly ERROR = 'RWPUW_ERROR'; + public static readonly CONTENT = 'RWPUW_CONTENT'; + + private _id = -1; + private _summary: string; + private _headline: string; + private _numQuestions = 0; + private _startMessage = ''; + private _endMessage = ''; + private _questionArray: PollQuestion[] = null; + private _pollType = ''; + private _npsPoll = false; + + constructor(type: string, id: number) + { + super(type); + this._id = id; + } + + public get id(): number + { + return this._id; + } + + public get summary(): string + { + return this._summary; + } + + public set summary(k: string) + { + this._summary = k; + } + + public get headline(): string + { + return this._headline; + } + + public set headline(k: string) + { + this._headline = k; + } + + public get numQuestions(): number + { + return this._numQuestions; + } + + public set numQuestions(k: number) + { + this._numQuestions = k; + } + + public get startMessage(): string + { + return this._startMessage; + } + + public set startMessage(k: string) + { + this._startMessage = k; + } + + public get endMessage(): string + { + return this._endMessage; + } + + public set endMessage(k: string) + { + this._endMessage = k; + } + + public get questionArray(): PollQuestion[] + { + return this._questionArray; + } + + public set questionArray(k: PollQuestion[]) + { + this._questionArray = k; + } + + public get pollType(): string + { + return this._pollType; + } + + public set pollType(k: string) + { + this._pollType = k; + } + + public get npsPoll(): boolean + { + return this._npsPoll; + } + + public set npsPoll(k: boolean) + { + this._npsPoll = k; + } +} diff --git a/src/api/nitro/room/widgets/events/RoomWidgetWordQuizUpdateEvent.ts b/src/api/nitro/room/widgets/events/RoomWidgetWordQuizUpdateEvent.ts new file mode 100644 index 00000000..3652881c --- /dev/null +++ b/src/api/nitro/room/widgets/events/RoomWidgetWordQuizUpdateEvent.ts @@ -0,0 +1,110 @@ +import { IQuestion } from '@nitrots/nitro-renderer'; +import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent'; + +export class RoomWidgetWordQuizUpdateEvent extends RoomWidgetUpdateEvent +{ + public static readonly NEW_QUESTION = 'RWPUW_NEW_QUESTION'; + public static readonly QUESTION_FINISHED = 'RWPUW_QUESION_FINSIHED'; + public static readonly QUESTION_ANSWERED = 'RWPUW_QUESTION_ANSWERED'; + + private _id: number = -1; + private _pollType: string = null; + private _pollId: number = -1; + private _questionId: number = -1; + private _duration: number = -1; + private _question: IQuestion = null; + private _userId: number = -1; + private _value: string; + private _answerCounts: Map; + + constructor(type: string, id: number) + { + super(type); + this._id = id; + } + + public get id(): number + { + return this._id; + } + + public get pollType(): string + { + return this._pollType; + } + + public set pollType(k: string) + { + this._pollType = k; + } + + public get pollId(): number + { + return this._pollId; + } + + public set pollId(k: number) + { + this._pollId = k; + } + + public get questionId(): number + { + return this._questionId; + } + + public set questionId(k: number) + { + this._questionId = k; + } + + public get duration(): number + { + return this._duration; + } + + public set duration(k: number) + { + this._duration = k; + } + + public get question(): IQuestion + { + return this._question; + } + + public set question(k: IQuestion) + { + this._question = k; + } + + public get userId(): number + { + return this._userId; + } + + public set userId(k: number) + { + this._userId = k; + } + + public get value(): string + { + return this._value; + } + + public set value(k: string) + { + this._value = k; + } + + public get answerCounts(): Map + { + return this._answerCounts; + } + + public set answerCounts(k: Map) + { + this._answerCounts = k; + } +} diff --git a/src/api/nitro/room/widgets/handlers/PollWidgetHandler.ts b/src/api/nitro/room/widgets/handlers/PollWidgetHandler.ts new file mode 100644 index 00000000..0d1b0457 --- /dev/null +++ b/src/api/nitro/room/widgets/handlers/PollWidgetHandler.ts @@ -0,0 +1,75 @@ +import { NitroEvent, RoomSessionPollEvent, RoomWidgetEnum } from '@nitrots/nitro-renderer'; +import { RoomWidgetPollUpdateEvent } from '../events/RoomWidgetPollUpdateEvent'; +import { RoomWidgetUpdateEvent } from '../events/RoomWidgetUpdateEvent'; +import { RoomWidgetMessage } from '../messages/RoomWidgetMessage'; +import { RoomWidgetPollMessage } from '../messages/RoomWidgetPollMessage'; +import { RoomWidgetHandler } from './RoomWidgetHandler'; + +export class PollWidgetHandler extends RoomWidgetHandler +{ + public processEvent(event: NitroEvent): void + { + const pollEvent = (event as RoomSessionPollEvent); + + let widgetEvent: RoomWidgetPollUpdateEvent; + + switch(event.type) + { + case RoomSessionPollEvent.OFFER: + widgetEvent = new RoomWidgetPollUpdateEvent(RoomWidgetPollUpdateEvent.OFFER, pollEvent.id); + widgetEvent.summary = pollEvent.summary; + widgetEvent.headline = pollEvent.headline; + break; + case RoomSessionPollEvent.ERROR: + widgetEvent = new RoomWidgetPollUpdateEvent(RoomWidgetPollUpdateEvent.ERROR, pollEvent.id); + widgetEvent.summary = pollEvent.summary; + widgetEvent.headline = pollEvent.headline; + break; + case RoomSessionPollEvent.CONTENT: + widgetEvent = new RoomWidgetPollUpdateEvent(RoomWidgetPollUpdateEvent.CONTENT, pollEvent.id); + widgetEvent.startMessage = pollEvent.startMessage; + widgetEvent.endMessage = pollEvent.endMessage; + widgetEvent.numQuestions = pollEvent.numQuestions; + widgetEvent.questionArray = pollEvent.questionArray; + widgetEvent.npsPoll = pollEvent.npsPoll; + break; + } + + if(!widgetEvent) return; + + this.container.eventDispatcher.dispatchEvent(widgetEvent); + } + + public processWidgetMessage(message: RoomWidgetMessage): RoomWidgetUpdateEvent + { + const pollMessage = (message as RoomWidgetPollMessage); + switch(message.type) + { + case RoomWidgetPollMessage.START: + this.container.roomSession.sendPollStartMessage(pollMessage.id); + break; + case RoomWidgetPollMessage.REJECT: + this.container.roomSession.sendPollRejectMessage(pollMessage.id); + break; + case RoomWidgetPollMessage.ANSWER: + this.container.roomSession.sendPollAnswerMessage(pollMessage.id, pollMessage.questionId, pollMessage.answers); + break; + } + return null; + } + + public get type(): string + { + return RoomWidgetEnum.ROOM_POLL; + } + + public get eventTypes(): string[] + { + return [RoomSessionPollEvent.OFFER, RoomSessionPollEvent.ERROR, RoomSessionPollEvent.CONTENT]; + } + + public get messageTypes(): string[] + { + return [RoomWidgetPollMessage.ANSWER, RoomWidgetPollMessage.REJECT, RoomWidgetPollMessage.START]; + } +} diff --git a/src/api/nitro/room/widgets/handlers/WordQuizWidgetHandler.ts b/src/api/nitro/room/widgets/handlers/WordQuizWidgetHandler.ts new file mode 100644 index 00000000..df24b06e --- /dev/null +++ b/src/api/nitro/room/widgets/handlers/WordQuizWidgetHandler.ts @@ -0,0 +1,74 @@ +import { AvatarAction, NitroEvent, RoomSessionWordQuizEvent, RoomWidgetEnum } from '@nitrots/nitro-renderer'; +import { RoomWidgetHandler } from '.'; +import { GetRoomEngine } from '../../GetRoomEngine'; +import { RoomWidgetUpdateEvent } from '../events'; +import { RoomWidgetWordQuizUpdateEvent } from '../events/RoomWidgetWordQuizUpdateEvent'; +import { RoomWidgetMessage } from '../messages'; + +export class WordQuizWidgetHandler extends RoomWidgetHandler +{ + public processEvent(event: NitroEvent): void + { + const roomQuizEvent = (event as RoomSessionWordQuizEvent); + let widgetEvent: RoomWidgetWordQuizUpdateEvent; + switch(event.type) + { + case RoomSessionWordQuizEvent.ANSWERED: + const roomId = this.container.roomSession.roomId; + const userData = this.container.roomSession.userDataManager.getUserData(roomQuizEvent.userId); + if(!userData) return; + widgetEvent = new RoomWidgetWordQuizUpdateEvent(RoomWidgetWordQuizUpdateEvent.QUESTION_ANSWERED, roomQuizEvent.id); + widgetEvent.value = roomQuizEvent.value; + widgetEvent.userId = roomQuizEvent.userId; + widgetEvent.answerCounts = roomQuizEvent.answerCounts; + + if(widgetEvent.value === '0') + { + GetRoomEngine().updateRoomObjectUserGesture(roomId, userData.roomIndex, AvatarAction.getGestureId(AvatarAction.GESTURE_SAD)); + } + else + { + GetRoomEngine().updateRoomObjectUserGesture(roomId, userData.roomIndex, AvatarAction.getGestureId(AvatarAction.GESTURE_SMILE)); + } + break; + case RoomSessionWordQuizEvent.FINISHED: + widgetEvent = new RoomWidgetWordQuizUpdateEvent(RoomWidgetWordQuizUpdateEvent.QUESTION_FINISHED, roomQuizEvent.id); + widgetEvent.pollId = roomQuizEvent.pollId; + widgetEvent.questionId = roomQuizEvent.questionId; + widgetEvent.answerCounts = roomQuizEvent.answerCounts; + break; + case RoomSessionWordQuizEvent.QUESTION: + widgetEvent = new RoomWidgetWordQuizUpdateEvent(RoomWidgetWordQuizUpdateEvent.NEW_QUESTION, roomQuizEvent.id); + widgetEvent.question = roomQuizEvent.question; + widgetEvent.duration = roomQuizEvent.duration; + widgetEvent.pollType = roomQuizEvent.pollType; + widgetEvent.questionId = roomQuizEvent.questionId; + widgetEvent.pollId = roomQuizEvent.pollId; + break; + } + + if(!widgetEvent) return; + + this.container.eventDispatcher.dispatchEvent(widgetEvent); + } + + public processWidgetMessage(message: RoomWidgetMessage): RoomWidgetUpdateEvent + { + return null; + } + + public get type(): string + { + return RoomWidgetEnum.WORD_QUIZZ; + } + + public get eventTypes(): string[] + { + return [RoomSessionWordQuizEvent.ANSWERED, RoomSessionWordQuizEvent.FINISHED, RoomSessionWordQuizEvent.QUESTION]; + } + + public get messageTypes(): string[] + { + return []; + } +} diff --git a/src/api/nitro/room/widgets/messages/RoomWidgetPollMessage.ts b/src/api/nitro/room/widgets/messages/RoomWidgetPollMessage.ts new file mode 100644 index 00000000..a74d97bf --- /dev/null +++ b/src/api/nitro/room/widgets/messages/RoomWidgetPollMessage.ts @@ -0,0 +1,44 @@ +import { RoomWidgetMessage } from './RoomWidgetMessage'; + +export class RoomWidgetPollMessage extends RoomWidgetMessage +{ + public static readonly START = 'RWPM_START'; + public static readonly REJECT = 'RWPM_REJECT'; + public static readonly ANSWER = 'RWPM_ANSWER'; + + private _id = -1; + private _questionId = 0; + private _answers: string[] = null; + + constructor(type: string, id: number) + { + super(type); + + this._id = id; + } + + public get id(): number + { + return this._id; + } + + public get questionId(): number + { + return this._questionId; + } + + public set questionId(k: number) + { + this._questionId = k; + } + + public get answers(): string[] + { + return this._answers; + } + + public set answers(k: string[]) + { + this._answers = k; + } +} diff --git a/src/assets/images/room-widgets/wordquiz-widget/thumbs-down-small.png b/src/assets/images/room-widgets/wordquiz-widget/thumbs-down-small.png new file mode 100644 index 00000000..78e51cfe Binary files /dev/null and b/src/assets/images/room-widgets/wordquiz-widget/thumbs-down-small.png differ diff --git a/src/assets/images/room-widgets/wordquiz-widget/thumbs-down.png b/src/assets/images/room-widgets/wordquiz-widget/thumbs-down.png new file mode 100644 index 00000000..fd320c51 Binary files /dev/null and b/src/assets/images/room-widgets/wordquiz-widget/thumbs-down.png differ diff --git a/src/assets/images/room-widgets/wordquiz-widget/thumbs-up-small.png b/src/assets/images/room-widgets/wordquiz-widget/thumbs-up-small.png new file mode 100644 index 00000000..b93111f0 Binary files /dev/null and b/src/assets/images/room-widgets/wordquiz-widget/thumbs-up-small.png differ diff --git a/src/assets/images/room-widgets/wordquiz-widget/thumbs-up.png b/src/assets/images/room-widgets/wordquiz-widget/thumbs-up.png new file mode 100644 index 00000000..dd650987 Binary files /dev/null and b/src/assets/images/room-widgets/wordquiz-widget/thumbs-up.png differ diff --git a/src/views/room/RoomView.tsx b/src/views/room/RoomView.tsx index a4125e9f..7a768344 100644 --- a/src/views/room/RoomView.tsx +++ b/src/views/room/RoomView.tsx @@ -2,6 +2,8 @@ import { EventDispatcher, NitroRectangle, RoomGeometry, RoomVariableEnum, Vector import { FC, useEffect, useRef, useState } from 'react'; import { DispatchMouseEvent, DispatchTouchEvent, DoorbellWidgetHandler, FriendRequestHandler, FurniChooserWidgetHandler, FurnitureContextMenuWidgetHandler, FurnitureCreditWidgetHandler, FurnitureCustomStackHeightWidgetHandler, FurnitureDimmerWidgetHandler, FurnitureExternalImageWidgetHandler, FurnitureMannequinWidgetHandler, FurniturePresentWidgetHandler, GetNitroInstance, GetRoomEngine, InitializeRoomInstanceRenderingCanvas, IRoomWidgetHandlerManager, RoomWidgetAvatarInfoHandler, RoomWidgetChatHandler, RoomWidgetChatInputHandler, RoomWidgetHandlerManager, RoomWidgetInfostandHandler, RoomWidgetRoomToolsHandler, RoomWidgetUpdateRoomViewEvent, UserChooserWidgetHandler } from '../../api'; import { FurnitureYoutubeDisplayWidgetHandler } from '../../api/nitro/room/widgets/handlers/FurnitureYoutubeDisplayWidgetHandler'; +import { PollWidgetHandler } from '../../api/nitro/room/widgets/handlers/PollWidgetHandler'; +import { WordQuizWidgetHandler } from '../../api/nitro/room/widgets/handlers/WordQuizWidgetHandler'; import { RoomContextProvider } from './context/RoomContext'; import { RoomColorView } from './RoomColorView'; import { RoomViewProps } from './RoomView.types'; @@ -37,6 +39,8 @@ export const RoomView: FC = props => widgetHandlerManager.registerHandler(new RoomWidgetChatHandler()); widgetHandlerManager.registerHandler(new UserChooserWidgetHandler()); widgetHandlerManager.registerHandler(new DoorbellWidgetHandler()); + widgetHandlerManager.registerHandler(new WordQuizWidgetHandler()); + widgetHandlerManager.registerHandler(new PollWidgetHandler()); widgetHandlerManager.registerHandler(new FriendRequestHandler()); widgetHandlerManager.registerHandler(new FurniChooserWidgetHandler()); diff --git a/src/views/room/widgets/RoomWidgets.scss b/src/views/room/widgets/RoomWidgets.scss index 4cac258a..6c2a2f2b 100644 --- a/src/views/room/widgets/RoomWidgets.scss +++ b/src/views/room/widgets/RoomWidgets.scss @@ -8,3 +8,4 @@ @import './object-location/ObjectLocationView'; @import './room-tools/RoomToolsWidgetView'; @import './choosers/ChooserWidgetView'; +@import './word-quiz/WordQuizWidgetView'; diff --git a/src/views/room/widgets/RoomWidgetsView.tsx b/src/views/room/widgets/RoomWidgetsView.tsx index 1b62c52e..b311b9d5 100644 --- a/src/views/room/widgets/RoomWidgetsView.tsx +++ b/src/views/room/widgets/RoomWidgetsView.tsx @@ -1,4 +1,4 @@ -import { RoomEngineEvent, RoomEngineObjectEvent, RoomEngineRoomAdEvent, RoomEngineTriggerWidgetEvent, RoomEngineUseProductEvent, RoomId, RoomObjectCategory, RoomObjectOperationType, RoomObjectVariable, RoomSessionChatEvent, RoomSessionDanceEvent, RoomSessionDimmerPresetsEvent, RoomSessionDoorbellEvent, RoomSessionErrorMessageEvent, RoomSessionEvent, RoomSessionFriendRequestEvent, RoomSessionPetInfoUpdateEvent, RoomSessionPresentEvent, RoomSessionUserBadgesEvent, RoomZoomEvent } from '@nitrots/nitro-renderer'; +import { RoomEngineEvent, RoomEngineObjectEvent, RoomEngineRoomAdEvent, RoomEngineTriggerWidgetEvent, RoomEngineUseProductEvent, RoomId, RoomObjectCategory, RoomObjectOperationType, RoomObjectVariable, RoomSessionChatEvent, RoomSessionDanceEvent, RoomSessionDimmerPresetsEvent, RoomSessionDoorbellEvent, RoomSessionErrorMessageEvent, RoomSessionEvent, RoomSessionFriendRequestEvent, RoomSessionPetInfoUpdateEvent, RoomSessionPollEvent, RoomSessionPresentEvent, RoomSessionUserBadgesEvent, RoomSessionWordQuizEvent, RoomZoomEvent } from '@nitrots/nitro-renderer'; import { FC, useCallback } from 'react'; import { CanManipulateFurniture, GetRoomEngine, GetSessionDataManager, IsFurnitureSelectionDisabled, LocalizeText, ProcessRoomObjectOperation, RoomWidgetFurniToWidgetMessage, RoomWidgetUpdateRoomEngineEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../api'; import { useRoomEngineEvent, useRoomSessionManagerEvent } from '../../../hooks/events'; @@ -15,6 +15,7 @@ import { FurnitureWidgetsView } from './furniture/FurnitureWidgetsView'; import { InfoStandWidgetView } from './infostand/InfoStandWidgetView'; import { RoomThumbnailWidgetView } from './room-thumbnail/RoomThumbnailWidgetView'; import { RoomToolsWidgetView } from './room-tools/RoomToolsWidgetView'; +import { WordQuizWidgetView } from './word-quiz/WordQuizWidgetView'; export const RoomWidgetsView: FC<{}> = props => { @@ -268,6 +269,12 @@ export const RoomWidgetsView: FC<{}> = props => useRoomSessionManagerEvent(RoomSessionFriendRequestEvent.RSFRE_FRIEND_REQUEST, onRoomSessionEvent); useRoomSessionManagerEvent(RoomSessionPresentEvent.RSPE_PRESENT_OPENED, onRoomSessionEvent); useRoomSessionManagerEvent(RoomSessionPetInfoUpdateEvent.PET_INFO, onRoomSessionEvent); + useRoomSessionManagerEvent(RoomSessionWordQuizEvent.ANSWERED, onRoomSessionEvent); + useRoomSessionManagerEvent(RoomSessionWordQuizEvent.FINISHED, onRoomSessionEvent); + useRoomSessionManagerEvent(RoomSessionWordQuizEvent.QUESTION, onRoomSessionEvent); + useRoomSessionManagerEvent(RoomSessionPollEvent.OFFER, onRoomSessionEvent); + useRoomSessionManagerEvent(RoomSessionPollEvent.ERROR, onRoomSessionEvent); + useRoomSessionManagerEvent(RoomSessionPollEvent.CONTENT, onRoomSessionEvent); const onRoomSessionErrorMessageEvent = useCallback((event: RoomSessionErrorMessageEvent) => { @@ -349,6 +356,7 @@ export const RoomWidgetsView: FC<{}> = props => + ); } diff --git a/src/views/room/widgets/word-quiz/WordQuizWidgetView.scss b/src/views/room/widgets/word-quiz/WordQuizWidgetView.scss new file mode 100644 index 00000000..5889d925 --- /dev/null +++ b/src/views/room/widgets/word-quiz/WordQuizWidgetView.scss @@ -0,0 +1,39 @@ +.wordquiz-question { + position: absolute; + top: 10px; + left: 50%; + transform: translateX(-50%); + font-size: large; + background: rgba($dark, 0.9); + //box-shadow: inset 0px 5px lighten(rgba($dark,.6),2.5), inset 0 -4px darken(rgba($dark,.6),4); + border-radius: $border-radius; + transition: all 0.2s ease; + + .question { + max-width: 300px; + } +} + +.word-quiz-dislike { + background: url("../../../../assets/images/room-widgets/wordquiz-widget/thumbs-down.png"); + width: 31px; + height: 34px; +} + +.word-quiz-like { + background: url("../../../../assets/images/room-widgets/wordquiz-widget/thumbs-up.png"); + width: 31px; + height: 34px; +} + +.word-quiz-dislike-sm { + background: url("../../../../assets/images/room-widgets/wordquiz-widget/thumbs-down-small.png"); + width: 22px; + height: 22px; +} + +.word-quiz-like-sm { + background: url("../../../../assets/images/room-widgets/wordquiz-widget/thumbs-up-small.png"); + height: 22px; + width: 22px; +} diff --git a/src/views/room/widgets/word-quiz/WordQuizWidgetView.tsx b/src/views/room/widgets/word-quiz/WordQuizWidgetView.tsx new file mode 100644 index 00000000..af258d59 --- /dev/null +++ b/src/views/room/widgets/word-quiz/WordQuizWidgetView.tsx @@ -0,0 +1,167 @@ +import { IQuestion } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useEffect, useState } from 'react'; +import { RoomWidgetWordQuizUpdateEvent } from '../../../../api/nitro/room/widgets/events/RoomWidgetWordQuizUpdateEvent'; +import { RoomWidgetPollMessage } from '../../../../api/nitro/room/widgets/messages/RoomWidgetPollMessage'; +import { BatchUpdates, CreateEventDispatcherHook } from '../../../../hooks'; +import { useRoomContext } from '../../context/RoomContext'; +import { VALUE_KEY_DISLIKE, VALUE_KEY_LIKE, VoteValue } from './common/VoteValue'; +import { QuestionView } from './views/question/QuestionView'; +import { VoteView } from './views/vote/VoteView'; + +const DEFAULT_DISPLAY_DELAY = 4000; +const SIGN_FADE_DELAY = 3; + +export const WordQuizWidgetView: FC<{}> = props => +{ + const { eventDispatcher = null, widgetHandler = null, roomSession = null } = useRoomContext(); + const [pollId, setPollId] = useState(-1); + const [question, setQuestion] = useState(null); + const [answerSent, setAnswerSent] = useState(false); + const [questionClearTimeout, setQuestionClearTimeout] = useState(null); + const [answerCounts, setAnswerCounts] = useState>(new Map()); + const [userAnswers, setUserAnswers] = useState>(new Map()); + + const clearQuestion = useCallback(() => + { + setPollId(-1); + setQuestion(null); + }, []); + + const onRoomWidgetWordQuizUpdateEvent = useCallback((event: RoomWidgetWordQuizUpdateEvent) => + { + switch(event.type) + { + case RoomWidgetWordQuizUpdateEvent.NEW_QUESTION: + BatchUpdates(() => + { + setPollId(event.id); + setQuestion(event.question); + setAnswerSent(false); + setAnswerCounts(new Map()); + setUserAnswers(new Map()); + if(questionClearTimeout) clearTimeout(questionClearTimeout); + }); + + if(event.duration > 0) + { + const delay = event.duration < 1000 ? DEFAULT_DISPLAY_DELAY : event.duration; + setQuestionClearTimeout(prevValue => + { + if(prevValue) clearTimeout(prevValue); + + return setTimeout((clearQuestion as TimerHandler), delay); + }) + } + break; + case RoomWidgetWordQuizUpdateEvent.QUESTION_ANSWERED: + const userData = roomSession.userDataManager.getUserData(event.userId); + if(!userData) return; + + setAnswerCounts(event.answerCounts); + + if(!userAnswers.has(userData.roomIndex)) + { + const answersCopy = new Map(userAnswers); + answersCopy.set(userData.roomIndex, { value: event.value, secondsLeft: SIGN_FADE_DELAY }); + setUserAnswers(answersCopy); + } + break; + case RoomWidgetWordQuizUpdateEvent.QUESTION_FINISHED: + if(question && question.id === event.questionId) + { + BatchUpdates(() => + { + setAnswerCounts(event.answerCounts); + setAnswerSent(true); + setQuestionClearTimeout(prevValue => + { + if(prevValue) clearTimeout(prevValue); + + return setTimeout((clearQuestion as TimerHandler), DEFAULT_DISPLAY_DELAY); + }); + }) + } + setUserAnswers(new Map()); + break; + } + }, [clearQuestion, question, questionClearTimeout, roomSession.userDataManager, userAnswers]); + + CreateEventDispatcherHook(RoomWidgetWordQuizUpdateEvent.NEW_QUESTION, eventDispatcher, onRoomWidgetWordQuizUpdateEvent); + CreateEventDispatcherHook(RoomWidgetWordQuizUpdateEvent.QUESTION_ANSWERED, eventDispatcher, onRoomWidgetWordQuizUpdateEvent); + CreateEventDispatcherHook(RoomWidgetWordQuizUpdateEvent.QUESTION_FINISHED, eventDispatcher, onRoomWidgetWordQuizUpdateEvent); + + const vote = useCallback((vote: string) => + { + if(answerSent || !question) return; + + const updateMessage = new RoomWidgetPollMessage(RoomWidgetPollMessage.ANSWER, pollId); + updateMessage.questionId = question.id; + updateMessage.answers = [vote]; + widgetHandler.processWidgetMessage(updateMessage); + setAnswerSent(true); + + }, [answerSent, pollId, question, widgetHandler]); + + const checkSignFade = useCallback(() => + { + setUserAnswers(prev => + { + const copy = new Map(prev); + const keysToRemove: number[] = []; + copy.forEach((value, key) => + { + value.secondsLeft--; + + if(value.secondsLeft <= 0) + { + keysToRemove.push(key); + } + }); + + keysToRemove.forEach(key => copy.delete(key)); + + return copy; + }) + + }, []); + + useEffect(() => + { + const interval = setInterval(() => + { + checkSignFade(); + }, 1000) + + return () => + { + clearInterval(interval); + } + }, [checkSignFade]); + + useEffect(() => + { + return () => + { + setQuestionClearTimeout(prev => + { + if(prev) clearTimeout(prev); + + return null; + }); + } + }, []); + + return ( + <> + {question && + + } + {userAnswers && + Array.from(userAnswers.entries()).map(([key, value], index) => + { + return + }) + } + + ); +} diff --git a/src/views/room/widgets/word-quiz/common/VoteValue.ts b/src/views/room/widgets/word-quiz/common/VoteValue.ts new file mode 100644 index 00000000..ecf4336e --- /dev/null +++ b/src/views/room/widgets/word-quiz/common/VoteValue.ts @@ -0,0 +1,8 @@ +export const VALUE_KEY_DISLIKE = '0'; +export const VALUE_KEY_LIKE = '1'; + +export interface VoteValue +{ + value: string; + secondsLeft: number; +} diff --git a/src/views/room/widgets/word-quiz/views/question/QuestionView.tsx b/src/views/room/widgets/word-quiz/views/question/QuestionView.tsx new file mode 100644 index 00000000..2d2b5d17 --- /dev/null +++ b/src/views/room/widgets/word-quiz/views/question/QuestionView.tsx @@ -0,0 +1,24 @@ +import { FC } from 'react'; +import { VALUE_KEY_DISLIKE, VALUE_KEY_LIKE } from '../../common/VoteValue'; +import { QuestionViewProps } from './QuestionView.types'; + +export const QuestionView:FC = props => +{ + const { question = null, canVote = null, vote = null, noVotes = null, yesVotes = null } = props; + + return ( +
+
+ { !canVote && } +
{question}
+ { !canVote && } +
+ {canVote && +
+ + +
+ } +
+ ) +} diff --git a/src/views/room/widgets/word-quiz/views/question/QuestionView.types.ts b/src/views/room/widgets/word-quiz/views/question/QuestionView.types.ts new file mode 100644 index 00000000..fde51dba --- /dev/null +++ b/src/views/room/widgets/word-quiz/views/question/QuestionView.types.ts @@ -0,0 +1,9 @@ + +export interface QuestionViewProps +{ + question: string; + canVote: boolean; + vote(value: string): void; + noVotes: number; + yesVotes: number; +} diff --git a/src/views/room/widgets/word-quiz/views/vote/VoteView.tsx b/src/views/room/widgets/word-quiz/views/vote/VoteView.tsx new file mode 100644 index 00000000..bee02143 --- /dev/null +++ b/src/views/room/widgets/word-quiz/views/vote/VoteView.tsx @@ -0,0 +1,16 @@ +import { RoomObjectCategory } from '@nitrots/nitro-renderer/src'; +import { FC } from 'react'; +import { ObjectLocationView } from '../../../object-location/ObjectLocationView'; +import { VALUE_KEY_DISLIKE } from '../../common/VoteValue'; +import { VoteViewProps } from './VoteView.types'; + +export const VoteView:FC = props => +{ + const { userIndex = null , vote = null } = props; + + return ( + + + + ) +} diff --git a/src/views/room/widgets/word-quiz/views/vote/VoteView.types.ts b/src/views/room/widgets/word-quiz/views/vote/VoteView.types.ts new file mode 100644 index 00000000..17062cc6 --- /dev/null +++ b/src/views/room/widgets/word-quiz/views/vote/VoteView.types.ts @@ -0,0 +1,5 @@ +export interface VoteViewProps +{ + userIndex: number; + vote: string; +}