mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-22 22:30:52 +01:00
Lots of catalog updates
This commit is contained in:
parent
4ef0a1d6d0
commit
a1605537c8
21
src/api/nitro/session/GetFurnitureDataForProductOffer.ts
Normal file
21
src/api/nitro/session/GetFurnitureDataForProductOffer.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { CatalogProductOfferData, FurnitureType, IFurnitureData } from 'nitro-renderer';
|
||||
import { GetSessionDataManager } from './GetSessionDataManager';
|
||||
|
||||
export function GetFurnitureDataForProductOffer(offer: CatalogProductOfferData): IFurnitureData
|
||||
{
|
||||
if(!offer) return null;
|
||||
|
||||
let furniData: IFurnitureData = null;
|
||||
|
||||
switch((offer.productType.toUpperCase()))
|
||||
{
|
||||
case FurnitureType.FLOOR:
|
||||
furniData = GetSessionDataManager().getFloorItemData(offer.furniClassId);
|
||||
break;
|
||||
case FurnitureType.WALL:
|
||||
furniData = GetSessionDataManager().getWallItemData(offer.furniClassId);
|
||||
break;
|
||||
}
|
||||
|
||||
return furniData;
|
||||
}
|
9
src/api/nitro/session/GetProductDataForLocalization.ts
Normal file
9
src/api/nitro/session/GetProductDataForLocalization.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { IProductData } from 'nitro-renderer';
|
||||
import { GetSessionDataManager } from './GetSessionDataManager';
|
||||
|
||||
export function GetProductDataForLocalization(localizationId: string): IProductData
|
||||
{
|
||||
if(!localizationId) return null;
|
||||
|
||||
return GetSessionDataManager().getProductData(localizationId);
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
export * from './GetFurnitureDataForProductOffer';
|
||||
export * from './GetRoomSession';
|
||||
export * from './GetRoomSessionManager';
|
||||
export * from './GetSessionDataManager';
|
||||
export * from './SendChatTypingMessage';
|
||||
export * from './StartRoomSession';
|
||||
|
@ -80,6 +80,10 @@ $light: #DFDFDF !default;
|
||||
$dark: $gray-900 !default;
|
||||
// scss-docs-end theme-color-variables
|
||||
|
||||
.bg-primary-split {
|
||||
background: repeating-linear-gradient(#1E7295, #1E7295 49.9%, #185D79 50.1%, #185D79 100%);
|
||||
}
|
||||
|
||||
// scss-docs-start theme-colors-map
|
||||
$theme-colors: (
|
||||
"primary": $primary,
|
||||
@ -364,7 +368,7 @@ $container-padding-x: $grid-gutter-width / 2 !default;
|
||||
// Define common padding and border radius sizes and more.
|
||||
|
||||
// scss-docs-start border-variables
|
||||
$border-width: 1.5px !default;
|
||||
$border-width: 1px !default;
|
||||
$border-widths: (
|
||||
1: 1px,
|
||||
2: 2px,
|
||||
|
@ -8,3 +8,19 @@
|
||||
@import './grid';
|
||||
@import './icons';
|
||||
@import './utils';
|
||||
|
||||
.btn-sm {
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
/* Chrome, Safari, Edge, Opera */
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
@ -18,6 +18,10 @@
|
||||
text-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.min-height-0 {
|
||||
min-height: 0 !important;
|
||||
}
|
||||
|
||||
ul {
|
||||
|
||||
&.columns-3 {
|
||||
|
@ -2,6 +2,10 @@ import { NitroEvent } from 'nitro-renderer';
|
||||
|
||||
export class CatalogEvent extends NitroEvent
|
||||
{
|
||||
public static SHOW_CATALOG: string = 'IE_SHOW_CATALOG';
|
||||
public static TOGGLE_CATALOG: string = 'IE_TOGGLE_CATALOG';
|
||||
public static SHOW_CATALOG: string = 'CE_SHOW_CATALOG';
|
||||
public static HIDE_CATALOG: string = 'CE_HIDE_CATALOG';
|
||||
public static TOGGLE_CATALOG: string = 'CE_TOGGLE_CATALOG';
|
||||
public static PURCHASE_SUCCESS: string = 'CE_PURCHASE_SUCCESS';
|
||||
public static PURCHASE_FAILED: string = 'CE_PURCHASE_FAILED';
|
||||
public static SOLD_OUT: string = 'CE_SOLD_OUT';
|
||||
}
|
||||
|
18
src/events/catalog/CatalogPurchaseFailureEvent.ts
Normal file
18
src/events/catalog/CatalogPurchaseFailureEvent.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { CatalogEvent } from './CatalogEvent';
|
||||
|
||||
export class CatalogPurchaseFailureEvent extends CatalogEvent
|
||||
{
|
||||
private _code: number;
|
||||
|
||||
constructor(code: number)
|
||||
{
|
||||
super(CatalogEvent.PURCHASE_FAILED);
|
||||
|
||||
this._code = code;
|
||||
}
|
||||
|
||||
public get code(): number
|
||||
{
|
||||
return this._code;
|
||||
}
|
||||
}
|
9
src/events/catalog/CatalogPurchaseSoldOutEvent.ts
Normal file
9
src/events/catalog/CatalogPurchaseSoldOutEvent.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { CatalogEvent } from './CatalogEvent';
|
||||
|
||||
export class CatalogPurchaseSoldOutEvent extends CatalogEvent
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super(CatalogEvent.SOLD_OUT);
|
||||
}
|
||||
}
|
19
src/events/catalog/CatalogPurchasedEvent.ts
Normal file
19
src/events/catalog/CatalogPurchasedEvent.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { CatalogPurchaseData } from 'nitro-renderer';
|
||||
import { CatalogEvent } from './CatalogEvent';
|
||||
|
||||
export class CatalogPurchasedEvent extends CatalogEvent
|
||||
{
|
||||
private _purchase: CatalogPurchaseData;
|
||||
|
||||
constructor(purchase: CatalogPurchaseData)
|
||||
{
|
||||
super(CatalogEvent.PURCHASE_SUCCESS);
|
||||
|
||||
this._purchase = purchase;
|
||||
}
|
||||
|
||||
public get purchase(): CatalogPurchaseData
|
||||
{
|
||||
return this._purchase;
|
||||
}
|
||||
}
|
@ -1 +1,4 @@
|
||||
export * from './CatalogEvent';
|
||||
export * from './CatalogPurchasedEvent';
|
||||
export * from './CatalogPurchaseFailureEvent';
|
||||
export * from './CatalogPurchaseSoldOutEvent';
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { createRef, MouseEvent, useEffect } from 'react';
|
||||
import { FC, MouseEvent, useEffect, useRef } from 'react';
|
||||
import Draggable from 'react-draggable';
|
||||
import { DraggableWindowProps } from './DraggableWindow.types';
|
||||
|
||||
const currentWindows: HTMLDivElement[] = [];
|
||||
|
||||
export function DraggableWindow(props: DraggableWindowProps): JSX.Element
|
||||
export const DraggableWindow: FC<DraggableWindowProps> = props =>
|
||||
{
|
||||
const elementRef = createRef<HTMLDivElement>();
|
||||
const { disableDrag = false } = props;
|
||||
|
||||
const elementRef = useRef<HTMLDivElement>();
|
||||
|
||||
function bringToTop(): void
|
||||
{
|
||||
@ -51,8 +53,8 @@ export function DraggableWindow(props: DraggableWindowProps): JSX.Element
|
||||
|
||||
bringToTop();
|
||||
|
||||
const left = ((document.body.clientWidth - element.clientWidth) / 2);
|
||||
const top = ((document.body.clientHeight - element.clientHeight) / 2);
|
||||
const left = ((element.parentElement.clientWidth - element.clientWidth) / 2);
|
||||
const top = ((element.parentElement.clientHeight - element.clientHeight) / 2);
|
||||
|
||||
element.style.left = `${ left }px`;
|
||||
element.style.top = `${ top }px`;
|
||||
@ -65,11 +67,20 @@ export function DraggableWindow(props: DraggableWindowProps): JSX.Element
|
||||
}
|
||||
}, [ elementRef ]);
|
||||
|
||||
return (
|
||||
<Draggable handle={ props.handle } { ...props.draggableOptions }>
|
||||
function getWindowContent(): JSX.Element
|
||||
{
|
||||
return (
|
||||
<div ref={ elementRef } className="position-absolute draggable-window" onMouseDownCapture={ onMouseDown }>
|
||||
{ props.children }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if(disableDrag) return getWindowContent();
|
||||
|
||||
return (
|
||||
<Draggable handle={ props.handle } { ...props.draggableOptions }>
|
||||
{ getWindowContent() }
|
||||
</Draggable>
|
||||
);
|
||||
}
|
||||
|
@ -5,5 +5,6 @@ export interface DraggableWindowProps
|
||||
{
|
||||
handle: string;
|
||||
draggableOptions?: Partial<DraggableProps>;
|
||||
disableDrag?: boolean;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
@ -35,4 +35,5 @@ $grid-active-border-color: $white;
|
||||
@import './utils/Styles';
|
||||
@import './App';
|
||||
@import './hooks/Styles';
|
||||
@import './layout/Layout';
|
||||
@import './views/Styles';
|
||||
|
@ -0,0 +1,2 @@
|
||||
@import './card/NitroCardView';
|
||||
@import './loading-spinner/LoadingSpinnerView';
|
@ -0,0 +1,8 @@
|
||||
$nitro-card-header-height: 33px;
|
||||
$nitro-card-tabs-height: 33px;
|
||||
$nitro-card-top-height: $nitro-card-header-height + $nitro-card-tabs-height;
|
||||
|
||||
@import './content/NitroCardContentView';
|
||||
@import './header/NitroCardHeaderView';
|
||||
@import './simple-header/NitroCardSimpleHeaderView';
|
||||
@import './tabs/NitroCardTabsView';
|
@ -4,11 +4,11 @@ import { NitroCardViewProps } from './NitroCardView.types';
|
||||
|
||||
export const NitroCardView: FC<NitroCardViewProps> = props =>
|
||||
{
|
||||
const { className = '' } = props;
|
||||
const { className = '', disableDrag = false } = props;
|
||||
|
||||
return (
|
||||
<DraggableWindow handle=".drag-handler">
|
||||
<div className={ 'nitro-card d-flex flex-column ' + className }>
|
||||
<DraggableWindow handle=".drag-handler" disableDrag= { disableDrag }>
|
||||
<div className={ 'nitro-card d-flex flex-column rounded border shadow overflow-hidden ' + className }>
|
||||
{ props.children }
|
||||
</div>
|
||||
</DraggableWindow>
|
||||
|
@ -1,4 +1,5 @@
|
||||
export interface NitroCardViewProps
|
||||
{
|
||||
className?: string;
|
||||
disableDrag?: boolean;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { NitroCardContentViewProps } from './NitroCardContextView.types';
|
||||
export const NitroCardContentView: FC<NitroCardContentViewProps> = props =>
|
||||
{
|
||||
return (
|
||||
<div className="bg-light rounded-bottom border border-top-0 p-2 shadow overflow-hidden content-area">
|
||||
<div className="bg-light p-2 content-area">
|
||||
{ props.children }
|
||||
</div>
|
||||
);
|
||||
|
@ -0,0 +1,4 @@
|
||||
.nitro-card-header {
|
||||
min-height: $nitro-card-header-height;
|
||||
max-height: $nitro-card-header-height;
|
||||
}
|
@ -6,7 +6,7 @@ export const NitroCardHeaderView: FC<NitroCardHeaderViewProps> = props =>
|
||||
const { headerText = null, onCloseClick = null } = props;
|
||||
|
||||
return (
|
||||
<div className="drag-handler d-flex align-items-center bg-primary border border-bottom-0 rounded-top px-2 py-1">
|
||||
<div className="drag-handler d-flex align-items-center bg-primary px-2 py-1 nitro-card-header">
|
||||
<div className="d-flex flex-grow-1 justify-content-center align-items-center">
|
||||
<div className="h4 m-0 text-white text-shadow">{ headerText }</div>
|
||||
</div>
|
||||
|
19
src/layout/card/simple-header/NitroCardSimpleHeaderView.tsx
Normal file
19
src/layout/card/simple-header/NitroCardSimpleHeaderView.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { FC } from 'react';
|
||||
import { NitroCardSimpleHeaderViewProps } from './NitroCardSimpleHeaderView.types';
|
||||
|
||||
export const NitroCardSimpleHeaderView: FC<NitroCardSimpleHeaderViewProps> = props =>
|
||||
{
|
||||
const { headerText = null, onCloseClick = null } = props;
|
||||
|
||||
return (
|
||||
<div className="d-flex align-items-center bg-light">
|
||||
<div className="col-1"></div>
|
||||
<div className="d-flex bg-primary-split flex-grow-1 justify-content-center align-items-center border border-top-0 rounded-bottom px-2 py-1">
|
||||
<div className="h5 m-0 text-white text-shadow">{ headerText }</div>
|
||||
</div>
|
||||
<div className="d-flex col-1 justify-content-center align-items-center">
|
||||
<i className="fas fa-times cursor-pointer" onClick={ onCloseClick }></i>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import { MouseEvent } from 'react';
|
||||
|
||||
export interface NitroCardSimpleHeaderViewProps
|
||||
{
|
||||
headerText: string;
|
||||
onCloseClick: (event: MouseEvent) => void;
|
||||
}
|
2
src/layout/card/simple-header/index.ts
Normal file
2
src/layout/card/simple-header/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './NitroCardSimpleHeaderView';
|
||||
export * from './NitroCardSimpleHeaderView.types';
|
@ -0,0 +1,6 @@
|
||||
.nitro-card-tabs {
|
||||
min-height: $nitro-card-tabs-height;
|
||||
max-height: $nitro-card-tabs-height;
|
||||
}
|
||||
|
||||
@import './tabs-item/NitroCardTabsItemView';
|
@ -4,7 +4,7 @@ import { NitroCardTabsViewProps } from './NitroCardTabsView.types';
|
||||
export const NitroCardTabsView: FC<NitroCardTabsViewProps> = props =>
|
||||
{
|
||||
return (
|
||||
<ul className="nav nav-tabs justify-content-center bg-secondary border-start border-end px-2 pt-1">
|
||||
<ul className="nav nav-tabs justify-content-center bg-secondary px-2 pt-1 nitro-card-tabs">
|
||||
{ props.children }
|
||||
</ul>
|
||||
);
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './card';
|
||||
export * from './loading-spinner/LoadingSpinnerView';
|
||||
|
42
src/layout/loading-spinner/LoadingSpinnerView.scss
Normal file
42
src/layout/loading-spinner/LoadingSpinnerView.scss
Normal file
@ -0,0 +1,42 @@
|
||||
.spinner-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.spinner {
|
||||
margin: 2px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border: $border-width solid $white;
|
||||
background-color: rgba($white, 0.8);
|
||||
border-radius: 100%;
|
||||
display: inline-block;
|
||||
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
|
||||
&:nth-child(1) {
|
||||
-webkit-animation-delay: -0.32s;
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
-webkit-animation-delay: -0.16s;
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-bouncedelay {
|
||||
0%, 80%, 100% { -webkit-transform: scale(0) }
|
||||
40% { -webkit-transform: scale(1.0) }
|
||||
}
|
||||
|
||||
@keyframes sk-bouncedelay {
|
||||
0%, 80%, 100% {
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
} 40% {
|
||||
-webkit-transform: scale(1.0);
|
||||
transform: scale(1.0);
|
||||
}
|
||||
}
|
12
src/layout/loading-spinner/LoadingSpinnerView.tsx
Normal file
12
src/layout/loading-spinner/LoadingSpinnerView.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { FC } from 'react';
|
||||
|
||||
export const LoadingSpinnerView: FC = props =>
|
||||
{
|
||||
return (
|
||||
<div className="spinner-container">
|
||||
<div className="spinner"></div>
|
||||
<div className="spinner"></div>
|
||||
<div className="spinner"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
.nitro-currency-icon {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
@ -2,9 +2,10 @@
|
||||
@import './badge-image/BadgeImage';
|
||||
@import './catalog/CatalogView';
|
||||
@import './catalog-icon/CatalogIconView';
|
||||
@import './friend-list/FriendListView';
|
||||
@import './hotel-view/HotelView';
|
||||
@import './inventory/InventoryView';
|
||||
@import './friend-list/FriendListView';
|
||||
@import './limited-edition/LimitedEdition';
|
||||
@import './loading/LoadingView';
|
||||
@import './main/MainView';
|
||||
@import './navigator/NavigatorView';
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { CatalogPageEvent, CatalogPagesEvent } from 'nitro-renderer';
|
||||
import { CatalogPageEvent, CatalogPagesEvent, CatalogPurchaseEvent, CatalogPurchaseFailedEvent, CatalogPurchaseUnavailableEvent, CatalogSearchEvent, CatalogSoldOutEvent } from 'nitro-renderer';
|
||||
import { FC, useCallback } from 'react';
|
||||
import { CatalogPurchaseFailureEvent } from '../../events';
|
||||
import { CatalogPurchasedEvent } from '../../events/catalog/CatalogPurchasedEvent';
|
||||
import { CatalogPurchaseSoldOutEvent } from '../../events/catalog/CatalogPurchaseSoldOutEvent';
|
||||
import { dispatchUiEvent } from '../../hooks/events/ui/ui-event';
|
||||
import { CreateMessageHook } from '../../hooks/messages/message-event';
|
||||
import { CatalogMessageHandlerProps } from './CatalogMessageHandler.types';
|
||||
import { useCatalogContext } from './context/CatalogContext';
|
||||
@ -33,8 +37,51 @@ export const CatalogMessageHandler: FC<CatalogMessageHandlerProps> = props =>
|
||||
});
|
||||
}, [ dispatchCatalogState ]);
|
||||
|
||||
const onCatalogPurchaseEvent = useCallback((event: CatalogPurchaseEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
dispatchUiEvent(new CatalogPurchasedEvent(parser.offer));
|
||||
}, []);
|
||||
|
||||
const onCatalogPurchaseFailedEvent = useCallback((event: CatalogPurchaseFailedEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
dispatchUiEvent(new CatalogPurchaseFailureEvent(parser.code));
|
||||
}, []);
|
||||
|
||||
const onCatalogPurchaseUnavailableEvent = useCallback((event: CatalogPurchaseUnavailableEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
}, []);
|
||||
|
||||
const onCatalogSoldOutEvent = useCallback((event: CatalogSoldOutEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
dispatchUiEvent(new CatalogPurchaseSoldOutEvent());
|
||||
}, []);
|
||||
|
||||
const onCatalogSearchEvent = useCallback((event: CatalogSearchEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
dispatchCatalogState({
|
||||
type: CatalogActions.SET_CATALOG_ACTIVE_OFFER,
|
||||
payload: {
|
||||
activeOffer: parser.offer
|
||||
}
|
||||
});
|
||||
}, [ dispatchCatalogState ]);
|
||||
|
||||
CreateMessageHook(CatalogPagesEvent, onCatalogPagesEvent);
|
||||
CreateMessageHook(CatalogPageEvent, onCatalogPageEvent);
|
||||
CreateMessageHook(CatalogPurchaseEvent, onCatalogPurchaseEvent);
|
||||
CreateMessageHook(CatalogPurchaseFailedEvent, onCatalogPurchaseFailedEvent);
|
||||
CreateMessageHook(CatalogPurchaseUnavailableEvent, onCatalogPurchaseUnavailableEvent);
|
||||
CreateMessageHook(CatalogSoldOutEvent, onCatalogSoldOutEvent);
|
||||
CreateMessageHook(CatalogSearchEvent, onCatalogSearchEvent);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
.nitro-catalog {
|
||||
width: 600px;
|
||||
width: 620px;
|
||||
|
||||
.content-area {
|
||||
height: 330px;
|
||||
max-height: 330px;
|
||||
}
|
||||
}
|
||||
|
||||
@import './views/CatalogViews';
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { CatalogModeComposer, ICatalogPageData } from 'nitro-renderer';
|
||||
import { CatalogModeComposer, ICatalogPageData, RoomPreviewer } from 'nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useReducer, useState } from 'react';
|
||||
import { GetRoomEngine } from '../../api';
|
||||
import { CatalogEvent } from '../../events';
|
||||
import { useUiEvent } from '../../hooks/events/ui/ui-event';
|
||||
import { SendMessageHook } from '../../hooks/messages/message-event';
|
||||
@ -8,16 +9,17 @@ import { LocalizeText } from '../../utils/LocalizeText';
|
||||
import { CatalogMessageHandler } from './CatalogMessageHandler';
|
||||
import { CatalogMode, CatalogViewProps } from './CatalogView.types';
|
||||
import { CatalogContextProvider } from './context/CatalogContext';
|
||||
import { CatalogReducer, initialCatalog } from './reducers/CatalogReducer';
|
||||
import { CatalogActions, CatalogReducer, initialCatalog } from './reducers/CatalogReducer';
|
||||
import { CatalogNavigationView } from './views/navigation/CatalogNavigationView';
|
||||
import { CatalogPageView } from './views/page/CatalogPageView';
|
||||
import { CatalogSearchView } from './views/search/CatalogSearchView';
|
||||
|
||||
export const CatalogView: FC<CatalogViewProps> = props =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const [ currentTab, setCurrentTab ] = useState<ICatalogPageData>(null);
|
||||
const [ roomPreviewer, setRoomPreviewer ] = useState<RoomPreviewer>(null);
|
||||
const [ catalogState, dispatchCatalogState ] = useReducer(CatalogReducer, initialCatalog);
|
||||
const { root = null } = catalogState;
|
||||
const { root = null, currentTab = null, searchResult = null } = catalogState;
|
||||
|
||||
const onCatalogEvent = useCallback((event: CatalogEvent) =>
|
||||
{
|
||||
@ -26,6 +28,9 @@ export const CatalogView: FC<CatalogViewProps> = props =>
|
||||
case CatalogEvent.SHOW_CATALOG:
|
||||
setIsVisible(true);
|
||||
return;
|
||||
case CatalogEvent.HIDE_CATALOG:
|
||||
setIsVisible(false);
|
||||
return;
|
||||
case CatalogEvent.TOGGLE_CATALOG:
|
||||
setIsVisible(value => !value);
|
||||
return;
|
||||
@ -33,6 +38,7 @@ export const CatalogView: FC<CatalogViewProps> = props =>
|
||||
}, []);
|
||||
|
||||
useUiEvent(CatalogEvent.SHOW_CATALOG, onCatalogEvent);
|
||||
useUiEvent(CatalogEvent.HIDE_CATALOG, onCatalogEvent);
|
||||
useUiEvent(CatalogEvent.TOGGLE_CATALOG, onCatalogEvent);
|
||||
|
||||
useEffect(() =>
|
||||
@ -43,12 +49,35 @@ export const CatalogView: FC<CatalogViewProps> = props =>
|
||||
{
|
||||
SendMessageHook(new CatalogModeComposer(CatalogMode.MODE_NORMAL));
|
||||
}
|
||||
else
|
||||
{
|
||||
setCurrentTab(catalogState.root.children[0]);
|
||||
}
|
||||
}, [ isVisible, catalogState.root ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setRoomPreviewer(new RoomPreviewer(GetRoomEngine(), ++RoomPreviewer.PREVIEW_COUNTER));
|
||||
|
||||
return () =>
|
||||
{
|
||||
setRoomPreviewer(prevValue =>
|
||||
{
|
||||
prevValue.dispose();
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
function setCurrentTab(page: ICatalogPageData): void
|
||||
{
|
||||
dispatchCatalogState({
|
||||
type: CatalogActions.SET_CATALOG_CURRENT_TAB,
|
||||
payload: {
|
||||
currentTab: page
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const currentNavigationPage = ((searchResult && searchResult.page) || currentTab);
|
||||
|
||||
return (
|
||||
<CatalogContextProvider value={ { catalogState, dispatchCatalogState } }>
|
||||
<CatalogMessageHandler />
|
||||
@ -62,12 +91,13 @@ export const CatalogView: FC<CatalogViewProps> = props =>
|
||||
}) }
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<div className="row">
|
||||
<div className="row h-100">
|
||||
<div className="col-3">
|
||||
<CatalogNavigationView page={ currentTab } />
|
||||
<CatalogSearchView />
|
||||
<CatalogNavigationView page={ currentNavigationPage } />
|
||||
</div>
|
||||
<div className="col">
|
||||
<CatalogPageView />
|
||||
<CatalogPageView roomPreviewer={ roomPreviewer } />
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
|
26
src/views/catalog/enums/FurniCategory.ts
Normal file
26
src/views/catalog/enums/FurniCategory.ts
Normal file
@ -0,0 +1,26 @@
|
||||
export class FurniCategory
|
||||
{
|
||||
public static DEFAULT: number = 1;
|
||||
public static WALL_PAPER: number = 2;
|
||||
public static FLOOR: number = 3;
|
||||
public static LANDSCAPE: number = 4;
|
||||
public static POST_IT: number = 5;
|
||||
public static POSTER: number = 6;
|
||||
public static SOUND_SET: number = 7;
|
||||
public static TRAX_SONG: number = 8;
|
||||
public static PRESENT: number = 9;
|
||||
public static ECOTRON_BOX: number = 10;
|
||||
public static TROPHY: number = 11;
|
||||
public static CREDIT_FURNI: number = 12;
|
||||
public static PET_SHAMPOO: number = 13;
|
||||
public static PET_CUSTOM_PART: number = 14;
|
||||
public static PET_CUSTOM_PART_SHAMPOO: number = 15;
|
||||
public static PET_SADDLE: number = 16;
|
||||
public static GUILD_FURNI: number = 17;
|
||||
public static GAME_FURNI: number = 18;
|
||||
public static MONSTERPLANT_SEED: number = 19;
|
||||
public static MONSTERPLANT_REVIVAL: number = 20;
|
||||
public static MONSTERPLANT_REBREED: number = 21;
|
||||
public static MONSTERPLANT_FERTILIZE: number = 22;
|
||||
public static FIGURE_PURCHASABLE_SET: number = 23;
|
||||
}
|
11
src/views/catalog/enums/ProductTypeEnum.ts
Normal file
11
src/views/catalog/enums/ProductTypeEnum.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export class ProductTypeEnum
|
||||
{
|
||||
public static WALL: string = 'i';
|
||||
public static FLOOR: string = 's';
|
||||
public static EFFECT: string = 'e';
|
||||
public static HABBO_CLUB: string = 'h';
|
||||
public static BADGE: string = 'b';
|
||||
public static GAME_TOKEN: string = 'GAME_TOKEN';
|
||||
public static PET: string = 'p';
|
||||
public static ROBOT: string = 'r';
|
||||
}
|
@ -1,34 +1,46 @@
|
||||
import { ICatalogPageData, ICatalogPageParser } from 'nitro-renderer';
|
||||
import { CatalogPageOfferData, ICatalogPageData, ICatalogPageParser } from 'nitro-renderer';
|
||||
import { Reducer } from 'react';
|
||||
import { ICatalogOffers, ICatalogSearchResult, SetOffersToNodes } from '../utils/CatalogUtilities';
|
||||
|
||||
export interface ICatalogState
|
||||
{
|
||||
needsCatalogUpdate: boolean;
|
||||
root: ICatalogPageData;
|
||||
offerRoot: ICatalogOffers;
|
||||
currentTab: ICatalogPageData;
|
||||
pageParser: ICatalogPageParser;
|
||||
activeOffer: CatalogPageOfferData;
|
||||
searchResult: ICatalogSearchResult;
|
||||
}
|
||||
|
||||
export interface ICatalogAction
|
||||
{
|
||||
type: string;
|
||||
payload: {
|
||||
flag?: boolean;
|
||||
root?: ICatalogPageData;
|
||||
offerRoot?: ICatalogOffers;
|
||||
currentTab?: ICatalogPageData;
|
||||
pageParser?: ICatalogPageParser;
|
||||
activeOffer?: CatalogPageOfferData;
|
||||
searchResult?: ICatalogSearchResult;
|
||||
}
|
||||
}
|
||||
|
||||
export class CatalogActions
|
||||
{
|
||||
public static SET_NEEDS_UPDATE: string = 'CA_SET_NEEDS_UPDATE';
|
||||
public static SET_CATALOG_ROOT: string = 'CA_SET_CATALOG_ROOT';
|
||||
public static SET_CATALOG_CURRENT_TAB: string = 'CA_SET_CATALOG_CURRENT_TAB';
|
||||
public static SET_CATALOG_PAGE_PARSER: string = 'CA_SET_CATALOG_PAGE';
|
||||
public static SET_CATALOG_ACTIVE_OFFER: string = 'CA_SET_ACTIVE_OFFER';
|
||||
public static SET_SEARCH_RESULT: string = 'CA_SET_SEARCH_RESULT';
|
||||
}
|
||||
|
||||
export const initialCatalog: ICatalogState = {
|
||||
needsCatalogUpdate: true,
|
||||
root: null,
|
||||
pageParser: null
|
||||
offerRoot: null,
|
||||
currentTab: null,
|
||||
pageParser: null,
|
||||
activeOffer: null,
|
||||
searchResult: null
|
||||
}
|
||||
|
||||
export const CatalogReducer: Reducer<ICatalogState, ICatalogAction> = (state, action) =>
|
||||
@ -37,13 +49,57 @@ export const CatalogReducer: Reducer<ICatalogState, ICatalogAction> = (state, ac
|
||||
{
|
||||
case CatalogActions.SET_CATALOG_ROOT: {
|
||||
const root = (action.payload.root || state.root || null);
|
||||
const currentTab = ((root && (root.children.length > 0) && root.children[0]) || null);
|
||||
|
||||
return { ...state, root };
|
||||
const offerRoot: ICatalogOffers = {};
|
||||
|
||||
SetOffersToNodes(offerRoot, root);
|
||||
|
||||
return { ...state, root, offerRoot, currentTab };
|
||||
}
|
||||
case CatalogActions.SET_CATALOG_CURRENT_TAB: {
|
||||
const currentTab = (action.payload.currentTab || state.currentTab || null);
|
||||
const searchResult = null;
|
||||
|
||||
return { ...state, currentTab, searchResult };
|
||||
}
|
||||
case CatalogActions.SET_CATALOG_PAGE_PARSER: {
|
||||
const pageParser = (action.payload.pageParser || state.pageParser || null);
|
||||
const pageParser = action.payload.pageParser;
|
||||
|
||||
return { ...state, pageParser };
|
||||
let activeOffer = null;
|
||||
|
||||
if(state.activeOffer)
|
||||
{
|
||||
for(const offer of pageParser.offers)
|
||||
{
|
||||
if(offer.offerId !== state.activeOffer.offerId) continue;
|
||||
|
||||
activeOffer = offer;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!activeOffer) activeOffer = ((pageParser && (pageParser.offers.length > 0) && pageParser.offers[0]) || null);
|
||||
|
||||
const searchResult = state.searchResult;
|
||||
|
||||
if(searchResult)
|
||||
{
|
||||
searchResult.furniture = null;
|
||||
}
|
||||
|
||||
return { ...state, pageParser, activeOffer, searchResult };
|
||||
}
|
||||
case CatalogActions.SET_CATALOG_ACTIVE_OFFER: {
|
||||
const activeOffer = (action.payload.activeOffer || state.activeOffer || null);
|
||||
|
||||
return { ...state, activeOffer };
|
||||
}
|
||||
case CatalogActions.SET_SEARCH_RESULT: {
|
||||
const searchResult = (action.payload.searchResult || null);
|
||||
|
||||
return { ...state, searchResult };
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
|
67
src/views/catalog/utils/CatalogUtilities.ts
Normal file
67
src/views/catalog/utils/CatalogUtilities.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { CatalogPageOfferData, ICatalogPageData, IFurnitureData } from 'nitro-renderer';
|
||||
import { GetProductDataForLocalization } from '../../../api/nitro/session/GetProductDataForLocalization';
|
||||
|
||||
export interface ICatalogOffers
|
||||
{
|
||||
[key: string]: ICatalogPageData[];
|
||||
}
|
||||
|
||||
export interface ICatalogSearchResult
|
||||
{
|
||||
page: ICatalogPageData;
|
||||
furniture: IFurnitureData[];
|
||||
}
|
||||
|
||||
export function GetOfferName(offer: CatalogPageOfferData): string
|
||||
{
|
||||
const productData = GetProductDataForLocalization(offer.localizationId);
|
||||
|
||||
if(productData) return productData.name;
|
||||
|
||||
return offer.localizationId;
|
||||
}
|
||||
|
||||
export function GetOfferNodes(offers: ICatalogOffers, offerId: number): ICatalogPageData[]
|
||||
{
|
||||
const pages = offers[offerId.toString()];
|
||||
const allowedPages: ICatalogPageData[] = [];
|
||||
|
||||
if(pages && pages.length)
|
||||
{
|
||||
for(const page of pages)
|
||||
{
|
||||
if(!page.visible) continue;
|
||||
|
||||
allowedPages.push(page);
|
||||
}
|
||||
}
|
||||
|
||||
return allowedPages;
|
||||
}
|
||||
|
||||
export function SetOffersToNodes(offers: ICatalogOffers, pageData: ICatalogPageData): void
|
||||
{
|
||||
if(pageData.offerIds && pageData.offerIds.length)
|
||||
{
|
||||
for(const offerId of pageData.offerIds)
|
||||
{
|
||||
let existing = offers[offerId.toString()];
|
||||
|
||||
if(!existing)
|
||||
{
|
||||
existing = [];
|
||||
|
||||
offers[offerId.toString()] = existing;
|
||||
}
|
||||
|
||||
if(existing.indexOf(pageData) >= 0) continue;
|
||||
|
||||
existing.push(pageData);
|
||||
}
|
||||
}
|
||||
|
||||
if(pageData.children && pageData.children.length)
|
||||
{
|
||||
for(const child of pageData.children) SetOffersToNodes(offers, child);
|
||||
}
|
||||
}
|
@ -1,6 +1,3 @@
|
||||
.nitro-catalog {
|
||||
|
||||
}
|
||||
|
||||
@import './navigation/CatalogNavigationView';
|
||||
@import './page/CatalogPageView';
|
||||
@import './search/CatalogSearchView';
|
||||
|
@ -3,8 +3,8 @@
|
||||
background-color: $grid-bg-color !important;
|
||||
|
||||
.navigation-container {
|
||||
height: 275px;
|
||||
max-height: 275px;
|
||||
height: 270px;
|
||||
max-height: 270px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,23 @@
|
||||
import { FC } from 'react';
|
||||
import { ICatalogPageData } from 'nitro-renderer';
|
||||
import { FC, useEffect } from 'react';
|
||||
import { CatalogNavigationViewProps } from './CatalogNavigationView.types';
|
||||
import { CatalogNavigationSetView } from './set/CatalogNavigationSetView';
|
||||
|
||||
export let ACTIVE_PAGES: ICatalogPageData[] = [];
|
||||
|
||||
export const CatalogNavigationView: FC<CatalogNavigationViewProps> = props =>
|
||||
{
|
||||
const { page = null } = props;
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!page) return;
|
||||
|
||||
ACTIVE_PAGES = [ page ];
|
||||
}, [ page ]);
|
||||
|
||||
return (
|
||||
<div className="nitro-catalog-navigation border border-2 rounded overflow-hidden">
|
||||
<div className="border border-2 rounded overflow-hidden nitro-catalog-navigation">
|
||||
<div className="navigation-container m-1">
|
||||
<CatalogNavigationSetView page={ page } isFirstSet={ true } />
|
||||
</div>
|
||||
|
@ -12,17 +12,19 @@ export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = pro
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isActive) return;
|
||||
if(!isActive || !page) return;
|
||||
|
||||
SendMessageHook(GetCatalogPageComposer(page.pageId, -1, CatalogMode.MODE_NORMAL));
|
||||
}, [ isActive, page ]);
|
||||
|
||||
function select(): void
|
||||
{
|
||||
if(!page) return;
|
||||
|
||||
setActiveChild(prevValue =>
|
||||
{
|
||||
if(prevValue === page) return null;
|
||||
|
||||
|
||||
return page;
|
||||
});
|
||||
}
|
||||
@ -31,7 +33,7 @@ export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = pro
|
||||
<div className="col pe-1 pb-1 catalog-navigation-item-container">
|
||||
<div className="d-flex align-items-center cursor-pointer catalog-navigation-item" onClick={ select }>
|
||||
<CatalogIconView icon={ page.icon } />
|
||||
<div className="flex-grow-1 text-black text-nowrap text-truncate overflow-hidden px-1">{ page.localization }</div>
|
||||
<div className="flex-grow-1 text-black text-truncate px-1">{ page.localization }</div>
|
||||
{ (page.children.length > 0) && <i className={ 'fas fa-caret-' + (isActive ? 'up' : 'down') } /> }
|
||||
</div>
|
||||
{ isActive && page.children && (page.children.length > 0) &&
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { ICatalogPageData } from 'nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { ACTIVE_PAGES } from '../CatalogNavigationView';
|
||||
import { CatalogNavigationItemView } from '../item/CatalogNavigationItemView';
|
||||
import { CatalogNavigationSetViewProps } from './CatalogNavigationSetView.types';
|
||||
|
||||
@ -10,10 +11,22 @@ export const CatalogNavigationSetView: FC<CatalogNavigationSetViewProps> = props
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isFirstSet) return;
|
||||
if(!isFirstSet || !page || (page.pageId === -1)) return;
|
||||
|
||||
if(page && page.children.length) setActiveChild(page.children[0]);
|
||||
}, [ page, isFirstSet ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!activeChild) return;
|
||||
|
||||
const index = (ACTIVE_PAGES.push(activeChild) - 1);
|
||||
|
||||
return () =>
|
||||
{
|
||||
ACTIVE_PAGES.splice(index, (ACTIVE_PAGES.length - index));
|
||||
}
|
||||
}, [ activeChild ]);
|
||||
|
||||
return (
|
||||
<div className="row row-cols-1 g-0 catalog-navigation-set-container w-100">
|
||||
|
@ -1 +1,5 @@
|
||||
@import './layout/CatalogLayout';
|
||||
@import './offer/CatalogPageOfferView';
|
||||
@import './offers/CatalogPageOffersView';
|
||||
@import './purchase/CatalogPurchaseView';
|
||||
@import './search-result/CatalogLayoutSearchResultView';
|
||||
|
@ -2,20 +2,18 @@ import { FC } from 'react';
|
||||
import { useCatalogContext } from '../../context/CatalogContext';
|
||||
import { CatalogPageViewProps } from './CatalogPageView.types';
|
||||
import { GetCatalogLayout } from './layout/GetCatalogLayout';
|
||||
import { CatalogLayoutSearchResultView } from './search-result/CatalogLayoutSearchResultView';
|
||||
|
||||
export const CatalogPageView: FC<CatalogPageViewProps> = props =>
|
||||
{
|
||||
const { roomPreviewer = null } = props;
|
||||
const { catalogState = null } = useCatalogContext();
|
||||
const { pageParser = null } = catalogState;
|
||||
const { pageParser = null, searchResult = null } = catalogState;
|
||||
|
||||
return (
|
||||
<div className="row h-100">
|
||||
<div className="col-7">
|
||||
{ pageParser && GetCatalogLayout(pageParser) }
|
||||
</div>
|
||||
<div className="col">
|
||||
preview area
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
if(searchResult && searchResult.furniture)
|
||||
{
|
||||
return <CatalogLayoutSearchResultView roomPreviewer={ roomPreviewer } furnitureDatas={ searchResult.furniture } />;
|
||||
}
|
||||
|
||||
return ((pageParser && GetCatalogLayout(pageParser, roomPreviewer)) || null);
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { RoomPreviewer } from 'nitro-renderer';
|
||||
|
||||
export interface CatalogPageViewProps
|
||||
{
|
||||
|
||||
roomPreviewer: RoomPreviewer;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { ICatalogPageParser } from 'nitro-renderer';
|
||||
import { ICatalogPageParser, RoomPreviewer } from 'nitro-renderer';
|
||||
|
||||
export interface CatalogLayoutProps
|
||||
{
|
||||
pageParser: ICatalogPageParser;
|
||||
roomPreviewer: RoomPreviewer;
|
||||
pageParser?: ICatalogPageParser;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ICatalogPageParser } from 'nitro-renderer';
|
||||
import { ICatalogPageParser, RoomPreviewer } from 'nitro-renderer';
|
||||
import { CatalogLayoutDefaultView } from './default/CatalogLayoutDefaultView';
|
||||
|
||||
export function GetCatalogLayout(pageParser: ICatalogPageParser): JSX.Element
|
||||
export function GetCatalogLayout(pageParser: ICatalogPageParser, roomPreviewer: RoomPreviewer): JSX.Element
|
||||
{
|
||||
switch(pageParser.catalogType)
|
||||
{
|
||||
@ -37,6 +37,6 @@ export function GetCatalogLayout(pageParser: ICatalogPageParser): JSX.Element
|
||||
return null;
|
||||
case 'default_3x3':
|
||||
default:
|
||||
return <CatalogLayoutDefaultView pageParser={ pageParser } />
|
||||
return <CatalogLayoutDefaultView roomPreviewer={ roomPreviewer } pageParser={ pageParser } />
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,123 @@
|
||||
import { FC } from 'react';
|
||||
import { Vector3d } from 'nitro-renderer';
|
||||
import { FC, useEffect } from 'react';
|
||||
import { GetAvatarRenderManager, GetFurnitureDataForProductOffer, GetSessionDataManager } from '../../../../../../api';
|
||||
import { LimitedEditionCompletePlateView } from '../../../../../limited-edition/complete-plate/LimitedEditionCompletePlateView';
|
||||
import { RoomPreviewerView } from '../../../../../room-previewer/RoomPreviewerView';
|
||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||
import { FurniCategory } from '../../../../enums/FurniCategory';
|
||||
import { ProductTypeEnum } from '../../../../enums/ProductTypeEnum';
|
||||
import { GetOfferName } from '../../../../utils/CatalogUtilities';
|
||||
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
|
||||
import { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView';
|
||||
import { CatalogLayoutDefaultViewProps } from './CatalogLayoutDefaultView.types';
|
||||
|
||||
export const CatalogLayoutDefaultView: FC<CatalogLayoutDefaultViewProps> = props =>
|
||||
{
|
||||
const { pageParser = null } = props;
|
||||
const { roomPreviewer = null, pageParser = null } = props;
|
||||
const { catalogState } = useCatalogContext();
|
||||
const { activeOffer = null } = catalogState;
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!roomPreviewer) return;
|
||||
|
||||
if(!activeOffer)
|
||||
{
|
||||
roomPreviewer && roomPreviewer.reset(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const product = activeOffer.products[0];
|
||||
|
||||
if(!product) return;
|
||||
|
||||
const furniData = GetFurnitureDataForProductOffer(product);
|
||||
|
||||
if(!furniData && product.productType !== ProductTypeEnum.ROBOT) return;
|
||||
|
||||
switch(product.productType)
|
||||
{
|
||||
case ProductTypeEnum.ROBOT: {
|
||||
roomPreviewer.updateObjectRoom('default', 'default', 'default');
|
||||
const figure = GetAvatarRenderManager().getFigureStringWithFigureIds(product.extraParam, 'm', []);
|
||||
|
||||
roomPreviewer.addAvatarIntoRoom(figure, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
case ProductTypeEnum.FLOOR: {
|
||||
roomPreviewer.updateObjectRoom('default', 'default', 'default');
|
||||
|
||||
if(furniData.specialType === FurniCategory.FIGURE_PURCHASABLE_SET)
|
||||
{
|
||||
const setIds: number[] = [];
|
||||
const sets = furniData.customParams.split(',');
|
||||
|
||||
for(const set of sets)
|
||||
{
|
||||
const setId = parseInt(set);
|
||||
|
||||
if(GetAvatarRenderManager().isValidFigureSetForGender(setId, GetSessionDataManager().gender)) setIds.push(setId);
|
||||
}
|
||||
|
||||
const figure = GetAvatarRenderManager().getFigureStringWithFigureIds(GetSessionDataManager().figure, GetSessionDataManager().gender, setIds);
|
||||
|
||||
roomPreviewer.addAvatarIntoRoom(figure, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
roomPreviewer.addFurnitureIntoRoom(product.furniClassId, new Vector3d(90));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ProductTypeEnum.WALL:
|
||||
|
||||
switch(furniData.className)
|
||||
{
|
||||
case 'floor':
|
||||
roomPreviewer.reset(false);
|
||||
roomPreviewer.updateObjectRoom(product.extraParam);
|
||||
break;
|
||||
case 'wallpaper':
|
||||
roomPreviewer.reset(false);
|
||||
roomPreviewer.updateObjectRoom(null, product.extraParam);
|
||||
break;
|
||||
case 'landscape':
|
||||
roomPreviewer.reset(false);
|
||||
roomPreviewer.updateObjectRoom(null, null, product.extraParam);
|
||||
break;
|
||||
default:
|
||||
roomPreviewer.updateObjectRoom('default', 'default', 'default');
|
||||
roomPreviewer.addWallItemIntoRoom(product.furniClassId, new Vector3d(90), product.extraParam);
|
||||
return;
|
||||
}
|
||||
|
||||
// const windowData = Nitro.instance.sessionDataManager.getWallItemDataByName('ads_twi_windw');
|
||||
|
||||
// if(windowData)
|
||||
// {
|
||||
// this._roomPreviewer.addWallItemIntoRoom(windowData.id, new Vector3d(90), windowData.customParams)
|
||||
// }
|
||||
return;
|
||||
}
|
||||
}, [ roomPreviewer, activeOffer ]);
|
||||
|
||||
const product = ((activeOffer && activeOffer.products[0]) || null);
|
||||
|
||||
return (
|
||||
<div className="d-flex">
|
||||
{ pageParser && pageParser.localization.texts[0] }
|
||||
<div className="row h-100">
|
||||
<div className="col-7">
|
||||
<CatalogPageOffersView offers={ pageParser.offers } />
|
||||
</div>
|
||||
{ product &&
|
||||
<div className="position-relative d-flex flex-column col">
|
||||
<RoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
|
||||
{ product.uniqueLimitedItem &&
|
||||
<LimitedEditionCompletePlateView uniqueLimitedItemsLeft={ product.uniqueLimitedItemsLeft } uniqueLimitedSeriesSize={ product.uniqueLimitedSeriesSize } /> }
|
||||
<div className="fs-6 text-black mt-1 overflow-hidden">{ GetOfferName(activeOffer) }</div>
|
||||
<CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } />
|
||||
</div> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
25
src/views/catalog/views/page/offer/CatalogPageOfferView.scss
Normal file
25
src/views/catalog/views/page/offer/CatalogPageOfferView.scss
Normal file
@ -0,0 +1,25 @@
|
||||
.catalog-offer-item-container {
|
||||
height: 48px;
|
||||
max-height: 48px;
|
||||
|
||||
.catalog-offer-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-color: $grid-border-color !important;
|
||||
background-color: $grid-bg-color !important;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
overflow: hidden;
|
||||
|
||||
&.active {
|
||||
border-color: $grid-active-border-color !important;
|
||||
background-color: $grid-active-bg-color !important;
|
||||
}
|
||||
|
||||
.badge {
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
font-size: 8px;
|
||||
}
|
||||
}
|
||||
}
|
67
src/views/catalog/views/page/offer/CatalogPageOfferView.tsx
Normal file
67
src/views/catalog/views/page/offer/CatalogPageOfferView.tsx
Normal file
@ -0,0 +1,67 @@
|
||||
import { FurnitureType, MouseEventType } from 'nitro-renderer';
|
||||
import { FC, MouseEvent, useCallback } from 'react';
|
||||
import { GetRoomEngine, GetSessionDataManager } from '../../../../../api';
|
||||
import { LimitedEditionStyledNumberView } from '../../../../limited-edition/styled-number/LimitedEditionStyledNumberView';
|
||||
import { useCatalogContext } from '../../../context/CatalogContext';
|
||||
import { CatalogActions } from '../../../reducers/CatalogReducer';
|
||||
import { CatalogPageOfferViewProps } from './CatalogPageOfferView.types';
|
||||
|
||||
export const CatalogPageOfferView: FC<CatalogPageOfferViewProps> = props =>
|
||||
{
|
||||
const { isActive = false, offer = null } = props;
|
||||
const { dispatchCatalogState = null } = useCatalogContext();
|
||||
|
||||
const onMouseEvent = useCallback((event: MouseEvent) =>
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case MouseEventType.MOUSE_DOWN:
|
||||
dispatchCatalogState({
|
||||
type: CatalogActions.SET_CATALOG_ACTIVE_OFFER,
|
||||
payload: {
|
||||
activeOffer: offer
|
||||
}
|
||||
});
|
||||
return;
|
||||
case MouseEventType.MOUSE_UP:
|
||||
return;
|
||||
case MouseEventType.ROLL_OUT:
|
||||
return;
|
||||
}
|
||||
}, [ offer, dispatchCatalogState ]);
|
||||
|
||||
const product = ((offer.products && offer.products[0]) || null);
|
||||
|
||||
if(!product) return null;
|
||||
|
||||
function getIconUrl(): string
|
||||
{
|
||||
const productType = product.productType.toUpperCase();
|
||||
|
||||
switch(productType)
|
||||
{
|
||||
case FurnitureType.BADGE:
|
||||
return GetSessionDataManager().getBadgeUrl(product.extraParam);
|
||||
case FurnitureType.FLOOR:
|
||||
return GetRoomEngine().getFurnitureFloorIconUrl(product.furniClassId);
|
||||
case FurnitureType.WALL:
|
||||
return GetRoomEngine().getFurnitureWallIconUrl(product.furniClassId, product.extraParam);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
const imageUrl = `url(${ getIconUrl() })`;
|
||||
|
||||
return (
|
||||
<div className="col pe-1 pb-1 catalog-offer-item-container">
|
||||
<div className={ 'position-relative border border-2 rounded catalog-offer-item cursor-pointer ' + (isActive ? 'active ' : '') + (product.uniqueLimitedItem ? 'unique-item ' : '') + ((product.uniqueLimitedItem && !product.uniqueLimitedItemsLeft) ? 'sold-out ' : '') } style={ { backgroundImage: imageUrl }} onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent }>
|
||||
{ (product.productCount > 1) && <span className="position-absolute badge border bg-danger px-1 rounded-circle">{ product.productCount }</span> }
|
||||
{ product.uniqueLimitedItem &&
|
||||
<div className="position-absolute unique-item-counter">
|
||||
<LimitedEditionStyledNumberView value={ product.uniqueLimitedSeriesSize } />
|
||||
</div> }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import { CatalogPageOfferData } from 'nitro-renderer';
|
||||
|
||||
export interface CatalogPageOfferViewProps
|
||||
{
|
||||
isActive: boolean;
|
||||
offer: CatalogPageOfferData;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
.catalog-offers-container {
|
||||
height: 314px;
|
||||
max-height: 314px;
|
||||
overflow-y: auto;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import { FC } from 'react';
|
||||
import { useCatalogContext } from '../../../context/CatalogContext';
|
||||
import { CatalogPageOfferView } from '../offer/CatalogPageOfferView';
|
||||
import { CatalogPageOffersViewProps } from './CatalogPageOffersView.types';
|
||||
|
||||
export const CatalogPageOffersView: FC<CatalogPageOffersViewProps> = props =>
|
||||
{
|
||||
const { offers = [] } = props;
|
||||
const { catalogState } = useCatalogContext();
|
||||
const { activeOffer = null } = catalogState;
|
||||
|
||||
return (
|
||||
<div className="row row-cols-5 align-content-start g-0 mb-n1 w-100 catalog-offers-container">
|
||||
{ offers && (offers.length > 0) && offers.map((offer, index) =>
|
||||
{
|
||||
return <CatalogPageOfferView key={ index } isActive={ (activeOffer === offer) } offer={ offer } />
|
||||
}) }
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { CatalogPageOfferData } from 'nitro-renderer';
|
||||
|
||||
export interface CatalogPageOffersViewProps
|
||||
{
|
||||
offers: CatalogPageOfferData[];
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
.quantity-input {
|
||||
min-height: 20px;
|
||||
max-height: 20px;
|
||||
width: 29px;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
@import './purchase-button/CatalogPurchaseButtonView';
|
@ -0,0 +1,75 @@
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { CurrencyIcon } from '../../../../../utils/currency-icon/CurrencyIcon';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { CatalogPurchaseViewProps } from './CatalogPurchaseView.types';
|
||||
import { CatalogPurchaseButtonView } from './purchase-button/CatalogPurchaseButtonView';
|
||||
|
||||
export const CatalogPurchaseView: FC<CatalogPurchaseViewProps> = props =>
|
||||
{
|
||||
const { offer = null, pageId = -1 } = props;
|
||||
const [ quantity, setQuantity ] = useState(1);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setQuantity(1);
|
||||
}, [ offer ]);
|
||||
|
||||
function increaseQuantity(): void
|
||||
{
|
||||
let newQuantity = quantity + 1;
|
||||
|
||||
if(newQuantity > 99) newQuantity = 99
|
||||
|
||||
setQuantity(newQuantity);
|
||||
}
|
||||
|
||||
function decreaseQuantity(): void
|
||||
{
|
||||
let newQuantity = quantity - 1;
|
||||
|
||||
if(newQuantity <= 0) newQuantity = 1;
|
||||
|
||||
setQuantity(newQuantity);
|
||||
}
|
||||
|
||||
function updateQuantity(amount: number): void
|
||||
{
|
||||
if(isNaN(amount) || (amount <= 0)) amount = 1;
|
||||
|
||||
if(amount > 99) amount = 99;
|
||||
|
||||
setQuantity(amount);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="d-flex flex-column flex-grow-1 justify-content-end">
|
||||
<div className="d-flex align-items-end">
|
||||
<div className="flex-grow-1 align-items-end">
|
||||
<span className="text-black">{ LocalizeText('catalog.bundlewidget.price') }</span>
|
||||
{ offer.bundlePurchaseAllowed &&
|
||||
<div className="d-flex align-items-center">
|
||||
<i className="fas fa-caret-left cursor-pointer me-1 text-black" onClick={ decreaseQuantity } />
|
||||
<input type="number" className="form-control form-control-sm quantity-input" value={ quantity } onChange={ event => updateQuantity(event.target.valueAsNumber)} />
|
||||
<i className="fas fa-caret-right cursor-pointer ms-1 text-black" onClick={ increaseQuantity } />
|
||||
</div> }
|
||||
</div>
|
||||
<div className="d-flex flex-column">
|
||||
{ (offer.priceCredits > 0) &&
|
||||
<div className="d-flex align-items-center justify-content-end">
|
||||
<span className="text-black ms-1">{ offer.priceCredits * quantity }</span>
|
||||
<CurrencyIcon type={ -1 } />
|
||||
</div> }
|
||||
{ (offer.priceActivityPoints > 0) &&
|
||||
<div className="d-flex align-items-center justify-content-end">
|
||||
<span className="text-black ms-1">{ offer.priceActivityPoints * quantity }</span>
|
||||
<CurrencyIcon type={ offer.priceActivityPointsType } />
|
||||
</div> }
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-column mt-1">
|
||||
<CatalogPurchaseButtonView className="btn-sm w-100" offer={ offer } pageId={ pageId } quantity={ quantity } />
|
||||
{ offer.giftable && <button type="button" className="btn btn-secondary btn-sm w-100 mt-1">{ LocalizeText('catalog.purchase_confirmation.gift') }</button> }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import { CatalogPageOfferData } from 'nitro-renderer';
|
||||
|
||||
export interface CatalogPurchaseViewProps
|
||||
{
|
||||
offer: CatalogPageOfferData;
|
||||
pageId: number;
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
import { CatalogPurchaseComposer } from 'nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { CatalogEvent } from '../../../../../../events';
|
||||
import { useUiEvent } from '../../../../../../hooks/events/ui/ui-event';
|
||||
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
|
||||
import { LoadingSpinnerView } from '../../../../../../layout';
|
||||
import { LocalizeText } from '../../../../../../utils/LocalizeText';
|
||||
import { GetCurrencyAmount } from '../../../../../purse/utils/CurrencyHelper';
|
||||
import { CatalogPurchaseButtonViewProps, CatalogPurchaseState } from './CatalogPurchaseButtonView.types';
|
||||
|
||||
export const CatalogPurchaseButtonView: FC<CatalogPurchaseButtonViewProps> = props =>
|
||||
{
|
||||
const { className = '', offer = null, pageId = -1, extra = null, quantity = 1 } = props;
|
||||
const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE);
|
||||
|
||||
const onCatalogEvent = useCallback((event: CatalogEvent) =>
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case CatalogEvent.PURCHASE_SUCCESS:
|
||||
setPurchaseState(CatalogPurchaseState.NONE);
|
||||
return;
|
||||
case CatalogEvent.SOLD_OUT:
|
||||
setPurchaseState(CatalogPurchaseState.SOLD_OUT);
|
||||
return;
|
||||
}
|
||||
}, []);
|
||||
|
||||
useUiEvent(CatalogEvent.PURCHASE_SUCCESS, onCatalogEvent);
|
||||
useUiEvent(CatalogEvent.SOLD_OUT, onCatalogEvent);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setPurchaseState(CatalogPurchaseState.NONE);
|
||||
}, [ offer, quantity ]);
|
||||
|
||||
const purchase = useCallback(() =>
|
||||
{
|
||||
setPurchaseState(CatalogPurchaseState.PURCHASE);
|
||||
|
||||
SendMessageHook(new CatalogPurchaseComposer(pageId, offer.offerId, extra, quantity));
|
||||
}, [ pageId, offer, extra, quantity ]);
|
||||
|
||||
const product = offer.products[0];
|
||||
|
||||
if(product && product.uniqueLimitedItem && !product.uniqueLimitedItemsLeft)
|
||||
{
|
||||
return <button type="button" className={ 'btn btn-danger ' + className } disabled>{ LocalizeText('catalog.alert.limited_edition_sold_out.title') }</button>;
|
||||
}
|
||||
|
||||
if((offer.priceCredits * quantity) > GetCurrencyAmount(-1))
|
||||
{
|
||||
return <button type="button" className={ 'btn btn-danger ' + className } disabled>{ LocalizeText('catalog.alert.notenough.title') }</button>;
|
||||
}
|
||||
|
||||
if((offer.priceActivityPoints * quantity) > GetCurrencyAmount(offer.priceActivityPointsType))
|
||||
{
|
||||
return <button type="button" className={ 'btn btn-danger ' + className } disabled>{ LocalizeText('catalog.alert.notenough.activitypoints.title.' + offer.priceActivityPointsType) }</button>;
|
||||
}
|
||||
|
||||
switch(purchaseState)
|
||||
{
|
||||
case CatalogPurchaseState.CONFIRM:
|
||||
return <button type="button" className={ 'btn btn-warning ' + className } onClick={ purchase }>{ LocalizeText('catalog.marketplace.confirm_title') }</button>;
|
||||
case CatalogPurchaseState.PURCHASE:
|
||||
return <button type="button" className={ 'btn btn-primary ' + className } disabled><LoadingSpinnerView /></button>;
|
||||
case CatalogPurchaseState.SOLD_OUT:
|
||||
return <button type="button" className={ 'btn btn-danger ' + className } disabled>{ LocalizeText('generic.failed') + ' - ' + LocalizeText('catalog.alert.limited_edition_sold_out.title') }</button>;
|
||||
case CatalogPurchaseState.NONE:
|
||||
default:
|
||||
return <button type="button" className={ 'btn btn-success ' + className } onClick={ event => setPurchaseState(CatalogPurchaseState.CONFIRM) }>{ LocalizeText('buy') }</button>
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import { CatalogPageOfferData } from 'nitro-renderer';
|
||||
|
||||
export interface CatalogPurchaseButtonViewProps
|
||||
{
|
||||
className?: string;
|
||||
offer: CatalogPageOfferData;
|
||||
pageId: number;
|
||||
extra?: string;
|
||||
quantity?: number;
|
||||
}
|
||||
|
||||
export class CatalogPurchaseState
|
||||
{
|
||||
public static NONE = 0;
|
||||
public static CONFIRM = 1;
|
||||
public static PURCHASE = 2;
|
||||
public static NO_CREDITS = 3;
|
||||
public static NO_POINTS = 4;
|
||||
public static SOLD_OUT = 5;
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
@import './offer/CatalogSearchResultOfferView';
|
||||
@import './offers/CatalogSearchResultOffersView';
|
@ -0,0 +1,123 @@
|
||||
import { Vector3d } from 'nitro-renderer';
|
||||
import { FC, useEffect } from 'react';
|
||||
import { GetAvatarRenderManager, GetFurnitureDataForProductOffer, GetSessionDataManager } from '../../../../../api';
|
||||
import { LimitedEditionCompletePlateView } from '../../../../limited-edition/complete-plate/LimitedEditionCompletePlateView';
|
||||
import { RoomPreviewerView } from '../../../../room-previewer/RoomPreviewerView';
|
||||
import { useCatalogContext } from '../../../context/CatalogContext';
|
||||
import { FurniCategory } from '../../../enums/FurniCategory';
|
||||
import { ProductTypeEnum } from '../../../enums/ProductTypeEnum';
|
||||
import { GetOfferName } from '../../../utils/CatalogUtilities';
|
||||
import { CatalogPurchaseView } from '../purchase/CatalogPurchaseView';
|
||||
import { CatalogLayoutSearchResultViewProps } from './CatalogLayoutSearchResultView.types';
|
||||
import { CatalogSearchResultOffersView } from './offers/CatalogSearchResultOffersView';
|
||||
|
||||
export const CatalogLayoutSearchResultView: FC<CatalogLayoutSearchResultViewProps> = props =>
|
||||
{
|
||||
const { roomPreviewer = null, furnitureDatas = null } = props;
|
||||
const { catalogState } = useCatalogContext();
|
||||
const { activeOffer = null } = catalogState;
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!roomPreviewer) return;
|
||||
|
||||
if(!activeOffer)
|
||||
{
|
||||
roomPreviewer && roomPreviewer.reset(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const product = activeOffer.products[0];
|
||||
|
||||
if(!product) return;
|
||||
|
||||
const furniData = GetFurnitureDataForProductOffer(product);
|
||||
|
||||
if(!furniData && product.productType !== ProductTypeEnum.ROBOT) return;
|
||||
|
||||
switch(product.productType)
|
||||
{
|
||||
case ProductTypeEnum.ROBOT: {
|
||||
roomPreviewer.updateObjectRoom('default', 'default', 'default');
|
||||
const figure = GetAvatarRenderManager().getFigureStringWithFigureIds(product.extraParam, 'm', []);
|
||||
|
||||
roomPreviewer.addAvatarIntoRoom(figure, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
case ProductTypeEnum.FLOOR: {
|
||||
roomPreviewer.updateObjectRoom('default', 'default', 'default');
|
||||
|
||||
if(furniData.specialType === FurniCategory.FIGURE_PURCHASABLE_SET)
|
||||
{
|
||||
const setIds: number[] = [];
|
||||
const sets = furniData.customParams.split(',');
|
||||
|
||||
for(const set of sets)
|
||||
{
|
||||
const setId = parseInt(set);
|
||||
|
||||
if(GetAvatarRenderManager().isValidFigureSetForGender(setId, GetSessionDataManager().gender)) setIds.push(setId);
|
||||
}
|
||||
|
||||
const figure = GetAvatarRenderManager().getFigureStringWithFigureIds(GetSessionDataManager().figure, GetSessionDataManager().gender, setIds);
|
||||
|
||||
roomPreviewer.addAvatarIntoRoom(figure, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
roomPreviewer.addFurnitureIntoRoom(product.furniClassId, new Vector3d(90));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ProductTypeEnum.WALL:
|
||||
|
||||
switch(furniData.className)
|
||||
{
|
||||
case 'floor':
|
||||
roomPreviewer.reset(false);
|
||||
roomPreviewer.updateObjectRoom(product.extraParam);
|
||||
break;
|
||||
case 'wallpaper':
|
||||
roomPreviewer.reset(false);
|
||||
roomPreviewer.updateObjectRoom(null, product.extraParam);
|
||||
break;
|
||||
case 'landscape':
|
||||
roomPreviewer.reset(false);
|
||||
roomPreviewer.updateObjectRoom(null, null, product.extraParam);
|
||||
break;
|
||||
default:
|
||||
roomPreviewer.updateObjectRoom('default', 'default', 'default');
|
||||
roomPreviewer.addWallItemIntoRoom(product.furniClassId, new Vector3d(90), product.extraParam);
|
||||
return;
|
||||
}
|
||||
|
||||
// const windowData = Nitro.instance.sessionDataManager.getWallItemDataByName('ads_twi_windw');
|
||||
|
||||
// if(windowData)
|
||||
// {
|
||||
// this._roomPreviewer.addWallItemIntoRoom(windowData.id, new Vector3d(90), windowData.customParams)
|
||||
// }
|
||||
return;
|
||||
}
|
||||
}, [ roomPreviewer, activeOffer ]);
|
||||
|
||||
const product = ((activeOffer && activeOffer.products[0]) || null);
|
||||
|
||||
return (
|
||||
<div className="row h-100">
|
||||
<div className="col-7">
|
||||
<CatalogSearchResultOffersView offers={ furnitureDatas } />
|
||||
</div>
|
||||
{ product &&
|
||||
<div className="position-relative d-flex flex-column col">
|
||||
<RoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
|
||||
{ product.uniqueLimitedItem &&
|
||||
<LimitedEditionCompletePlateView uniqueLimitedItemsLeft={ product.uniqueLimitedItemsLeft } uniqueLimitedSeriesSize={ product.uniqueLimitedSeriesSize } /> }
|
||||
<div className="fs-6 text-black mt-1 overflow-hidden">{ GetOfferName(activeOffer) }</div>
|
||||
<CatalogPurchaseView offer={ activeOffer } pageId={ -1 } />
|
||||
</div> }
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import { IFurnitureData, RoomPreviewer } from 'nitro-renderer';
|
||||
|
||||
export interface CatalogLayoutSearchResultViewProps
|
||||
{
|
||||
roomPreviewer: RoomPreviewer;
|
||||
furnitureDatas: IFurnitureData[];
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
import { CatalogSearchComposer, FurnitureType, MouseEventType } from 'nitro-renderer';
|
||||
import { FC, MouseEvent, useCallback } from 'react';
|
||||
import { GetRoomEngine, GetSessionDataManager } from '../../../../../../api';
|
||||
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
|
||||
import { CatalogSearchResultOfferViewProps } from './CatalogSearchResultOfferView.types';
|
||||
|
||||
export const CatalogSearchResultOfferView: FC<CatalogSearchResultOfferViewProps> = props =>
|
||||
{
|
||||
const { isActive = false, offer = null } = props;
|
||||
|
||||
const onMouseEvent = useCallback((event: MouseEvent) =>
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case MouseEventType.MOUSE_DOWN:
|
||||
SendMessageHook(new CatalogSearchComposer(offer.purchaseOfferId));
|
||||
return;
|
||||
case MouseEventType.MOUSE_UP:
|
||||
return;
|
||||
case MouseEventType.ROLL_OUT:
|
||||
return;
|
||||
}
|
||||
}, [ offer ]);
|
||||
|
||||
function getIconUrl(): string
|
||||
{
|
||||
const productType = offer.type.toUpperCase();
|
||||
|
||||
switch(productType)
|
||||
{
|
||||
case FurnitureType.BADGE:
|
||||
return GetSessionDataManager().getBadgeUrl(offer.customParams);
|
||||
case FurnitureType.FLOOR:
|
||||
return GetRoomEngine().getFurnitureFloorIconUrl(offer.id);
|
||||
case FurnitureType.WALL:
|
||||
return GetRoomEngine().getFurnitureWallIconUrl(offer.id, offer.customParams);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
const imageUrl = `url(${ getIconUrl() })`;
|
||||
|
||||
return (
|
||||
<div className="col pe-1 pb-1 catalog-offer-item-container">
|
||||
<div className={ 'position-relative border border-2 rounded catalog-offer-item cursor-pointer ' + (isActive ? 'active ' : '') } style={ { backgroundImage: imageUrl }} onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent } />
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import { IFurnitureData } from 'nitro-renderer';
|
||||
|
||||
export interface CatalogSearchResultOfferViewProps
|
||||
{
|
||||
isActive: boolean;
|
||||
offer: IFurnitureData;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
.catalog-offers-container {
|
||||
height: 314px;
|
||||
max-height: 314px;
|
||||
overflow-y: auto;
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { FC } from 'react';
|
||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||
import { CatalogSearchResultOfferView } from '../offer/CatalogSearchResultOfferView';
|
||||
import { CatalogSearchResultOffersViewProps } from './CatalogSearchResultOffersView.types';
|
||||
|
||||
export const CatalogSearchResultOffersView: FC<CatalogSearchResultOffersViewProps> = props =>
|
||||
{
|
||||
const { offers = [] } = props;
|
||||
const { catalogState } = useCatalogContext();
|
||||
const { activeOffer = null } = catalogState;
|
||||
|
||||
return (
|
||||
<div className="row row-cols-5 align-content-start g-0 mb-n1 w-100 catalog-offers-container">
|
||||
{ offers && (offers.length > 0) && offers.map((offer, index) =>
|
||||
{
|
||||
const isActive = (activeOffer && (activeOffer.products[0].furniClassId === offer.id));
|
||||
|
||||
return <CatalogSearchResultOfferView key={ index } isActive={ isActive } offer={ offer } />
|
||||
}) }
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { IFurnitureData } from 'nitro-renderer';
|
||||
|
||||
export interface CatalogSearchResultOffersViewProps
|
||||
{
|
||||
offers: IFurnitureData[];
|
||||
}
|
125
src/views/catalog/views/search/CatalogSearchView.tsx
Normal file
125
src/views/catalog/views/search/CatalogSearchView.tsx
Normal file
@ -0,0 +1,125 @@
|
||||
import { ICatalogPageData, IFurnitureData } from 'nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { GetSessionDataManager } from '../../../../api';
|
||||
import { LocalizeText } from '../../../../utils/LocalizeText';
|
||||
import { useCatalogContext } from '../../context/CatalogContext';
|
||||
import { CatalogActions } from '../../reducers/CatalogReducer';
|
||||
import { GetOfferNodes } from '../../utils/CatalogUtilities';
|
||||
import { CatalogSearchViewProps } from './CatalogSearchView.types';
|
||||
|
||||
export const CatalogSearchView: FC<CatalogSearchViewProps> = props =>
|
||||
{
|
||||
const [ searchValue, setSearchValue ] = useState('');
|
||||
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
||||
const { offerRoot = null, searchResult = null } = catalogState;
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setSearchValue(prevValue =>
|
||||
{
|
||||
if(!searchResult && prevValue && prevValue.length) return '';
|
||||
|
||||
return prevValue;
|
||||
});
|
||||
}, [ searchResult ]);
|
||||
|
||||
const processSearch = useCallback((search: string) =>
|
||||
{
|
||||
if(!search || !search.length || !offerRoot)
|
||||
{
|
||||
dispatchCatalogState({
|
||||
type: CatalogActions.SET_SEARCH_RESULT,
|
||||
payload: {
|
||||
searchResult: null
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
search = search.toLocaleLowerCase();
|
||||
|
||||
const furnitureData = GetSessionDataManager().getAllFurnitureData({
|
||||
loadFurnitureData: null
|
||||
});
|
||||
|
||||
if(!furnitureData) return;
|
||||
|
||||
const foundPages: ICatalogPageData[] = [];
|
||||
const foundFurniture: IFurnitureData[] = [];
|
||||
|
||||
for(const furniture of furnitureData)
|
||||
{
|
||||
if((furniture.purchaseOfferId === -1) && (furniture.rentOfferId === -1)) continue;
|
||||
|
||||
const pages = [
|
||||
...GetOfferNodes(offerRoot, furniture.purchaseOfferId),
|
||||
...GetOfferNodes(offerRoot, furniture.rentOfferId)
|
||||
];
|
||||
|
||||
if(!pages.length) continue;
|
||||
|
||||
const searchValue = [ furniture.className, furniture.name ].join(' ').toLocaleLowerCase();
|
||||
|
||||
if(searchValue.indexOf(search) === -1) continue;
|
||||
|
||||
foundPages.push(...pages);
|
||||
foundFurniture.push(furniture);
|
||||
}
|
||||
|
||||
const uniquePages = foundPages.filter((value, index, self) =>
|
||||
{
|
||||
return (self.indexOf(value) === index);
|
||||
});
|
||||
|
||||
const catalogPage: ICatalogPageData = {
|
||||
visible: true,
|
||||
icon: 0,
|
||||
pageId: -1,
|
||||
pageName: LocalizeText('generic.search'),
|
||||
localization: LocalizeText('generic.search'),
|
||||
children: [ ...uniquePages ],
|
||||
offerIds: []
|
||||
};
|
||||
|
||||
dispatchCatalogState({
|
||||
type: CatalogActions.SET_SEARCH_RESULT,
|
||||
payload: {
|
||||
searchResult: {
|
||||
page: catalogPage,
|
||||
furniture: foundFurniture
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [ offerRoot, dispatchCatalogState ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!searchValue)
|
||||
{
|
||||
processSearch(searchValue);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const timeout = setTimeout(() => processSearch(searchValue), 300);
|
||||
|
||||
return () =>
|
||||
{
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}, [ searchValue, processSearch ]);
|
||||
|
||||
return (
|
||||
<div className="d-flex mb-1">
|
||||
<div className="d-flex flex-grow-1 me-1">
|
||||
<input type="text" className="form-control form-control-sm" placeholder="search" value={ searchValue } onChange={ event => setSearchValue(event.target.value) } />
|
||||
</div>
|
||||
<div className="d-flex">
|
||||
<button type="button" className="btn btn-primary btn-sm">
|
||||
<i className="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
export interface CatalogSearchViewProps
|
||||
{
|
||||
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
.nitro-inventory {
|
||||
width: 475px;
|
||||
// height: 300px;
|
||||
// max-height: 300px;
|
||||
|
||||
.content-area {
|
||||
height: 243.5px;
|
||||
|
@ -1,3 +1,9 @@
|
||||
.limited-edition-info-container {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
@import './item/InventoryFurnitureItemView';
|
||||
@import './results/InventoryFurnitureResultsView';
|
||||
@import './search/InventoryFurnitureSearchView';
|
||||
|
@ -3,6 +3,7 @@ import { FC, useEffect, useState } from 'react';
|
||||
import { GetRoomEngine } from '../../../../api';
|
||||
import { SendMessageHook } from '../../../../hooks/messages/message-event';
|
||||
import { LocalizeText } from '../../../../utils/LocalizeText';
|
||||
import { LimitedEditionCompactPlateView } from '../../../limited-edition/compact-plate/LimitedEditionCompactPlateView';
|
||||
import { RoomPreviewerView } from '../../../room-previewer/RoomPreviewerView';
|
||||
import { useInventoryContext } from '../../context/InventoryContext';
|
||||
import { InventoryFurnitureActions } from '../../reducers/InventoryFurnitureReducer';
|
||||
@ -120,8 +121,12 @@ export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props =>
|
||||
<InventoryFurnitureSearchView groupItems={ groupItems } setGroupItems={ setFilteredGroupItems } />
|
||||
<InventoryFurnitureResultsView groupItems={ filteredGroupItems } />
|
||||
</div>
|
||||
<div className="d-flex flex-column col justify-space-between">
|
||||
<div className="position-relative d-flex flex-column col justify-space-between">
|
||||
<RoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
|
||||
{ groupItem && groupItem.stuffData.isUnique &&
|
||||
<div className="position-absolute limited-edition-info-container">
|
||||
<LimitedEditionCompactPlateView uniqueNumber={ groupItem.stuffData.uniqueNumber } uniqueSeries={ groupItem.stuffData.uniqueSeries } />
|
||||
</div> }
|
||||
{ groupItem && <div className="d-flex flex-column flex-grow-1">
|
||||
<p className="flex-grow-1 fs-6 text-black my-2">{ groupItem.name }</p>
|
||||
{ !!roomSession && <button type="button" className="btn btn-success" onClick={ event => attemptItemPlacement(groupItem) }>{ LocalizeText('inventory.furni.placetoroom') }</button> }
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { MouseEventType } from 'nitro-renderer';
|
||||
import { FC, MouseEvent, useCallback, useState } from 'react';
|
||||
import { LimitedEditionStyledNumberView } from '../../../../limited-edition/styled-number/LimitedEditionStyledNumberView';
|
||||
import { useInventoryContext } from '../../../context/InventoryContext';
|
||||
import { InventoryFurnitureActions } from '../../../reducers/InventoryFurnitureReducer';
|
||||
import { attemptItemPlacement } from '../../../utils/FurnitureUtilities';
|
||||
@ -39,8 +40,12 @@ export const InventoryFurnitureItemView: FC<InventoryFurnitureItemViewProps> = p
|
||||
|
||||
return (
|
||||
<div className="col pe-1 pb-1 inventory-furniture-item-container">
|
||||
<div className={ 'position-relative border border-2 rounded inventory-furniture-item cursor-pointer ' + (isActive && 'active') } style={ { backgroundImage: imageUrl }} onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent }>
|
||||
<div className={ 'position-relative border border-2 rounded inventory-furniture-item cursor-pointer ' + (isActive ? 'active ' : '') + (groupItem.stuffData.isUnique ? 'unique-item ' : '') } style={ { backgroundImage: imageUrl }} onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent }>
|
||||
<span className="position-absolute badge border bg-danger px-1 rounded-circle">{ groupItem.getUnlockedCount() }</span>
|
||||
{ groupItem.stuffData.isUnique &&
|
||||
<div className="position-absolute unique-item-counter">
|
||||
<LimitedEditionStyledNumberView value={ groupItem.stuffData.uniqueNumber } />
|
||||
</div> }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
46
src/views/limited-edition/LimitedEdition.scss
Normal file
46
src/views/limited-edition/LimitedEdition.scss
Normal file
@ -0,0 +1,46 @@
|
||||
.unique-item {
|
||||
background: url("../../assets/images/unique/grid-bg.png") center no-repeat, rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
content: ' ';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url("../../assets/images/unique/grid-bg-glass.png") center no-repeat;
|
||||
}
|
||||
|
||||
&.sold-out:before {
|
||||
background: url("../../assets/images/unique/grid-bg-sold-out.png") center no-repeat, url("../../assets/images/unique/grid-bg-glass.png") center no-repeat;
|
||||
}
|
||||
|
||||
.unique-item-counter {
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
bottom: 1px;
|
||||
width: 100%;
|
||||
height: 9px;
|
||||
background: url("../../assets/images/unique/grid-count-bg.png") center no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
.unique-sold-out-blocker {
|
||||
width: 364px;
|
||||
height: 30px;
|
||||
background: url("../../assets/images/unique/catalog-info-sold-out.png");
|
||||
|
||||
div {
|
||||
float: right;
|
||||
width: 140px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
margin-top: 5px;
|
||||
margin-right: 17px;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
@import './compact-plate/LimitedEditionCompactPlateView';
|
||||
@import './complete-plate/LimitedEditionCompletePlateView';
|
||||
@import './styled-number/LimitedEditionStyledNumberView';
|
@ -0,0 +1,17 @@
|
||||
.unique-compact-plate {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
right: 16px;
|
||||
width: 34px;
|
||||
height: 37px;
|
||||
background: url("../../../assets/images/unique/inventory-info-amount-bg.png");
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 9.5px;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
import { FC } from 'react';
|
||||
import { LimitedEditionStyledNumberView } from '../styled-number/LimitedEditionStyledNumberView';
|
||||
import { LimitedEditionCompactPlateViewProps } from './LimitedEditionCompactPlateView.types';
|
||||
|
||||
export const LimitedEditionCompactPlateView: FC<LimitedEditionCompactPlateViewProps> = props =>
|
||||
{
|
||||
const { uniqueNumber = 0, uniqueSeries = 0 } = props;
|
||||
|
||||
return (
|
||||
<div className="unique-compact-plate" style={ { zIndex: 1 } }>
|
||||
<div>
|
||||
<LimitedEditionStyledNumberView value={ uniqueNumber } />
|
||||
</div>
|
||||
<div>
|
||||
<LimitedEditionStyledNumberView value={ uniqueSeries } />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export interface LimitedEditionCompactPlateViewProps
|
||||
{
|
||||
uniqueNumber: number;
|
||||
uniqueSeries: number;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
.unique-complete-plate {
|
||||
top: 145px;
|
||||
left: 10px;
|
||||
width: 170px;
|
||||
height: 29px;
|
||||
background: url("../../../assets/images/unique/catalog-info-amount-bg.png");
|
||||
|
||||
div {
|
||||
position: relative;
|
||||
padding-left: 45px;
|
||||
padding-right: 20px;
|
||||
font-size: 10px;
|
||||
color: #000;
|
||||
|
||||
div {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import { FC } from 'react';
|
||||
import { LocalizeText } from '../../../utils/LocalizeText';
|
||||
import { LimitedEditionStyledNumberView } from '../styled-number/LimitedEditionStyledNumberView';
|
||||
import { LimitedEditionCompletePlateViewProps } from './LimitedEditionCompletePlateView.types';
|
||||
|
||||
export const LimitedEditionCompletePlateView: FC<LimitedEditionCompletePlateViewProps> = props =>
|
||||
{
|
||||
const { uniqueLimitedItemsLeft = 0, uniqueLimitedSeriesSize = 0 } = props;
|
||||
|
||||
return (
|
||||
<div className="unique-complete-plate mt-1 mx-auto" style={ { zIndex: 1 } }>
|
||||
<div>
|
||||
<div>
|
||||
<LimitedEditionStyledNumberView value={ uniqueLimitedItemsLeft } />
|
||||
</div>
|
||||
{ LocalizeText('unique.items.left') }
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<LimitedEditionStyledNumberView value={ uniqueLimitedSeriesSize } />
|
||||
</div>
|
||||
{ LocalizeText('unique.items.number.sold') }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export interface LimitedEditionCompletePlateViewProps
|
||||
{
|
||||
uniqueLimitedItemsLeft: number;
|
||||
uniqueLimitedSeriesSize: number;
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
.limited-edition-number {
|
||||
display: inline-block;
|
||||
outline: 0;
|
||||
height: 5px;
|
||||
margin-right: 1px;
|
||||
background-image: url('../../../assets/images/unique/numbers.png');
|
||||
background-repeat: no-repeat;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
&.n-0 {
|
||||
width: 4px;
|
||||
background-position: -1px 0px;
|
||||
}
|
||||
|
||||
&.n-1 {
|
||||
width: 2px;
|
||||
background-position: -6px 0px;
|
||||
}
|
||||
|
||||
&.n-2 {
|
||||
width: 4px;
|
||||
background-position: -9px 0px;
|
||||
}
|
||||
|
||||
&.n-3 {
|
||||
width: 4px;
|
||||
background-position: -14px 0px;
|
||||
}
|
||||
|
||||
&.n-4 {
|
||||
width: 4px;
|
||||
background-position: -19px 0px;
|
||||
}
|
||||
|
||||
&.n-5 {
|
||||
width: 4px;
|
||||
background-position: -24px 0px;
|
||||
}
|
||||
|
||||
&.n-6 {
|
||||
width: 4px;
|
||||
background-position: -29px 0px;
|
||||
}
|
||||
|
||||
&.n-7 {
|
||||
width: 4px;
|
||||
background-position: -34px 0px;
|
||||
}
|
||||
|
||||
&.n-8 {
|
||||
width: 4px;
|
||||
background-position: -39px 0px;
|
||||
}
|
||||
|
||||
&.n-9 {
|
||||
width: 4px;
|
||||
background-position: -44px 0px;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import { FC } from 'react';
|
||||
import { LimitedEditionStyledNumberViewProps } from './LimitedEditionStyledNumberView.types';
|
||||
|
||||
export const LimitedEditionStyledNumberView: FC<LimitedEditionStyledNumberViewProps> = props =>
|
||||
{
|
||||
const { value = 0 } = props;
|
||||
|
||||
const numbers = value.toString().split('');
|
||||
|
||||
return (
|
||||
<>
|
||||
{ numbers.map((number, index) =>
|
||||
{
|
||||
return <i key={ index } className={ 'limited-edition-number n-' + number } />;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
export interface LimitedEditionStyledNumberViewProps
|
||||
{
|
||||
value: number;
|
||||
}
|
@ -7,6 +7,7 @@ import { CurrencyView } from './currency/CurrencyView';
|
||||
import { PurseMessageHandler } from './PurseMessageHandler';
|
||||
import { PurseViewProps } from './PurseView.types';
|
||||
import { initialPurse, PurseReducer } from './reducers/PurseReducer';
|
||||
import { SetLastCurrencies } from './utils/CurrencyHelper';
|
||||
|
||||
export const PurseView: FC<PurseViewProps> = props =>
|
||||
{
|
||||
@ -23,6 +24,8 @@ export const PurseView: FC<PurseViewProps> = props =>
|
||||
SendMessageHook(new UserCurrencyComposer());
|
||||
}, []);
|
||||
|
||||
SetLastCurrencies(currencies);
|
||||
|
||||
return (
|
||||
<PurseContextProvider value={ { purseState, dispatchPurseState }}>
|
||||
<PurseMessageHandler />
|
||||
|
20
src/views/purse/utils/CurrencyHelper.ts
Normal file
20
src/views/purse/utils/CurrencyHelper.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Currency } from './Currency';
|
||||
|
||||
let lastCurrencies: Currency[] = [];
|
||||
|
||||
export function SetLastCurrencies(currencies: Currency[]): void
|
||||
{
|
||||
lastCurrencies = currencies;
|
||||
}
|
||||
|
||||
export function GetCurrencyAmount(type: number): number
|
||||
{
|
||||
for(const currency of lastCurrencies)
|
||||
{
|
||||
if(currency.type !== type) continue;
|
||||
|
||||
return currency.amount;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -7,6 +7,12 @@
|
||||
border-radius: $border-radius;
|
||||
height: 100%;
|
||||
|
||||
&.border-0 {
|
||||
&::after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
|
@ -26,6 +26,8 @@ export function RoomView(props: RoomViewProps): JSX.Element
|
||||
return;
|
||||
}
|
||||
|
||||
Nitro.instance.renderer.resize(window.innerWidth, window.innerHeight);
|
||||
|
||||
const canvasId = 1;
|
||||
|
||||
const displayObject = GetRoomEngine().getRoomInstanceDisplay(roomSession.roomId, canvasId, Nitro.instance.width, Nitro.instance.height, RoomGeometry.SCALE_ZOOMED_IN);
|
||||
|
Loading…
Reference in New Issue
Block a user