mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-27 08:00:51 +01:00
Merge branch 'feature/hotelview' into dev
This commit is contained in:
commit
d0d9519fd1
@ -25,8 +25,20 @@
|
|||||||
],
|
],
|
||||||
"hotelview": {
|
"hotelview": {
|
||||||
"widgets": {
|
"widgets": {
|
||||||
"types": "news,",
|
"slot.1.widget": "promoarticle",
|
||||||
"slot1": "news"
|
"slot.1.conf": "",
|
||||||
|
"slot.2.widget": "widgetcontainer",
|
||||||
|
"slot.2.conf": "image:${image.library.url}web_promo_small/spromo_Canal_Bundle.png,texts:2021NitroPromo,btnLink:https://google.com",
|
||||||
|
"slot.3.widget": "promoarticle",
|
||||||
|
"slot.3.conf": "",
|
||||||
|
"slot.4.widget": "",
|
||||||
|
"slot.4.conf": "",
|
||||||
|
"slot.5.widget": "",
|
||||||
|
"slot.5.conf": "",
|
||||||
|
"slot.6.widget": "achievementcompetition_hall_of_fame",
|
||||||
|
"slot.6.conf": "",
|
||||||
|
"slot.7.widget": "",
|
||||||
|
"slot.7.conf": ""
|
||||||
},
|
},
|
||||||
"images": {
|
"images": {
|
||||||
"background": "${asset.url}/images/reception/stretch_blue.png",
|
"background": "${asset.url}/images/reception/stretch_blue.png",
|
||||||
|
@ -80,6 +80,7 @@ $cello-light: #21516e !default;
|
|||||||
$cello-dark: #1e465e !default;
|
$cello-dark: #1e465e !default;
|
||||||
$pale-sky: #677181 !default;
|
$pale-sky: #677181 !default;
|
||||||
$oslo-gray: #8F9297 !default;
|
$oslo-gray: #8F9297 !default;
|
||||||
|
$gainsboro: #d9d9d9 !default;
|
||||||
$ghost: #c8cad0 !default;
|
$ghost: #c8cad0 !default;
|
||||||
$gray-chateau: #a3a7b1 !default;
|
$gray-chateau: #a3a7b1 !default;
|
||||||
$gable-green: #1C323F !default;
|
$gable-green: #1C323F !default;
|
||||||
@ -125,7 +126,8 @@ $theme-colors: (
|
|||||||
"white": $white,
|
"white": $white,
|
||||||
"black": $black,
|
"black": $black,
|
||||||
"muted": $muted,
|
"muted": $muted,
|
||||||
"purple": $purple
|
"purple": $purple,
|
||||||
|
"gainsboro": $gainsboro
|
||||||
) !default;
|
) !default;
|
||||||
// scss-docs-end theme-colors-map
|
// scss-docs-end theme-colors-map
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - 55px);
|
height: calc(100% - 55px);
|
||||||
background: rgba($black, 1);
|
background: rgba($black, 1);
|
||||||
|
color:#000;
|
||||||
|
|
||||||
.avatar-image {
|
.avatar-image {
|
||||||
bottom: 12px;
|
bottom: 12px;
|
||||||
@ -13,6 +14,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.background {
|
.background {
|
||||||
|
top:0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-position: left;
|
background-position: left;
|
||||||
@ -73,4 +75,25 @@
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right top;
|
background-position: right top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.landing-widgets {
|
||||||
|
z-index: 9;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-slot {
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
background: $black;
|
||||||
|
box-shadow: 0 1px rgba($white,.5);
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@import './views/widgets/promo-article/PromoArticleWidgetView.scss';
|
||||||
|
@import './views/widgets/bonus-rare/BonusRareWidgetView.scss';
|
||||||
|
@import './views/widgets/hall-of-fame/HallOfFameWidgetView.scss';
|
||||||
|
@import './views/widgets/widgetcontainer/WidgetContainerView.scss'
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
import { RoomSessionEvent } from '@nitrots/nitro-renderer';
|
import { RoomSessionEvent } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useState } from 'react';
|
import { FC, useCallback, useState } from 'react';
|
||||||
import { GetConfiguration, GetNitroInstance } from '../../api';
|
import { GetConfiguration, GetConfigurationManager } from '../../api';
|
||||||
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
|
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
|
||||||
import { HotelViewProps } from './HotelView.types';
|
import { HotelViewProps } from './HotelView.types';
|
||||||
|
import { WidgetSlotView } from './views/widget-slot/WidgetSlotView';
|
||||||
|
|
||||||
export const HotelView: FC<HotelViewProps> = props =>
|
export const HotelView: FC<HotelViewProps> = props =>
|
||||||
{
|
{
|
||||||
const [ isVisible, setIsVisible ] = useState(true);
|
const [isVisible, setIsVisible] = useState(true);
|
||||||
|
const widgetSlotCount = 7;
|
||||||
|
|
||||||
const onRoomSessionEvent = useCallback((event: RoomSessionEvent) =>
|
const onRoomSessionEvent = useCallback((event: RoomSessionEvent) =>
|
||||||
{
|
{
|
||||||
switch(event.type)
|
switch (event.type)
|
||||||
{
|
{
|
||||||
case RoomSessionEvent.CREATED:
|
case RoomSessionEvent.CREATED:
|
||||||
setIsVisible(false);
|
setIsVisible(false);
|
||||||
@ -24,24 +26,75 @@ export const HotelView: FC<HotelViewProps> = props =>
|
|||||||
useRoomSessionManagerEvent(RoomSessionEvent.CREATED, onRoomSessionEvent);
|
useRoomSessionManagerEvent(RoomSessionEvent.CREATED, onRoomSessionEvent);
|
||||||
useRoomSessionManagerEvent(RoomSessionEvent.ENDED, onRoomSessionEvent);
|
useRoomSessionManagerEvent(RoomSessionEvent.ENDED, onRoomSessionEvent);
|
||||||
|
|
||||||
if(!isVisible) return null;
|
if (!isVisible) return null;
|
||||||
|
|
||||||
const backgroundColor = GetConfiguration('hotelview')['images']['background.colour'];
|
const backgroundColor = GetConfiguration('hotelview')['images']['background.colour'];
|
||||||
const background = GetNitroInstance().core.configuration.interpolate(GetConfiguration('hotelview')['images']['background']);
|
const background = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['background']);
|
||||||
const sun = GetNitroInstance().core.configuration.interpolate(GetConfiguration('hotelview')['images']['sun']);
|
const sun = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['sun']);
|
||||||
const drape = GetNitroInstance().core.configuration.interpolate(GetConfiguration('hotelview')['images']['drape']);
|
const drape = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['drape']);
|
||||||
const left = GetNitroInstance().core.configuration.interpolate(GetConfiguration('hotelview')['images']['left']);
|
const left = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['left']);
|
||||||
const rightRepeat = GetNitroInstance().core.configuration.interpolate(GetConfiguration('hotelview')['images']['right.repeat']);
|
const rightRepeat = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['right.repeat']);
|
||||||
const right = GetNitroInstance().core.configuration.interpolate(GetConfiguration('hotelview')['images']['right']);
|
const right = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['right']);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="nitro-hotel-view" style={ (backgroundColor && backgroundColor) ? { background: backgroundColor } : {} }>
|
<div className="nitro-hotel-view" style={(backgroundColor && backgroundColor) ? { background: backgroundColor } : {}}>
|
||||||
<div className="background position-absolute" style={ (background && background.length) ? { backgroundImage: `url(${ background })` } : {} } />
|
<div className="container h-100 py-3 overflow-hidden landing-widgets">
|
||||||
<div className="sun position-absolute" style={ (sun && sun.length) ? { backgroundImage: `url(${ sun })` } : {} } />
|
<div className="row h-100">
|
||||||
<div className="drape position-absolute" style={ (drape && drape.length) ? { backgroundImage: `url(${ drape })` } : {} } />
|
<div className="col-9 h-100 d-flex flex-column">
|
||||||
<div className="left position-absolute" style={ (left && left.length) ? { backgroundImage: `url(${ left })` } : {} } />
|
<WidgetSlotView
|
||||||
<div className="right-repeat position-absolute" style={ (rightRepeat && rightRepeat.length) ? { backgroundImage: `url(${ rightRepeat })` } : {} } />
|
widgetSlot={ 1 }
|
||||||
<div className="right position-absolute" style={ (right && right.length) ? { backgroundImage: `url(${ right })` } : {} } />
|
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 1 + '.widget']}
|
||||||
|
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 1 + '.conf']}
|
||||||
|
className="col-6"
|
||||||
|
/>
|
||||||
|
<div className="col-12 row mx-0">
|
||||||
|
<WidgetSlotView
|
||||||
|
widgetSlot={ 2 }
|
||||||
|
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 2 + '.widget']}
|
||||||
|
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 2 + '.conf']}
|
||||||
|
className="col-7"
|
||||||
|
/>
|
||||||
|
<WidgetSlotView
|
||||||
|
widgetSlot={ 3 }
|
||||||
|
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 3 + '.widget']}
|
||||||
|
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 3 + '.conf']}
|
||||||
|
className="col-5"
|
||||||
|
/>
|
||||||
|
<WidgetSlotView
|
||||||
|
widgetSlot={ 4 }
|
||||||
|
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 4 + '.widget']}
|
||||||
|
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 4 + '.conf']}
|
||||||
|
className="col-7"
|
||||||
|
/>
|
||||||
|
<WidgetSlotView
|
||||||
|
widgetSlot={ 5 }
|
||||||
|
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 5 + '.widget']}
|
||||||
|
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 5 + '.conf']}
|
||||||
|
className="col-5"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<WidgetSlotView
|
||||||
|
widgetSlot={ 6 }
|
||||||
|
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 6 + '.widget']}
|
||||||
|
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 6 + '.conf']}
|
||||||
|
className="mt-auto"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-3 h-100">
|
||||||
|
<WidgetSlotView
|
||||||
|
widgetSlot={ 7 }
|
||||||
|
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 7 + '.widget']}
|
||||||
|
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 7 +'.conf']}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="background position-absolute" style={(background && background.length) ? { backgroundImage: `url(${background})` } : {}} />
|
||||||
|
<div className="sun position-absolute" style={(sun && sun.length) ? { backgroundImage: `url(${sun})` } : {}} />
|
||||||
|
<div className="drape position-absolute" style={(drape && drape.length) ? { backgroundImage: `url(${drape})` } : {}} />
|
||||||
|
<div className="left position-absolute" style={(left && left.length) ? { backgroundImage: `url(${left})` } : {}} />
|
||||||
|
<div className="right-repeat position-absolute" style={(rightRepeat && rightRepeat.length) ? { backgroundImage: `url(${rightRepeat})` } : {}} />
|
||||||
|
<div className="right position-absolute" style={(right && right.length) ? { backgroundImage: `url(${right})` } : {}} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
14
src/views/hotel-view/views/widget-slot/WidgetSlotView.tsx
Normal file
14
src/views/hotel-view/views/widget-slot/WidgetSlotView.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { GetWidgetLayout } from '../widgets/GetWidgetLayout';
|
||||||
|
import { WidgetSlotViewProps } from './WidgetSlotView.types';
|
||||||
|
|
||||||
|
export const WidgetSlotView: FC<WidgetSlotViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { widgetType = null, widgetSlot = 0, widgetConf = null, className= '', ...rest } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`widget-slot slot-${widgetSlot} ${(className || '')}`} { ...rest }>
|
||||||
|
<GetWidgetLayout widgetType={widgetType} slot={widgetSlot} widgetConf={widgetConf} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
import { DetailsHTMLAttributes } from 'react';
|
||||||
|
|
||||||
|
export interface WidgetSlotViewProps extends DetailsHTMLAttributes<HTMLDivElement>
|
||||||
|
{
|
||||||
|
widgetType: string;
|
||||||
|
widgetSlot: number;
|
||||||
|
widgetConf: string;
|
||||||
|
}
|
23
src/views/hotel-view/views/widgets/GetWidgetLayout.tsx
Normal file
23
src/views/hotel-view/views/widgets/GetWidgetLayout.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { BonusRareWidgetView } from './bonus-rare/BonusRareWidgetView';
|
||||||
|
import { GetWidgetLayoutProps } from './GetWidgetLayout.types';
|
||||||
|
import { HallOfFameWidgetView } from './hall-of-fame/HallOfFameWidgetView';
|
||||||
|
import { PromoArticleWidgetView } from './promo-article/PromoArticleWidgetView';
|
||||||
|
import { WidgetContainerView } from './widgetcontainer/WIdgetContainerView';
|
||||||
|
|
||||||
|
export const GetWidgetLayout: FC<GetWidgetLayoutProps> = props =>
|
||||||
|
{
|
||||||
|
switch (props.widgetType)
|
||||||
|
{
|
||||||
|
case "promoarticle":
|
||||||
|
return <PromoArticleWidgetView />;
|
||||||
|
case "achievementcompetition_hall_of_fame":
|
||||||
|
return <HallOfFameWidgetView slot={props.slot} conf={props.widgetConf} />;
|
||||||
|
case "bonusrare":
|
||||||
|
return <BonusRareWidgetView />;
|
||||||
|
case "widgetcontainer":
|
||||||
|
return <WidgetContainerView conf={props.widgetConf} />
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
export interface GetWidgetLayoutProps
|
||||||
|
{
|
||||||
|
widgetType: string;
|
||||||
|
slot: number;
|
||||||
|
widgetConf: string;
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
.bonus-rare {
|
||||||
|
height: 100px;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.bonus-bar-container {
|
||||||
|
height: 30px;
|
||||||
|
width: 300px;
|
||||||
|
border: 2px ridge #e2e2e2;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
import { BonusRareInfoMessageEvent, GetBonusRareInfoMessageComposer } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { CreateMessageHook, SendMessageHook } from '../../../../../hooks/messages/message-event';
|
||||||
|
import { BonusRareWidgetViewProps } from './BonusRareWidgetView.types';
|
||||||
|
|
||||||
|
export const BonusRareWidgetView: FC<BonusRareWidgetViewProps> = props =>
|
||||||
|
{
|
||||||
|
const [ productType, setProductType ] = useState<string>(null);
|
||||||
|
const [ productClassId, setProductClassId ] = useState<number>(null);
|
||||||
|
const [ totalCoinsForBonus, setTotalCoinsForBonus ] = useState<number>(null);
|
||||||
|
const [ coinsStillRequiredToBuy, setCoinsStillRequiredToBuy ] = useState<number>(null);
|
||||||
|
|
||||||
|
const onBonusRareInfoMessageEvent = useCallback((event: BonusRareInfoMessageEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
setProductType(parser.productType);
|
||||||
|
setProductClassId(parser.productClassId);
|
||||||
|
setTotalCoinsForBonus(parser.totalCoinsForBonus);
|
||||||
|
setCoinsStillRequiredToBuy(parser.coinsStillRequiredToBuy);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
CreateMessageHook(BonusRareInfoMessageEvent, onBonusRareInfoMessageEvent);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
SendMessageHook(new GetBonusRareInfoMessageComposer());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if(!productType) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bonus-rare widget d-flex">
|
||||||
|
{ productType }
|
||||||
|
<div className="bg-light-dark rounded overflow-hidden position-relative bonus-bar-container">
|
||||||
|
<div className="d-flex justify-content-center align-items-center w-100 h-100 position-absolute small top-0">{(totalCoinsForBonus - coinsStillRequiredToBuy) + '/' + totalCoinsForBonus}</div>
|
||||||
|
<div className="small bg-info rounded position-absolute top-0 h-100" style={{ width: ((totalCoinsForBonus - coinsStillRequiredToBuy) / totalCoinsForBonus) * 100 + '%' }}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
export interface BonusRareWidgetViewProps
|
||||||
|
{}
|
@ -0,0 +1,21 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../../utils';
|
||||||
|
import { AvatarImageView } from '../../../../shared/avatar-image/AvatarImageView';
|
||||||
|
import { HallOfFameItemViewProps } from './HallOfFameItemView.types';
|
||||||
|
|
||||||
|
export const HallOfFameItemView: FC<HallOfFameItemViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { data = null, level = 0 } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="hof-user-container cursor-pointer">
|
||||||
|
<div className="hof-tooltip">
|
||||||
|
<div className="hof-tooltip-content">
|
||||||
|
<div className="fw-bold">{ level }. { data.userName }</div>
|
||||||
|
<div className="muted fst-italic small text-center">{ LocalizeText('landing.view.competition.hof.points', [ 'points' ], [ data.currentScore.toString() ])}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<AvatarImageView figure={ data.figure } direction={ 2 } />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
import { HallOfFameEntryData } from '@nitrots/nitro-renderer';
|
||||||
|
|
||||||
|
export interface HallOfFameItemViewProps
|
||||||
|
{
|
||||||
|
data: HallOfFameEntryData;
|
||||||
|
level: number;
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
.hall-of-fame {
|
||||||
|
background-color: rgba($black,.3);
|
||||||
|
border-radius: $border-radius;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
.hof-user-container {
|
||||||
|
display:inline-flex;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.hof-tooltip {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hof-tooltip {
|
||||||
|
position: absolute;
|
||||||
|
display: inline;
|
||||||
|
z-index: 2;
|
||||||
|
display: none;
|
||||||
|
background-color: #1c323f;
|
||||||
|
border: 2px solid rgba($white, 0.5);
|
||||||
|
border-radius: $border-radius;
|
||||||
|
bottom:calc(100% - 20px);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: auto;
|
||||||
|
padding:2px;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
.hof-tooltip-content {
|
||||||
|
padding:3px;
|
||||||
|
background-color: #3d5f6e;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: -7px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: auto;
|
||||||
|
height: 10px;
|
||||||
|
width: 10px;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
border-color: transparent rgba($white, 0.5) rgba($white, 0.5) transparent;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-image {
|
||||||
|
position:relative;
|
||||||
|
display:inline;
|
||||||
|
left:0;
|
||||||
|
top:0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
import { CommunityGoalHallOfFameData, CommunityGoalHallOfFameMessageEvent, GetCommunityGoalHallOfFameMessageComposer } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { CreateMessageHook, SendMessageHook } from '../../../../../hooks/messages/message-event';
|
||||||
|
import { HallOfFameItemView } from '../hall-of-fame-item/HallOfFameItemView';
|
||||||
|
import { HallOfFameWidgetViewProps } from './HallOfFameWidgetView.types';
|
||||||
|
|
||||||
|
export const HallOfFameWidgetView: FC<HallOfFameWidgetViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { slot = -1, conf = '' } = props;
|
||||||
|
const [ data, setData ] = useState<CommunityGoalHallOfFameData>(null);
|
||||||
|
|
||||||
|
const onCommunityGoalHallOfFameMessageEvent = useCallback((event: CommunityGoalHallOfFameMessageEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
setData(parser.data);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
CreateMessageHook(CommunityGoalHallOfFameMessageEvent, onCommunityGoalHallOfFameMessageEvent);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
SendMessageHook(new GetCommunityGoalHallOfFameMessageComposer(conf));
|
||||||
|
}, [ conf ]);
|
||||||
|
|
||||||
|
if(!data) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="hall-of-fame widget">
|
||||||
|
{ data.hof && (data.hof.length > 0) && data.hof.map((entry, index) =>
|
||||||
|
{
|
||||||
|
return <HallOfFameItemView data={ entry } level={ (index + 1) } />;
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
export interface HallOfFameWidgetViewProps
|
||||||
|
{
|
||||||
|
slot: number;
|
||||||
|
conf: string;
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
.promo-articles {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.promo-articles-bullet {
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: $white;
|
||||||
|
border: 1px solid $white;
|
||||||
|
height: 13px;
|
||||||
|
width: 13px;
|
||||||
|
margin-right: 3px;
|
||||||
|
|
||||||
|
&.promo-articles-bullet-active {
|
||||||
|
background: $black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.promo-article {
|
||||||
|
.promo-article-image {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
margin-right: 10px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: top center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
import { GetPromoArticlesComposer, PromoArticleData, PromoArticlesMessageEvent } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { CreateMessageHook, SendMessageHook } from '../../../../../hooks';
|
||||||
|
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||||
|
import { PromoArticleWidgetViewProps } from './PromoArticleWidgetView.types';
|
||||||
|
|
||||||
|
export const PromoArticleWidgetView: FC<PromoArticleWidgetViewProps> = props =>
|
||||||
|
{
|
||||||
|
const [articles, setArticles] = useState<PromoArticleData[]>(null);
|
||||||
|
const [index, setIndex] = useState(0);
|
||||||
|
|
||||||
|
const handleSelect = (selectedIndex) =>
|
||||||
|
{
|
||||||
|
setIndex(selectedIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPromoArticlesMessageEvent = useCallback((event: PromoArticlesMessageEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
setArticles(parser.articles);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
CreateMessageHook(PromoArticlesMessageEvent, onPromoArticlesMessageEvent);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
SendMessageHook(new GetPromoArticlesComposer());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!articles) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="promo-articles widget mb-2">
|
||||||
|
<div className="d-flex flex-row align-items-center w-100 mb-1">
|
||||||
|
<small className="flex-shrink-0 pe-1">{ LocalizeText('landing.view.promo.article.header') }</small>
|
||||||
|
<hr className="w-100 my-0"/>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex flex-row mb-1">
|
||||||
|
{articles && (articles.length > 0) && articles.map((article, ind) =>
|
||||||
|
<div className={`promo-articles-bullet cursor-pointer ` + (article === articles[index] ? 'promo-articles-bullet-active' : '')} key={article.id} onClick={event => handleSelect(ind)} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{articles && articles[index] &&
|
||||||
|
<div className="promo-article d-flex flex-row row mx-0">
|
||||||
|
<div className="promo-article-image" style={ {backgroundImage: `url(${articles[index].imageUrl})`} }/>
|
||||||
|
<div className="col-3 d-flex flex-column h-100">
|
||||||
|
<h3 className="my-0">{articles[index].title}</h3>
|
||||||
|
<b>{ articles[index].bodyText }</b>
|
||||||
|
<button className="btn btn-sm mt-auto btn-gainsboro">{ articles[index].buttonText }</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
export interface PromoArticleWidgetViewProps
|
||||||
|
{}
|
@ -0,0 +1,9 @@
|
|||||||
|
.widgetcontainer {
|
||||||
|
.widgetcontainer-image {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
margin-right: 10px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: top center;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
import { FC, useCallback, useMemo } from 'react';
|
||||||
|
import { GetConfigurationManager } from '../../../../../api/core';
|
||||||
|
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||||
|
import { WidgetContainerViewProps } from './WidgetContainerView.types';
|
||||||
|
|
||||||
|
export const WidgetContainerView: FC<WidgetContainerViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { conf = null } = props;
|
||||||
|
|
||||||
|
const config = useMemo(() =>
|
||||||
|
{
|
||||||
|
const config = {};
|
||||||
|
|
||||||
|
if(!conf || !conf.length) return config;
|
||||||
|
|
||||||
|
let options = conf.split(",");
|
||||||
|
|
||||||
|
options.forEach(option =>
|
||||||
|
{
|
||||||
|
let [ key, value ] = option.split(':');
|
||||||
|
|
||||||
|
if(key && value) config[key] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}, [ conf ]);
|
||||||
|
|
||||||
|
const getOption = useCallback((key: string) =>
|
||||||
|
{
|
||||||
|
const option = config[key];
|
||||||
|
|
||||||
|
if(!option) return null;
|
||||||
|
|
||||||
|
switch(key)
|
||||||
|
{
|
||||||
|
case 'image':
|
||||||
|
return GetConfigurationManager().interpolate(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
return option;
|
||||||
|
}, [ config ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="widgetcontainer widget d-flex flex-row overflow-hidden">
|
||||||
|
<div className="widgetcontainer-image flex-shrink-0" style={{ backgroundImage: `url(${getOption('image')})` }} />
|
||||||
|
<div className="d-flex flex-column align-self-center">
|
||||||
|
<h3 className="my-0">{LocalizeText(`landing.view.${getOption('texts')}.header`)}</h3>
|
||||||
|
<i>{ LocalizeText(`landing.view.${getOption('texts')}.body`) }</i>
|
||||||
|
<button className="btn btn-sm btn-gainsboro align-self-start px-3 mt-auto">{ LocalizeText(`landing.view.${getOption('texts')}.button`) }</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
export interface WidgetContainerViewProps
|
||||||
|
{
|
||||||
|
conf: string;
|
||||||
|
}
|
@ -1,21 +1,23 @@
|
|||||||
import { AvatarScaleType, AvatarSetType } from '@nitrots/nitro-renderer';
|
import { AvatarScaleType, AvatarSetType } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useRef, useState } from 'react';
|
||||||
import { GetAvatarRenderManager } from '../../../api';
|
import { GetAvatarRenderManager } from '../../../api';
|
||||||
import { AvatarImageViewProps } from './AvatarImageView.types';
|
import { AvatarImageViewProps } from './AvatarImageView.types';
|
||||||
|
|
||||||
export const AvatarImageView: FC<AvatarImageViewProps> = props =>
|
export const AvatarImageView: FC<AvatarImageViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { figure = '', gender = 'M', headOnly = false, direction = 0, scale = 1 } = props;
|
const { figure = '', gender = 'M', headOnly = false, direction = 0, scale = 1 } = props;
|
||||||
|
|
||||||
const [ avatarUrl, setAvatarUrl ] = useState<string>(null);
|
const [ avatarUrl, setAvatarUrl ] = useState<string>(null);
|
||||||
const [ randomValue, setRandomValue ] = useState(-1);
|
const [ randomValue, setRandomValue ] = useState(-1);
|
||||||
|
const isDisposed = useRef(false);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(randomValue) {}
|
|
||||||
|
|
||||||
const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, gender, {
|
const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, gender, {
|
||||||
resetFigure: figure => setRandomValue(Math.random()),
|
resetFigure: figure => {
|
||||||
|
if(isDisposed.current) return;
|
||||||
|
|
||||||
|
setRandomValue(Math.random());
|
||||||
|
},
|
||||||
dispose: () => {},
|
dispose: () => {},
|
||||||
disposed: false
|
disposed: false
|
||||||
}, null);
|
}, null);
|
||||||
@ -35,6 +37,16 @@ export const AvatarImageView: FC<AvatarImageViewProps> = props =>
|
|||||||
avatarImage.dispose();
|
avatarImage.dispose();
|
||||||
}, [ figure, gender, direction, headOnly, randomValue ]);
|
}, [ figure, gender, direction, headOnly, randomValue ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
isDisposed.current = false;
|
||||||
|
|
||||||
|
return () =>
|
||||||
|
{
|
||||||
|
isDisposed.current = true;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const url = `url('${ avatarUrl }')`;
|
const url = `url('${ avatarUrl }')`;
|
||||||
|
|
||||||
return <div className={ 'avatar-image scale-' + scale } style={ (avatarUrl && url.length) ? { backgroundImage: url } : {} }></div>;
|
return <div className={ 'avatar-image scale-' + scale } style={ (avatarUrl && url.length) ? { backgroundImage: url } : {} }></div>;
|
||||||
|
Loading…
Reference in New Issue
Block a user