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/avatareditor/avatar-editor-spritesheet.png b/src/assets/images/avatareditor/avatar-editor-spritesheet.png new file mode 100644 index 00000000..0c91ca0f Binary files /dev/null and b/src/assets/images/avatareditor/avatar-editor-spritesheet.png differ diff --git a/src/assets/images/avatareditor/hc-icon.png b/src/assets/images/avatareditor/hc-icon.png deleted file mode 100644 index 40f19ad3..00000000 Binary files a/src/assets/images/avatareditor/hc-icon.png and /dev/null differ diff --git a/src/assets/images/friendlist/icons/icon_new_message.png b/src/assets/images/friendlist/icons/icon_new_message.png deleted file mode 100644 index f7a31fa0..00000000 Binary files a/src/assets/images/friendlist/icons/icon_new_message.png and /dev/null differ diff --git a/src/assets/images/friends/friends-spritesheet.png b/src/assets/images/friends/friends-spritesheet.png new file mode 100644 index 00000000..aa72325d Binary files /dev/null and b/src/assets/images/friends/friends-spritesheet.png differ diff --git a/src/assets/images/icons/accept.png b/src/assets/images/friends/icon-accept.png similarity index 100% rename from src/assets/images/icons/accept.png rename to src/assets/images/friends/icon-accept.png diff --git a/src/assets/images/friends/icon-add.png b/src/assets/images/friends/icon-add.png new file mode 100644 index 00000000..d570c8f5 Binary files /dev/null and b/src/assets/images/friends/icon-add.png differ diff --git a/src/assets/images/profile/icons/bobba.png b/src/assets/images/friends/icon-bobba.png similarity index 100% rename from src/assets/images/profile/icons/bobba.png rename to src/assets/images/friends/icon-bobba.png diff --git a/src/assets/images/friendlist/icons/icon_chat.png b/src/assets/images/friends/icon-chat.png similarity index 100% rename from src/assets/images/friendlist/icons/icon_chat.png rename to src/assets/images/friends/icon-chat.png diff --git a/src/assets/images/icons/deny.png b/src/assets/images/friends/icon-deny.png similarity index 100% rename from src/assets/images/icons/deny.png rename to src/assets/images/friends/icon-deny.png diff --git a/src/assets/images/friendlist/icons/icon_follow.png b/src/assets/images/friends/icon-follow.png similarity index 100% rename from src/assets/images/friendlist/icons/icon_follow.png rename to src/assets/images/friends/icon-follow.png diff --git a/src/assets/images/toolbar/icons/friend-bar/chat.png b/src/assets/images/friends/icon-friendbar-chat.png similarity index 100% rename from src/assets/images/toolbar/icons/friend-bar/chat.png rename to src/assets/images/friends/icon-friendbar-chat.png diff --git a/src/assets/images/toolbar/icons/friend-bar/visit.png b/src/assets/images/friends/icon-friendbar-visit.png similarity index 100% rename from src/assets/images/toolbar/icons/friend-bar/visit.png rename to src/assets/images/friends/icon-friendbar-visit.png diff --git a/src/assets/images/profile/icons/heart.png b/src/assets/images/friends/icon-heart.png similarity index 100% rename from src/assets/images/profile/icons/heart.png rename to src/assets/images/friends/icon-heart.png diff --git a/src/assets/images/friends/icon-new-message.png b/src/assets/images/friends/icon-new-message.png new file mode 100644 index 00000000..46d23f5a Binary files /dev/null and b/src/assets/images/friends/icon-new-message.png differ diff --git a/src/assets/images/friendlist/icons/icon_relationship_none.png b/src/assets/images/friends/icon-none.png similarity index 100% rename from src/assets/images/friendlist/icons/icon_relationship_none.png rename to src/assets/images/friends/icon-none.png diff --git a/src/assets/images/icons/user-profile-hover.png b/src/assets/images/friends/icon-profile-sm-hover.png similarity index 100% rename from src/assets/images/icons/user-profile-hover.png rename to src/assets/images/friends/icon-profile-sm-hover.png diff --git a/src/assets/images/icons/user-profile.png b/src/assets/images/friends/icon-profile-sm.png similarity index 100% rename from src/assets/images/icons/user-profile.png rename to src/assets/images/friends/icon-profile-sm.png diff --git a/src/assets/images/toolbar/icons/friend-bar/profile.png b/src/assets/images/friends/icon-profile.png similarity index 100% rename from src/assets/images/toolbar/icons/friend-bar/profile.png rename to src/assets/images/friends/icon-profile.png diff --git a/src/assets/images/profile/icons/smile.png b/src/assets/images/friends/icon-smile.png similarity index 100% rename from src/assets/images/profile/icons/smile.png rename to src/assets/images/friends/icon-smile.png diff --git a/src/assets/images/friendlist/icons/icon_warning.png b/src/assets/images/friends/icon-warning.png similarity index 100% rename from src/assets/images/friendlist/icons/icon_warning.png rename to src/assets/images/friends/icon-warning.png 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/assets/styles/icons.scss b/src/assets/styles/icons.scss index 87fd77ce..a53d7b2b 100644 --- a/src/assets/styles/icons.scss +++ b/src/assets/styles/icons.scss @@ -159,18 +159,6 @@ } } - &.icon-deny { - background: url("../images/icons/deny.png"); - width: 13px; - height: 14px; - } - - &.icon-accept { - background: url("../images/icons/accept.png"); - width: 13px; - height: 14px; - } - &.icon-wired-trigger { background-image: url("../images/wired/icon_trigger.png"); width: 13px; @@ -189,176 +177,6 @@ height: 14px; } - &.arrow-left-icon { - background-image: url("../images/avatareditor/arrow-left-icon.png"); - width: 28px; - height: 21px; - } - - &.arrow-right-icon { - background-image: url("../images/avatareditor/arrow-right-icon.png"); - width: 28px; - height: 21px; - } - - &.clear-icon { - background-image: url("../images/avatareditor/clear-icon.png"); - width: 27px; - height: 27px; - } - - &.ca-icon { - background-image: url("../images/avatareditor/ca-icon.png"); - width: 25px; - height: 25px; - - &.selected { - background-image: url("../images/avatareditor/ca-selected-icon.png"); - } - } - - &.cc-icon { - background-image: url("../images/avatareditor/cc-icon.png"); - width: 31px; - height: 29px; - - &.selected { - background-image: url("../images/avatareditor/cc-selected-icon.png"); - } - } - - &.ch-icon { - background-image: url("../images/avatareditor/ch-icon.png"); - width: 29px; - height: 24px; - - &.selected { - background-image: url("../images/avatareditor/ch-selected-icon.png"); - } - } - - &.cp-icon { - background-image: url("../images/avatareditor/cp-icon.png"); - width: 30px; - height: 24px; - - &.selected { - background-image: url("../images/avatareditor/cp-selected-icon.png"); - } - } - - &.ea-icon { - background-image: url("../images/avatareditor/ea-icon.png"); - width: 35px; - height: 16px; - - &.selected { - background-image: url("../images/avatareditor/ea-selected-icon.png"); - } - } - - &.fa-icon { - background-image: url("../images/avatareditor/fa-icon.png"); - width: 27px; - height: 20px; - - &.selected { - background-image: url("../images/avatareditor/fa-selected-icon.png"); - } - } - - &.female-icon { - background-image: url("../images/avatareditor/female-icon.png"); - width: 18px; - height: 27px; - - &.selected { - background-image: url("../images/avatareditor/female-selected-icon.png"); - } - } - - &.ha-icon { - background-image: url("../images/avatareditor/ha-icon.png"); - width: 25px; - height: 22px; - - &.selected { - background-image: url("../images/avatareditor/ha-selected-icon.png"); - } - } - - &.he-icon { - background-image: url("../images/avatareditor/he-icon.png"); - width: 31px; - height: 27px; - - &.selected { - background-image: url("../images/avatareditor/he-selected-icon.png"); - } - } - - &.hr-icon { - background-image: url("../images/avatareditor/hr-icon.png"); - width: 29px; - height: 25px; - - &.selected { - background-image: url("../images/avatareditor/hr-selected-icon.png"); - } - } - - &.lg-icon { - background-image: url("../images/avatareditor/lg-icon.png"); - width: 19px; - height: 20px; - - &.selected { - background-image: url("../images/avatareditor/lg-selected-icon.png"); - } - } - - &.loading-icon { - background-image: url("../images/icons/loading-icon.png"); - width: 17px; - height: 21px; - } - - &.male-icon { - background-image: url("../images/avatareditor/male-icon.png"); - width: 21px; - height: 21px; - - &.selected { - background-image: url("../images/avatareditor/male-selected-icon.png"); - } - } - - &.sh-icon { - background-image: url("../images/avatareditor/sh-icon.png"); - width: 37px; - height: 10px; - - &.selected { - background-image: url("../images/avatareditor/sh-selected-icon.png"); - } - } - - &.wa-icon { - background-image: url("../images/avatareditor/wa-icon.png"); - width: 36px; - height: 18px; - - &.selected { - background-image: url("../images/avatareditor/wa-selected-icon.png"); - } - } - - &.sellable-icon { - background-image: url("../images/avatareditor/sellable-icon.png"); - width: 17px; - height: 15px; - } - &.chatstyles-icon { background-image: url("../images/chat/styles-icon.png"); width: 17px; @@ -491,12 +309,6 @@ height: 15px; } - &.icon-fb-profile { - background: url("../images/toolbar/icons/friend-bar/profile.png"); - width: 21px; - height: 21px; - } - &.icon-camera-colormatrix { background: url("../images/icons/camera-colormatrix.png"); width: 32px; @@ -509,34 +321,6 @@ height: 21px; } - &.icon-user-profile { - background: url("../images/icons/user-profile.png"); - width: 13px; - height: 11px; - - &:hover { - background: url("../images/icons/user-profile-hover.png"); - } - } - - &.icon-fb-profile { - background: url("../images/toolbar/icons/friend-bar/profile.png"); - width: 21px; - height: 21px; - } - - &.icon-fb-chat { - background: url("../images/toolbar/icons/friend-bar/chat.png"); - width: 20px; - height: 21px; - } - - &.icon-fb-visit { - background: url("../images/toolbar/icons/friend-bar/visit.png"); - width: 21px; - height: 21px; - } - &.icon-pf-online { background: url("../images/profile/icons/online.gif"); width: 40px; @@ -555,30 +339,6 @@ height: 10px; } - &.icon-relationship-none { - background: url("../images/friendlist/icons/icon_relationship_none.png"); - width: 16px; - height: 14px; - } - - &.icon-relationship-heart { - background: url("../images/profile/icons/heart.png"); - width: 16px; - height: 14px; - } - - &.icon-relationship-bobba { - background: url("../images/profile/icons/bobba.png"); - width: 16px; - height: 14px; - } - - &.icon-relationship-smile { - background: url("../images/profile/icons/smile.png"); - width: 16px; - height: 14px; - } - &.icon-group-type-0 { background: url("../images/groups/icons/grouptype_icon_0.png"); width: 16px; @@ -681,18 +441,6 @@ height: 11px; } - &.icon-friendlist-follow { - background: url("../images/friendlist/icons/icon_follow.png"); - width: 16px; - height: 14px; - } - - &.icon-friendlist-chat { - background: url("../images/friendlist/icons/icon_chat.png"); - width: 17px; - height: 16px; - } - &.icon-youtube-next { background: url("../images/room-widgets/youtube-widget/next.png"); width: 21px; @@ -704,17 +452,6 @@ width: 21px; height: 16px; } - &.icon-friendlist-warning { - background: url("../images/friendlist/icons/icon_warning.png"); - width: 23px; - height: 21px; - } - - &.icon-friendlist-new-message { - background: url("../images/friendlist/icons/icon_new_message.png"); - width: 14px; - height: 16px; - } &.icon-hc-banner { background: url("../images/catalog/hc_big.png"); diff --git a/src/assets/styles/utils.scss b/src/assets/styles/utils.scss index e0ccc5d4..0d186c72 100644 --- a/src/assets/styles/utils.scss +++ b/src/assets/styles/utils.scss @@ -70,3 +70,11 @@ ul { .grayscale { filter: grayscale(1); } + +.flex-none { + flex: none; +} + +.z-index-1 { + z-index: 1; +} diff --git a/src/layout/base/NitroLayoutBase.tsx b/src/layout/base/NitroLayoutBase.tsx index 7007a972..e3f01587 100644 --- a/src/layout/base/NitroLayoutBase.tsx +++ b/src/layout/base/NitroLayoutBase.tsx @@ -3,7 +3,7 @@ import { NitroLayoutBaseProps } from './NitroLayoutBase.types'; export const NitroLayoutBase: FC = props => { - const { className = '', overflow = null, position = null, gap = null, children = null, ...rest } = props; + const { className = '', overflow = null, position = null, gap = null, ref = null, innerRef = null, children = null, ...rest } = props; const getClassName = useMemo(() => { @@ -21,7 +21,7 @@ export const NitroLayoutBase: FC = props => }, [ className, overflow, position, gap ]); return ( -
+
{ children }
); diff --git a/src/layout/base/NitroLayoutBase.types.ts b/src/layout/base/NitroLayoutBase.types.ts index 846ddaf2..2366835f 100644 --- a/src/layout/base/NitroLayoutBase.types.ts +++ b/src/layout/base/NitroLayoutBase.types.ts @@ -1,8 +1,9 @@ -import { DetailedHTMLProps, HTMLAttributes } from 'react'; +import { DetailedHTMLProps, HTMLAttributes, LegacyRef } from 'react'; import { NitroLayoutOverflow, NitroLayoutPosition, NitroLayoutSpacing } from '../common'; export interface NitroLayoutBaseProps extends DetailedHTMLProps, HTMLDivElement> { + innerRef?: LegacyRef; overflow?: NitroLayoutOverflow; position?: NitroLayoutPosition; gap?: NitroLayoutSpacing; diff --git a/src/layout/card/accordion/NitroCardAccordionContext.tsx b/src/layout/card/accordion/NitroCardAccordionContext.tsx new file mode 100644 index 00000000..61377d54 --- /dev/null +++ b/src/layout/card/accordion/NitroCardAccordionContext.tsx @@ -0,0 +1,21 @@ +import { createContext, Dispatch, FC, ProviderProps, SetStateAction, useContext } from 'react'; + +export interface INitroCardAccordionContext +{ + closers: Function[]; + setClosers: Dispatch>; + closeAll: () => void; +} + +const NitroCardAccordionContext = createContext({ + closers: null, + setClosers: null, + closeAll: null +}); + +export const NitroCardAccordionContextProvider: FC> = props => +{ + return ; +} + +export const useNitroCardAccordionContext = () => useContext(NitroCardAccordionContext); diff --git a/src/layout/card/accordion/NitroCardAccordionView.scss b/src/layout/card/accordion/NitroCardAccordionView.scss index 417a6cc6..906aa63f 100644 --- a/src/layout/card/accordion/NitroCardAccordionView.scss +++ b/src/layout/card/accordion/NitroCardAccordionView.scss @@ -1 +1,6 @@ +.nitro-card-accordion { + display: flex; + height: 100%; +} + @import "./set/NitroCardAccordionSetView"; diff --git a/src/layout/card/accordion/NitroCardAccordionView.tsx b/src/layout/card/accordion/NitroCardAccordionView.tsx index 40a68384..8088a8c7 100644 --- a/src/layout/card/accordion/NitroCardAccordionView.tsx +++ b/src/layout/card/accordion/NitroCardAccordionView.tsx @@ -1,13 +1,32 @@ -import { FC } from 'react'; +import { FC, useCallback, useMemo, useState } from 'react'; +import { NitroLayoutFlexColumn } from '../..'; +import { NitroCardAccordionContextProvider } from './NitroCardAccordionContext'; import { NitroCardAccordionViewProps } from './NitroCardAccordionView.types'; export const NitroCardAccordionView: FC = props => { - const { className = '' } = props; + const { className = '', children = null, ...rest } = props; + const [ closers, setClosers ] = useState([]); + + const getClassName = useMemo(() => + { + let newClassName = 'nitro-card-accordion text-black'; + + if(className && className.length) newClassName += ` ${ className }`; + + return newClassName; + }, [ className ]); + + const closeAll = useCallback(() => + { + for(const closer of closers) closer(); + }, [ closers ]); return ( -
- { props.children } -
+ + + { children } + + ); } diff --git a/src/layout/card/accordion/NitroCardAccordionView.types.ts b/src/layout/card/accordion/NitroCardAccordionView.types.ts index 73b8bbe3..fe87b21a 100644 --- a/src/layout/card/accordion/NitroCardAccordionView.types.ts +++ b/src/layout/card/accordion/NitroCardAccordionView.types.ts @@ -1,4 +1,6 @@ -export interface NitroCardAccordionViewProps +import { NitroLayoutFlexColumnProps } from '../..'; + +export interface NitroCardAccordionViewProps extends NitroLayoutFlexColumnProps { - className?: string; + } diff --git a/src/layout/card/accordion/set/NitroCardAccordionSetView.scss b/src/layout/card/accordion/set/NitroCardAccordionSetView.scss index 24324967..70c3be5c 100644 --- a/src/layout/card/accordion/set/NitroCardAccordionSetView.scss +++ b/src/layout/card/accordion/set/NitroCardAccordionSetView.scss @@ -1,9 +1,12 @@ .nitro-card-accordion-set { - .nitro-card-accordion-set-header { + + &.active { + height: 100%; + background: rgba($white, 0.5); border-bottom: 1px solid rgba($black, 0.2); } - .nitro-card-accordion-set-content { - background: rgba($white, 0.5); + .nitro-card-accordion-set-header { + border-bottom: 1px solid rgba($black, 0.2); } } diff --git a/src/layout/card/accordion/set/NitroCardAccordionSetView.tsx b/src/layout/card/accordion/set/NitroCardAccordionSetView.tsx index dcc513ea..94958c02 100644 --- a/src/layout/card/accordion/set/NitroCardAccordionSetView.tsx +++ b/src/layout/card/accordion/set/NitroCardAccordionSetView.tsx @@ -1,13 +1,15 @@ import classNames from 'classnames'; -import { FC, useEffect, useMemo, useState } from 'react'; +import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { NitroLayoutFlex } from '../../..'; import { NitroLayoutBase } from '../../../base'; +import { useNitroCardAccordionContext } from '../NitroCardAccordionContext'; import { NitroCardAccordionSetViewProps } from './NitroCardAccordionSetView.types'; export const NitroCardAccordionSetView: FC = props => { const { headerText = '', isExpanded = false, className = '', children = null, ...rest } = props; const [ isOpen, setIsOpen ] = useState(false); + const { setClosers = null, closeAll = null } = useNitroCardAccordionContext(); const getClassName = useMemo(() => { @@ -20,14 +22,66 @@ export const NitroCardAccordionSetView: FC = pro return newClassName; }, [ className, isOpen ]); + const onClick = useCallback(() => + { + closeAll(); + + setIsOpen(prevValue => !prevValue); + // if(isOpen) + // { + // closeAll(); + // } + // else + // { + // BatchUpdates(() => + // { + // closeAll(); + // setIsOpen(true); + // }); + // } + }, [ closeAll ]); + + const close = useCallback(() => + { + setIsOpen(false); + }, []); + useEffect(() => { setIsOpen(isExpanded); }, [ isExpanded ]); + useEffect(() => + { + const closeFunction = close; + + setClosers(prevValue => + { + const newClosers = [ ...prevValue ]; + + newClosers.push(closeFunction); + + return newClosers; + }); + + return () => + { + setClosers(prevValue => + { + const newClosers = [ ...prevValue ]; + + const index = newClosers.indexOf(closeFunction); + + if(index >= 0) newClosers.splice(index, 1); + + return newClosers; + }); + } + }, [ close, setClosers ]); + return ( - setIsOpen(!isOpen) }> +
{ headerText }
diff --git a/src/layout/common/NitroLayoutVariant.type.ts b/src/layout/common/NitroLayoutVariant.type.ts index cf973357..9593fd85 100644 --- a/src/layout/common/NitroLayoutVariant.type.ts +++ b/src/layout/common/NitroLayoutVariant.type.ts @@ -1 +1 @@ -export type NitroLayoutVariant = 'primary' | 'success' | 'danger' | 'secondary'; +export type NitroLayoutVariant = 'primary' | 'success' | 'danger' | 'secondary' | 'link' | 'black'; diff --git a/src/layout/index.ts b/src/layout/index.ts index 2d721551..df1d8eff 100644 --- a/src/layout/index.ts +++ b/src/layout/index.ts @@ -14,3 +14,4 @@ export * from './notification-alert'; export * from './notification-bubble'; export * from './transitions'; export * from './trophy'; +export * from './user-profile-icon'; diff --git a/src/layout/user-profile-icon/UserProfileIconView.tsx b/src/layout/user-profile-icon/UserProfileIconView.tsx new file mode 100644 index 00000000..d532570b --- /dev/null +++ b/src/layout/user-profile-icon/UserProfileIconView.tsx @@ -0,0 +1,26 @@ +import { FC, useMemo } from 'react'; +import { GetUserProfile } from '../../api'; +import { NitroLayoutBase } from '../base'; +import { UserProfileIconViewProps } from './UserProfileIconView.types'; + +export const UserProfileIconView: FC = props => +{ + const { userId = 0, userName = null } = props; + + const { className = '', children = null, ...rest } = props; + + const getClassName = useMemo(() => + { + let newClassName = 'nitro-friends-spritesheet icon-profile-sm me-1 cursor-pointer'; + + if(className && className.length) newClassName += ` ${ className }`; + + return newClassName; + }, [ className ]); + + return ( + GetUserProfile(userId) }> + { children } + + ); +} diff --git a/src/layout/user-profile-icon/UserProfileIconView.types.ts b/src/layout/user-profile-icon/UserProfileIconView.types.ts new file mode 100644 index 00000000..8f8702dd --- /dev/null +++ b/src/layout/user-profile-icon/UserProfileIconView.types.ts @@ -0,0 +1,7 @@ +import { NitroLayoutBaseProps } from '../base'; + +export interface UserProfileIconViewProps extends NitroLayoutBaseProps +{ + userId?: number; + userName?: string; +} diff --git a/src/layout/user-profile-icon/index.ts b/src/layout/user-profile-icon/index.ts new file mode 100644 index 00000000..cdc2fff5 --- /dev/null +++ b/src/layout/user-profile-icon/index.ts @@ -0,0 +1,2 @@ +export * from './UserProfileIconView'; +export * from './UserProfileIconView.types'; diff --git a/src/views/avatar-editor/AvatarEditorView.scss b/src/views/avatar-editor/AvatarEditorView.scss index 7e2ee641..167b3402 100644 --- a/src/views/avatar-editor/AvatarEditorView.scss +++ b/src/views/avatar-editor/AvatarEditorView.scss @@ -1,3 +1,217 @@ +.nitro-avatar-editor-spritesheet { + background: url('../../assets/images/avatareditor/avatar-editor-spritesheet.png') transparent no-repeat; + + &.arrow-left-icon { + width: 28px; + height: 21px; + background-position: -226px -131px; + } + + &.arrow-right-icon { + width: 28px; + height: 21px; + background-position: -226px -162px; + } + + &.ca-icon { + width: 25px; + height: 25px; + background-position: -226px -61px; + + &.selected { + width: 25px; + height: 25px; + background-position: -226px -96px; + } + } + + &.cc-icon { + width: 31px; + height: 29px; + background-position: -145px -5px; + + &.selected { + width: 31px; + height: 29px; + background-position: -145px -44px; + } + } + + &.ch-icon { + width: 29px; + height: 24px; + background-position: -186px -39px; + + &.selected { + width: 29px; + height: 24px; + background-position: -186px -73px; + } + } + + &.clear-icon { + width: 27px; + height: 27px; + background-position: -145px -157px; + } + + &.cp-icon { + width: 30px; + height: 24px; + background-position: -145px -264px; + + &.selected { + width: 30px; + height: 24px; + background-position: -186px -5px; + } + } + + + &.ea-icon { + width: 35px; + height: 16px; + background-position: -226px -193px; + + &.selected { + width: 35px; + height: 16px; + background-position: -226px -219px; + } + } + + &.fa-icon { + width: 27px; + height: 20px; + background-position: -186px -137px; + + &.selected { + width: 27px; + height: 20px; + background-position: -186px -107px; + } + } + + &.female-icon { + width: 18px; + height: 27px; + background-position: -186px -202px; + + &.selected { + width: 18px; + height: 27px; + background-position: -186px -239px; + } + } + + &.ha-icon { + width: 25px; + height: 22px; + background-position: -226px -245px; + + &.selected { + width: 25px; + height: 22px; + background-position: -226px -277px; + } + } + + &.he-icon { + width: 31px; + height: 27px; + background-position: -145px -83px; + + &.selected { + width: 31px; + height: 27px; + background-position: -145px -120px; + } + } + + &.hr-icon { + width: 29px; + height: 25px; + background-position: -145px -194px; + + &.selected { + width: 29px; + height: 25px; + background-position: -145px -229px; + } + } + + &.lg-icon { + width: 19px; + height: 20px; + background-position: -303px -45px; + + &.selected { + width: 19px; + height: 20px; + background-position: -303px -75px; + } + } + + &.loading-icon { + width: 21px; + height: 25px; + background-position: -186px -167px; + } + + + &.male-icon { + width: 21px; + height: 21px; + background-position: -186px -276px; + + &.selected { + width: 21px; + height: 21px; + background-position: -272px -5px; + } + } + + + &.sellable-icon { + width: 17px; + height: 15px; + background-position: -303px -105px; + } + + + &.sh-icon { + width: 37px; + height: 10px; + background-position: -303px -5px; + + &.selected { + width: 37px; + height: 10px; + background-position: -303px -25px; + } + } + + + &.spotlight { + width: 130px; + height: 305px; + background-position: -5px -5px; + } + + + &.wa-icon { + width: 36px; + height: 18px; + background-position: -226px -5px; + + &.selected { + width: 36px; + height: 18px; + background-position: -226px -33px; + } + } +} + .nitro-avatar-editor { width: $avatar-editor-width; height: $avatar-editor-height; @@ -37,13 +251,10 @@ z-index: 4; } - .avatar-spotlight { + .spotlight { position: absolute; top: -10px; - width: 100%; - height: 305px; margin: 0 auto; - background: transparent url('../../assets/images/avatareditor/spotlight.png') no-repeat center; opacity: 0.3; pointer-events: none; z-index: 3; diff --git a/src/views/avatar-editor/views/figure-preview/AvatarEditorFigurePreviewView.tsx b/src/views/avatar-editor/views/figure-preview/AvatarEditorFigurePreviewView.tsx index f29d21e3..e6018e8c 100644 --- a/src/views/avatar-editor/views/figure-preview/AvatarEditorFigurePreviewView.tsx +++ b/src/views/avatar-editor/views/figure-preview/AvatarEditorFigurePreviewView.tsx @@ -45,11 +45,11 @@ export const AvatarEditorFigurePreviewView: FC - + - rotateFigure(figureData.direction + 1) } /> - rotateFigure(figureData.direction - 1) } /> +
rotateFigure(figureData.direction + 1) } /> +
rotateFigure(figureData.direction - 1) } /> ); diff --git a/src/views/avatar-editor/views/figure-set-item/AvatarEditorFigureSetItemView.tsx b/src/views/avatar-editor/views/figure-set-item/AvatarEditorFigureSetItemView.tsx index 16ab3ef7..ca4a85f1 100644 --- a/src/views/avatar-editor/views/figure-set-item/AvatarEditorFigureSetItemView.tsx +++ b/src/views/avatar-editor/views/figure-set-item/AvatarEditorFigureSetItemView.tsx @@ -26,8 +26,8 @@ export const AvatarEditorFigureSetItemView: FC onClick(partItem) }> { partItem.isHC && } - { partItem.isClear && } - { partItem.isSellable && } + { partItem.isClear &&
} + { partItem.isSellable &&
} ); } diff --git a/src/views/avatar-editor/views/model/AvatarEditorModelView.tsx b/src/views/avatar-editor/views/model/AvatarEditorModelView.tsx index 1fe1a562..cb6e3b4d 100644 --- a/src/views/avatar-editor/views/model/AvatarEditorModelView.tsx +++ b/src/views/avatar-editor/views/model/AvatarEditorModelView.tsx @@ -52,10 +52,10 @@ export const AvatarEditorModelView: FC = props => { model.canSetGender && <> setGender(FigureData.MALE) }> - +
setGender(FigureData.FEMALE) }> - +
} { !model.canSetGender && model.categories && (model.categories.size > 0) && Array.from(model.categories.keys()).map(name => @@ -64,7 +64,7 @@ export const AvatarEditorModelView: FC = props => return ( selectCategory(name) }> - +
); })} diff --git a/src/views/chat-history/ChatHistoryView.scss b/src/views/chat-history/ChatHistoryView.scss index d103ad8c..9f8e6385 100644 --- a/src/views/chat-history/ChatHistoryView.scss +++ b/src/views/chat-history/ChatHistoryView.scss @@ -1,18 +1,13 @@ -.nitro-chat-history -{ - width: 300px; +.nitro-chat-history { + width: $chat-history-width; + height: $chat-history-height; - .chat-history-content - { - - .chat-history-container - { + .chat-history-content { + .chat-history-container { min-height: 200px; - .chat-history-list - { - .chathistory-entry - { + .chat-history-list { + .chathistory-entry { .light { background-color: #121f27; } diff --git a/src/views/friends/FriendsView.scss b/src/views/friends/FriendsView.scss index 35888832..6bfa3033 100644 --- a/src/views/friends/FriendsView.scss +++ b/src/views/friends/FriendsView.scss @@ -1,6 +1,95 @@ +.nitro-friends-spritesheet { + background: url('../../assets/images/friends/friends-spritesheet.png') transparent no-repeat; + + &.icon-friendbar-visit { + width: 21px; height: 21px; + background-position: -38px -5px; + } + + &.icon-heart { + width: 16px; height: 14px; + background-position: -5px -67px; + } + + &.icon-new-message { + width: 14px; height: 14px; + background-position: -96px -53px; + } + + &.icon-none { + width: 16px; height: 14px; + background-position: -31px -67px; + } + + &.icon-profile { + width: 21px; height: 21px; + background-position: -5px -36px; + } + + &.icon-profile-sm { + width: 13px; height: 11px; + background-position: -51px -91px; + + &:hover { + width: 13px; height: 11px; + background-position: -74px -91px; + } + } + + &.icon-smile { + width: 16px; height: 14px; + background-position: -57px -67px; + } + + &.icon-warning { + width: 23px; height: 21px; + background-position: -5px -5px; + } + + &.icon-accept { + width: 13px; height: 14px; + background-position: -5px -91px; + } + + &.icon-add { + width: 16px; height: 15px; + background-position: -69px -31px; + } + + &.icon-bobba { + width: 16px; height: 14px; + background-position: -96px -5px; + } + + &.icon-chat { + width: 17px; height: 16px; + background-position: -69px -5px; + } + + &.icon-deny { + width: 13px; height: 14px; + background-position: -28px -91px; + } + + &.icon-follow { + width: 16px; height: 14px; + background-position: -96px -29px; + } + + &.icon-friendbar-chat { + width: 20px; height: 21px; + background-position: -36px -36px; + } +} + .nitro-friends { width: $friends-list-width; height: $friends-list-height; + + .search-input { + border: 0; + border-bottom: 1px solid rgba($black, 0.2); + } } @import "./views/friend-bar/FriendBarView"; diff --git a/src/views/friends/FriendsView.tsx b/src/views/friends/FriendsView.tsx index fedb706f..768698c2 100644 --- a/src/views/friends/FriendsView.tsx +++ b/src/views/friends/FriendsView.tsx @@ -344,7 +344,7 @@ export const FriendsView: FC<{}> = props => }, [ requests ]); return ( - + { isReady && createPortal(, document.getElementById('toolbar-friend-bar-container')) } { isVisible && diff --git a/src/views/friends/context/FriendsContext.tsx b/src/views/friends/context/FriendsContext.tsx index a93a2f43..a86f130d 100644 --- a/src/views/friends/context/FriendsContext.tsx +++ b/src/views/friends/context/FriendsContext.tsx @@ -5,6 +5,8 @@ const FriendsContext = createContext({ friends: null, requests: null, settings: null, + canRequestFriend: null, + requestFriend: null, acceptFriend: null, declineFriend: null }); diff --git a/src/views/friends/context/FriendsContext.type.ts b/src/views/friends/context/FriendsContext.type.ts index 2eaf79a9..772372d3 100644 --- a/src/views/friends/context/FriendsContext.type.ts +++ b/src/views/friends/context/FriendsContext.type.ts @@ -8,6 +8,8 @@ export interface IFriendsContext friends: MessengerFriend[]; requests: MessengerRequest[]; settings: MessengerSettings; + canRequestFriend: (userId: number) => boolean; + requestFriend: (userId: number, userName: string) => void; acceptFriend: (userId: number) => void; declineFriend: (userId: number, declineAll?: boolean) => void; } diff --git a/src/views/friends/views/friend-bar-item/FriendBarItemView.tsx b/src/views/friends/views/friend-bar-item/FriendBarItemView.tsx index 36079517..045edf63 100644 --- a/src/views/friends/views/friend-bar-item/FriendBarItemView.tsx +++ b/src/views/friends/views/friend-bar-item/FriendBarItemView.tsx @@ -2,6 +2,7 @@ import { FollowFriendMessageComposer, MouseEventType } from '@nitrots/nitro-rend import { FC, useCallback, useEffect, useRef, useState } from 'react'; import { GetUserProfile, LocalizeText, OpenMessengerChat } from '../../../../api'; import { SendMessageHook } from '../../../../hooks/messages'; +import { NitroLayoutBase } from '../../../../layout/base'; import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView'; import { FriendBarItemViewProps } from './FriendBarItemView.types'; @@ -63,9 +64,10 @@ export const FriendBarItemView: FC = props =>
{ friend.name }
{ isVisible &&
- - { friend.followingAllowed && } - GetUserProfile(friend.id) } className="icon icon-fb-profile cursor-pointer" /> + + { friend.followingAllowed && + } + GetUserProfile(friend.id) } />
}
); diff --git a/src/views/friends/views/friend-bar/FriendBarView.tsx b/src/views/friends/views/friend-bar/FriendBarView.tsx index a0347c94..aaff2555 100644 --- a/src/views/friends/views/friend-bar/FriendBarView.tsx +++ b/src/views/friends/views/friend-bar/FriendBarView.tsx @@ -1,4 +1,6 @@ import { FC, useMemo, useState } from 'react'; +import { NitroLayoutButton, NitroLayoutFlex } from '../../../../layout'; +import { NitroLayoutBase } from '../../../../layout/base'; import { FriendBarItemView } from '../friend-bar-item/FriendBarItemView'; import { FriendBarViewProps } from './FriendBarView.types'; @@ -24,10 +26,10 @@ export const FriendBarView: FC = props => }, [ maxDisplayCount, indexOffset, onlineFriends ]); return ( -
- + + setIndexOffset(indexOffset - 1) }> + + { Array.from(Array(maxDisplayCount), (e, i) => { return ; @@ -35,6 +37,6 @@ export const FriendBarView: FC = props => -
+
); } diff --git a/src/views/friends/views/friends-group-item/FriendsGroupItemView.tsx b/src/views/friends/views/friends-group-item/FriendsGroupItemView.tsx index 60b2066a..1c7b2932 100644 --- a/src/views/friends/views/friends-group-item/FriendsGroupItemView.tsx +++ b/src/views/friends/views/friends-group-item/FriendsGroupItemView.tsx @@ -2,7 +2,8 @@ import { FollowFriendMessageComposer, SetRelationshipStatusComposer } from '@nit import { FC, useCallback, useState } from 'react'; import { LocalizeText, OpenMessengerChat } from '../../../../api'; import { SendMessageHook } from '../../../../hooks'; -import { UserProfileIconView } from '../../../shared/user-profile-icon/UserProfileIconView'; +import { NitroLayoutFlex, UserProfileIconView } from '../../../../layout'; +import { NitroLayoutBase } from '../../../../layout/base'; import { MessengerFriend } from '../../common/MessengerFriend'; import { FriendsGroupItemViewProps } from './FriendsGroupItemView.types'; @@ -52,19 +53,23 @@ export const FriendsGroupItemView: FC = props =>
{ friend.name }
-
- { !isExpanded && <> - { friend.followingAllowed && } - { friend.online && } - setIsExpanded(true) } title={ LocalizeText('infostand.link.relationship') } /> + + { !isExpanded && + <> + { friend.followingAllowed && + } + { friend.online && + } + setIsExpanded(true) } title={ LocalizeText('infostand.link.relationship') } /> } - { isExpanded && <> - updateRelationship(MessengerFriend.RELATIONSHIP_HEART) } /> - updateRelationship(MessengerFriend.RELATIONSHIP_SMILE) } /> - updateRelationship(MessengerFriend.RELATIONSHIP_BOBBA) } /> - updateRelationship(MessengerFriend.RELATIONSHIP_NONE) } /> - } -
+ { isExpanded && + <> + updateRelationship(MessengerFriend.RELATIONSHIP_HEART) } /> + updateRelationship(MessengerFriend.RELATIONSHIP_SMILE) } /> + updateRelationship(MessengerFriend.RELATIONSHIP_BOBBA) } /> + updateRelationship(MessengerFriend.RELATIONSHIP_NONE) } /> + } +
); } diff --git a/src/views/friends/views/friends-list/FriendsListView.tsx b/src/views/friends/views/friends-list/FriendsListView.tsx index f706d334..fcf0c9c5 100644 --- a/src/views/friends/views/friends-list/FriendsListView.tsx +++ b/src/views/friends/views/friends-list/FriendsListView.tsx @@ -3,6 +3,7 @@ import { LocalizeText } from '../../../../api'; import { NitroCardAccordionSetView, NitroCardAccordionView, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../layout'; import { FriendsGroupView } from '../friends-group/FriendsGroupView'; import { FriendsRequestView } from '../friends-request/FriendsRequestView'; +import { FriendsSearchView } from '../friends-search/FriendsSearchView'; import { FriendsListViewProps } from './FriendsListView.types'; const MODE_FRIENDS: number = 0; @@ -15,7 +16,7 @@ export const FriendsListView: FC = props => const [ mode, setMode ] = useState(0); return ( - + setMode(MODE_FRIENDS) }> @@ -34,13 +35,10 @@ export const FriendsListView: FC = props => - - } { (mode === MODE_SEARCH) && - <> - } + } ); diff --git a/src/views/friends/views/friends-request-item/FriendsRequestItemView.tsx b/src/views/friends/views/friends-request-item/FriendsRequestItemView.tsx index e0929968..f5ae26d9 100644 --- a/src/views/friends/views/friends-request-item/FriendsRequestItemView.tsx +++ b/src/views/friends/views/friends-request-item/FriendsRequestItemView.tsx @@ -1,6 +1,6 @@ import { FC } from 'react'; -import { NitroCardAccordionItemView } from '../../../../layout'; -import { UserProfileIconView } from '../../../shared/user-profile-icon/UserProfileIconView'; +import { NitroCardAccordionItemView, NitroLayoutFlex, UserProfileIconView } from '../../../../layout'; +import { NitroLayoutBase } from '../../../../layout/base'; import { useFriendsContext } from '../../context/FriendsContext'; import { FriendsRequestItemViewProps } from './FriendsRequestItemView.types'; @@ -14,11 +14,11 @@ export const FriendsRequestItemView: FC = props => return ( -
{ request.name }
-
- acceptFriend(request.requesterUserId) } /> - declineFriend(request.requesterUserId) } /> -
+ { request.name } + + acceptFriend(request.requesterUserId) } /> + declineFriend(request.requesterUserId) } /> +
); }; diff --git a/src/views/friends/views/friends-search/FriendsSearchView.tsx b/src/views/friends/views/friends-search/FriendsSearchView.tsx new file mode 100644 index 00000000..891038ba --- /dev/null +++ b/src/views/friends/views/friends-search/FriendsSearchView.tsx @@ -0,0 +1,80 @@ +import { HabboSearchComposer, HabboSearchResultData, HabboSearchResultEvent } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useEffect, useState } from 'react'; +import { LocalizeText, OpenMessengerChat } from '../../../../api'; +import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../hooks'; +import { NitroCardAccordionItemView, NitroCardAccordionSetView, NitroCardAccordionView, NitroLayoutFlex, UserProfileIconView } from '../../../../layout'; +import { NitroLayoutBase } from '../../../../layout/base'; +import { useFriendsContext } from '../../context/FriendsContext'; + +export const FriendsSearchView: FC<{}> = props => +{ + const [ searchValue, setSearchValue ] = useState(''); + const [ friendResults, setFriendResults ] = useState([]); + const [ otherResults, setOtherResults ] = useState([]); + const { canRequestFriend = null, requestFriend = null } = useFriendsContext(); + + const onHabboSearchResultEvent = useCallback((event: HabboSearchResultEvent) => + { + const parser = event.getParser(); + + BatchUpdates(() => + { + setFriendResults(parser.friends); + setOtherResults(parser.others); + }); + }, []); + + CreateMessageHook(HabboSearchResultEvent, onHabboSearchResultEvent); + + useEffect(() => + { + if(!searchValue || !searchValue.length) return; + + const timeout = setTimeout(() => + { + if(!searchValue || !searchValue.length) return; + + SendMessageHook(new HabboSearchComposer(searchValue)); + }, 500); + + return () => clearTimeout(timeout); + }, [ searchValue ]); + + return ( + <> + setSearchValue(event.target.value) } /> + + + { (friendResults.length > 0) && friendResults.map(result => + { + return ( + + + { result.avatarName } + + { result.isAvatarOnline && + OpenMessengerChat(result.avatarId) } title={ LocalizeText('friendlist.tip.im') } /> } + + + ); + }) } + + + { (otherResults.length > 0) && otherResults.map(result => + { + return ( + + + { result.avatarName } + + { canRequestFriend(result.avatarId) && + requestFriend(result.avatarId, result.avatarName) } title={ LocalizeText('friendlist.tip.addfriend') } /> } + + + ); + }) } + + + + ); +} diff --git a/src/views/friends/views/messenger-thread-group/FriendsMessengerThreadGroup.tsx b/src/views/friends/views/messenger-thread-group/FriendsMessengerThreadGroup.tsx index b0d30ea7..47ce9415 100644 --- a/src/views/friends/views/messenger-thread-group/FriendsMessengerThreadGroup.tsx +++ b/src/views/friends/views/messenger-thread-group/FriendsMessengerThreadGroup.tsx @@ -1,5 +1,7 @@ import { FC } from 'react'; import { GetSessionDataManager } from '../../../../api'; +import { NitroLayoutFlex } from '../../../../layout'; +import { NitroLayoutBase } from '../../../../layout/base'; import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView'; import { MessengerThreadChat } from '../../common/MessengerThreadChat'; import { FriendsMessengerThreadGroupProps } from './FriendsMessengerThreadGroup.types'; @@ -17,13 +19,13 @@ export const FriendsMessengerThreadGroup: FC = { group.chats.map((chat, index) => { return ( -
+ { chat.type === MessengerThreadChat.SECURITY_NOTIFICATION && -
- -
{ chat.message }
-
} -
+ + + { chat.message } + } + ); }) }
@@ -31,18 +33,18 @@ export const FriendsMessengerThreadGroup: FC = } return ( -
+ { (group.userId > 0) && -
+ -
} -
- { group.chats.map((chat, index) =>
{ chat.message }
) } -
+ } + + { group.chats.map((chat, index) => { chat.message }) } + { (group.userId === 0) && -
+ -
} -
+ } +
); } diff --git a/src/views/friends/views/messenger/FriendsMessengerView.tsx b/src/views/friends/views/messenger/FriendsMessengerView.tsx index 115d7d55..7faec597 100644 --- a/src/views/friends/views/messenger/FriendsMessengerView.tsx +++ b/src/views/friends/views/messenger/FriendsMessengerView.tsx @@ -3,7 +3,8 @@ import { FC, KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } import { AddEventLinkTracker, GetUserProfile, LocalizeText, RemoveLinkEventTracker } from '../../../../api'; import { FriendsMessengerIconEvent } from '../../../../events'; import { BatchUpdates, CreateMessageHook, dispatchUiEvent, SendMessageHook } from '../../../../hooks'; -import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutFlex } from '../../../../layout'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutButton, NitroLayoutButtonGroup, NitroLayoutFlex, NitroLayoutFlexColumn } from '../../../../layout'; +import { NitroLayoutBase } from '../../../../layout/base'; import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView'; import { MessengerThread } from '../../common/MessengerThread'; import { MessengerThreadChat } from '../../common/MessengerThreadChat'; @@ -232,7 +233,7 @@ export const FriendsMessengerView: FC<{}> = props => if(!isVisible) return null; return ( - + setIsVisible(false) } /> @@ -241,8 +242,9 @@ export const FriendsMessengerView: FC<{}> = props => const messageThreadIndex = messageThreads.indexOf(thread); return ( -
setActiveThreadIndex(messageThreadIndex) }> - { thread.unread && } +
setActiveThreadIndex(messageThreadIndex) }> + { thread.unread && + }
); @@ -250,32 +252,38 @@ export const FriendsMessengerView: FC<{}> = props => { (activeThreadIndex >= 0) && -
+ { LocalizeText('messenger.window.separator', [ 'FRIEND_NAME' ], [ messageThreads[activeThreadIndex].participant.name ]) } -
} + }
{ (activeThreadIndex >= 0) && <> -
-
- - -
- - -
-
+ + + + + + + + + + + { LocalizeText('messenger.window.button.report') } + + closeThread(activeThreadIndex) }> + + + + -
-
+ + setMessageText(event.target.value) } onKeyDown={ onKeyDown } /> - -
+ + { LocalizeText('widgets.chatinput.say') } + + } diff --git a/src/views/friends/views/request-item/FriendsRequestItemView.tsx b/src/views/friends/views/request-item/FriendsRequestItemView.tsx index f25c42f9..50c7d243 100644 --- a/src/views/friends/views/request-item/FriendsRequestItemView.tsx +++ b/src/views/friends/views/request-item/FriendsRequestItemView.tsx @@ -1,7 +1,7 @@ import { AcceptFriendMessageComposer, DeclineFriendMessageComposer } from '@nitrots/nitro-renderer'; import { FC, useCallback } from 'react'; -import { SendMessageHook } from '../../../../hooks/messages/message-event'; -import { UserProfileIconView } from '../../../shared/user-profile-icon/UserProfileIconView'; +import { SendMessageHook } from '../../../../hooks'; +import { UserProfileIconView } from '../../../../layout'; import { FriendsRequestItemViewProps } from './FriendsRequestItemView.types'; export const FriendsRequestItemView: FC = props => diff --git a/src/views/hotel-view/views/widgets/hall-of-fame-item/HallOfFameItemView.tsx b/src/views/hotel-view/views/widgets/hall-of-fame-item/HallOfFameItemView.tsx index 431a6f6d..2aef86fe 100644 --- a/src/views/hotel-view/views/widgets/hall-of-fame-item/HallOfFameItemView.tsx +++ b/src/views/hotel-view/views/widgets/hall-of-fame-item/HallOfFameItemView.tsx @@ -1,7 +1,7 @@ import { FC } from 'react'; import { LocalizeText } from '../../../../../api'; +import { UserProfileIconView } from '../../../../../layout'; import { AvatarImageView } from '../../../../shared/avatar-image/AvatarImageView'; -import { UserProfileIconView } from '../../../../shared/user-profile-icon/UserProfileIconView'; import { HallOfFameItemViewProps } from './HallOfFameItemView.types'; export const HallOfFameItemView: FC = props => diff --git a/src/views/navigator/views/room-info/NavigatorRoomInfoView.tsx b/src/views/navigator/views/room-info/NavigatorRoomInfoView.tsx index f0a9ab42..92c9712f 100644 --- a/src/views/navigator/views/room-info/NavigatorRoomInfoView.tsx +++ b/src/views/navigator/views/room-info/NavigatorRoomInfoView.tsx @@ -7,9 +7,8 @@ import { FloorplanEditorEvent } from '../../../../events/floorplan-editor/Floorp import { RoomWidgetThumbnailEvent } from '../../../../events/room-widgets/thumbnail'; import { dispatchUiEvent } from '../../../../hooks/events'; import { SendMessageHook } from '../../../../hooks/messages'; -import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView, UserProfileIconView } from '../../../../layout'; import { BadgeImageView } from '../../../shared/badge-image/BadgeImageView'; -import { UserProfileIconView } from '../../../shared/user-profile-icon/UserProfileIconView'; import { useNavigatorContext } from '../../context/NavigatorContext'; import { NavigatorActions } from '../../reducers/NavigatorReducer'; import { NavigatorRoomInfoViewProps } from './NavigatorRoomInfoView.types'; diff --git a/src/views/navigator/views/room-settings/views/tab-rights/NavigatorRoomSettingsRightsTabView.tsx b/src/views/navigator/views/room-settings/views/tab-rights/NavigatorRoomSettingsRightsTabView.tsx index f91a328f..e4c633be 100644 --- a/src/views/navigator/views/room-settings/views/tab-rights/NavigatorRoomSettingsRightsTabView.tsx +++ b/src/views/navigator/views/room-settings/views/tab-rights/NavigatorRoomSettingsRightsTabView.tsx @@ -2,7 +2,7 @@ import { RemoveAllRightsMessageComposer, RoomGiveRightsComposer, RoomTakeRightsC import { FC, useCallback, useMemo } from 'react'; import { LocalizeText } from '../../../../../../api'; import { SendMessageHook } from '../../../../../../hooks'; -import { UserProfileIconView } from '../../../../../shared/user-profile-icon/UserProfileIconView'; +import { UserProfileIconView } from '../../../../../../layout'; import { NavigatorRoomSettingsRightsTabViewProps } from './NavigatorRoomSettingsRightsTabView.types'; export const NavigatorRoomSettingsRightsTabView: FC = props => 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/infostand/views/furni/InfoStandWidgetFurniView.tsx b/src/views/room/widgets/infostand/views/furni/InfoStandWidgetFurniView.tsx index 1c918dc6..e885a059 100644 --- a/src/views/room/widgets/infostand/views/furni/InfoStandWidgetFurniView.tsx +++ b/src/views/room/widgets/infostand/views/furni/InfoStandWidgetFurniView.tsx @@ -2,10 +2,10 @@ import { CrackableDataType, GroupInformationComposer, GroupInformationEvent, Roo import { FC, useCallback, useEffect, useState } from 'react'; import { CreateLinkEvent, GetGroupInformation, GetRoomEngine, LocalizeText, RoomWidgetFurniActionMessage } from '../../../../../../api'; import { CreateMessageHook, SendMessageHook } from '../../../../../../hooks'; +import { UserProfileIconView } from '../../../../../../layout'; import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView'; import { LimitedEditionCompactPlateView } from '../../../../../shared/limited-edition/compact-plate/LimitedEditionCompactPlateView'; import { RarityLevelView } from '../../../../../shared/rarity-level/RarityLevelView'; -import { UserProfileIconView } from '../../../../../shared/user-profile-icon/UserProfileIconView'; import { useRoomContext } from '../../../../context/RoomContext'; import { InfoStandBaseView } from '../base/InfoStandBaseView'; import { InfoStandWidgetFurniViewProps } from './InfoStandWidgetFurniView.types'; diff --git a/src/views/room/widgets/infostand/views/pet/InfoStandWidgetPetView.tsx b/src/views/room/widgets/infostand/views/pet/InfoStandWidgetPetView.tsx index 0de4f613..a2482ced 100644 --- a/src/views/room/widgets/infostand/views/pet/InfoStandWidgetPetView.tsx +++ b/src/views/room/widgets/infostand/views/pet/InfoStandWidgetPetView.tsx @@ -1,7 +1,7 @@ import { FC } from 'react'; import { LocalizeText } from '../../../../../../api'; +import { UserProfileIconView } from '../../../../../../layout'; import { PetImageView } from '../../../../../shared/pet-image/PetImageView'; -import { UserProfileIconView } from '../../../../../shared/user-profile-icon/UserProfileIconView'; import { InfoStandBaseView } from '../base/InfoStandBaseView'; import { InfoStandWidgetPetViewProps } from './InfoStandWidgetPetView.types'; diff --git a/src/views/room/widgets/infostand/views/rentable-bot/InfoStandWidgetRentableBotView.tsx b/src/views/room/widgets/infostand/views/rentable-bot/InfoStandWidgetRentableBotView.tsx index af4a2566..e4214850 100644 --- a/src/views/room/widgets/infostand/views/rentable-bot/InfoStandWidgetRentableBotView.tsx +++ b/src/views/room/widgets/infostand/views/rentable-bot/InfoStandWidgetRentableBotView.tsx @@ -1,10 +1,10 @@ import { BotRemoveComposer } from '@nitrots/nitro-renderer'; import { FC, useCallback, useMemo } from 'react'; import { LocalizeText } from '../../../../../../api'; -import { SendMessageHook } from '../../../../../../hooks/messages'; +import { SendMessageHook } from '../../../../../../hooks'; +import { UserProfileIconView } from '../../../../../../layout'; import { AvatarImageView } from '../../../../../shared/avatar-image/AvatarImageView'; import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView'; -import { UserProfileIconView } from '../../../../../shared/user-profile-icon/UserProfileIconView'; import { BotSkillsEnum } from '../../../avatar-info/common/BotSkillsEnum'; import { InfoStandBaseView } from '../base/InfoStandBaseView'; import { InfoStandWidgetRentableBotViewProps } from './InfoStandWidgetRentableBotView.types'; diff --git a/src/views/room/widgets/infostand/views/user/InfoStandWidgetUserView.tsx b/src/views/room/widgets/infostand/views/user/InfoStandWidgetUserView.tsx index c2560891..5228d30b 100644 --- a/src/views/room/widgets/infostand/views/user/InfoStandWidgetUserView.tsx +++ b/src/views/room/widgets/infostand/views/user/InfoStandWidgetUserView.tsx @@ -4,9 +4,9 @@ import { FC, FocusEvent, KeyboardEvent, useCallback, useEffect, useState } from import { GetGroupInformation, LocalizeText, RoomWidgetChangeMottoMessage, RoomWidgetUpdateInfostandUserEvent } from '../../../../../../api'; import { CreateMessageHook, SendMessageHook } from '../../../../../../hooks'; import { CreateEventDispatcherHook } from '../../../../../../hooks/events'; +import { UserProfileIconView } from '../../../../../../layout'; import { AvatarImageView } from '../../../../../shared/avatar-image/AvatarImageView'; import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView'; -import { UserProfileIconView } from '../../../../../shared/user-profile-icon/UserProfileIconView'; import { RelationshipsContainerView } from '../../../../../user-profile/views/relationships-container/RelationshipsContainerView'; import { useRoomContext } from '../../../../context/RoomContext'; import { InfoStandWidgetUserViewProps } from './InfoStandWidgetUserView.types'; 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; +} diff --git a/src/views/shared/user-profile-icon/UserProfileIconView.tsx b/src/views/shared/user-profile-icon/UserProfileIconView.tsx deleted file mode 100644 index 23225082..00000000 --- a/src/views/shared/user-profile-icon/UserProfileIconView.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { FC } from 'react'; -import { GetUserProfile } from '../../../api'; -import { UserProfileIconViewProps } from './UserProfileIconView.types'; - -export const UserProfileIconView: FC = props => -{ - const { userId = 0, userName = null } = props; - - return (<> - GetUserProfile(userId) } /> - ); -} diff --git a/src/views/shared/user-profile-icon/UserProfileIconView.types.ts b/src/views/shared/user-profile-icon/UserProfileIconView.types.ts deleted file mode 100644 index 1662dfa8..00000000 --- a/src/views/shared/user-profile-icon/UserProfileIconView.types.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface UserProfileIconViewProps -{ - userId?: number; - userName?: string; -}