mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-22 22:30:52 +01:00
Eslint and fix some imports
This commit is contained in:
parent
10a7dcad24
commit
df81cc7f92
27
index.html
27
index.html
@ -44,7 +44,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root" class="h-full w-full"></div>
|
||||
<div id="root" class="w-full h-full"></div>
|
||||
<script>
|
||||
window.NitroConfig = {
|
||||
"config.urls": ["/renderer-config.json", "/ui-config.json"],
|
||||
@ -62,31 +62,6 @@
|
||||
"friend.id":
|
||||
new URLSearchParams(window.location.search).get("friend") ||
|
||||
0,
|
||||
"external.texts.url": [
|
||||
"https://images.habbo.ws/assets/gamedata/" +
|
||||
(new URLSearchParams(window.location.search).get(
|
||||
"lang",
|
||||
) || null) +
|
||||
"/ExternalTexts.json",
|
||||
"https://images.habbo.ws/assets/gamedata/" +
|
||||
(new URLSearchParams(window.location.search).get(
|
||||
"lang",
|
||||
) || null) +
|
||||
"/UITexts.json",
|
||||
],
|
||||
"furnidata.url":
|
||||
"https://images.habbo.ws/assets/gamedata/" +
|
||||
(new URLSearchParams(window.location.search).get("lang") ||
|
||||
null) +
|
||||
"/FurnitureData.json",
|
||||
"productdata.url":
|
||||
"https://images.habbo.ws/assets/gamedata/" +
|
||||
(new URLSearchParams(window.location.search).get("lang") ||
|
||||
null) +
|
||||
"/ProductData.json",
|
||||
lang:
|
||||
new URLSearchParams(window.location.search).get("lang") ||
|
||||
null,
|
||||
};
|
||||
</script>
|
||||
<script type="module" src="./src/index.tsx"></script>
|
||||
|
6326
package-lock.json
generated
6326
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
src/App.tsx
10
src/App.tsx
@ -10,7 +10,7 @@ NitroVersion.UI_VERSION = GetUIVersion();
|
||||
|
||||
export const App: FC<{}> = props =>
|
||||
{
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
const [ isReady, setIsReady ] = useState(false);
|
||||
|
||||
useMessageEvent<LoadGameUrlEvent>(LoadGameUrlEvent, event =>
|
||||
{
|
||||
@ -94,10 +94,10 @@ export const App: FC<{}> = props =>
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={classNames('w-full h-full overflow-hidden text-base', !(window.devicePixelRatio % 1) && '[image-rendering:pixelated]')}>
|
||||
{!isReady &&
|
||||
<LoadingView />}
|
||||
{isReady && <MainView />}
|
||||
<div className={ classNames('w-full h-full overflow-hidden text-base', !(window.devicePixelRatio % 1) && '[image-rendering:pixelated]') }>
|
||||
{ !isReady &&
|
||||
<LoadingView /> }
|
||||
{ isReady && <MainView /> }
|
||||
<div id="draggable-windows-container" />
|
||||
</div>
|
||||
);
|
||||
|
@ -56,7 +56,7 @@ export const Base: FC<BaseProps<HTMLDivElement>> = props =>
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [display, fit, fitV, grow, shrink, fullWidth, fullHeight, overflow, position, float, pointer, visible, textColor, classNames]);
|
||||
}, [ display, fit, fitV, grow, shrink, fullWidth, fullHeight, overflow, position, float, pointer, visible, textColor, classNames ]);
|
||||
|
||||
const getClassName = useMemo(() =>
|
||||
{
|
||||
@ -65,7 +65,7 @@ export const Base: FC<BaseProps<HTMLDivElement>> = props =>
|
||||
if (className.length) newClassName += (' ' + className);
|
||||
|
||||
return newClassName.trim();
|
||||
}, [getClassNames, className]);
|
||||
}, [ getClassNames, className ]);
|
||||
|
||||
const getStyle = useMemo(() =>
|
||||
{
|
||||
@ -74,11 +74,11 @@ export const Base: FC<BaseProps<HTMLDivElement>> = props =>
|
||||
if (Object.keys(style).length) newStyle = { ...newStyle, ...style };
|
||||
|
||||
return newStyle;
|
||||
}, [style]);
|
||||
}, [ style ]);
|
||||
|
||||
return (
|
||||
<div ref={innerRef} className={getClassName} style={getStyle} {...rest}>
|
||||
{children}
|
||||
<div ref={ innerRef } className={ getClassName } style={ getStyle } { ...rest }>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ export const Button: FC<ButtonProps> = props =>
|
||||
|
||||
// fucked up method i know (i dont have a clue what im doing because im a ninja)
|
||||
|
||||
const newClassNames: string[] = ['pointer-events-auto inline-block font-normal leading-normal text-[#fff] text-center no-underline align-middle cursor-pointer select-none border-[1px] border-[solid] border-[transparent] px-[.75rem] py-[.375rem] text-[.9rem] rounded-[.25rem] [transition:color_.15s_ease-in-out,_background-color_.15s_ease-in-out,_border-color_.15s_ease-in-out,_box-shadow_.15s_ease-in-out]'];
|
||||
const newClassNames: string[] = [ 'pointer-events-auto inline-block font-normal leading-normal text-[#fff] text-center no-underline align-middle cursor-pointer select-none border-[1px] border-[solid] border-[transparent] px-[.75rem] py-[.375rem] text-[.9rem] rounded-[.25rem] [transition:color_.15s_ease-in-out,_background-color_.15s_ease-in-out,_border-color_.15s_ease-in-out,_box-shadow_.15s_ease-in-out]' ];
|
||||
|
||||
if (variant)
|
||||
{
|
||||
@ -62,7 +62,7 @@ export const Button: FC<ButtonProps> = props =>
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [variant, size, active, disabled, classNames]);
|
||||
}, [ variant, size, active, disabled, classNames ]);
|
||||
|
||||
return <Flex center classNames={getClassNames} {...rest} />;
|
||||
return <Flex center classNames={ getClassNames } { ...rest } />;
|
||||
}
|
||||
|
@ -21,18 +21,18 @@ export const Column: FC<ColumnProps> = props =>
|
||||
|
||||
if (size)
|
||||
{
|
||||
let colClassName = `col-span-${size}`;
|
||||
let colClassName = `col-span-${ size }`;
|
||||
|
||||
if (isCssGrid) colClassName = `${colClassName}`;
|
||||
if (isCssGrid) colClassName = `${ colClassName }`;
|
||||
|
||||
newClassNames.push(colClassName);
|
||||
}
|
||||
|
||||
if (offset)
|
||||
{
|
||||
let colClassName = `offset-${offset}`;
|
||||
let colClassName = `offset-${ offset }`;
|
||||
|
||||
if (isCssGrid) colClassName = `g-start-${offset}`;
|
||||
if (isCssGrid) colClassName = `g-start-${ offset }`;
|
||||
|
||||
newClassNames.push(colClassName);
|
||||
}
|
||||
@ -40,7 +40,7 @@ export const Column: FC<ColumnProps> = props =>
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [size, offset, isCssGrid, classNames]);
|
||||
}, [ size, offset, isCssGrid, classNames ]);
|
||||
|
||||
return <Flex classNames={getClassNames} column={column} gap={gap} {...rest} />;
|
||||
return <Flex classNames={ getClassNames } column={ column } gap={ gap } { ...rest } />;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ export const Flex: FC<FlexProps> = props =>
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [column, reverse, gap, center, alignSelf, alignItems, justifyContent, classNames]);
|
||||
}, [ column, reverse, gap, center, alignSelf, alignItems, justifyContent, classNames ]);
|
||||
|
||||
return <Base classNames={getClassNames} display={display} {...rest} />;
|
||||
return <Base classNames={ getClassNames } display={ display } { ...rest } />;
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ export const Grid: FC<GridProps> = props =>
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [inline, gap, maxContent, alignSelf, alignItems, justifyContent, center, classNames]);
|
||||
}, [ inline, gap, maxContent, alignSelf, alignItems, justifyContent, center, classNames ]);
|
||||
|
||||
const getStyle = useMemo(() =>
|
||||
{
|
||||
@ -54,11 +54,11 @@ export const Grid: FC<GridProps> = props =>
|
||||
if (Object.keys(style).length) newStyle = { ...newStyle, ...style };
|
||||
|
||||
return newStyle;
|
||||
}, [columnCount, style]);
|
||||
}, [ columnCount, style ]);
|
||||
|
||||
return (
|
||||
<GridContextProvider value={{ isCssGrid: true }}>
|
||||
<Base classNames={getClassNames} fullHeight={fullHeight} style={getStyle} {...rest} />
|
||||
<GridContextProvider value={ { isCssGrid: true } }>
|
||||
<Base classNames={ getClassNames } fullHeight={ fullHeight } style={ getStyle } { ...rest } />
|
||||
</GridContextProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
// @flow strict
|
||||
"use client"
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
'use client'
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
function ReactPopover({
|
||||
children,
|
||||
content,
|
||||
trigger = "click"
|
||||
trigger = 'click'
|
||||
})
|
||||
{
|
||||
const [show, setShow] = useState(false);
|
||||
const [ show, setShow ] = useState(false);
|
||||
const wrapperRef = useRef(null);
|
||||
|
||||
const handleMouseOver = () =>
|
||||
{
|
||||
if (trigger === "hover")
|
||||
if (trigger === 'hover')
|
||||
{
|
||||
setShow(true);
|
||||
};
|
||||
@ -21,7 +21,7 @@ function ReactPopover({
|
||||
|
||||
const handleMouseLeft = () =>
|
||||
{
|
||||
if (trigger === "hover")
|
||||
if (trigger === 'hover')
|
||||
{
|
||||
setShow(false);
|
||||
};
|
||||
@ -40,31 +40,31 @@ function ReactPopover({
|
||||
if (show)
|
||||
{
|
||||
// Bind the event listener
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () =>
|
||||
{
|
||||
// Unbind the event listener on clean up
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
};
|
||||
}
|
||||
}, [show, wrapperRef]);
|
||||
}, [ show, wrapperRef ]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
onMouseEnter={handleMouseOver}
|
||||
onMouseLeave={handleMouseLeft}
|
||||
className="w-fit h-fit relative flex justify-center">
|
||||
ref={ wrapperRef }
|
||||
className="w-fit h-fit relative flex justify-center"
|
||||
onMouseEnter={ handleMouseOver }
|
||||
onMouseLeave={ handleMouseLeft }>
|
||||
<div
|
||||
onClick={() => setShow(!show)}
|
||||
onClick={ () => setShow(!show) }
|
||||
>
|
||||
{children}
|
||||
{ children }
|
||||
</div>
|
||||
<div
|
||||
hidden={!show}
|
||||
className="min-w-fit w-[200px] h-fit absolute bottom-[100%] z-50 transition-all">
|
||||
className="min-w-fit w-[200px] h-fit absolute bottom-[100%] z-50 transition-all"
|
||||
hidden={ !show }>
|
||||
<div className="rounded bg-white p-3 shadow-[10px_30px_150px_rgba(46,38,92,0.25)] mb-[10px]">
|
||||
{content}
|
||||
{ content }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -26,7 +26,7 @@ export const Text: FC<TextProps> = props =>
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['inline'];
|
||||
const newClassNames: string[] = [ 'inline' ];
|
||||
|
||||
if (variant) newClassNames.push('text-' + variant);
|
||||
|
||||
@ -57,7 +57,7 @@ export const Text: FC<TextProps> = props =>
|
||||
if (textBreak) newClassNames.push('text-break');
|
||||
|
||||
return newClassNames;
|
||||
}, [variant, fontWeight, fontSize, align, bold, underline, italics, truncate, center, textEnd, small, wrap, noWrap, textBreak]);
|
||||
}, [ variant, fontWeight, fontSize, align, bold, underline, italics, truncate, center, textEnd, small, wrap, noWrap, textBreak ]);
|
||||
|
||||
return <Base classNames={getClassNames} {...rest} />;
|
||||
return <Base classNames={ getClassNames } { ...rest } />;
|
||||
}
|
||||
|
@ -8,12 +8,12 @@ export const NitroCardContentView: FC<ColumnProps> = props =>
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
// Theme Changer
|
||||
const newClassNames: string[] = ['container-fluid', 'h-full p-[8px] overflow-auto', 'bg-light'];
|
||||
const newClassNames: string[] = [ 'container-fluid', 'h-full p-[8px] overflow-auto', 'bg-light' ];
|
||||
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [classNames]);
|
||||
}, [ classNames ]);
|
||||
|
||||
return <Column classNames={getClassNames} overflow={overflow} {...rest} />;
|
||||
return <Column classNames={ getClassNames } overflow={ overflow } { ...rest } />;
|
||||
}
|
||||
|
@ -24,18 +24,15 @@ export const NitroCardHeaderView: FC<NitroCardHeaderViewProps> = props =>
|
||||
}
|
||||
|
||||
return (
|
||||
<Column center className={"relative flex items-center justify-center flex-col drag-handler min-h-card-header max-h-card-header bg-card-header"} {...rest}>
|
||||
<Column center className={ 'relative flex items-center justify-center flex-col drag-handler min-h-card-header max-h-card-header bg-card-header' } { ...rest }>
|
||||
<Flex center fullWidth>
|
||||
<span className="text-xl text-white drop-shadow-lg">{headerText}</span>
|
||||
|
||||
{isGalleryPhoto &&
|
||||
<Base className="end-4 nitro-card-header-report-camera" position="absolute" onClick={onReportPhoto}>
|
||||
<span className="text-xl text-white drop-shadow-lg">{ headerText }</span>
|
||||
{ isGalleryPhoto &&
|
||||
<Base className="end-4 nitro-card-header-report-camera" position="absolute" onClick={ onReportPhoto }>
|
||||
<FaFlag className="fa-icon" />
|
||||
</Base>
|
||||
}
|
||||
|
||||
|
||||
<div className="absolute flex items-center justify-center cursor-pointer right-2 p-[2px] ubuntu-close-button" onClick={onCloseClick} onMouseDownCapture={onMouseDown}>
|
||||
<div className="absolute flex items-center justify-center cursor-pointer right-2 p-[2px] ubuntu-close-button" onClick={ onCloseClick } onMouseDownCapture={ onMouseDown }>
|
||||
</div>
|
||||
|
||||
</Flex>
|
||||
|
@ -15,7 +15,7 @@ export const NitroCardView: FC<NitroCardViewProps> = props =>
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['resize', 'rounded', 'shadow',];
|
||||
const newClassNames: string[] = [ 'resize', 'rounded', 'shadow', ];
|
||||
|
||||
// Card Theme Changer
|
||||
newClassNames.push('border-[1px] border-[#283F5D]');
|
||||
@ -25,12 +25,12 @@ export const NitroCardView: FC<NitroCardViewProps> = props =>
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [theme, classNames]);
|
||||
}, [ classNames ]);
|
||||
|
||||
return (
|
||||
<NitroCardContextProvider value={{ theme }}>
|
||||
<DraggableWindow disableDrag={disableDrag} handleSelector={handleSelector} uniqueKey={uniqueKey} windowPosition={windowPosition}>
|
||||
<Column classNames={getClassNames} gap={gap} innerRef={elementRef} overflow={overflow} position={position} {...rest} />
|
||||
<NitroCardContextProvider value={ { theme } }>
|
||||
<DraggableWindow disableDrag={ disableDrag } handleSelector={ handleSelector } uniqueKey={ uniqueKey } windowPosition={ windowPosition }>
|
||||
<Column classNames={ getClassNames } gap={ gap } innerRef={ elementRef } overflow={ overflow } position={ position } { ...rest } />
|
||||
</DraggableWindow>
|
||||
</NitroCardContextProvider>
|
||||
);
|
||||
|
@ -14,23 +14,23 @@ export const NitroCardTabsItemView: FC<NitroCardTabsItemViewProps> = props =>
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['overflow-hidden relative cursor-pointer rounded-t-md flex bg-card-tab-item px-3 py-1 z-[1] border-card-border border-t border-l border-r before:absolute before:w-[93%] before:h-[3px] before:rounded-md before:top-[1.5px] before:left-0 before:right-0 before:m-auto before:z-[1] before:bg-[#C2C9D1]',
|
||||
isActive && 'bg-card-tab-item-active -mb-[1px] before:bg-white'];
|
||||
const newClassNames: string[] = [ 'overflow-hidden relative cursor-pointer rounded-t-md flex bg-card-tab-item px-3 py-1 z-[1] border-card-border border-t border-l border-r before:absolute before:w-[93%] before:h-[3px] before:rounded-md before:top-[1.5px] before:left-0 before:right-0 before:m-auto before:z-[1] before:bg-[#C2C9D1]',
|
||||
isActive && 'bg-card-tab-item-active -mb-[1px] before:bg-white' ];
|
||||
|
||||
//if (isActive) newClassNames.push('bg-[#dfdfdf] border-b-[1px_solid_black]');
|
||||
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [isActive, classNames]);
|
||||
}, [ isActive, classNames ]);
|
||||
|
||||
return (
|
||||
<Flex classNames={getClassNames} overflow={overflow} pointer={pointer} position={position} {...rest}>
|
||||
<Flex classNames={ getClassNames } overflow={ overflow } pointer={ pointer } position={ position } { ...rest }>
|
||||
<Flex center shrink>
|
||||
{children}
|
||||
{ children }
|
||||
</Flex>
|
||||
{(count > 0) &&
|
||||
<LayoutItemCountView count={count} />}
|
||||
{ (count > 0) &&
|
||||
<LayoutItemCountView count={ count } /> }
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
@ -7,16 +7,16 @@ export const NitroCardTabsView: FC<FlexProps> = props =>
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['justify-center gap-0.5 flex bg-card-tabs min-h-card-tabs max-h-card-tabs pt-1 border-b border-card-border px-2'];
|
||||
const newClassNames: string[] = [ 'justify-center gap-0.5 flex bg-card-tabs min-h-card-tabs max-h-card-tabs pt-1 border-b border-card-border px-2' ];
|
||||
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [classNames]);
|
||||
}, [ classNames ]);
|
||||
|
||||
return (
|
||||
<Flex classNames={getClassNames} gap={gap} justifyContent={justifyContent} {...rest}>
|
||||
{children}
|
||||
<Flex classNames={ getClassNames } gap={ gap } justifyContent={ justifyContent } { ...rest }>
|
||||
{ children }
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
@ -16,28 +16,28 @@ export interface LayoutAvatarImageViewProps extends BaseProps<HTMLDivElement>
|
||||
export const LayoutAvatarImageView: FC<LayoutAvatarImageViewProps> = props =>
|
||||
{
|
||||
const { figure = '', gender = 'M', headOnly = false, direction = 0, scale = 1, classNames = [], style = {}, ...rest } = props;
|
||||
const [avatarUrl, setAvatarUrl] = useState<string>(null);
|
||||
const [isReady, setIsReady] = useState<boolean>(false);
|
||||
const [ avatarUrl, setAvatarUrl ] = useState<string>(null);
|
||||
const [ isReady, setIsReady ] = useState<boolean>(false);
|
||||
const isDisposed = useRef(false);
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['avatar-image relative w-[90px] h-[130px] bg-no-repeat bg-[center_-8px] pointer-events-none'];
|
||||
const newClassNames: string[] = [ 'avatar-image relative w-[90px] h-[130px] bg-no-repeat bg-[center_-8px] pointer-events-none' ];
|
||||
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [classNames]);
|
||||
}, [ classNames ]);
|
||||
|
||||
const getStyle = useMemo(() =>
|
||||
{
|
||||
let newStyle: CSSProperties = {};
|
||||
|
||||
if (avatarUrl && avatarUrl.length) newStyle.backgroundImage = `url('${avatarUrl}')`;
|
||||
if (avatarUrl && avatarUrl.length) newStyle.backgroundImage = `url('${ avatarUrl }')`;
|
||||
|
||||
if (scale !== 1)
|
||||
{
|
||||
newStyle.transform = `scale(${scale})`;
|
||||
newStyle.transform = `scale(${ scale })`;
|
||||
|
||||
if (!(scale % 1)) newStyle.imageRendering = 'pixelated';
|
||||
}
|
||||
@ -45,13 +45,13 @@ export const LayoutAvatarImageView: FC<LayoutAvatarImageViewProps> = props =>
|
||||
if (Object.keys(style).length) newStyle = { ...newStyle, ...style };
|
||||
|
||||
return newStyle;
|
||||
}, [avatarUrl, scale, style]);
|
||||
}, [ avatarUrl, scale, style ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (!isReady) return;
|
||||
|
||||
const figureKey = [figure, gender, direction, headOnly].join('-');
|
||||
const figureKey = [ figure, gender, direction, headOnly ].join('-');
|
||||
|
||||
if (AVATAR_IMAGE_CACHE.has(figureKey))
|
||||
{
|
||||
@ -85,7 +85,7 @@ export const LayoutAvatarImageView: FC<LayoutAvatarImageViewProps> = props =>
|
||||
|
||||
resetFigure(figure);
|
||||
}
|
||||
}, [figure, gender, direction, headOnly, isReady]);
|
||||
}, [ figure, gender, direction, headOnly, isReady ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -99,5 +99,5 @@ export const LayoutAvatarImageView: FC<LayoutAvatarImageViewProps> = props =>
|
||||
}
|
||||
}, []);
|
||||
|
||||
return <Base classNames={getClassNames} style={getStyle} {...rest} />;
|
||||
return <Base classNames={ getClassNames } style={ getStyle } { ...rest } />;
|
||||
}
|
||||
|
@ -16,11 +16,11 @@ export interface LayoutBadgeImageViewProps extends BaseProps<HTMLDivElement>
|
||||
export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
|
||||
{
|
||||
const { badgeCode = null, isGroup = false, showInfo = false, customTitle = null, isGrayscale = false, scale = 1, classNames = [], style = {}, children = null, ...rest } = props;
|
||||
const [imageElement, setImageElement] = useState<HTMLImageElement>(null);
|
||||
const [ imageElement, setImageElement ] = useState<HTMLImageElement>(null);
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['relative w-[40px] h-[40px] bg-no-repeat bg-center'];
|
||||
const newClassNames: string[] = [ 'relative w-[40px] h-[40px] bg-no-repeat bg-center' ];
|
||||
|
||||
if (isGroup) newClassNames.push('group-badge');
|
||||
|
||||
@ -29,7 +29,7 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [classNames, isGroup, isGrayscale]);
|
||||
}, [ classNames, isGroup, isGrayscale ]);
|
||||
|
||||
const getStyle = useMemo(() =>
|
||||
{
|
||||
@ -37,13 +37,13 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
|
||||
|
||||
if (imageElement)
|
||||
{
|
||||
newStyle.backgroundImage = `url(${(isGroup) ? imageElement.src : GetConfigurationValue<string>('badge.asset.url').replace('%badgename%', badgeCode.toString())})`;
|
||||
newStyle.backgroundImage = `url(${ (isGroup) ? imageElement.src : GetConfigurationValue<string>('badge.asset.url').replace('%badgename%', badgeCode.toString()) })`;
|
||||
newStyle.width = imageElement.width;
|
||||
newStyle.height = imageElement.height;
|
||||
|
||||
if (scale !== 1)
|
||||
{
|
||||
newStyle.transform = `scale(${scale})`;
|
||||
newStyle.transform = `scale(${ scale })`;
|
||||
|
||||
if (!(scale % 1)) newStyle.imageRendering = 'pixelated';
|
||||
|
||||
@ -55,7 +55,7 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
|
||||
if (Object.keys(style).length) newStyle = { ...newStyle, ...style };
|
||||
|
||||
return newStyle;
|
||||
}, [badgeCode, isGroup, imageElement, scale, style]);
|
||||
}, [ badgeCode, isGroup, imageElement, scale, style ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -91,16 +91,16 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
|
||||
}
|
||||
|
||||
return () => GetEventDispatcher().removeEventListener(BadgeImageReadyEvent.IMAGE_READY, onBadgeImageReadyEvent);
|
||||
}, [badgeCode, isGroup]);
|
||||
}, [ badgeCode, isGroup ]);
|
||||
|
||||
return (
|
||||
<Base className='group' classNames={getClassNames} style={getStyle} {...rest}>
|
||||
{(showInfo && GetConfigurationValue<boolean>('badge.descriptions.enabled', true)) &&
|
||||
<Base className="group" classNames={ getClassNames } style={ getStyle } { ...rest }>
|
||||
{ (showInfo && GetConfigurationValue<boolean>('badge.descriptions.enabled', true)) &&
|
||||
<Base className="hidden group-hover:block before:absolute before:content-['_'] before:w-[0] before:h-[0] before:!border-l-[10px] before:!border-b-[10px] before:!border-t-[10px] before:top-[10px] before:-right-[10px] before:[border-left-color:white] before:[border-top-color:transparent] before:[border-bottom-color:transparent] z-50 absolute pointer-events-none select-none w-[210px] rounded-[.25rem] bg-[#fff] -left-[220px] text-black py-1 px-2 small">
|
||||
<div className="font-bold mb-1">{isGroup ? customTitle : LocalizeBadgeName(badgeCode)}</div>
|
||||
<div>{isGroup ? LocalizeText('group.badgepopup.body') : LocalizeBadgeDescription(badgeCode)}</div>
|
||||
</Base>}
|
||||
{children}
|
||||
<div className="font-bold mb-1">{ isGroup ? customTitle : LocalizeBadgeName(badgeCode) }</div>
|
||||
<div>{ isGroup ? LocalizeText('group.badgepopup.body') : LocalizeBadgeDescription(badgeCode) }</div>
|
||||
</Base> }
|
||||
{ children }
|
||||
</Base>
|
||||
);
|
||||
}
|
||||
|
@ -13,12 +13,12 @@ export const LayoutCurrencyIcon: FC<CurrencyIconProps> = props =>
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['nitro-currency-icon', 'bg-center bg-no-repeat w-[15px] h-[15px]'];
|
||||
const newClassNames: string[] = [ 'nitro-currency-icon', 'bg-center bg-no-repeat w-[15px] h-[15px]' ];
|
||||
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [classNames]);
|
||||
}, [ classNames ]);
|
||||
|
||||
const urlString = useMemo(() =>
|
||||
{
|
||||
@ -26,8 +26,8 @@ export const LayoutCurrencyIcon: FC<CurrencyIconProps> = props =>
|
||||
|
||||
url = url.replace('%type%', type.toString());
|
||||
|
||||
return `url(${url})`;
|
||||
}, [type]);
|
||||
return `url(${ url })`;
|
||||
}, [ type ]);
|
||||
|
||||
const getStyle = useMemo(() =>
|
||||
{
|
||||
@ -38,7 +38,7 @@ export const LayoutCurrencyIcon: FC<CurrencyIconProps> = props =>
|
||||
if (Object.keys(style).length) newStyle = { ...newStyle, ...style };
|
||||
|
||||
return newStyle;
|
||||
}, [style, urlString]);
|
||||
}, [ style, urlString ]);
|
||||
|
||||
return <Base classNames={getClassNames} style={getStyle} {...rest} />
|
||||
return <Base classNames={ getClassNames } style={ getStyle } { ...rest } />
|
||||
}
|
||||
|
@ -21,19 +21,19 @@ export const LayoutGiftTagView: FC<LayoutGiftTagViewProps> = props =>
|
||||
return (
|
||||
<Flex className="nitro-gift-card text-black" overflow="hidden">
|
||||
<div className="flex items-center justify-center gift-face flex-shrink-0">
|
||||
{!userName && <div className="gift-incognito"></div>}
|
||||
{figure && <div className="gift-avatar">
|
||||
<LayoutAvatarImageView direction={2} figure={figure} headOnly={true} />
|
||||
</div>}
|
||||
{ !userName && <div className="gift-incognito"></div> }
|
||||
{ figure && <div className="gift-avatar">
|
||||
<LayoutAvatarImageView direction={ 2 } figure={ figure } headOnly={ true } />
|
||||
</div> }
|
||||
</div>
|
||||
<Flex className="w-full pt-4 pb-4 pe-4 ps-3" overflow="hidden">
|
||||
<Column className="!flex-grow" justifyContent="between" overflow="auto">
|
||||
{!editable &&
|
||||
<Text textBreak className="gift-message">{message}</Text>}
|
||||
{editable && (onChange !== null) &&
|
||||
<textarea className="gift-message h-full" maxLength={140} placeholder={LocalizeText('catalog.gift_wrapping_new.message_hint')} value={message} onChange={(e) => onChange(e.target.value)}></textarea>}
|
||||
{userName &&
|
||||
<Text italics textEnd className="pe-1">{LocalizeText('catalog.gift_wrapping_new.message_from', ['name'], [userName])}</Text>}
|
||||
{ !editable &&
|
||||
<Text textBreak className="gift-message">{ message }</Text> }
|
||||
{ editable && (onChange !== null) &&
|
||||
<textarea className="gift-message h-full" maxLength={ 140 } placeholder={ LocalizeText('catalog.gift_wrapping_new.message_hint') } value={ message } onChange={ (e) => onChange(e.target.value) }></textarea> }
|
||||
{ userName &&
|
||||
<Text italics textEnd className="pe-1">{ LocalizeText('catalog.gift_wrapping_new.message_from', [ 'name' ], [ userName ]) }</Text> }
|
||||
</Column>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
@ -24,7 +24,7 @@ export const LayoutGridItem: FC<LayoutGridItemProps> = props =>
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['layout-grid-item', 'border', 'border-2', 'border-muted', 'rounded'];
|
||||
const newClassNames: string[] = [ 'layout-grid-item', 'border', 'border-2', 'border-muted', 'rounded' ];
|
||||
|
||||
|
||||
if (itemActive) newClassNames.push('!bg-[#ececec] !border-[#fff]');
|
||||
@ -44,33 +44,33 @@ export const LayoutGridItem: FC<LayoutGridItemProps> = props =>
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [itemActive, itemUniqueSoldout, itemUniqueNumber, itemUnseen, itemHighlight, disabled, itemImage, classNames]);
|
||||
}, [ itemActive, itemUniqueSoldout, itemUniqueNumber, itemUnseen, itemHighlight, disabled, itemImage, classNames ]);
|
||||
|
||||
const getStyle = useMemo(() =>
|
||||
{
|
||||
let newStyle = { ...style };
|
||||
|
||||
if (itemImage && !(itemUniqueSoldout || (itemUniqueNumber > 0))) newStyle.backgroundImage = `url(${itemImage})`;
|
||||
if (itemImage && !(itemUniqueSoldout || (itemUniqueNumber > 0))) newStyle.backgroundImage = `url(${ itemImage })`;
|
||||
|
||||
if (itemColor) newStyle.backgroundColor = itemColor;
|
||||
|
||||
if (Object.keys(style).length) newStyle = { ...newStyle, ...style };
|
||||
|
||||
return newStyle;
|
||||
}, [style, itemImage, itemColor, itemUniqueSoldout, itemUniqueNumber]);
|
||||
}, [ style, itemImage, itemColor, itemUniqueSoldout, itemUniqueNumber ]);
|
||||
|
||||
return (
|
||||
<Column pointer center={center} classNames={getClassNames} column={column} overflow={overflow} position={position} style={getStyle} {...rest}>
|
||||
{(itemCount > itemCountMinimum) &&
|
||||
<LayoutItemCountView count={itemCount} />}
|
||||
{(itemUniqueNumber > 0) &&
|
||||
<Column pointer center={ center } classNames={ getClassNames } column={ column } overflow={ overflow } position={ position } style={ getStyle } { ...rest }>
|
||||
{ (itemCount > itemCountMinimum) &&
|
||||
<LayoutItemCountView count={ itemCount } /> }
|
||||
{ (itemUniqueNumber > 0) &&
|
||||
<>
|
||||
<Base fit className="unique-bg-override" style={{ backgroundImage: `url(${itemImage})` }} />
|
||||
<Base fit className="unique-bg-override" style={ { backgroundImage: `url(${ itemImage })` } } />
|
||||
<div className="absolute bottom-0 unique-item-counter">
|
||||
<LayoutLimitedEditionStyledNumberView value={itemUniqueNumber} />
|
||||
<LayoutLimitedEditionStyledNumberView value={ itemUniqueNumber } />
|
||||
</div>
|
||||
</>}
|
||||
{children}
|
||||
</> }
|
||||
{ children }
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
@ -12,17 +12,17 @@ export const LayoutItemCountView: FC<LayoutItemCountViewProps> = props =>
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['inline-block px-[.65em] py-[.35em] text-[.75em] font-bold leading-none text-[#fff] text-center whitespace-nowrap align-baseline rounded-[.25rem]', '!border-[1px] !border-[solid] !border-[#283F5D]', 'border-black', 'bg-danger', 'px-1', 'top-[2px] right-[2px] text-[9.5px] px-[3px] py-[2px] '];
|
||||
const newClassNames: string[] = [ 'inline-block px-[.65em] py-[.35em] text-[.75em] font-bold leading-none text-[#fff] text-center whitespace-nowrap align-baseline rounded-[.25rem]', '!border-[1px] !border-[solid] !border-[#283F5D]', 'border-black', 'bg-danger', 'px-1', 'top-[2px] right-[2px] text-[9.5px] px-[3px] py-[2px] ' ];
|
||||
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [classNames]);
|
||||
}, [ classNames ]);
|
||||
|
||||
return (
|
||||
<Base classNames={getClassNames} position="absolute" {...rest}>
|
||||
{count}
|
||||
{children}
|
||||
<Base classNames={ getClassNames } position="absolute" { ...rest }>
|
||||
{ count }
|
||||
{ children }
|
||||
</Base>
|
||||
);
|
||||
}
|
||||
|
@ -33,10 +33,10 @@ export const LayoutMiniCameraView: FC<LayoutMiniCameraViewProps> = props =>
|
||||
return (
|
||||
<DraggableWindow handleSelector=".nitro-room-thumbnail-camera">
|
||||
<div className="nitro-room-thumbnail-camera px-2">
|
||||
<div ref={elementRef} className={'camera-frame'} />
|
||||
<div ref={ elementRef } className={ 'camera-frame' } />
|
||||
<div className="flex align-items-end h-full pb-2">
|
||||
<button className="btn btn-sm btn-danger w-full mb-1 me-2" onClick={onClose}>{LocalizeText('cancel')}</button>
|
||||
<button className="btn btn-sm btn-success w-full mb-1" onClick={takePicture}>{LocalizeText('navigator.thumbeditor.save')}</button>
|
||||
<button className="btn btn-sm btn-danger w-full mb-1 me-2" onClick={ onClose }>{ LocalizeText('cancel') }</button>
|
||||
<button className="btn btn-sm btn-success w-full mb-1" onClick={ takePicture }>{ LocalizeText('navigator.thumbeditor.save') }</button>
|
||||
</div>
|
||||
</div>
|
||||
</DraggableWindow>
|
||||
|
@ -12,16 +12,16 @@ export interface LayoutNotificationBubbleViewProps extends FlexProps
|
||||
export const LayoutNotificationBubbleView: FC<LayoutNotificationBubbleViewProps> = props =>
|
||||
{
|
||||
const { fadesOut = true, timeoutMs = 8000, onClose = null, overflow = 'hidden', classNames = [], ...rest } = props;
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['text-sm bg-[#1c1c20f2] px-[5px] py-[6px] [box-shadow:inset_0_5px_#22222799,_inset_0_-4px_#12121599] ', 'rounded'];
|
||||
const newClassNames: string[] = [ 'text-sm bg-[#1c1c20f2] px-[5px] py-[6px] [box-shadow:inset_0_5px_#22222799,_inset_0_-4px_#12121599] ', 'rounded' ];
|
||||
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [classNames]);
|
||||
}, [ classNames ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -42,11 +42,11 @@ export const LayoutNotificationBubbleView: FC<LayoutNotificationBubbleViewProps>
|
||||
}, timeoutMs);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [fadesOut, timeoutMs, onClose]);
|
||||
}, [ fadesOut, timeoutMs, onClose ]);
|
||||
|
||||
return (
|
||||
<TransitionAnimation inProp={isVisible} timeout={300} type={TransitionAnimationTypes.FADE_IN}>
|
||||
<Flex classNames={getClassNames} overflow={overflow} onClick={onClose} {...rest} />
|
||||
<TransitionAnimation inProp={ isVisible } timeout={ 300 } type={ TransitionAnimationTypes.FADE_IN }>
|
||||
<Flex classNames={ getClassNames } overflow={ overflow } onClick={ onClose } { ...rest } />
|
||||
</TransitionAnimation>
|
||||
);
|
||||
}
|
||||
|
@ -14,19 +14,19 @@ export const LayoutProgressBar: FC<LayoutProgressBarProps> = props =>
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['border-[1px] border-[solid] border-[#fff] p-[2px] h-[20px] rounded-[.25rem] overflow-hidden bg-[#1E7295] ', 'text-white'];
|
||||
const newClassNames: string[] = [ 'border-[1px] border-[solid] border-[#fff] p-[2px] h-[20px] rounded-[.25rem] overflow-hidden bg-[#1E7295] ', 'text-white' ];
|
||||
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [classNames]);
|
||||
}, [ classNames ]);
|
||||
|
||||
return (
|
||||
<Column position={position} justifyContent={justifyContent} classNames={getClassNames} {...rest}>
|
||||
{text && (text.length > 0) &&
|
||||
<Flex fit center position="absolute" className="[text-shadow:0px_4px_4px_rgba(0,_0,_0,_.25)] z-20">{text}</Flex>}
|
||||
<Base className="h-full z-10 [transition:all_1s] rounded-[.125rem] bg-[repeating-linear-gradient(#2DABC2,_#2DABC2_50%,_#2B91A7_50%,_#2B91A7_100%)]" style={{ width: (~~((((progress - 0) * (100 - 0)) / (maxProgress - 0)) + 0) + '%') }} />
|
||||
{children}
|
||||
<Column classNames={ getClassNames } justifyContent={ justifyContent } position={ position } { ...rest }>
|
||||
{ text && (text.length > 0) &&
|
||||
<Flex center fit className="[text-shadow:0px_4px_4px_rgba(0,_0,_0,_.25)] z-20" position="absolute">{ text }</Flex> }
|
||||
<Base className="h-full z-10 [transition:all_1s] rounded-[.125rem] bg-[repeating-linear-gradient(#2DABC2,_#2DABC2_50%,_#2B91A7_50%,_#2B91A7_100%)]" style={ { width: (~~((((progress - 0) * (100 - 0)) / (maxProgress - 0)) + 0) + '%') } } />
|
||||
{ children }
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
@ -14,24 +14,24 @@ export const LayoutRoomThumbnailView: FC<LayoutRoomThumbnailViewProps> = props =
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['relative w-[110px] h-[110px] bg-[url("@/assets/images/navigator/thumbnail_placeholder.png")] bg-no-repeat bg-center', 'rounded', '!border-[1px] !border-[solid] !border-[#283F5D]'];
|
||||
const newClassNames: string[] = [ 'relative w-[110px] h-[110px] bg-[url("@/assets/images/navigator/thumbnail_placeholder.png")] bg-no-repeat bg-center', 'rounded', '!border-[1px] !border-[solid] !border-[#283F5D]' ];
|
||||
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [classNames]);
|
||||
}, [ classNames ]);
|
||||
|
||||
const getImageUrl = useMemo(() =>
|
||||
{
|
||||
if (customUrl && customUrl.length) return (GetConfigurationValue<string>('image.library.url') + customUrl);
|
||||
|
||||
return (GetConfigurationValue<string>('thumbnails.url').replace('%thumbnail%', roomId.toString()));
|
||||
}, [customUrl, roomId]);
|
||||
}, [ customUrl, roomId ]);
|
||||
|
||||
return (
|
||||
<Base classNames={getClassNames} overflow={overflow} shrink={shrink} {...rest}>
|
||||
{getImageUrl && <img alt="" src={getImageUrl} />}
|
||||
{children}
|
||||
<Base classNames={ getClassNames } overflow={ overflow } shrink={ shrink } { ...rest }>
|
||||
{ getImageUrl && <img alt="" src={ getImageUrl } /> }
|
||||
{ children }
|
||||
</Base>
|
||||
);
|
||||
}
|
||||
|
@ -14,16 +14,16 @@ export const UserProfileIconView: FC<UserProfileIconViewProps> = props =>
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['bg-[url("@/assets/images/friends/friends-spritesheet.png")]', 'w-[13px] h-[11px] bg-[-51px_-91px]'];
|
||||
const newClassNames: string[] = [ 'bg-[url("@/assets/images/friends/friends-spritesheet.png")]', 'w-[13px] h-[11px] bg-[-51px_-91px]' ];
|
||||
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [classNames]);
|
||||
}, [ classNames ]);
|
||||
|
||||
return (
|
||||
<Base classNames={getClassNames} pointer={pointer} onClick={event => GetUserProfile(userId)} {...rest}>
|
||||
{children}
|
||||
<Base classNames={ getClassNames } pointer={ pointer } onClick={ event => GetUserProfile(userId) } { ...rest }>
|
||||
{ children }
|
||||
</Base>
|
||||
);
|
||||
}
|
||||
|
@ -16,23 +16,23 @@ export const LayoutLimitedEditionCompletePlateView: FC<LayoutLimitedEditionCompl
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = ['unique-complete-plate'];
|
||||
const newClassNames: string[] = [ 'unique-complete-plate' ];
|
||||
|
||||
if (classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [classNames]);
|
||||
}, [ classNames ]);
|
||||
|
||||
return (
|
||||
<Base classNames={getClassNames} {...rest}>
|
||||
<Column className="plate-container" gap={0}>
|
||||
<Base classNames={ getClassNames } { ...rest }>
|
||||
<Column className="plate-container" gap={ 0 }>
|
||||
<div className="flex justify-between items-center">
|
||||
{LocalizeText('unique.items.left')}
|
||||
<div><LayoutLimitedEditionStyledNumberView value={uniqueLimitedItemsLeft} /></div>
|
||||
{ LocalizeText('unique.items.left') }
|
||||
<div><LayoutLimitedEditionStyledNumberView value={ uniqueLimitedItemsLeft } /></div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
{LocalizeText('unique.items.number.sold')}
|
||||
<div><LayoutLimitedEditionStyledNumberView value={uniqueLimitedSeriesSize} /></div>
|
||||
{ LocalizeText('unique.items.number.sold') }
|
||||
<div><LayoutLimitedEditionStyledNumberView value={ uniqueLimitedSeriesSize } /></div>
|
||||
</div>
|
||||
</Column>
|
||||
</Base>
|
||||
|
@ -15,12 +15,12 @@ export function getTransitionAnimationStyle(type: string, transition: Transition
|
||||
case ENTERING:
|
||||
return {
|
||||
animationName: 'bounceIn',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
case EXITING:
|
||||
return {
|
||||
animationName: 'bounceOut',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
}
|
||||
case TransitionAnimationTypes.SLIDE_LEFT:
|
||||
@ -31,12 +31,12 @@ export function getTransitionAnimationStyle(type: string, transition: Transition
|
||||
case ENTERING:
|
||||
return {
|
||||
animationName: 'slideInLeft',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
case EXITING:
|
||||
return {
|
||||
animationName: 'slideOutLeft',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
}
|
||||
case TransitionAnimationTypes.SLIDE_RIGHT:
|
||||
@ -47,12 +47,12 @@ export function getTransitionAnimationStyle(type: string, transition: Transition
|
||||
case ENTERING:
|
||||
return {
|
||||
animationName: 'slideInRight',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
case EXITING:
|
||||
return {
|
||||
animationName: 'slideOutRight',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
}
|
||||
case TransitionAnimationTypes.FLIP_X:
|
||||
@ -63,12 +63,12 @@ export function getTransitionAnimationStyle(type: string, transition: Transition
|
||||
case ENTERING:
|
||||
return {
|
||||
animationName: 'flipInX',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
case EXITING:
|
||||
return {
|
||||
animationName: 'flipOutX',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
}
|
||||
case TransitionAnimationTypes.FADE_UP:
|
||||
@ -79,12 +79,12 @@ export function getTransitionAnimationStyle(type: string, transition: Transition
|
||||
case ENTERING:
|
||||
return {
|
||||
animationName: 'fadeInUp',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
case EXITING:
|
||||
return {
|
||||
animationName: 'fadeOutDown',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
}
|
||||
case TransitionAnimationTypes.FADE_IN:
|
||||
@ -95,12 +95,12 @@ export function getTransitionAnimationStyle(type: string, transition: Transition
|
||||
case ENTERING:
|
||||
return {
|
||||
animationName: 'fadeIn',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
case EXITING:
|
||||
return {
|
||||
animationName: 'fadeOut',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
}
|
||||
case TransitionAnimationTypes.FADE_DOWN:
|
||||
@ -111,12 +111,12 @@ export function getTransitionAnimationStyle(type: string, transition: Transition
|
||||
case ENTERING:
|
||||
return {
|
||||
animationName: 'fadeInDown',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
case EXITING:
|
||||
return {
|
||||
animationName: 'fadeOutUp',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
}
|
||||
case TransitionAnimationTypes.HEAD_SHAKE:
|
||||
@ -127,7 +127,7 @@ export function getTransitionAnimationStyle(type: string, transition: Transition
|
||||
case ENTERING:
|
||||
return {
|
||||
animationName: 'headShake',
|
||||
animationDuration: `${timeout}ms`
|
||||
animationDuration: `${ timeout }ms`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import { AchievementsCategoryListView } from './views/category-list/Achievements
|
||||
|
||||
export const AchievementsView: FC<{}> = props =>
|
||||
{
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const { achievementCategories = [], selectedCategoryCode = null, setSelectedCategoryCode = null, achievementScore = 0, getProgress = 0, getMaxProgress = 0, selectedCategory = null } = useAchievements();
|
||||
|
||||
useEffect(() =>
|
||||
@ -45,27 +45,27 @@ export const AchievementsView: FC<{}> = props =>
|
||||
|
||||
return (
|
||||
<NitroCardView className="w-[375px] h-[405px]" theme="primary-slim" uniqueKey="achievements">
|
||||
<NitroCardHeaderView headerText={LocalizeText('inventory.achievements')} onCloseClick={event => setIsVisible(false)} />
|
||||
{selectedCategory &&
|
||||
<NitroCardHeaderView headerText={ LocalizeText('inventory.achievements') } onCloseClick={ event => setIsVisible(false) } />
|
||||
{ selectedCategory &&
|
||||
<div className="flex relative gap-3 justify-center container-fluid p-1 bg-muted items-center cursor-pointer">
|
||||
<div className="bg-[url('@/assets/images/achievements/back-arrow.png')] bg-center no-repeat w-[33px] h-[34px]" onClick={event => setSelectedCategoryCode(null)} />
|
||||
<Column className="!flex-grow" gap={0}>
|
||||
<Text className="text-small" fontSize={4} fontWeight="bold">{LocalizeText(`quests.${selectedCategory.code}.name`)}</Text>
|
||||
<Text>{LocalizeText('achievements.details.categoryprogress', ['progress', 'limit'], [selectedCategory.getProgress().toString(), selectedCategory.getMaxProgress().toString()])}</Text>
|
||||
<div className="bg-[url('@/assets/images/achievements/back-arrow.png')] bg-center no-repeat w-[33px] h-[34px]" onClick={ event => setSelectedCategoryCode(null) } />
|
||||
<Column className="!flex-grow" gap={ 0 }>
|
||||
<Text className="text-small" fontSize={ 4 } fontWeight="bold">{ LocalizeText(`quests.${ selectedCategory.code }.name`) }</Text>
|
||||
<Text>{ LocalizeText('achievements.details.categoryprogress', [ 'progress', 'limit' ], [ selectedCategory.getProgress().toString(), selectedCategory.getMaxProgress().toString() ]) }</Text>
|
||||
</Column>
|
||||
<LayoutImage imageUrl={AchievementUtilities.getAchievementCategoryImageUrl(selectedCategory, null, true)} />
|
||||
</div>}
|
||||
<NitroCardContentView gap={1}>
|
||||
{!selectedCategory &&
|
||||
<LayoutImage imageUrl={ AchievementUtilities.getAchievementCategoryImageUrl(selectedCategory, null, true) } />
|
||||
</div> }
|
||||
<NitroCardContentView gap={ 1 }>
|
||||
{ !selectedCategory &&
|
||||
<>
|
||||
<AchievementsCategoryListView categories={achievementCategories} selectedCategoryCode={selectedCategoryCode} setSelectedCategoryCode={setSelectedCategoryCode} />
|
||||
<Column className="!flex-grow" gap={1} justifyContent="end">
|
||||
<Text center small>{LocalizeText('achievements.categories.score', ['score'], [achievementScore.toString()])}</Text>
|
||||
<LayoutProgressBar maxProgress={getMaxProgress} progress={getProgress} text={LocalizeText('achievements.categories.totalprogress', ['progress', 'limit'], [getProgress.toString(), getMaxProgress.toString()])} />
|
||||
<AchievementsCategoryListView categories={ achievementCategories } selectedCategoryCode={ selectedCategoryCode } setSelectedCategoryCode={ setSelectedCategoryCode } />
|
||||
<Column className="!flex-grow" gap={ 1 } justifyContent="end">
|
||||
<Text center small>{ LocalizeText('achievements.categories.score', [ 'score' ], [ achievementScore.toString() ]) }</Text>
|
||||
<LayoutProgressBar maxProgress={ getMaxProgress } progress={ getProgress } text={ LocalizeText('achievements.categories.totalprogress', [ 'progress', 'limit' ], [ getProgress.toString(), getMaxProgress.toString() ]) } />
|
||||
</Column>
|
||||
</>}
|
||||
{selectedCategory &&
|
||||
<AchievementCategoryView category={selectedCategory} />}
|
||||
</> }
|
||||
{ selectedCategory &&
|
||||
<AchievementCategoryView category={ selectedCategory } /> }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
|
@ -16,37 +16,37 @@ export const AchievementDetailsView: FC<AchievementDetailsViewProps> = props =>
|
||||
if (!achievement) return null;
|
||||
|
||||
return (
|
||||
<Flex shrink className="bg-muted rounded p-2 text-black" gap={2} overflow="hidden">
|
||||
<Column center gap={1}>
|
||||
<AchievementBadgeView achievement={achievement} className="nitro-achievements-relative w-[40px] h-[40px] bg-no-repeat bg-center" scale={2} />
|
||||
<Flex shrink className="bg-muted rounded p-2 text-black" gap={ 2 } overflow="hidden">
|
||||
<Column center gap={ 1 }>
|
||||
<AchievementBadgeView achievement={ achievement } className="nitro-achievements-relative w-[40px] h-[40px] bg-no-repeat bg-center" scale={ 2 } />
|
||||
<Text fontWeight="bold">
|
||||
{LocalizeText('achievements.details.level', ['level', 'limit'], [AchievementUtilities.getAchievementLevel(achievement).toString(), achievement.levelCount.toString()])}
|
||||
{ LocalizeText('achievements.details.level', [ 'level', 'limit' ], [ AchievementUtilities.getAchievementLevel(achievement).toString(), achievement.levelCount.toString() ]) }
|
||||
</Text>
|
||||
</Column>
|
||||
<Column fullWidth justifyContent="center" overflow="hidden">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text truncate fontWeight="bold">
|
||||
{LocalizeBadgeName(AchievementUtilities.getAchievementBadgeCode(achievement))}
|
||||
{ LocalizeBadgeName(AchievementUtilities.getAchievementBadgeCode(achievement)) }
|
||||
</Text>
|
||||
<Text textBreak>
|
||||
{LocalizeBadgeDescription(AchievementUtilities.getAchievementBadgeCode(achievement))}
|
||||
{ LocalizeBadgeDescription(AchievementUtilities.getAchievementBadgeCode(achievement)) }
|
||||
</Text>
|
||||
</div>
|
||||
{((achievement.levelRewardPoints > 0) || (achievement.scoreLimit > 0)) &&
|
||||
{ ((achievement.levelRewardPoints > 0) || (achievement.scoreLimit > 0)) &&
|
||||
<div className="flex flex-col gap-1">
|
||||
{(achievement.levelRewardPoints > 0) &&
|
||||
{ (achievement.levelRewardPoints > 0) &&
|
||||
<div className="flex items-center gap-1">
|
||||
<Text truncate className="small">
|
||||
{LocalizeText('achievements.details.reward')}
|
||||
{ LocalizeText('achievements.details.reward') }
|
||||
</Text>
|
||||
<Flex center className="font-bold small" gap={1}>
|
||||
{achievement.levelRewardPoints}
|
||||
<LayoutCurrencyIcon type={achievement.levelRewardPointType} />
|
||||
<Flex center className="font-bold small" gap={ 1 }>
|
||||
{ achievement.levelRewardPoints }
|
||||
<LayoutCurrencyIcon type={ achievement.levelRewardPointType } />
|
||||
</Flex>
|
||||
</div>}
|
||||
{(achievement.scoreLimit > 0) &&
|
||||
<LayoutProgressBar maxProgress={(achievement.scoreLimit + achievement.scoreAtStartOfLevel)} progress={(achievement.currentPoints + achievement.scoreAtStartOfLevel)} text={LocalizeText('achievements.details.progress', ['progress', 'limit'], [(achievement.currentPoints + achievement.scoreAtStartOfLevel).toString(), (achievement.scoreLimit + achievement.scoreAtStartOfLevel).toString()])} />}
|
||||
</div>}
|
||||
</div> }
|
||||
{ (achievement.scoreLimit > 0) &&
|
||||
<LayoutProgressBar maxProgress={ (achievement.scoreLimit + achievement.scoreAtStartOfLevel) } progress={ (achievement.currentPoints + achievement.scoreAtStartOfLevel) } text={ LocalizeText('achievements.details.progress', [ 'progress', 'limit' ], [ (achievement.currentPoints + achievement.scoreAtStartOfLevel).toString(), (achievement.scoreLimit + achievement.scoreAtStartOfLevel).toString() ]) } /> }
|
||||
</div> }
|
||||
</Column>
|
||||
</Flex>
|
||||
)
|
||||
|
@ -15,8 +15,8 @@ export const AchievementsCategoryListView: FC<AchievementsCategoryListViewProps>
|
||||
const { categories = null, selectedCategoryCode = null, setSelectedCategoryCode = null } = props;
|
||||
|
||||
return (
|
||||
<AutoGrid columnCount={3} columnMinWidth={90} columnMinHeight={100}>
|
||||
{categories && (categories.length > 0) && categories.map((category, index) => <AchievementsCategoryListItemView key={index} category={category} selectedCategoryCode={selectedCategoryCode} setSelectedCategoryCode={setSelectedCategoryCode} />)}
|
||||
<AutoGrid columnCount={ 3 } columnMinHeight={ 100 } columnMinWidth={ 90 }>
|
||||
{ categories && (categories.length > 0) && categories.map((category, index) => <AchievementsCategoryListItemView key={ index } category={ category } selectedCategoryCode={ selectedCategoryCode } setSelectedCategoryCode={ setSelectedCategoryCode } />) }
|
||||
</AutoGrid>
|
||||
);
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ const DEFAULT_DIRECTION: number = 4;
|
||||
|
||||
export const AvatarEditorFigurePreviewView: FC<{}> = props =>
|
||||
{
|
||||
const [direction, setDirection] = useState<number>(DEFAULT_DIRECTION);
|
||||
const [ direction, setDirection ] = useState<number>(DEFAULT_DIRECTION);
|
||||
const { getFigureString = null } = useAvatarEditor();
|
||||
|
||||
const rotateFigure = (newDirection: number) =>
|
||||
@ -28,12 +28,12 @@ export const AvatarEditorFigurePreviewView: FC<{}> = props =>
|
||||
|
||||
return (
|
||||
<div className="flex flex-col figure-preview-container overflow-hidden relative">
|
||||
<LayoutAvatarImageView direction={direction} figure={getFigureString} scale={2} />
|
||||
<LayoutAvatarImageView direction={ direction } figure={ getFigureString } scale={ 2 } />
|
||||
<AvatarEditorIcon className="avatar-spotlight" icon="spotlight" />
|
||||
<div className="avatar-shadow" />
|
||||
<div className="arrow-container">
|
||||
<AvatarEditorIcon icon="arrow-left" onClick={event => rotateFigure(direction + 1)} />
|
||||
<AvatarEditorIcon icon="arrow-right" onClick={event => rotateFigure(direction - 1)} />
|
||||
<AvatarEditorIcon icon="arrow-left" onClick={ event => rotateFigure(direction + 1) } />
|
||||
<AvatarEditorIcon icon="arrow-right" onClick={ event => rotateFigure(direction - 1) } />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -30,16 +30,16 @@ export const AvatarEditorIcon = forwardRef<HTMLDivElement, PropsWithChildren<{
|
||||
*/
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
ref={ ref }
|
||||
|
||||
className={classNames(
|
||||
className={ classNames(
|
||||
'nitro-avatar-editor-spritesheet',
|
||||
'cursor-pointer',
|
||||
`${icon}-icon`,
|
||||
`${ icon }-icon`,
|
||||
selected && 'selected',
|
||||
className
|
||||
)}
|
||||
{...rest} />
|
||||
) }
|
||||
{ ...rest } />
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -10,71 +10,71 @@ export const AvatarEditorModelView: FC<{
|
||||
name: string,
|
||||
categories: IAvatarEditorCategory[]
|
||||
}> = props =>
|
||||
{
|
||||
const { name = '', categories = [] } = props;
|
||||
const [ didChange, setDidChange ] = useState<boolean>(false);
|
||||
const [ activeSetType, setActiveSetType ] = useState<string>('');
|
||||
const { maxPaletteCount = 1, gender = null, setGender = null, selectedColorParts = null, getFirstSelectableColor = null, selectEditorColor = null } = useAvatarEditor();
|
||||
|
||||
const activeCategory = useMemo(() =>
|
||||
{
|
||||
const { name = '', categories = [] } = props;
|
||||
const [didChange, setDidChange] = useState<boolean>(false);
|
||||
const [activeSetType, setActiveSetType] = useState<string>('');
|
||||
const { maxPaletteCount = 1, gender = null, setGender = null, selectedColorParts = null, getFirstSelectableColor = null, selectEditorColor = null } = useAvatarEditor();
|
||||
return categories.find(category => category.setType === activeSetType) ?? null;
|
||||
}, [ categories, activeSetType ]);
|
||||
|
||||
const activeCategory = useMemo(() =>
|
||||
{
|
||||
return categories.find(category => category.setType === activeSetType) ?? null;
|
||||
}, [categories, activeSetType]);
|
||||
const selectSet = useCallback((setType: string) =>
|
||||
{
|
||||
const selectedPalettes = selectedColorParts[setType];
|
||||
|
||||
const selectSet = useCallback((setType: string) =>
|
||||
{
|
||||
const selectedPalettes = selectedColorParts[setType];
|
||||
if (!selectedPalettes || !selectedPalettes.length) selectEditorColor(setType, 0, getFirstSelectableColor(setType));
|
||||
|
||||
if (!selectedPalettes || !selectedPalettes.length) selectEditorColor(setType, 0, getFirstSelectableColor(setType));
|
||||
setActiveSetType(setType);
|
||||
}, [ getFirstSelectableColor, selectEditorColor, selectedColorParts ]);
|
||||
|
||||
setActiveSetType(setType);
|
||||
}, [getFirstSelectableColor, selectEditorColor, selectedColorParts]);
|
||||
useEffect(() =>
|
||||
{
|
||||
if (!categories || !categories.length || !didChange) return;
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (!categories || !categories.length || !didChange) return;
|
||||
selectSet(categories[0]?.setType);
|
||||
setDidChange(false);
|
||||
}, [ categories, didChange, selectSet ]);
|
||||
|
||||
selectSet(categories[0]?.setType);
|
||||
setDidChange(false);
|
||||
}, [categories, didChange, selectSet]);
|
||||
useEffect(() =>
|
||||
{
|
||||
setDidChange(true);
|
||||
}, [ categories ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setDidChange(true);
|
||||
}, [categories]);
|
||||
if (!activeCategory) return null;
|
||||
|
||||
if (!activeCategory) return null;
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-12 gap-2 overflow-hidden">
|
||||
<div className="flex flex-col col-span-2">
|
||||
{(name === AvatarEditorFigureCategory.GENERIC) &&
|
||||
return (
|
||||
<div className="grid grid-cols-12 gap-2 overflow-hidden">
|
||||
<div className="flex flex-col col-span-2">
|
||||
{ (name === AvatarEditorFigureCategory.GENERIC) &&
|
||||
<>
|
||||
<div className="category-item items-center justify-center cursor-pointer flex" onClick={event => setGender(AvatarFigurePartType.MALE)}>
|
||||
<AvatarEditorIcon icon="male" selected={gender === FigureDataContainer.MALE} />
|
||||
<div className="category-item items-center justify-center cursor-pointer flex" onClick={ event => setGender(AvatarFigurePartType.MALE) }>
|
||||
<AvatarEditorIcon icon="male" selected={ gender === FigureDataContainer.MALE } />
|
||||
</div>
|
||||
<div className="category-item items-center justify-center cursor-pointer flex" onClick={event => setGender(AvatarFigurePartType.FEMALE)}>
|
||||
<AvatarEditorIcon icon="female" selected={gender === FigureDataContainer.FEMALE} />
|
||||
<div className="category-item items-center justify-center cursor-pointer flex" onClick={ event => setGender(AvatarFigurePartType.FEMALE) }>
|
||||
<AvatarEditorIcon icon="female" selected={ gender === FigureDataContainer.FEMALE } />
|
||||
</div>
|
||||
</>}
|
||||
{(name !== AvatarEditorFigureCategory.GENERIC) && (categories.length > 0) && categories.map(category =>
|
||||
{
|
||||
return (
|
||||
<div key={category.setType} className="category-item items-center justify-center cursor-pointer flex" onClick={event => selectSet(category.setType)}>
|
||||
<AvatarEditorIcon icon={category.setType} selected={(activeSetType === category.setType)} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="flex flex-col overflow-hidden col-span-5">
|
||||
<AvatarEditorFigureSetView category={activeCategory} columnCount={3} />
|
||||
</div>
|
||||
<div className="flex flex-col overflow-hidden col-span-5">
|
||||
{(maxPaletteCount >= 1) &&
|
||||
<AvatarEditorPaletteSetView category={activeCategory} columnCount={5} paletteIndex={0} />}
|
||||
{(maxPaletteCount === 2) &&
|
||||
<AvatarEditorPaletteSetView category={activeCategory} columnCount={5} paletteIndex={1} />}
|
||||
</div>
|
||||
</> }
|
||||
{ (name !== AvatarEditorFigureCategory.GENERIC) && (categories.length > 0) && categories.map(category =>
|
||||
{
|
||||
return (
|
||||
<div key={ category.setType } className="category-item items-center justify-center cursor-pointer flex" onClick={ event => selectSet(category.setType) }>
|
||||
<AvatarEditorIcon icon={ category.setType } selected={ (activeSetType === category.setType) } />
|
||||
</div>
|
||||
);
|
||||
}) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<div className="flex flex-col overflow-hidden col-span-5">
|
||||
<AvatarEditorFigureSetView category={ activeCategory } columnCount={ 3 } />
|
||||
</div>
|
||||
<div className="flex flex-col overflow-hidden col-span-5">
|
||||
{ (maxPaletteCount >= 1) &&
|
||||
<AvatarEditorPaletteSetView category={ activeCategory } columnCount={ 5 } paletteIndex={ 0 } /> }
|
||||
{ (maxPaletteCount === 2) &&
|
||||
<AvatarEditorPaletteSetView category={ activeCategory } columnCount={ 5 } paletteIndex={ 1 } /> }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ const DEFAULT_FEMALE_FIGURE: string = 'hr-515-33.hd-600-1.ch-635-70.lg-716-66-62
|
||||
|
||||
export const AvatarEditorView: FC<{}> = props =>
|
||||
{
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const { setIsVisible: setEditorVisibility, avatarModels, activeModelKey, setActiveModelKey, loadAvatarData, getFigureStringWithFace, gender, figureSetIds = [], randomizeCurrentFigure = null, getFigureString = null } = useAvatarEditor();
|
||||
|
||||
const processAction = (action: string) =>
|
||||
@ -69,49 +69,49 @@ export const AvatarEditorView: FC<{}> = props =>
|
||||
useEffect(() =>
|
||||
{
|
||||
setEditorVisibility(isVisible)
|
||||
}, [isVisible, setEditorVisibility]);
|
||||
}, [ isVisible, setEditorVisibility ]);
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView className="w-[620px] h-[374px] nitro-avatar-editor" uniqueKey="avatar-editor">
|
||||
<NitroCardHeaderView headerText={LocalizeText('avatareditor.title')} onCloseClick={event => setIsVisible(false)} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText('avatareditor.title') } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardTabsView>
|
||||
{Object.keys(avatarModels).map(modelKey =>
|
||||
{ Object.keys(avatarModels).map(modelKey =>
|
||||
{
|
||||
const isActive = (activeModelKey === modelKey);
|
||||
|
||||
return (
|
||||
<NitroCardTabsItemView key={modelKey} isActive={isActive} onClick={event => setActiveModelKey(modelKey)}>
|
||||
{LocalizeText(`avatareditor.category.${modelKey}`)}
|
||||
<NitroCardTabsItemView key={ modelKey } isActive={ isActive } onClick={ event => setActiveModelKey(modelKey) }>
|
||||
{ LocalizeText(`avatareditor.category.${ modelKey }`) }
|
||||
</NitroCardTabsItemView>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<Grid className="grid gap-2 overflow-hidden">
|
||||
<div className="flex flex-col col-span-9 overflow-hidden">
|
||||
{((activeModelKey.length > 0) && (activeModelKey !== AvatarEditorFigureCategory.WARDROBE)) &&
|
||||
<AvatarEditorModelView categories={avatarModels[activeModelKey]} name={activeModelKey} />}
|
||||
{(activeModelKey === AvatarEditorFigureCategory.WARDROBE) &&
|
||||
<AvatarEditorWardrobeView />}
|
||||
{ ((activeModelKey.length > 0) && (activeModelKey !== AvatarEditorFigureCategory.WARDROBE)) &&
|
||||
<AvatarEditorModelView categories={ avatarModels[activeModelKey] } name={ activeModelKey } /> }
|
||||
{ (activeModelKey === AvatarEditorFigureCategory.WARDROBE) &&
|
||||
<AvatarEditorWardrobeView /> }
|
||||
</div>
|
||||
<div className="flex flex-col col-span-3 overflow-hidden gap-1">
|
||||
<AvatarEditorFigurePreviewView />
|
||||
<div className="flex flex-col !flex-grow gap-1">
|
||||
<div className="relative inline-flex align-middle">
|
||||
<Button className='flex-auto ' variant="secondary" onClick={event => processAction(AvatarEditorAction.ACTION_RESET)}>
|
||||
<Button className="flex-auto " variant="secondary" onClick={ event => processAction(AvatarEditorAction.ACTION_RESET) }>
|
||||
<FaRedo className="fa-icon" />
|
||||
</Button>
|
||||
<Button className='flex-auto' variant="secondary" onClick={event => processAction(AvatarEditorAction.ACTION_CLEAR)}>
|
||||
<Button className="flex-auto" variant="secondary" onClick={ event => processAction(AvatarEditorAction.ACTION_CLEAR) }>
|
||||
<FaTrash className="fa-icon" />
|
||||
</Button>
|
||||
<Button className='flex-auto' variant="secondary" onClick={event => processAction(AvatarEditorAction.ACTION_RANDOMIZE)}>
|
||||
<Button className="flex-auto" variant="secondary" onClick={ event => processAction(AvatarEditorAction.ACTION_RANDOMIZE) }>
|
||||
<FaDice className="fa-icon" />
|
||||
</Button>
|
||||
</div>
|
||||
<Button className="w-full" variant="success" onClick={event => processAction(AvatarEditorAction.ACTION_SAVE)}>
|
||||
{LocalizeText('avatareditor.save')}
|
||||
<Button className="w-full" variant="success" onClick={ event => processAction(AvatarEditorAction.ACTION_SAVE) }>
|
||||
{ LocalizeText('avatareditor.save') }
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -10,16 +10,16 @@ export const AvatarEditorPaletteSetItem: FC<{
|
||||
isSelected: boolean;
|
||||
width?: string;
|
||||
} & LayoutGridItemProps> = props =>
|
||||
{
|
||||
const { setType = null, partColor = null, isSelected = false, width = '100%', ...rest } = props;
|
||||
{
|
||||
const { setType = null, partColor = null, isSelected = false, width = '100%', ...rest } = props;
|
||||
|
||||
if (!partColor) return null;
|
||||
if (!partColor) return null;
|
||||
|
||||
const isHC = !GetConfigurationValue<boolean>('hc.disabled', false) && (partColor.clubLevel > 0);
|
||||
const isHC = !GetConfigurationValue<boolean>('hc.disabled', false) && (partColor.clubLevel > 0);
|
||||
|
||||
return (
|
||||
<InfiniteGrid.Item itemHighlight className="clear-bg" itemActive={isSelected} itemColor={ColorConverter.int2rgb(partColor.rgb)} {...rest}>
|
||||
{isHC && <LayoutCurrencyIcon className="absolute end-1 bottom-1" type="hc" />}
|
||||
</InfiniteGrid.Item>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<InfiniteGrid.Item itemHighlight className="clear-bg" itemActive={ isSelected } itemColor={ ColorConverter.int2rgb(partColor.rgb) } { ...rest }>
|
||||
{ isHC && <LayoutCurrencyIcon className="absolute end-1 bottom-1" type="hc" /> }
|
||||
</InfiniteGrid.Item>
|
||||
);
|
||||
}
|
||||
|
@ -10,27 +10,27 @@ export const AvatarEditorPaletteSetView: FC<{
|
||||
paletteIndex: number;
|
||||
columnCount: number;
|
||||
}> = props =>
|
||||
{
|
||||
const { category = null, paletteIndex = -1, columnCount = 3 } = props;
|
||||
const { selectedColorParts = null, selectEditorColor = null } = useAvatarEditor();
|
||||
|
||||
const isPartColorSelected = (partColor: IPartColor) =>
|
||||
{
|
||||
const { category = null, paletteIndex = -1, columnCount = 3 } = props;
|
||||
const { selectedColorParts = null, selectEditorColor = null } = useAvatarEditor();
|
||||
if (!category || !category.setType || !selectedColorParts || !selectedColorParts[category.setType] || !selectedColorParts[category.setType][paletteIndex]) return false;
|
||||
|
||||
const isPartColorSelected = (partColor: IPartColor) =>
|
||||
{
|
||||
if (!category || !category.setType || !selectedColorParts || !selectedColorParts[category.setType] || !selectedColorParts[category.setType][paletteIndex]) return false;
|
||||
const selectedColorPart = selectedColorParts[category.setType][paletteIndex];
|
||||
|
||||
const selectedColorPart = selectedColorParts[category.setType][paletteIndex];
|
||||
|
||||
return (selectedColorPart.id === partColor.id);
|
||||
}
|
||||
|
||||
return (
|
||||
<InfiniteGrid<IPartColor> columnCount={columnCount} itemRender={(item: IPartColor) =>
|
||||
{
|
||||
if (!item) return null;
|
||||
|
||||
return (
|
||||
<AvatarEditorPaletteSetItem isSelected={isPartColorSelected(item)} partColor={item} setType={category.setType} width={`calc(100% / ${columnCount}`} onClick={event => selectEditorColor(category.setType, paletteIndex, item.id)} />
|
||||
)
|
||||
}} items={category.colorItems[paletteIndex]} overscan={columnCount} />
|
||||
);
|
||||
return (selectedColorPart.id === partColor.id);
|
||||
}
|
||||
|
||||
return (
|
||||
<InfiniteGrid<IPartColor> columnCount={ columnCount } itemRender={ (item: IPartColor) =>
|
||||
{
|
||||
if (!item) return null;
|
||||
|
||||
return (
|
||||
<AvatarEditorPaletteSetItem isSelected={ isPartColorSelected(item) } partColor={ item } setType={ category.setType } width={ `calc(100% / ${ columnCount }` } onClick={ event => selectEditorColor(category.setType, paletteIndex, item.id) } />
|
||||
)
|
||||
} } items={ category.colorItems[paletteIndex] } overscan={ columnCount } />
|
||||
);
|
||||
}
|
||||
|
@ -12,9 +12,9 @@ const MODE_CHECKOUT: number = 3;
|
||||
|
||||
export const CameraWidgetView: FC<{}> = props =>
|
||||
{
|
||||
const [mode, setMode] = useState<number>(MODE_NONE);
|
||||
const [base64Url, setSavedPictureUrl] = useState<string>(null);
|
||||
const { availableEffects = [], selectedPictureIndex = -1, cameraRoll = [], setCameraRoll = null, myLevel = 0, price = { credits: 0, duckets: 0, publishDucketPrice: 0 } } = useCamera();
|
||||
const [ mode, setMode ] = useState<number>(MODE_NONE);
|
||||
const [ base64Url, setSavedPictureUrl ] = useState<string>(null);
|
||||
const { availableEffects = [], selectedPictureIndex = -1, cameraRoll = [], setCameraRoll = null, myLevel = 0, price = { credits: 0, duckets: 0, publishDucketPrice: 0 }} = useCamera();
|
||||
|
||||
|
||||
const processAction = (type: string) =>
|
||||
@ -30,7 +30,7 @@ export const CameraWidgetView: FC<{}> = props =>
|
||||
case 'delete':
|
||||
setCameraRoll(prevValue =>
|
||||
{
|
||||
const clone = [...prevValue];
|
||||
const clone = [ ...prevValue ];
|
||||
|
||||
clone.splice(selectedPictureIndex, 1);
|
||||
|
||||
@ -89,9 +89,9 @@ export const CameraWidgetView: FC<{}> = props =>
|
||||
|
||||
return (
|
||||
<>
|
||||
{(mode === MODE_CAPTURE) && <CameraWidgetCaptureView onClose={() => processAction('close')} onDelete={() => processAction('delete')} onEdit={() => processAction('edit')} />}
|
||||
{(mode === MODE_EDITOR) && <CameraWidgetEditorView availableEffects={availableEffects} myLevel={myLevel} picture={cameraRoll[selectedPictureIndex]} onCancel={() => processAction('editor_cancel')} onCheckout={checkoutPictureUrl} onClose={() => processAction('close')} />}
|
||||
{(mode === MODE_CHECKOUT) && <CameraWidgetCheckoutView base64Url={base64Url} price={price} onCancelClick={() => processAction('editor_cancel')} onCloseClick={() => processAction('close')}></CameraWidgetCheckoutView>}
|
||||
{ (mode === MODE_CAPTURE) && <CameraWidgetCaptureView onClose={ () => processAction('close') } onDelete={ () => processAction('delete') } onEdit={ () => processAction('edit') } /> }
|
||||
{ (mode === MODE_EDITOR) && <CameraWidgetEditorView availableEffects={ availableEffects } myLevel={ myLevel } picture={ cameraRoll[selectedPictureIndex] } onCancel={ () => processAction('editor_cancel') } onCheckout={ checkoutPictureUrl } onClose={ () => processAction('close') } /> }
|
||||
{ (mode === MODE_CHECKOUT) && <CameraWidgetCheckoutView base64Url={ base64Url } price={ price } onCancelClick={ () => processAction('editor_cancel') } onCloseClick={ () => processAction('close') }></CameraWidgetCheckoutView> }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ export const CameraWidgetCaptureView: FC<CameraWidgetCaptureViewProps> = props =
|
||||
|
||||
const texture = GetRoomEngine().createTextureFromRoom(GetRoomSession().roomId, 1, getCameraBounds());
|
||||
|
||||
const clone = [...cameraRoll];
|
||||
const clone = [ ...cameraRoll ];
|
||||
|
||||
if (clone.length >= CAMERA_ROLL_LIMIT)
|
||||
{
|
||||
@ -59,31 +59,31 @@ export const CameraWidgetCaptureView: FC<CameraWidgetCaptureViewProps> = props =
|
||||
|
||||
return (
|
||||
<DraggableWindow uniqueKey="nitro-camera-capture">
|
||||
<Column center className="relative" gap={0}>
|
||||
{selectedPicture && <img alt="" className="absolute top-[37px] left-[10px] w-[320px] h-[320px]" src={selectedPicture.imageUrl} />}
|
||||
<Column center className="relative" gap={ 0 }>
|
||||
{ selectedPicture && <img alt="" className="absolute top-[37px] left-[10px] w-[320px] h-[320px]" src={ selectedPicture.imageUrl } /> }
|
||||
<div className="relative w-[340px] h-[462px] bg-[url('@/assets/images/room-widgets/camera-widget/camera-spritesheet.png')] bg-[-1px_-1px] drag-handler">
|
||||
<div className="absolute top-[8px] right-[8px] rounded-[.25rem] [box-shadow:0_0_0_1.5px_#fff] border-[2px] border-[solid] border-[#921911] bg-[repeating-linear-gradient(rgb(245,_80,_65),_rgb(245,_80,_65)_50%,_rgb(194,_48,_39)_50%,_rgb(194,_48,_39)_100%)] cursor-pointer leading-none px-[3px] py-px" onClick={onClose}>
|
||||
<div className="absolute top-[8px] right-[8px] rounded-[.25rem] [box-shadow:0_0_0_1.5px_#fff] border-[2px] border-[solid] border-[#921911] bg-[repeating-linear-gradient(rgb(245,_80,_65),_rgb(245,_80,_65)_50%,_rgb(194,_48,_39)_50%,_rgb(194,_48,_39)_100%)] cursor-pointer leading-none px-[3px] py-px" onClick={ onClose }>
|
||||
<FaTimes className="fa-icon" />
|
||||
</div>
|
||||
{!selectedPicture && <div ref={elementRef} className="absolute top-[37px] left-[10px] w-[320px] h-[320px] bg-[url('@/assets/images/room-widgets/camera-widget/camera-spritesheet.png')] bg-[-343px_-1px]" />}
|
||||
{selectedPicture &&
|
||||
{ !selectedPicture && <div ref={ elementRef } className="absolute top-[37px] left-[10px] w-[320px] h-[320px] bg-[url('@/assets/images/room-widgets/camera-widget/camera-spritesheet.png')] bg-[-343px_-1px]" /> }
|
||||
{ selectedPicture &&
|
||||
<div className="absolute top-[37px] left-[10px] w-[320px] h-[320px] ">
|
||||
<div className="bg-[rgba(0,_0,_0,_0.5)] w-full absolute bottom-0 py-2 text-center inline-flex justify-center">
|
||||
<Button variant='success' className="me-3" title={LocalizeText('camera.editor.button.tooltip')} onClick={onEdit}>{LocalizeText('camera.editor.button.text')}</Button>
|
||||
<Button variant='danger' onClick={onDelete}>{LocalizeText('camera.delete.button.text')}</Button>
|
||||
<Button className="me-3" title={ LocalizeText('camera.editor.button.tooltip') } variant="success" onClick={ onEdit }>{ LocalizeText('camera.editor.button.text') }</Button>
|
||||
<Button variant="danger" onClick={ onDelete }>{ LocalizeText('camera.delete.button.text') }</Button>
|
||||
</div>
|
||||
</div>}
|
||||
</div> }
|
||||
<div className="flex justify-center">
|
||||
<div className="w-[94px] h-[94px] cursor-pointer mt-[362px] bg-[url('@/assets/images/room-widgets/camera-widget/camera-spritesheet.png')] bg-[-343px_-321px]" title={LocalizeText('camera.take.photo.button.tooltip')} onClick={takePicture} />
|
||||
<div className="w-[94px] h-[94px] cursor-pointer mt-[362px] bg-[url('@/assets/images/room-widgets/camera-widget/camera-spritesheet.png')] bg-[-343px_-321px]" title={ LocalizeText('camera.take.photo.button.tooltip') } onClick={ takePicture } />
|
||||
</div>
|
||||
</div>
|
||||
{(cameraRoll.length > 0) &&
|
||||
{ (cameraRoll.length > 0) &&
|
||||
<div className="w-[330px] bg-[#bab8b4] rounded-bl-[10px] rounded-br-[10px] border-[1px] border-[solid] border-[black] [box-shadow:inset_1px_0px_white,_inset_-1px_-1px_white] flex justify-center py-2">
|
||||
{cameraRoll.map((picture, index) =>
|
||||
{ cameraRoll.map((picture, index) =>
|
||||
{
|
||||
return <img className='w-[56px] h-[56px] border-[1px] border-[solid] border-[black] object-contain [image-rendering:initial]' key={index} alt="" src={picture.imageUrl} onClick={event => setSelectedPictureIndex(index)} />;
|
||||
})}
|
||||
</div>}
|
||||
return <img key={ index } alt="" className="w-[56px] h-[56px] border-[1px] border-[solid] border-[black] object-contain [image-rendering:initial]" src={ picture.imageUrl } onClick={ event => setSelectedPictureIndex(index) } />;
|
||||
}) }
|
||||
</div> }
|
||||
</Column>
|
||||
</DraggableWindow>
|
||||
);
|
||||
|
@ -15,12 +15,12 @@ export interface CameraWidgetCheckoutViewProps
|
||||
export const CameraWidgetCheckoutView: FC<CameraWidgetCheckoutViewProps> = props =>
|
||||
{
|
||||
const { base64Url = null, onCloseClick = null, onCancelClick = null, price = null } = props;
|
||||
const [pictureUrl, setPictureUrl] = useState<string>(null);
|
||||
const [publishUrl, setPublishUrl] = useState<string>(null);
|
||||
const [picturesBought, setPicturesBought] = useState(0);
|
||||
const [wasPicturePublished, setWasPicturePublished] = useState(false);
|
||||
const [isWaiting, setIsWaiting] = useState(false);
|
||||
const [publishCooldown, setPublishCooldown] = useState(0);
|
||||
const [ pictureUrl, setPictureUrl ] = useState<string>(null);
|
||||
const [ publishUrl, setPublishUrl ] = useState<string>(null);
|
||||
const [ picturesBought, setPicturesBought ] = useState(0);
|
||||
const [ wasPicturePublished, setWasPicturePublished ] = useState(false);
|
||||
const [ isWaiting, setIsWaiting ] = useState(false);
|
||||
const [ publishCooldown, setPublishCooldown ] = useState(0);
|
||||
|
||||
const publishDisabled = useMemo(() => GetConfigurationValue<boolean>('camera.publish.disabled', false), []);
|
||||
|
||||
@ -77,81 +77,81 @@ export const CameraWidgetCheckoutView: FC<CameraWidgetCheckoutViewProps> = props
|
||||
if (!base64Url) return;
|
||||
|
||||
GetRoomEngine().saveBase64AsScreenshot(base64Url);
|
||||
}, [base64Url]);
|
||||
}, [ base64Url ]);
|
||||
|
||||
if (!price) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-camera-checkout" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={LocalizeText('camera.confirm_phase.title')} onCloseClick={event => processAction('close')} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText('camera.confirm_phase.title') } onCloseClick={ event => processAction('close') } />
|
||||
<NitroCardContentView>
|
||||
<div className="flex items-center justify-center">
|
||||
{(pictureUrl && pictureUrl.length) &&
|
||||
<LayoutImage className="picture-preview border" imageUrl={pictureUrl} />}
|
||||
{(!pictureUrl || !pictureUrl.length) &&
|
||||
{ (pictureUrl && pictureUrl.length) &&
|
||||
<LayoutImage className="picture-preview border" imageUrl={ pictureUrl } /> }
|
||||
{ (!pictureUrl || !pictureUrl.length) &&
|
||||
<div className="flex items-center justify-center picture-preview border">
|
||||
<Text bold>{LocalizeText('camera.loading')}</Text>
|
||||
</div>}
|
||||
<Text bold>{ LocalizeText('camera.loading') }</Text>
|
||||
</div> }
|
||||
</div>
|
||||
<div className="flex items-center bg-muted rounded p-2 justify-between">
|
||||
<Column gap={1} size={publishDisabled ? 10 : 6}>
|
||||
<Column gap={ 1 } size={ publishDisabled ? 10 : 6 }>
|
||||
<Text bold>
|
||||
{LocalizeText('camera.purchase.header')}
|
||||
{ LocalizeText('camera.purchase.header') }
|
||||
</Text>
|
||||
{((price.credits > 0) || (price.duckets > 0)) &&
|
||||
{ ((price.credits > 0) || (price.duckets > 0)) &&
|
||||
<div className="flex gap-1">
|
||||
<Text>{LocalizeText('catalog.purchase.confirmation.dialog.cost')}</Text>
|
||||
{(price.credits > 0) &&
|
||||
<Text>{ LocalizeText('catalog.purchase.confirmation.dialog.cost') }</Text>
|
||||
{ (price.credits > 0) &&
|
||||
<div className="flex gap-1">
|
||||
<Text bold>{price.credits}</Text>
|
||||
<LayoutCurrencyIcon type={-1} />
|
||||
</div>}
|
||||
{(price.duckets > 0) &&
|
||||
<Text bold>{ price.credits }</Text>
|
||||
<LayoutCurrencyIcon type={ -1 } />
|
||||
</div> }
|
||||
{ (price.duckets > 0) &&
|
||||
<div className="flex gap-1">
|
||||
<Text bold>{price.duckets}</Text>
|
||||
<LayoutCurrencyIcon type={5} />
|
||||
</div>}
|
||||
</div>}
|
||||
{(picturesBought > 0) &&
|
||||
<Text bold>{ price.duckets }</Text>
|
||||
<LayoutCurrencyIcon type={ 5 } />
|
||||
</div> }
|
||||
</div> }
|
||||
{ (picturesBought > 0) &&
|
||||
<Text>
|
||||
<Text bold>{LocalizeText('camera.purchase.count.info')}</Text> {picturesBought}
|
||||
<u className="ms-1 cursor-pointer" onClick={() => CreateLinkEvent('inventory/toggle')}>{LocalizeText('camera.open.inventory')}</u>
|
||||
</Text>}
|
||||
<Text bold>{ LocalizeText('camera.purchase.count.info') }</Text> { picturesBought }
|
||||
<u className="ms-1 cursor-pointer" onClick={ () => CreateLinkEvent('inventory/toggle') }>{ LocalizeText('camera.open.inventory') }</u>
|
||||
</Text> }
|
||||
</Column>
|
||||
<div className="flex items-center">
|
||||
<Button disabled={isWaiting} variant="success" onClick={event => processAction('buy')}>{LocalizeText(!picturesBought ? 'buy' : 'camera.buy.another.button.text')}</Button>
|
||||
<Button disabled={ isWaiting } variant="success" onClick={ event => processAction('buy') }>{ LocalizeText(!picturesBought ? 'buy' : 'camera.buy.another.button.text') }</Button>
|
||||
</div>
|
||||
</div>
|
||||
{!publishDisabled &&
|
||||
{ !publishDisabled &&
|
||||
<div className="flex items-center justify-between bg-muted rounded p-2">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>
|
||||
{LocalizeText(wasPicturePublished ? 'camera.publish.successful' : 'camera.publish.explanation')}
|
||||
{ LocalizeText(wasPicturePublished ? 'camera.publish.successful' : 'camera.publish.explanation') }
|
||||
</Text>
|
||||
<Text>
|
||||
{LocalizeText(wasPicturePublished ? 'camera.publish.success.short.info' : 'camera.publish.detailed.explanation')}
|
||||
{ LocalizeText(wasPicturePublished ? 'camera.publish.success.short.info' : 'camera.publish.detailed.explanation') }
|
||||
</Text>
|
||||
{wasPicturePublished && <a href={publishUrl} rel="noreferrer" target="_blank">{LocalizeText('camera.link.to.published')}</a>}
|
||||
{!wasPicturePublished && (price.publishDucketPrice > 0) &&
|
||||
{ wasPicturePublished && <a href={ publishUrl } rel="noreferrer" target="_blank">{ LocalizeText('camera.link.to.published') }</a> }
|
||||
{ !wasPicturePublished && (price.publishDucketPrice > 0) &&
|
||||
<div className="flex gap-1">
|
||||
<Text>{LocalizeText('catalog.purchase.confirmation.dialog.cost')}</Text>
|
||||
<Text>{ LocalizeText('catalog.purchase.confirmation.dialog.cost') }</Text>
|
||||
<div className="flex gap-1">
|
||||
<Text bold>{price.publishDucketPrice}</Text>
|
||||
<LayoutCurrencyIcon type={5} />
|
||||
<Text bold>{ price.publishDucketPrice }</Text>
|
||||
<LayoutCurrencyIcon type={ 5 } />
|
||||
</div>
|
||||
</div>}
|
||||
{(publishCooldown > 0) && <div className="mt-1 text-center font-bold ">{LocalizeText('camera.publish.wait', ['minutes'], [Math.ceil(publishCooldown / 60).toString()])}</div>}
|
||||
</div> }
|
||||
{ (publishCooldown > 0) && <div className="mt-1 text-center font-bold ">{ LocalizeText('camera.publish.wait', [ 'minutes' ], [ Math.ceil(publishCooldown / 60).toString() ]) }</div> }
|
||||
</div>
|
||||
{!wasPicturePublished &&
|
||||
{ !wasPicturePublished &&
|
||||
<div className="flex align-items-end">
|
||||
<Button disabled={(isWaiting || (publishCooldown > 0))} variant="success" onClick={event => processAction('publish')}>
|
||||
{LocalizeText('camera.publish.button.text')}
|
||||
<Button disabled={ (isWaiting || (publishCooldown > 0)) } variant="success" onClick={ event => processAction('publish') }>
|
||||
{ LocalizeText('camera.publish.button.text') }
|
||||
</Button>
|
||||
</div>}
|
||||
</div>}
|
||||
<Text center>{LocalizeText('camera.warning.disclaimer')}</Text>
|
||||
</div> }
|
||||
</div> }
|
||||
<Text center>{ LocalizeText('camera.warning.disclaimer') }</Text>
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={event => processAction('cancel')}>{LocalizeText('generic.cancel')}</Button>
|
||||
<Button onClick={ event => processAction('cancel') }>{ LocalizeText('generic.cancel') }</Button>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
|
@ -12,7 +12,7 @@ export interface CameraWidgetShowPhotoViewProps
|
||||
export const CameraWidgetShowPhotoView: FC<CameraWidgetShowPhotoViewProps> = props =>
|
||||
{
|
||||
const { currentIndex = -1, currentPhotos = null } = props;
|
||||
const [imageIndex, setImageIndex] = useState(0);
|
||||
const [ imageIndex, setImageIndex ] = useState(0);
|
||||
|
||||
const currentImage = (currentPhotos && currentPhotos.length) ? currentPhotos[imageIndex] : null;
|
||||
|
||||
@ -43,27 +43,27 @@ export const CameraWidgetShowPhotoView: FC<CameraWidgetShowPhotoViewProps> = pro
|
||||
useEffect(() =>
|
||||
{
|
||||
setImageIndex(currentIndex);
|
||||
}, [currentIndex]);
|
||||
}, [ currentIndex ]);
|
||||
|
||||
if (!currentImage) return null;
|
||||
|
||||
return (
|
||||
<Grid style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Flex center className="picture-preview border border-black" style={currentImage.w ? { backgroundImage: 'url(' + currentImage.w + ')' } : {}}>
|
||||
{!currentImage.w &&
|
||||
<Text bold>{LocalizeText('camera.loading')}</Text>}
|
||||
<Grid style={ { display: 'flex', flexDirection: 'column' } }>
|
||||
<Flex center className="picture-preview border border-black" style={ currentImage.w ? { backgroundImage: 'url(' + currentImage.w + ')' } : {} }>
|
||||
{ !currentImage.w &&
|
||||
<Text bold>{ LocalizeText('camera.loading') }</Text> }
|
||||
</Flex>
|
||||
{currentImage.m && currentImage.m.length &&
|
||||
<Text center>{currentImage.m}</Text>}
|
||||
{ currentImage.m && currentImage.m.length &&
|
||||
<Text center>{ currentImage.m }</Text> }
|
||||
<div className="flex items-center justify-between">
|
||||
<Text>{(currentImage.n || '')}</Text>
|
||||
<Text>{new Date(currentImage.t * 1000).toLocaleDateString()}</Text>
|
||||
<Text>{ (currentImage.n || '') }</Text>
|
||||
<Text>{ new Date(currentImage.t * 1000).toLocaleDateString() }</Text>
|
||||
</div>
|
||||
{(currentPhotos.length > 1) &&
|
||||
{ (currentPhotos.length > 1) &&
|
||||
<Flex className="picture-preview-buttons">
|
||||
<FaArrowLeft className="cursor-pointer picture-preview-buttons-previous fa-icon" onClick={previous} />
|
||||
<Text underline className="cursor-pointer" onClick={event => GetUserProfile(currentImage.oi)}>{currentImage.o}</Text>
|
||||
<FaArrowRight className="cursor-pointer picture-preview-buttons-next fa-icon" onClick={next} />
|
||||
<FaArrowLeft className="cursor-pointer picture-preview-buttons-previous fa-icon" onClick={ previous } />
|
||||
<Text underline className="cursor-pointer" onClick={ event => GetUserProfile(currentImage.oi) }>{ currentImage.o }</Text>
|
||||
<FaArrowRight className="cursor-pointer picture-preview-buttons-next fa-icon" onClick={ next } />
|
||||
</Flex>
|
||||
}
|
||||
</Grid>
|
||||
|
@ -16,27 +16,27 @@ export interface CameraWidgetEditorViewProps
|
||||
onCheckout: (pictureUrl: string) => void;
|
||||
}
|
||||
|
||||
const TABS: string[] = [CameraEditorTabs.COLORMATRIX, CameraEditorTabs.COMPOSITE];
|
||||
const TABS: string[] = [ CameraEditorTabs.COLORMATRIX, CameraEditorTabs.COMPOSITE ];
|
||||
|
||||
export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
||||
{
|
||||
const { picture = null, availableEffects = null, myLevel = 1, onClose = null, onCancel = null, onCheckout = null } = props;
|
||||
const [currentTab, setCurrentTab] = useState(TABS[0]);
|
||||
const [selectedEffectName, setSelectedEffectName] = useState<string>(null);
|
||||
const [selectedEffects, setSelectedEffects] = useState<IRoomCameraWidgetSelectedEffect[]>([]);
|
||||
const [effectsThumbnails, setEffectsThumbnails] = useState<CameraPictureThumbnail[]>([]);
|
||||
const [isZoomed, setIsZoomed] = useState(false);
|
||||
const [currentPictureUrl, setCurrentPictureUrl] = useState<string>('');
|
||||
const [ currentTab, setCurrentTab ] = useState(TABS[0]);
|
||||
const [ selectedEffectName, setSelectedEffectName ] = useState<string>(null);
|
||||
const [ selectedEffects, setSelectedEffects ] = useState<IRoomCameraWidgetSelectedEffect[]>([]);
|
||||
const [ effectsThumbnails, setEffectsThumbnails ] = useState<CameraPictureThumbnail[]>([]);
|
||||
const [ isZoomed, setIsZoomed ] = useState(false);
|
||||
const [ currentPictureUrl, setCurrentPictureUrl ] = useState<string>('');
|
||||
|
||||
const getColorMatrixEffects = useMemo(() =>
|
||||
{
|
||||
return availableEffects.filter(effect => effect.colorMatrix);
|
||||
}, [availableEffects]);
|
||||
}, [ availableEffects ]);
|
||||
|
||||
const getCompositeEffects = useMemo(() =>
|
||||
{
|
||||
return availableEffects.filter(effect => effect.texture);
|
||||
}, [availableEffects]);
|
||||
}, [ availableEffects ]);
|
||||
|
||||
const getEffectList = useCallback(() =>
|
||||
{
|
||||
@ -46,26 +46,26 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
||||
}
|
||||
|
||||
return getCompositeEffects;
|
||||
}, [currentTab, getColorMatrixEffects, getCompositeEffects]);
|
||||
}, [ currentTab, getColorMatrixEffects, getCompositeEffects ]);
|
||||
|
||||
const getSelectedEffectIndex = useCallback((name: string) =>
|
||||
{
|
||||
if (!name || !name.length || !selectedEffects || !selectedEffects.length) return -1;
|
||||
|
||||
return selectedEffects.findIndex(effect => (effect.effect.name === name));
|
||||
}, [selectedEffects])
|
||||
}, [ selectedEffects ])
|
||||
|
||||
const getCurrentEffectIndex = useMemo(() =>
|
||||
{
|
||||
return getSelectedEffectIndex(selectedEffectName)
|
||||
}, [selectedEffectName, getSelectedEffectIndex])
|
||||
}, [ selectedEffectName, getSelectedEffectIndex ])
|
||||
|
||||
const getCurrentEffect = useMemo(() =>
|
||||
{
|
||||
if (!selectedEffectName) return null;
|
||||
|
||||
return (selectedEffects[getCurrentEffectIndex] || null);
|
||||
}, [selectedEffectName, getCurrentEffectIndex, selectedEffects]);
|
||||
}, [ selectedEffectName, getCurrentEffectIndex, selectedEffects ]);
|
||||
|
||||
const setSelectedEffectAlpha = useCallback((alpha: number) =>
|
||||
{
|
||||
@ -75,14 +75,14 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
||||
|
||||
setSelectedEffects(prevValue =>
|
||||
{
|
||||
const clone = [...prevValue];
|
||||
const clone = [ ...prevValue ];
|
||||
const currentEffect = clone[index];
|
||||
|
||||
clone[getCurrentEffectIndex] = new RoomCameraWidgetSelectedEffect(currentEffect.effect, alpha);
|
||||
|
||||
return clone;
|
||||
});
|
||||
}, [getCurrentEffectIndex, setSelectedEffects]);
|
||||
}, [ getCurrentEffectIndex, setSelectedEffects ]);
|
||||
|
||||
const processAction = useCallback((type: string, effectName: string = null) =>
|
||||
{
|
||||
@ -111,7 +111,7 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
||||
|
||||
setSelectedEffects(prevValue =>
|
||||
{
|
||||
return [...prevValue, new RoomCameraWidgetSelectedEffect(effect, 1)];
|
||||
return [ ...prevValue, new RoomCameraWidgetSelectedEffect(effect, 1) ];
|
||||
});
|
||||
|
||||
setSelectedEffectName(effect.name);
|
||||
@ -124,7 +124,7 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
||||
|
||||
setSelectedEffects(prevValue =>
|
||||
{
|
||||
const clone = [...prevValue];
|
||||
const clone = [ ...prevValue ];
|
||||
|
||||
clone.splice(existingIndex, 1);
|
||||
|
||||
@ -154,7 +154,7 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
||||
setIsZoomed(!isZoomed);
|
||||
return;
|
||||
}
|
||||
}, [isZoomed, availableEffects, selectedEffectName, currentPictureUrl, getSelectedEffectIndex, onCancel, onCheckout, onClose, setIsZoomed, setSelectedEffects]);
|
||||
}, [ isZoomed, availableEffects, selectedEffectName, currentPictureUrl, getSelectedEffectIndex, onCancel, onCheckout, onClose, setIsZoomed, setSelectedEffects ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -164,14 +164,14 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
||||
|
||||
for await (const effect of availableEffects)
|
||||
{
|
||||
const image = await GetRoomCameraWidgetManager().applyEffects(picture.texture, [new RoomCameraWidgetSelectedEffect(effect, 1)], false);
|
||||
const image = await GetRoomCameraWidgetManager().applyEffects(picture.texture, [ new RoomCameraWidgetSelectedEffect(effect, 1) ], false);
|
||||
|
||||
thumbnails.push(new CameraPictureThumbnail(effect.name, image.src));
|
||||
}
|
||||
|
||||
setEffectsThumbnails(thumbnails);
|
||||
})();
|
||||
}, [picture, availableEffects]);
|
||||
}, [ picture, availableEffects ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -183,57 +183,57 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
||||
|
||||
setCurrentPictureUrl(imageUrl.src);
|
||||
})();
|
||||
}, [picture, selectedEffects, isZoomed]);
|
||||
}, [ picture, selectedEffects, isZoomed ]);
|
||||
|
||||
return (
|
||||
<NitroCardView className="w-[600px] h-[500px]">
|
||||
<NitroCardHeaderView headerText={LocalizeText('camera.editor.button.text')} onCloseClick={event => processAction('close')} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText('camera.editor.button.text') } onCloseClick={ event => processAction('close') } />
|
||||
<NitroCardTabsView>
|
||||
{TABS.map(tab =>
|
||||
{ TABS.map(tab =>
|
||||
{
|
||||
return <NitroCardTabsItemView key={tab} isActive={currentTab === tab} onClick={event => processAction('change_tab', tab)}><i className={'nitro-icon icon-camera-' + tab}></i></NitroCardTabsItemView>
|
||||
})}
|
||||
return <NitroCardTabsItemView key={ tab } isActive={ currentTab === tab } onClick={ event => processAction('change_tab', tab) }><i className={ 'nitro-icon icon-camera-' + tab }></i></NitroCardTabsItemView>
|
||||
}) }
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<Grid>
|
||||
<Column overflow="hidden" size={5}>
|
||||
<CameraWidgetEffectListView effects={getEffectList()} myLevel={myLevel} processAction={processAction} selectedEffects={selectedEffects} thumbnails={effectsThumbnails} />
|
||||
<Column overflow="hidden" size={ 5 }>
|
||||
<CameraWidgetEffectListView effects={ getEffectList() } myLevel={ myLevel } processAction={ processAction } selectedEffects={ selectedEffects } thumbnails={ effectsThumbnails } />
|
||||
</Column>
|
||||
<Column justifyContent="between" overflow="hidden" size={7}>
|
||||
<Column justifyContent="between" overflow="hidden" size={ 7 }>
|
||||
<Column center>
|
||||
<LayoutImage className="w-[320px] h-[320px]" imageUrl={currentPictureUrl} />
|
||||
{selectedEffectName &&
|
||||
<Column center fullWidth gap={1}>
|
||||
<Text>{LocalizeText('camera.effect.name.' + selectedEffectName)}</Text>
|
||||
<LayoutImage className="w-[320px] h-[320px]" imageUrl={ currentPictureUrl } />
|
||||
{ selectedEffectName &&
|
||||
<Column center fullWidth gap={ 1 }>
|
||||
<Text>{ LocalizeText('camera.effect.name.' + selectedEffectName) }</Text>
|
||||
<ReactSlider
|
||||
className={'nitro-slider'}
|
||||
max={1}
|
||||
min={0}
|
||||
renderThumb={(props, state) => <div {...props}>{state.valueNow}</div>}
|
||||
step={0.01}
|
||||
value={getCurrentEffect.alpha}
|
||||
onChange={event => setSelectedEffectAlpha(event)} />
|
||||
</Column>}
|
||||
className={ 'nitro-slider' }
|
||||
max={ 1 }
|
||||
min={ 0 }
|
||||
renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> }
|
||||
step={ 0.01 }
|
||||
value={ getCurrentEffect.alpha }
|
||||
onChange={ event => setSelectedEffectAlpha(event) } />
|
||||
</Column> }
|
||||
</Column>
|
||||
<div className="flex justify-between">
|
||||
<div className="relative inline-flex align-middle">
|
||||
<Button onClick={event => processAction('clear_effects')}>
|
||||
<Button onClick={ event => processAction('clear_effects') }>
|
||||
<FaTrash className="fa-icon" />
|
||||
</Button>
|
||||
<Button onClick={event => processAction('download')}>
|
||||
<Button onClick={ event => processAction('download') }>
|
||||
<FaSave className="fa-icon" />
|
||||
</Button>
|
||||
<Button onClick={event => processAction('zoom')}>
|
||||
{isZoomed && <FaSearchMinus className="fa-icon" />}
|
||||
{!isZoomed && <FaSearchPlus className="fa-icon" />}
|
||||
<Button onClick={ event => processAction('zoom') }>
|
||||
{ isZoomed && <FaSearchMinus className="fa-icon" /> }
|
||||
{ !isZoomed && <FaSearchPlus className="fa-icon" /> }
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
<Button onClick={event => processAction('cancel')}>
|
||||
{LocalizeText('generic.cancel')}
|
||||
<Button onClick={ event => processAction('cancel') }>
|
||||
{ LocalizeText('generic.cancel') }
|
||||
</Button>
|
||||
<Button onClick={event => processAction('checkout')}>
|
||||
{LocalizeText('camera.preview.button.text')}
|
||||
<Button onClick={ event => processAction('checkout') }>
|
||||
{ LocalizeText('camera.preview.button.text') }
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -22,22 +22,22 @@ export const CameraWidgetEffectListItemView: FC<CameraWidgetEffectListItemViewPr
|
||||
<>
|
||||
asassa
|
||||
|
||||
<LayoutGridItem itemActive={isActive} title={LocalizeText(!isLocked ? (`camera.effect.name.${effect.name}`) : `camera.effect.required.level ${effect.minLevel}`)} onClick={event => (!isActive && selectEffect())}>
|
||||
{isActive &&
|
||||
<Button className="rounded-circle remove-effect" variant="danger" onClick={removeEffect}>
|
||||
<LayoutGridItem itemActive={ isActive } title={ LocalizeText(!isLocked ? (`camera.effect.name.${ effect.name }`) : `camera.effect.required.level ${ effect.minLevel }`) } onClick={ event => (!isActive && selectEffect()) }>
|
||||
{ isActive &&
|
||||
<Button className="rounded-circle remove-effect" variant="danger" onClick={ removeEffect }>
|
||||
<FaTimes className="fa-icon" />
|
||||
</Button>}
|
||||
{!isLocked && (thumbnailUrl && thumbnailUrl.length > 0) &&
|
||||
</Button> }
|
||||
{ !isLocked && (thumbnailUrl && thumbnailUrl.length > 0) &&
|
||||
<div className="effect-thumbnail-image border">
|
||||
<img alt="" src={thumbnailUrl} />
|
||||
</div>}
|
||||
{isLocked &&
|
||||
<img alt="" src={ thumbnailUrl } />
|
||||
</div> }
|
||||
{ isLocked &&
|
||||
<Text bold center>
|
||||
<div>
|
||||
<FaLock className="fa-icon" />
|
||||
</div>
|
||||
{effect.minLevel}
|
||||
</Text>}
|
||||
{ effect.minLevel }
|
||||
</Text> }
|
||||
</LayoutGridItem>
|
||||
</>
|
||||
);
|
||||
|
@ -18,14 +18,14 @@ export const CameraWidgetEffectListView: FC<CameraWidgetEffectListViewProps> = p
|
||||
const { myLevel = 0, selectedEffects = [], effects = [], thumbnails = [], processAction = null } = props;
|
||||
|
||||
return (
|
||||
<Grid columnCount={3} overflow="auto">
|
||||
{effects && (effects.length > 0) && effects.map((effect, index) =>
|
||||
<Grid columnCount={ 3 } overflow="auto">
|
||||
{ effects && (effects.length > 0) && effects.map((effect, index) =>
|
||||
{
|
||||
const thumbnailUrl = (thumbnails.find(thumbnail => (thumbnail.effectName === effect.name)));
|
||||
const isActive = (selectedEffects.findIndex(selectedEffect => (selectedEffect.effect.name === effect.name)) > -1);
|
||||
|
||||
return <CameraWidgetEffectListItemView key={index} effect={effect} isActive={isActive} isLocked={(effect.minLevel > myLevel)} removeEffect={() => processAction('remove_effect', effect.name)} selectEffect={() => processAction('select_effect', effect.name)} thumbnailUrl={((thumbnailUrl && thumbnailUrl.thumbnailUrl) || null)} />
|
||||
})}
|
||||
return <CameraWidgetEffectListItemView key={ index } effect={ effect } isActive={ isActive } isLocked={ (effect.minLevel > myLevel) } removeEffect={ () => processAction('remove_effect', effect.name) } selectEffect={ () => processAction('select_effect', effect.name) } thumbnailUrl={ ((thumbnailUrl && thumbnailUrl.thumbnailUrl) || null) } />
|
||||
}) }
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ const TOTAL_SHOWN_ITEMS = 5;
|
||||
export const CalendarView: FC<CalendarViewProps> = props =>
|
||||
{
|
||||
const { onClose = null, campaignName = null, currentDay = null, numDays = null, missedDays = null, openedDays = null, openPackage = null, receivedProducts = null } = props;
|
||||
const [selectedDay, setSelectedDay] = useState(currentDay);
|
||||
const [index, setIndex] = useState(Math.max(0, (selectedDay - 1)));
|
||||
const [ selectedDay, setSelectedDay ] = useState(currentDay);
|
||||
const [ index, setIndex ] = useState(Math.max(0, (selectedDay - 1)));
|
||||
|
||||
const getDayState = (day: number) =>
|
||||
{
|
||||
@ -98,44 +98,44 @@ export const CalendarView: FC<CalendarViewProps> = props =>
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-campaign-calendar" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={LocalizeText(`campaign.calendar.${campaignName}.title`)} onCloseClick={onClose} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText(`campaign.calendar.${ campaignName }.title`) } onCloseClick={ onClose } />
|
||||
<NitroCardContentView>
|
||||
<Grid alignItems="center" fullHeight={false} justifyContent="between">
|
||||
<Column size={1} />
|
||||
<Column size={10}>
|
||||
<Grid alignItems="center" fullHeight={ false } justifyContent="between">
|
||||
<Column size={ 1 } />
|
||||
<Column size={ 10 }>
|
||||
<div className="flex items-center gap-1 justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text fontSize={3}>{LocalizeText('campaign.calendar.heading.day', ['number'], [(selectedDay + 1).toString()])}</Text>
|
||||
<Text>{dayMessage(selectedDay)}</Text>
|
||||
<Text fontSize={ 3 }>{ LocalizeText('campaign.calendar.heading.day', [ 'number' ], [ (selectedDay + 1).toString() ]) }</Text>
|
||||
<Text>{ dayMessage(selectedDay) }</Text>
|
||||
</div>
|
||||
<div>
|
||||
{GetSessionDataManager().isModerator &&
|
||||
<Button variant="danger" onClick={forceOpen}>Force open</Button>}
|
||||
{ GetSessionDataManager().isModerator &&
|
||||
<Button variant="danger" onClick={ forceOpen }>Force open</Button> }
|
||||
</div>
|
||||
</div>
|
||||
</Column>
|
||||
<Column size={1} />
|
||||
<Column size={ 1 } />
|
||||
</Grid>
|
||||
<div className="flex h-full gap-2">
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="campaign-spritesheet prev cursor-pointer" onClick={onClickPrev} />
|
||||
<div className="campaign-spritesheet prev cursor-pointer" onClick={ onClickPrev } />
|
||||
</div>
|
||||
<Column center fullWidth>
|
||||
<Grid fit columnCount={TOTAL_SHOWN_ITEMS} gap={1}>
|
||||
{[...Array(TOTAL_SHOWN_ITEMS)].map((e, i) =>
|
||||
<Grid fit columnCount={ TOTAL_SHOWN_ITEMS } gap={ 1 }>
|
||||
{ [ ...Array(TOTAL_SHOWN_ITEMS) ].map((e, i) =>
|
||||
{
|
||||
const day = (index + i);
|
||||
|
||||
return (
|
||||
<Column key={i} overflow="hidden">
|
||||
<CalendarItemView active={(selectedDay === day)} itemId={day} product={receivedProducts.has(day) ? receivedProducts.get(day) : null} state={getDayState(day)} onClick={onClickItem} />
|
||||
<Column key={ i } overflow="hidden">
|
||||
<CalendarItemView active={ (selectedDay === day) } itemId={ day } product={ receivedProducts.has(day) ? receivedProducts.get(day) : null } state={ getDayState(day) } onClick={ onClickItem } />
|
||||
</Column>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</Grid>
|
||||
</Column>
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="campaign-spritesheet next cursor-pointer" onClick={onClickNext} />
|
||||
<div className="campaign-spritesheet next cursor-pointer" onClick={ onClickNext } />
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
|
@ -64,46 +64,46 @@ export const CatalogView: FC<{}> = props =>
|
||||
AddLinkEventTracker(linkTracker);
|
||||
|
||||
return () => RemoveLinkEventTracker(linkTracker);
|
||||
}, [setIsVisible, openPageByOfferId, openPageByName]);
|
||||
}, [ setIsVisible, openPageByOfferId, openPageByName ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isVisible &&
|
||||
<NitroCardView className="w-[630px] h-[400px]" style={GetConfigurationValue('catalog.headers') ? { width: 710 } : {}} uniqueKey="catalog">
|
||||
<NitroCardHeaderView headerText={LocalizeText('catalog.title')} onCloseClick={event => setIsVisible(false)} />
|
||||
{ isVisible &&
|
||||
<NitroCardView className="w-[630px] h-[400px]" style={ GetConfigurationValue('catalog.headers') ? { width: 710 } : {} } uniqueKey="catalog">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('catalog.title') } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardTabsView>
|
||||
{rootNode && (rootNode.children.length > 0) && rootNode.children.map(child =>
|
||||
{ rootNode && (rootNode.children.length > 0) && rootNode.children.map(child =>
|
||||
{
|
||||
if (!child.isVisible) return null;
|
||||
|
||||
return (
|
||||
<NitroCardTabsItemView key={child.pageId} isActive={child.isActive} onClick={event =>
|
||||
<NitroCardTabsItemView key={ child.pageId } isActive={ child.isActive } onClick={ event =>
|
||||
{
|
||||
if (searchResult) setSearchResult(null);
|
||||
|
||||
activateNode(child);
|
||||
}} >
|
||||
<div className={`flex items-center gap-${GetConfigurationValue('catalog.tab.icons') ? 1 : 0}`}>
|
||||
{GetConfigurationValue('catalog.tab.icons') && <CatalogIconView icon={child.iconId} />}
|
||||
{child.localization}
|
||||
} } >
|
||||
<div className={ `flex items-center gap-${ GetConfigurationValue('catalog.tab.icons') ? 1 : 0 }` }>
|
||||
{ GetConfigurationValue('catalog.tab.icons') && <CatalogIconView icon={ child.iconId } /> }
|
||||
{ child.localization }
|
||||
</div>
|
||||
</NitroCardTabsItemView>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<Grid>
|
||||
{!navigationHidden &&
|
||||
<Column overflow="hidden" size={3}>
|
||||
{activeNodes && (activeNodes.length > 0) &&
|
||||
<CatalogNavigationView node={activeNodes[0]} />}
|
||||
</Column>}
|
||||
<Column overflow="hidden" size={!navigationHidden ? 9 : 12}>
|
||||
{GetCatalogLayout(currentPage, () => setNavigationHidden(true))}
|
||||
{ !navigationHidden &&
|
||||
<Column overflow="hidden" size={ 3 }>
|
||||
{ activeNodes && (activeNodes.length > 0) &&
|
||||
<CatalogNavigationView node={ activeNodes[0] } /> }
|
||||
</Column> }
|
||||
<Column overflow="hidden" size={ !navigationHidden ? 9 : 12 }>
|
||||
{ GetCatalogLayout(currentPage, () => setNavigationHidden(true)) }
|
||||
</Column>
|
||||
</Grid>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>}
|
||||
</NitroCardView> }
|
||||
<CatalogGiftView />
|
||||
<MarketplacePostOfferView />
|
||||
</>
|
||||
|
@ -9,17 +9,17 @@ export interface CatalogHeaderViewProps
|
||||
export const CatalogHeaderView: FC<CatalogHeaderViewProps> = props =>
|
||||
{
|
||||
const { imageUrl = null } = props;
|
||||
const [displayImageUrl, setDisplayImageUrl] = useState('');
|
||||
const [ displayImageUrl, setDisplayImageUrl ] = useState('');
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setDisplayImageUrl(imageUrl ?? GetConfigurationValue<string>('catalog.asset.image.url').replace('%name%', 'catalog_header_roombuilder'));
|
||||
}, [imageUrl]);
|
||||
}, [ imageUrl ]);
|
||||
|
||||
return <div className="flex justify-center items-center w-full nitro-catalog-header">
|
||||
<img src={displayImageUrl} onError={({ currentTarget }) =>
|
||||
<img src={ displayImageUrl } onError={ ({ currentTarget }) =>
|
||||
{
|
||||
currentTarget.src = GetConfigurationValue<string>('catalog.asset.image.url').replace('%name%', 'catalog_header_roombuilder');
|
||||
}} />
|
||||
} } />
|
||||
</div>;
|
||||
}
|
||||
|
@ -9,26 +9,26 @@ import { classNames } from '../../../../layout';
|
||||
|
||||
export const CatalogGiftView: FC<{}> = props =>
|
||||
{
|
||||
const [isVisible, setIsVisible] = useState<boolean>(false);
|
||||
const [pageId, setPageId] = useState<number>(0);
|
||||
const [offerId, setOfferId] = useState<number>(0);
|
||||
const [extraData, setExtraData] = useState<string>('');
|
||||
const [receiverName, setReceiverName] = useState<string>('');
|
||||
const [showMyFace, setShowMyFace] = useState<boolean>(true);
|
||||
const [message, setMessage] = useState<string>('');
|
||||
const [colors, setColors] = useState<{ id: number, color: string }[]>([]);
|
||||
const [selectedBoxIndex, setSelectedBoxIndex] = useState<number>(0);
|
||||
const [selectedRibbonIndex, setSelectedRibbonIndex] = useState<number>(0);
|
||||
const [selectedColorId, setSelectedColorId] = useState<number>(0);
|
||||
const [maxBoxIndex, setMaxBoxIndex] = useState<number>(0);
|
||||
const [maxRibbonIndex, setMaxRibbonIndex] = useState<number>(0);
|
||||
const [receiverNotFound, setReceiverNotFound] = useState<boolean>(false);
|
||||
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
||||
const [ pageId, setPageId ] = useState<number>(0);
|
||||
const [ offerId, setOfferId ] = useState<number>(0);
|
||||
const [ extraData, setExtraData ] = useState<string>('');
|
||||
const [ receiverName, setReceiverName ] = useState<string>('');
|
||||
const [ showMyFace, setShowMyFace ] = useState<boolean>(true);
|
||||
const [ message, setMessage ] = useState<string>('');
|
||||
const [ colors, setColors ] = useState<{ id: number, color: string }[]>([]);
|
||||
const [ selectedBoxIndex, setSelectedBoxIndex ] = useState<number>(0);
|
||||
const [ selectedRibbonIndex, setSelectedRibbonIndex ] = useState<number>(0);
|
||||
const [ selectedColorId, setSelectedColorId ] = useState<number>(0);
|
||||
const [ maxBoxIndex, setMaxBoxIndex ] = useState<number>(0);
|
||||
const [ maxRibbonIndex, setMaxRibbonIndex ] = useState<number>(0);
|
||||
const [ receiverNotFound, setReceiverNotFound ] = useState<boolean>(false);
|
||||
const { catalogOptions = null } = useCatalog();
|
||||
const { friends } = useFriends();
|
||||
const { giftConfiguration = null } = catalogOptions;
|
||||
const [boxTypes, setBoxTypes] = useState<number[]>([]);
|
||||
const [suggestions, setSuggestions] = useState([]);
|
||||
const [isAutocompleteVisible, setIsAutocompleteVisible] = useState(true);
|
||||
const [ boxTypes, setBoxTypes ] = useState<number[]>([]);
|
||||
const [ suggestions, setSuggestions ] = useState([]);
|
||||
const [ isAutocompleteVisible, setIsAutocompleteVisible ] = useState(true);
|
||||
|
||||
const onClose = useCallback(() =>
|
||||
{
|
||||
@ -45,19 +45,19 @@ export const CatalogGiftView: FC<{}> = props =>
|
||||
setSuggestions([]);
|
||||
|
||||
if (colors.length) setSelectedColorId(colors[0].id);
|
||||
}, [colors]);
|
||||
}, [ colors ]);
|
||||
|
||||
const isBoxDefault = useMemo(() =>
|
||||
{
|
||||
return giftConfiguration ? (giftConfiguration.defaultStuffTypes.findIndex(s => (s === boxTypes[selectedBoxIndex])) > -1) : false;
|
||||
}, [boxTypes, giftConfiguration, selectedBoxIndex]);
|
||||
}, [ boxTypes, giftConfiguration, selectedBoxIndex ]);
|
||||
|
||||
const boxExtraData = useMemo(() =>
|
||||
{
|
||||
if (!giftConfiguration) return '';
|
||||
|
||||
return ((boxTypes[selectedBoxIndex] * 1000) + giftConfiguration.ribbonTypes[selectedRibbonIndex]).toString();
|
||||
}, [giftConfiguration, selectedBoxIndex, selectedRibbonIndex, boxTypes]);
|
||||
}, [ giftConfiguration, selectedBoxIndex, selectedRibbonIndex, boxTypes ]);
|
||||
|
||||
const isColorable = useMemo(() =>
|
||||
{
|
||||
@ -68,12 +68,12 @@ export const CatalogGiftView: FC<{}> = props =>
|
||||
const boxType = boxTypes[selectedBoxIndex];
|
||||
|
||||
return (boxType === 8 || (boxType >= 3 && boxType <= 6)) ? false : true;
|
||||
}, [giftConfiguration, selectedBoxIndex, isBoxDefault, boxTypes]);
|
||||
}, [ giftConfiguration, selectedBoxIndex, isBoxDefault, boxTypes ]);
|
||||
|
||||
const colourId = useMemo(() =>
|
||||
{
|
||||
return isBoxDefault ? boxTypes[selectedBoxIndex] : selectedColorId;
|
||||
}, [isBoxDefault, boxTypes, selectedBoxIndex, selectedColorId])
|
||||
}, [ isBoxDefault, boxTypes, selectedBoxIndex, selectedColorId ])
|
||||
|
||||
const allFriends = friends.filter((friend: MessengerFriend) => friend.id !== -1);
|
||||
|
||||
@ -125,13 +125,13 @@ export const CatalogGiftView: FC<{}> = props =>
|
||||
SendMessageComposer(new PurchaseFromCatalogAsGiftComposer(pageId, offerId, extraData, receiverName, message, colourId, selectedBoxIndex, selectedRibbonIndex, showMyFace));
|
||||
return;
|
||||
}
|
||||
}, [colourId, extraData, maxBoxIndex, maxRibbonIndex, message, offerId, pageId, receiverName, selectedBoxIndex, selectedRibbonIndex, showMyFace]);
|
||||
}, [ colourId, extraData, maxBoxIndex, maxRibbonIndex, message, offerId, pageId, receiverName, selectedBoxIndex, selectedRibbonIndex, showMyFace ]);
|
||||
|
||||
useMessageEvent<GiftReceiverNotFoundEvent>(GiftReceiverNotFoundEvent, event => setReceiverNotFound(true));
|
||||
|
||||
useUiEvent([
|
||||
CatalogPurchasedEvent.PURCHASE_SUCCESS,
|
||||
CatalogEvent.INIT_GIFT], event =>
|
||||
CatalogEvent.INIT_GIFT ], event =>
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
@ -154,7 +154,7 @@ export const CatalogGiftView: FC<{}> = props =>
|
||||
useEffect(() =>
|
||||
{
|
||||
setReceiverNotFound(false);
|
||||
}, [receiverName]);
|
||||
}, [ receiverName ]);
|
||||
|
||||
const createBoxTypes = useCallback(() =>
|
||||
{
|
||||
@ -162,7 +162,7 @@ export const CatalogGiftView: FC<{}> = props =>
|
||||
|
||||
setBoxTypes(prev =>
|
||||
{
|
||||
let newPrev = [...giftConfiguration.boxTypes];
|
||||
let newPrev = [ ...giftConfiguration.boxTypes ];
|
||||
|
||||
newPrev.push(giftConfiguration.defaultStuffTypes[Math.floor((Math.random() * (giftConfiguration.defaultStuffTypes.length - 1)))]);
|
||||
|
||||
@ -171,7 +171,7 @@ export const CatalogGiftView: FC<{}> = props =>
|
||||
|
||||
return newPrev;
|
||||
})
|
||||
}, [giftConfiguration])
|
||||
}, [ giftConfiguration ])
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -195,93 +195,93 @@ export const CatalogGiftView: FC<{}> = props =>
|
||||
setSelectedColorId(newColors[0].id);
|
||||
setColors(newColors);
|
||||
}
|
||||
}, [giftConfiguration, createBoxTypes]);
|
||||
}, [ giftConfiguration, createBoxTypes ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (!isVisible) return;
|
||||
|
||||
createBoxTypes();
|
||||
}, [createBoxTypes, isVisible])
|
||||
}, [ createBoxTypes, isVisible ])
|
||||
|
||||
if (!giftConfiguration || !giftConfiguration.isEnabled || !isVisible) return null;
|
||||
|
||||
const boxName = 'catalog.gift_wrapping_new.box.' + (isBoxDefault ? 'default' : boxTypes[selectedBoxIndex]);
|
||||
const ribbonName = `catalog.gift_wrapping_new.ribbon.${selectedRibbonIndex}`;
|
||||
const ribbonName = `catalog.gift_wrapping_new.ribbon.${ selectedRibbonIndex }`;
|
||||
const priceText = 'catalog.gift_wrapping_new.' + (isBoxDefault ? 'freeprice' : 'price');
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-catalog-gift" theme="primary-slim" uniqueKey="catalog-gift">
|
||||
<NitroCardHeaderView headerText={LocalizeText('catalog.gift_wrapping.title')} onCloseClick={onClose} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText('catalog.gift_wrapping.title') } onCloseClick={ onClose } />
|
||||
<NitroCardContentView className="text-black">
|
||||
<FormGroup column>
|
||||
<Text>{LocalizeText('catalog.gift_wrapping.receiver')}</Text>
|
||||
<input className={classNames('form-control form-control-sm', receiverNotFound && 'is-invalid')} type="text" value={receiverName} onChange={(e) => onTextChanged(e)} />
|
||||
{(suggestions.length > 0 && isAutocompleteVisible) &&
|
||||
<Text>{ LocalizeText('catalog.gift_wrapping.receiver') }</Text>
|
||||
<input className={ classNames('form-control form-control-sm', receiverNotFound && 'is-invalid') } type="text" value={ receiverName } onChange={ (e) => onTextChanged(e) } />
|
||||
{ (suggestions.length > 0 && isAutocompleteVisible) &&
|
||||
<Column className="autocomplete-gift-container">
|
||||
{suggestions.map((friend: MessengerFriend) => (
|
||||
<div key={friend.id} className="autocomplete-gift-item" onClick={(e) => selectedReceiverName(friend.name)}>{friend.name}</div>
|
||||
))}
|
||||
{ suggestions.map((friend: MessengerFriend) => (
|
||||
<div key={ friend.id } className="autocomplete-gift-item" onClick={ (e) => selectedReceiverName(friend.name) }>{ friend.name }</div>
|
||||
)) }
|
||||
</Column>
|
||||
}
|
||||
{receiverNotFound &&
|
||||
<div className="invalid-feedback">{LocalizeText('catalog.gift_wrapping.receiver_not_found.title')}</div>}
|
||||
{ receiverNotFound &&
|
||||
<div className="invalid-feedback">{ LocalizeText('catalog.gift_wrapping.receiver_not_found.title') }</div> }
|
||||
</FormGroup>
|
||||
<LayoutGiftTagView editable={true} figure={GetSessionDataManager().figure} message={message} userName={GetSessionDataManager().userName} onChange={(value) => setMessage(value)} />
|
||||
<LayoutGiftTagView editable={ true } figure={ GetSessionDataManager().figure } message={ message } userName={ GetSessionDataManager().userName } onChange={ (value) => setMessage(value) } />
|
||||
<div className="form-check">
|
||||
<input checked={showMyFace} className="form-check-input" name="showMyFace" type="checkbox" onChange={(e) => setShowMyFace(value => !value)} />
|
||||
<label className="form-check-label">{LocalizeText('catalog.gift_wrapping.show_face.title')}</label>
|
||||
<input checked={ showMyFace } className="form-check-input" name="showMyFace" type="checkbox" onChange={ (e) => setShowMyFace(value => !value) } />
|
||||
<label className="form-check-label">{ LocalizeText('catalog.gift_wrapping.show_face.title') }</label>
|
||||
</div>
|
||||
<div className="items-center gap-2">
|
||||
{selectedColorId &&
|
||||
{ selectedColorId &&
|
||||
<div className="gift-preview">
|
||||
<LayoutFurniImageView extraData={boxExtraData} productClassId={colourId} productType={ProductTypeEnum.FLOOR} />
|
||||
</div>}
|
||||
<LayoutFurniImageView extraData={ boxExtraData } productClassId={ colourId } productType={ ProductTypeEnum.FLOOR } />
|
||||
</div> }
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex gap-2">
|
||||
<div className="relative inline-flex align-middle">
|
||||
<Button variant="primary" onClick={() => handleAction('prev_box')}>
|
||||
<Button variant="primary" onClick={ () => handleAction('prev_box') }>
|
||||
<FaChevronLeft className="fa-icon" />
|
||||
</Button>
|
||||
<Button variant="primary" onClick={() => handleAction('next_box')}>
|
||||
<Button variant="primary" onClick={ () => handleAction('next_box') }>
|
||||
<FaChevronRight className="fa-icon" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text fontWeight="bold">{LocalizeText(boxName)}</Text>
|
||||
<Text fontWeight="bold">{ LocalizeText(boxName) }</Text>
|
||||
<div className="flex items-center gap-1">
|
||||
{LocalizeText(priceText, ['price'], [giftConfiguration.price.toString()])}
|
||||
<LayoutCurrencyIcon type={-1} />
|
||||
{ LocalizeText(priceText, [ 'price' ], [ giftConfiguration.price.toString() ]) }
|
||||
<LayoutCurrencyIcon type={ -1 } />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Flex alignItems="center" className={isColorable ? '' : 'opacity-50 pointer-events-none'} gap={2}>
|
||||
<Flex alignItems="center" className={ isColorable ? '' : 'opacity-50 pointer-events-none' } gap={ 2 }>
|
||||
<div className="relative inline-flex align-middle">
|
||||
<Button variant="primary" onClick={() => handleAction('prev_ribbon')}>
|
||||
<Button variant="primary" onClick={ () => handleAction('prev_ribbon') }>
|
||||
<FaChevronLeft className="fa-icon" />
|
||||
</Button>
|
||||
<Button variant="primary" onClick={() => handleAction('next_ribbon')}>
|
||||
<Button variant="primary" onClick={ () => handleAction('next_ribbon') }>
|
||||
<FaChevronRight className="fa-icon" />
|
||||
</Button>
|
||||
</div>
|
||||
<Text fontWeight="bold">{LocalizeText(ribbonName)}</Text>
|
||||
<Text fontWeight="bold">{ LocalizeText(ribbonName) }</Text>
|
||||
</Flex>
|
||||
</div>
|
||||
</div>
|
||||
<Column className={isColorable ? '' : 'opacity-50 pointer-events-none'} gap={1}>
|
||||
<Column className={ isColorable ? '' : 'opacity-50 pointer-events-none' } gap={ 1 }>
|
||||
<Text fontWeight="bold">
|
||||
{LocalizeText('catalog.gift_wrapping.pick_color')}
|
||||
{ LocalizeText('catalog.gift_wrapping.pick_color') }
|
||||
</Text>
|
||||
<div className="relative inline-flex align-middle w-full">
|
||||
{colors.map(color => <Button key={color.id} active={(color.id === selectedColorId)} disabled={!isColorable} style={{ backgroundColor: color.color }} variant="dark" onClick={() => setSelectedColorId(color.id)} />)}
|
||||
{ colors.map(color => <Button key={ color.id } active={ (color.id === selectedColorId) } disabled={ !isColorable } style={ { backgroundColor: color.color } } variant="dark" onClick={ () => setSelectedColorId(color.id) } />) }
|
||||
</div>
|
||||
</Column>
|
||||
<div className="flex items-center justify-between">
|
||||
<Button className="text-black" variant="link" onClick={onClose}>
|
||||
{LocalizeText('cancel')}
|
||||
<Button className="text-black" variant="link" onClick={ onClose }>
|
||||
{ LocalizeText('cancel') }
|
||||
</Button>
|
||||
<Button variant="success" onClick={() => handleAction('buy')}>
|
||||
{LocalizeText('catalog.gift_wrapping.give_gift')}
|
||||
<Button variant="success" onClick={ () => handleAction('buy') }>
|
||||
{ LocalizeText('catalog.gift_wrapping.give_gift') }
|
||||
</Button>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
|
@ -18,19 +18,18 @@ export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = pro
|
||||
const { activateNode = null } = useCatalog();
|
||||
|
||||
return (
|
||||
<div className={child ? 'pl-[5px] border-s-2 border-[#b6bec5]' : ''}>
|
||||
<LayoutGridItem className={' !h-[23px] bg-[#cdd3d9] !border-[0] px-[3px] py-px text-sm'} column={false} gap={1} itemActive={node.isActive} onClick={event => activateNode(node)}>
|
||||
<CatalogIconView icon={node.iconId} />
|
||||
<Text truncate className="!flex-grow">{node.localization}</Text>
|
||||
{node.isBranch &&
|
||||
<div className={ child ? 'pl-[5px] border-s-2 border-[#b6bec5]' : '' }>
|
||||
<LayoutGridItem className={ ' !h-[23px] bg-[#cdd3d9] !border-[0] px-[3px] py-px text-sm' } column={ false } gap={ 1 } itemActive={ node.isActive } onClick={ event => activateNode(node) }>
|
||||
<CatalogIconView icon={ node.iconId } />
|
||||
<Text truncate className="!flex-grow">{ node.localization }</Text>
|
||||
{ node.isBranch &&
|
||||
<>
|
||||
{node.isOpen && <FaCaretUp className="fa-icon" />}
|
||||
{!node.isOpen && <FaCaretDown className="fa-icon" />}
|
||||
</>}
|
||||
{ node.isOpen && <FaCaretUp className="fa-icon" /> }
|
||||
{ !node.isOpen && <FaCaretDown className="fa-icon" /> }
|
||||
</> }
|
||||
</LayoutGridItem>
|
||||
|
||||
{node.isOpen && node.isBranch &&
|
||||
<CatalogNavigationSetView child={true} node={node} />}
|
||||
{ node.isOpen && node.isBranch &&
|
||||
<CatalogNavigationSetView child={ true } node={ node } /> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -20,13 +20,13 @@ export const CatalogNavigationView: FC<CatalogNavigationViewProps> = props =>
|
||||
<>
|
||||
<CatalogSearchView />
|
||||
<Column fullHeight className="!border-[#b6bec5] bg-[#cdd3d9] border-[2px] border-[solid] rounded p-1" overflow="hidden">
|
||||
<AutoGrid columnCount={1} gap={1} id="nitro-catalog-main-navigation">
|
||||
{searchResult && (searchResult.filteredNodes.length > 0) && searchResult.filteredNodes.map((n, index) =>
|
||||
<AutoGrid columnCount={ 1 } gap={ 1 } id="nitro-catalog-main-navigation">
|
||||
{ searchResult && (searchResult.filteredNodes.length > 0) && searchResult.filteredNodes.map((n, index) =>
|
||||
{
|
||||
return <CatalogNavigationItemView key={index} node={n} />;
|
||||
})}
|
||||
{!searchResult &&
|
||||
<CatalogNavigationSetView node={node} />}
|
||||
return <CatalogNavigationItemView key={ index } node={ n } />;
|
||||
}) }
|
||||
{ !searchResult &&
|
||||
<CatalogNavigationSetView node={ node } /> }
|
||||
</AutoGrid>
|
||||
</Column>
|
||||
</>
|
||||
|
@ -14,8 +14,8 @@ export interface CatalogRedeemVoucherViewProps
|
||||
export const CatalogRedeemVoucherView: FC<CatalogRedeemVoucherViewProps> = props =>
|
||||
{
|
||||
const { text = null } = props;
|
||||
const [voucher, setVoucher] = useState<string>('');
|
||||
const [isWaiting, setIsWaiting] = useState(false);
|
||||
const [ voucher, setVoucher ] = useState<string>('');
|
||||
const [ isWaiting, setIsWaiting ] = useState(false);
|
||||
const { simpleAlert = null } = useNotification();
|
||||
|
||||
const redeemVoucher = () =>
|
||||
@ -33,7 +33,7 @@ export const CatalogRedeemVoucherView: FC<CatalogRedeemVoucherViewProps> = props
|
||||
|
||||
let message = LocalizeText('catalog.alert.voucherredeem.ok.description');
|
||||
|
||||
if (parser.productName) message = LocalizeText('catalog.alert.voucherredeem.ok.description.furni', ['productName', 'productDescription'], [parser.productName, parser.productDescription]);
|
||||
if (parser.productName) message = LocalizeText('catalog.alert.voucherredeem.ok.description.furni', [ 'productName', 'productDescription' ], [ parser.productName, parser.productDescription ]);
|
||||
|
||||
simpleAlert(message, null, null, null, LocalizeText('catalog.alert.voucherredeem.ok.title'));
|
||||
|
||||
@ -45,7 +45,7 @@ export const CatalogRedeemVoucherView: FC<CatalogRedeemVoucherViewProps> = props
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
simpleAlert(LocalizeText(`catalog.alert.voucherredeem.error.description.${parser.errorCode}`), null, null, null, LocalizeText('catalog.alert.voucherredeem.error.title'));
|
||||
simpleAlert(LocalizeText(`catalog.alert.voucherredeem.error.description.${ parser.errorCode }`), null, null, null, LocalizeText('catalog.alert.voucherredeem.error.title'));
|
||||
|
||||
setIsWaiting(false);
|
||||
});
|
||||
@ -56,11 +56,10 @@ export const CatalogRedeemVoucherView: FC<CatalogRedeemVoucherViewProps> = props
|
||||
|
||||
|
||||
<NitroInput
|
||||
placeholder={text}
|
||||
value={voucher}
|
||||
onChange={event => setVoucher(event.target.value)} />
|
||||
|
||||
<Button disabled={isWaiting} variant="primary" onClick={redeemVoucher}>
|
||||
placeholder={ text }
|
||||
value={ voucher }
|
||||
onChange={ event => setVoucher(event.target.value) } />
|
||||
<Button disabled={ isWaiting } variant="primary" onClick={ redeemVoucher }>
|
||||
<FaTag className="fa-icon" />
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@ import { NitroInput } from '../../../../../layout';
|
||||
|
||||
export const CatalogSearchView: FC<{}> = props =>
|
||||
{
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const [ searchValue, setSearchValue ] = useState('');
|
||||
const { currentType = null, rootNode = null, offersToNodes = null, searchResult = null, setSearchResult = null, setCurrentPage = null } = useCatalog();
|
||||
|
||||
useEffect(() =>
|
||||
@ -37,7 +37,7 @@ export const CatalogSearchView: FC<{}> = props =>
|
||||
|
||||
if ((currentType === CatalogType.NORMAL) && furniture.excludeDynamic) continue;
|
||||
|
||||
const searchValues = [furniture.className, furniture.name, furniture.description].join(' ').replace(/ /gi, '').toLowerCase();
|
||||
const searchValues = [ furniture.className, furniture.name, furniture.description ].join(' ').replace(/ /gi, '').toLowerCase();
|
||||
|
||||
if ((currentType === CatalogType.BUILDER) && (furniture.purchaseOfferId === -1) && (furniture.rentOfferId === -1))
|
||||
{
|
||||
@ -75,7 +75,7 @@ export const CatalogSearchView: FC<{}> = props =>
|
||||
}, 300);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [offersToNodes, currentType, rootNode, searchValue, setCurrentPage, setSearchResult]);
|
||||
}, [ offersToNodes, currentType, rootNode, searchValue, setCurrentPage, setSearchResult ]);
|
||||
|
||||
return (
|
||||
<div className="flex gap-1">
|
||||
@ -87,20 +87,20 @@ export const CatalogSearchView: FC<{}> = props =>
|
||||
|
||||
|
||||
<NitroInput
|
||||
placeholder={LocalizeText('generic.search')}
|
||||
value={searchValue}
|
||||
onChange={event => setSearchValue(event.target.value)} />
|
||||
placeholder={ LocalizeText('generic.search') }
|
||||
value={ searchValue }
|
||||
onChange={ event => setSearchValue(event.target.value) } />
|
||||
|
||||
|
||||
</Flex>
|
||||
{(!searchValue || !searchValue.length) &&
|
||||
{ (!searchValue || !searchValue.length) &&
|
||||
<Button className="catalog-search-button" variant="primary">
|
||||
<FaSearch className="fa-icon" />
|
||||
</Button>}
|
||||
{searchValue && !!searchValue.length &&
|
||||
<Button className="catalog-search-button" variant="primary" onClick={event => setSearchValue('')}>
|
||||
</Button> }
|
||||
{ searchValue && !!searchValue.length &&
|
||||
<Button className="catalog-search-button" variant="primary" onClick={ event => setSearchValue('') }>
|
||||
<FaTimes className="fa-icon" />
|
||||
</Button>}
|
||||
</Button> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -20,33 +20,33 @@ export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutProps> = props =>
|
||||
<>
|
||||
<CatalogFirstProductSelectorWidgetView />
|
||||
<Grid>
|
||||
<Column overflow="hidden" size={7}>
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
<CatalogItemGridWidgetView shrink />
|
||||
<Column gap={1} overflow="hidden">
|
||||
<Text shrink truncate fontWeight="bold">{LocalizeText('catalog_selectbadge')}</Text>
|
||||
<Column gap={ 1 } overflow="hidden">
|
||||
<Text shrink truncate fontWeight="bold">{ LocalizeText('catalog_selectbadge') }</Text>
|
||||
<CatalogBadgeSelectorWidgetView />
|
||||
</Column>
|
||||
</Column>
|
||||
<Column center={!currentOffer} overflow="hidden" size={5}>
|
||||
{!currentOffer &&
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{!!page.localization.getImage(1) && <img alt="" src={page.localization.getImage(1)} />}
|
||||
<Text center dangerouslySetInnerHTML={{ __html: page.localization.getText(0) }} />
|
||||
</>}
|
||||
{currentOffer &&
|
||||
{ !!page.localization.getImage(1) && <img alt="" src={ page.localization.getImage(1) } /> }
|
||||
<Text center dangerouslySetInnerHTML={ { __html: page.localization.getText(0) } } />
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<div className="relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
</div>
|
||||
<Column className="!flex-grow" gap={1}>
|
||||
<Column className="!flex-grow" gap={ 1 }>
|
||||
<CatalogLimitedItemWidgetView />
|
||||
<Text truncate className="!flex-grow">{currentOffer.localizationName}</Text>
|
||||
<Text truncate className="!flex-grow">{ currentOffer.localizationName }</Text>
|
||||
<div className="flex justify-end">
|
||||
<CatalogTotalPriceWidget alignItems="end" />
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</>}
|
||||
</> }
|
||||
</Column>
|
||||
</Grid>
|
||||
</>
|
||||
|
@ -21,9 +21,9 @@ export interface CatalogLayoutColorGroupViewProps extends CatalogLayoutProps
|
||||
export const CatalogLayoutColorGroupingView: FC<CatalogLayoutColorGroupViewProps> = props =>
|
||||
{
|
||||
const { page = null } = props;
|
||||
const [colorableItems, setColorableItems] = useState<Map<string, number[]>>(new Map<string, number[]>());
|
||||
const [ colorableItems, setColorableItems ] = useState<Map<string, number[]>>(new Map<string, number[]>());
|
||||
const { currentOffer = null, setCurrentOffer = null } = useCatalog();
|
||||
const [colorsShowing, setColorsShowing] = useState<boolean>(false);
|
||||
const [ colorsShowing, setColorsShowing ] = useState<boolean>(false);
|
||||
|
||||
const sortByColorIndex = (a: IPurchasableOffer, b: IPurchasableOffer) =>
|
||||
{
|
||||
@ -63,7 +63,7 @@ export const CatalogLayoutColorGroupingView: FC<CatalogLayoutColorGroupViewProps
|
||||
|
||||
const selectColor = (colorIndex: number, productName: string) =>
|
||||
{
|
||||
const fullName = `${productName}*${colorIndex}`;
|
||||
const fullName = `${ productName }*${ colorIndex }`;
|
||||
const index = page.offers.findIndex(offer => offer.product.furnitureData.fullName === fullName);
|
||||
if (index > -1)
|
||||
{
|
||||
@ -128,39 +128,39 @@ export const CatalogLayoutColorGroupingView: FC<CatalogLayoutColorGroupViewProps
|
||||
offers.sort(sortyByFurnitureClassName);
|
||||
setColorableItems(updatedColorableItems);
|
||||
return offers;
|
||||
}, [page.offers]);
|
||||
}, [ page.offers ]);
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Column overflow="hidden" size={7}>
|
||||
<AutoGrid columnCount={5}>
|
||||
{(!colorsShowing || !currentOffer || !colorableItems.has(currentOffer.product.furnitureData.className)) &&
|
||||
offers.map((offer, index) => <CatalogGridOfferView key={index} itemActive={(currentOffer && (currentOffer.product.furnitureData.hasIndexedColor ? currentOffer.product.furnitureData.className === offer.product.furnitureData.className : currentOffer.offerId === offer.offerId))} offer={offer} selectOffer={selectOffer} />)
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
<AutoGrid columnCount={ 5 }>
|
||||
{ (!colorsShowing || !currentOffer || !colorableItems.has(currentOffer.product.furnitureData.className)) &&
|
||||
offers.map((offer, index) => <CatalogGridOfferView key={ index } itemActive={ (currentOffer && (currentOffer.product.furnitureData.hasIndexedColor ? currentOffer.product.furnitureData.className === offer.product.furnitureData.className : currentOffer.offerId === offer.offerId)) } offer={ offer } selectOffer={ selectOffer } />)
|
||||
}
|
||||
{(colorsShowing && currentOffer && colorableItems.has(currentOffer.product.furnitureData.className)) &&
|
||||
colorableItems.get(currentOffer.product.furnitureData.className).map((color, index) => <LayoutGridItem key={index} itemHighlight className="clear-bg" itemActive={(currentOffer.product.furnitureData.colorIndex === index)} itemColor={ColorConverter.int2rgb(color)} onClick={event => selectColor(index, currentOffer.product.furnitureData.className)} />)
|
||||
{ (colorsShowing && currentOffer && colorableItems.has(currentOffer.product.furnitureData.className)) &&
|
||||
colorableItems.get(currentOffer.product.furnitureData.className).map((color, index) => <LayoutGridItem key={ index } itemHighlight className="clear-bg" itemActive={ (currentOffer.product.furnitureData.colorIndex === index) } itemColor={ ColorConverter.int2rgb(color) } onClick={ event => selectColor(index, currentOffer.product.furnitureData.className) } />)
|
||||
}
|
||||
</AutoGrid>
|
||||
</Column>
|
||||
<Column center={!currentOffer} overflow="hidden" size={5}>
|
||||
{!currentOffer &&
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{!!page.localization.getImage(1) && <img alt="" src={page.localization.getImage(1)} />}
|
||||
<Text center dangerouslySetInnerHTML={{ __html: page.localization.getText(0) }} />
|
||||
</>}
|
||||
{currentOffer &&
|
||||
{ !!page.localization.getImage(1) && <img alt="" src={ page.localization.getImage(1) } /> }
|
||||
<Text center dangerouslySetInnerHTML={ { __html: page.localization.getText(0) } } />
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<div className="relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
<CatalogAddOnBadgeWidgetView className="bg-muted rounded bottom-1 end-1" position="absolute" />
|
||||
{currentOffer.product.furnitureData.hasIndexedColor &&
|
||||
<Button className="bottom-1 start-1" position="absolute" onClick={event => setColorsShowing(prev => !prev)}>
|
||||
{ currentOffer.product.furnitureData.hasIndexedColor &&
|
||||
<Button className="bottom-1 start-1" position="absolute" onClick={ event => setColorsShowing(prev => !prev) }>
|
||||
<FaFillDrip className="fa-icon" />
|
||||
</Button>}
|
||||
</Button> }
|
||||
</div>
|
||||
<Column className="!flex-grow" gap={1}>
|
||||
<Column className="!flex-grow" gap={ 1 }>
|
||||
<CatalogLimitedItemWidgetView />
|
||||
<Text truncate className="!flex-grow">{currentOffer.localizationName}</Text>
|
||||
<Text truncate className="!flex-grow">{ currentOffer.localizationName }</Text>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<CatalogSpinnerWidgetView />
|
||||
@ -169,7 +169,7 @@ export const CatalogLayoutColorGroupingView: FC<CatalogLayoutColorGroupViewProps
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</>}
|
||||
</> }
|
||||
</Column>
|
||||
</Grid>
|
||||
);
|
||||
|
@ -20,31 +20,31 @@ export const CatalogLayoutDefaultView: FC<CatalogLayoutProps> = props =>
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Column overflow="hidden" size={7}>
|
||||
{GetConfigurationValue('catalog.headers') &&
|
||||
<CatalogHeaderView imageUrl={currentPage.localization.getImage(0)} />}
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
{ GetConfigurationValue('catalog.headers') &&
|
||||
<CatalogHeaderView imageUrl={ currentPage.localization.getImage(0) } /> }
|
||||
<CatalogItemGridWidgetView />
|
||||
</Column>
|
||||
<Column center={!currentOffer} overflow="hidden" size={5}>
|
||||
{!currentOffer &&
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{!!page.localization.getImage(1) &&
|
||||
<LayoutImage imageUrl={page.localization.getImage(1)} />}
|
||||
<Text center dangerouslySetInnerHTML={{ __html: page.localization.getText(0) }} />
|
||||
</>}
|
||||
{currentOffer &&
|
||||
{ !!page.localization.getImage(1) &&
|
||||
<LayoutImage imageUrl={ page.localization.getImage(1) } /> }
|
||||
<Text center dangerouslySetInnerHTML={ { __html: page.localization.getText(0) } } />
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<Flex center overflow="hidden" style={{ height: 140 }}>
|
||||
{(currentOffer.product.productType !== ProductTypeEnum.BADGE) &&
|
||||
<Flex center overflow="hidden" style={ { height: 140 } }>
|
||||
{ (currentOffer.product.productType !== ProductTypeEnum.BADGE) &&
|
||||
<>
|
||||
<CatalogViewProductWidgetView />
|
||||
<CatalogAddOnBadgeWidgetView className="bg-muted rounded bottom-1 end-1" />
|
||||
</>}
|
||||
{(currentOffer.product.productType === ProductTypeEnum.BADGE) && <CatalogAddOnBadgeWidgetView className="scale-2" />}
|
||||
</> }
|
||||
{ (currentOffer.product.productType === ProductTypeEnum.BADGE) && <CatalogAddOnBadgeWidgetView className="scale-2" /> }
|
||||
</Flex>
|
||||
<Column grow gap={1}>
|
||||
<Column grow gap={ 1 }>
|
||||
<CatalogLimitedItemWidgetView />
|
||||
<Text grow truncate>{currentOffer.localizationName}</Text>
|
||||
<Text grow truncate>{ currentOffer.localizationName }</Text>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<CatalogSpinnerWidgetView />
|
||||
@ -53,7 +53,7 @@ export const CatalogLayoutDefaultView: FC<CatalogLayoutProps> = props =>
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</>}
|
||||
</> }
|
||||
</Column>
|
||||
</Grid>
|
||||
</>
|
||||
|
@ -16,23 +16,23 @@ export const CatalogLayouGuildCustomFurniView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Column overflow="hidden" size={7}>
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
<CatalogItemGridWidgetView />
|
||||
</Column>
|
||||
<Column center={!currentOffer} overflow="hidden" size={5}>
|
||||
{!currentOffer &&
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{!!page.localization.getImage(1) && <img alt="" src={page.localization.getImage(1)} />}
|
||||
<Text center dangerouslySetInnerHTML={{ __html: page.localization.getText(0) }} />
|
||||
</>}
|
||||
{currentOffer &&
|
||||
{ !!page.localization.getImage(1) && <img alt="" src={ page.localization.getImage(1) } /> }
|
||||
<Text center dangerouslySetInnerHTML={ { __html: page.localization.getText(0) } } />
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<div className="relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
<CatalogGuildBadgeWidgetView className="bottom-1 end-1" position="absolute" />
|
||||
</div>
|
||||
<Column grow gap={1}>
|
||||
<Text truncate>{currentOffer.localizationName}</Text>
|
||||
<Column grow gap={ 1 }>
|
||||
<Text truncate>{ currentOffer.localizationName }</Text>
|
||||
<div className="!flex-grow">
|
||||
<CatalogGuildSelectorWidgetView />
|
||||
</div>
|
||||
@ -41,7 +41,7 @@ export const CatalogLayouGuildCustomFurniView: FC<CatalogLayoutProps> = props =>
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</>}
|
||||
</> }
|
||||
</Column>
|
||||
</Grid>
|
||||
);
|
||||
|
@ -12,36 +12,36 @@ import { CatalogLayoutProps } from './CatalogLayout.types';
|
||||
export const CatalogLayouGuildForumView: FC<CatalogLayoutProps> = props =>
|
||||
{
|
||||
const { page = null } = props;
|
||||
const [selectedGroupIndex, setSelectedGroupIndex] = useState<number>(0);
|
||||
const [ selectedGroupIndex, setSelectedGroupIndex ] = useState<number>(0);
|
||||
const { currentOffer = null, setCurrentOffer = null, catalogOptions = null } = useCatalog();
|
||||
const { groups = null } = catalogOptions;
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageComposer(new CatalogGroupsComposer());
|
||||
}, [page]);
|
||||
}, [ page ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<CatalogFirstProductSelectorWidgetView />
|
||||
<Grid>
|
||||
<Column className="bg-muted rounded p-2 text-black" overflow="hidden" size={7}>
|
||||
<div className="overflow-auto" dangerouslySetInnerHTML={{ __html: page.localization.getText(1) }} />
|
||||
<Column className="bg-muted rounded p-2 text-black" overflow="hidden" size={ 7 }>
|
||||
<div className="overflow-auto" dangerouslySetInnerHTML={ { __html: page.localization.getText(1) } } />
|
||||
</Column>
|
||||
<Column gap={1} overflow="hidden" size={5}>
|
||||
{!!currentOffer &&
|
||||
<Column gap={ 1 } overflow="hidden" size={ 5 }>
|
||||
{ !!currentOffer &&
|
||||
<>
|
||||
<Column grow gap={1}>
|
||||
<Text truncate>{currentOffer.localizationName}</Text>
|
||||
<Column grow gap={ 1 }>
|
||||
<Text truncate>{ currentOffer.localizationName }</Text>
|
||||
<div className="!flex-grow">
|
||||
<CatalogGuildSelectorWidgetView />
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<CatalogTotalPriceWidget alignItems="end" />
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView noGiftOption={true} />
|
||||
<CatalogPurchaseWidgetView noGiftOption={ true } />
|
||||
</Column>
|
||||
</>}
|
||||
</> }
|
||||
</Column>
|
||||
</Grid>
|
||||
</>
|
||||
|
@ -8,7 +8,7 @@ export const CatalogLayoutInfoLoyaltyView: FC<CatalogLayoutProps> = props =>
|
||||
return (
|
||||
<div className="h-full nitro-catalog-layout-info-loyalty text-black flex flex-row">
|
||||
<div className="overflow-auto h-full flex flex-col info-loyalty-content">
|
||||
<div dangerouslySetInnerHTML={{ __html: page.localization.getText(0) }} />
|
||||
<div dangerouslySetInnerHTML={ { __html: page.localization.getText(0) } } />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -11,14 +11,14 @@ export const CatalogLayoutPets3View: FC<CatalogLayoutProps> = props =>
|
||||
return (
|
||||
<Column grow className="bg-muted rounded text-black p-2" overflow="hidden">
|
||||
<div className="items-center gap-2">
|
||||
{imageUrl && <img alt="" src={imageUrl} />}
|
||||
<div className="fs-5" dangerouslySetInnerHTML={{ __html: page.localization.getText(1) }} />
|
||||
{ imageUrl && <img alt="" src={ imageUrl } /> }
|
||||
<div className="fs-5" dangerouslySetInnerHTML={ { __html: page.localization.getText(1) } } />
|
||||
</div>
|
||||
<Column grow alignItems="center" overflow="auto">
|
||||
<div dangerouslySetInnerHTML={{ __html: page.localization.getText(2) }} />
|
||||
<div dangerouslySetInnerHTML={ { __html: page.localization.getText(2) } } />
|
||||
</Column>
|
||||
<div className="flex items-center">
|
||||
<div className="font-bold " dangerouslySetInnerHTML={{ __html: page.localization.getText(3) }} />
|
||||
<div className="font-bold " dangerouslySetInnerHTML={ { __html: page.localization.getText(3) } } />
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
|
@ -9,12 +9,12 @@ import { CatalogLayoutProps } from './CatalogLayout.types';
|
||||
export const CatalogLayoutRoomAdsView: FC<CatalogLayoutProps> = props =>
|
||||
{
|
||||
const { page = null } = props;
|
||||
const [eventName, setEventName] = useState<string>('');
|
||||
const [eventDesc, setEventDesc] = useState<string>('');
|
||||
const [roomId, setRoomId] = useState<number>(-1);
|
||||
const [availableRooms, setAvailableRooms] = useState<RoomEntryData[]>([]);
|
||||
const [extended, setExtended] = useState<boolean>(false);
|
||||
const [categoryId, setCategoryId] = useState<number>(1);
|
||||
const [ eventName, setEventName ] = useState<string>('');
|
||||
const [ eventDesc, setEventDesc ] = useState<string>('');
|
||||
const [ roomId, setRoomId ] = useState<number>(-1);
|
||||
const [ availableRooms, setAvailableRooms ] = useState<RoomEntryData[]>([]);
|
||||
const [ extended, setExtended ] = useState<boolean>(false);
|
||||
const [ categoryId, setCategoryId ] = useState<number>(1);
|
||||
const { categories = null } = useNavigator();
|
||||
const { setIsVisible = null } = useCatalog();
|
||||
const { promoteInformation, isExtended, setIsExtended } = useRoomPromote();
|
||||
@ -31,7 +31,7 @@ export const CatalogLayoutRoomAdsView: FC<CatalogLayoutProps> = props =>
|
||||
setIsExtended(false); // This is from hook useRoomPromotte
|
||||
}
|
||||
|
||||
}, [isExtended, eventName, eventDesc, categoryId]);
|
||||
}, [ isExtended, eventName, eventDesc, categoryId, promoteInformation.data, setIsExtended ]);
|
||||
|
||||
const resetData = () =>
|
||||
{
|
||||
@ -73,38 +73,34 @@ export const CatalogLayoutRoomAdsView: FC<CatalogLayoutProps> = props =>
|
||||
}, []);
|
||||
|
||||
return (<>
|
||||
<Text bold center>{LocalizeText('roomad.catalog_header')}</Text>
|
||||
<Column className="text-black" overflow="hidden" size={12}>
|
||||
<div>{LocalizeText('roomad.catalog_text', ['duration'], ['120'])}</div>
|
||||
<div className="bg-muted rounded p-1">
|
||||
<Column gap={2}>
|
||||
<Text bold>{LocalizeText('navigator.category')}</Text>
|
||||
<select className="form-select form-select-sm" disabled={extended} value={categoryId} onChange={event => setCategoryId(parseInt(event.target.value))}>
|
||||
{categories && categories.map((cat, index) => <option key={index} value={cat.id}>{LocalizeText(cat.name)}</option>)}
|
||||
<Text bold center>{ LocalizeText('roomad.catalog_header') }</Text>
|
||||
<Column className="text-black" overflow="hidden" size={ 12 }>
|
||||
<div>{ LocalizeText('roomad.catalog_text', [ 'duration' ], [ '120' ]) }</div>
|
||||
<div className="p-1 rounded bg-muted">
|
||||
<Column gap={ 2 }>
|
||||
<Text bold>{ LocalizeText('navigator.category') }</Text>
|
||||
<select className="form-select form-select-sm" disabled={ extended } value={ categoryId } onChange={ event => setCategoryId(parseInt(event.target.value)) }>
|
||||
{ categories && categories.map((cat, index) => <option key={ index } value={ cat.id }>{ LocalizeText(cat.name) }</option>) }
|
||||
</select>
|
||||
</Column>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>{LocalizeText('roomad.catalog_name')}</Text>
|
||||
|
||||
|
||||
|
||||
|
||||
<NitroInput maxLength={64} readOnly={extended} value={eventName} onChange={event => setEventName(event.target.value)} />
|
||||
<Text bold>{ LocalizeText('roomad.catalog_name') }</Text>
|
||||
<NitroInput maxLength={ 64 } readOnly={ extended } value={ eventName } onChange={ event => setEventName(event.target.value) } />
|
||||
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>{LocalizeText('roomad.catalog_description')}</Text>
|
||||
<textarea className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] form-control-sm" maxLength={64} readOnly={extended} value={eventDesc} onChange={event => setEventDesc(event.target.value)} />
|
||||
<Text bold>{ LocalizeText('roomad.catalog_description') }</Text>
|
||||
<textarea className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] form-control-sm" maxLength={ 64 } readOnly={ extended } value={ eventDesc } onChange={ event => setEventDesc(event.target.value) } />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>{LocalizeText('roomad.catalog_roomname')}</Text>
|
||||
<select className="form-select form-select-sm" disabled={extended} value={roomId} onChange={event => setRoomId(parseInt(event.target.value))}>
|
||||
<option disabled value={-1}>{LocalizeText('roomad.catalog_roomname')}</option>
|
||||
{availableRooms && availableRooms.map((room, index) => <option key={index} value={room.roomId}>{room.roomName}</option>)}
|
||||
<Text bold>{ LocalizeText('roomad.catalog_roomname') }</Text>
|
||||
<select className="form-select form-select-sm" disabled={ extended } value={ roomId } onChange={ event => setRoomId(parseInt(event.target.value)) }>
|
||||
<option disabled value={ -1 }>{ LocalizeText('roomad.catalog_roomname') }</option>
|
||||
{ availableRooms && availableRooms.map((room, index) => <option key={ index } value={ room.roomId }>{ room.roomName }</option>) }
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Button disabled={(!eventName || !eventDesc || roomId === -1)} variant={(!eventName || !eventDesc || roomId === -1) ? 'danger' : 'success'} onClick={purchaseAd}>{extended ? LocalizeText('roomad.extend.event') : LocalizeText('buy')}</Button>
|
||||
<Button disabled={ (!eventName || !eventDesc || roomId === -1) } variant={ (!eventName || !eventDesc || roomId === -1) ? 'danger' : 'success' } onClick={ purchaseAd }>{ extended ? LocalizeText('roomad.extend.event') : LocalizeText('buy') }</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Column>
|
||||
|
@ -15,19 +15,19 @@ export const CatalogLayoutRoomBundleView: FC<CatalogLayoutProps> = props =>
|
||||
<>
|
||||
<CatalogFirstProductSelectorWidgetView />
|
||||
<Grid>
|
||||
<Column overflow="hidden" size={7}>
|
||||
{!!page.localization.getText(2) &&
|
||||
<Text dangerouslySetInnerHTML={{ __html: page.localization.getText(2) }} />}
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
{ !!page.localization.getText(2) &&
|
||||
<Text dangerouslySetInnerHTML={ { __html: page.localization.getText(2) } } /> }
|
||||
<Column grow className="bg-muted p-2 rounded" overflow="hidden">
|
||||
<CatalogBundleGridWidgetView fullWidth className="nitro-catalog-layout-bundle-grid" />
|
||||
</Column>
|
||||
</Column>
|
||||
<Column gap={1} overflow="hidden" size={5}>
|
||||
{!!page.localization.getText(1) &&
|
||||
<Text center small overflow="auto">{page.localization.getText(1)}</Text>}
|
||||
<Column grow gap={0} overflow="hidden" position="relative">
|
||||
{!!page.localization.getImage(1) &&
|
||||
<img alt="" className="!flex-grow" src={page.localization.getImage(1)} />}
|
||||
<Column gap={ 1 } overflow="hidden" size={ 5 }>
|
||||
{ !!page.localization.getText(1) &&
|
||||
<Text center small overflow="auto">{ page.localization.getText(1) }</Text> }
|
||||
<Column grow gap={ 0 } overflow="hidden" position="relative">
|
||||
{ !!page.localization.getImage(1) &&
|
||||
<img alt="" className="!flex-grow" src={ page.localization.getImage(1) } /> }
|
||||
<CatalogAddOnBadgeWidgetView className="bg-muted rounded bottom-0 start-0" position="absolute" />
|
||||
<CatalogSimplePriceWidgetView />
|
||||
</Column>
|
||||
|
@ -15,19 +15,19 @@ export const CatalogLayoutSingleBundleView: FC<CatalogLayoutProps> = props =>
|
||||
<>
|
||||
<CatalogFirstProductSelectorWidgetView />
|
||||
<Grid>
|
||||
<Column overflow="hidden" size={7}>
|
||||
{!!page.localization.getText(2) &&
|
||||
<Text dangerouslySetInnerHTML={{ __html: page.localization.getText(2) }} />}
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
{ !!page.localization.getText(2) &&
|
||||
<Text dangerouslySetInnerHTML={ { __html: page.localization.getText(2) } } /> }
|
||||
<Column grow className="bg-muted p-2 rounded" overflow="hidden">
|
||||
<CatalogBundleGridWidgetView fullWidth className="nitro-catalog-layout-bundle-grid" />
|
||||
</Column>
|
||||
</Column>
|
||||
<Column gap={1} overflow="hidden" size={5}>
|
||||
{!!page.localization.getText(1) &&
|
||||
<Text center small overflow="auto">{page.localization.getText(1)}</Text>}
|
||||
<Column grow gap={0} overflow="hidden" position="relative">
|
||||
{!!page.localization.getImage(1) &&
|
||||
<img alt="" className="!flex-grow" src={page.localization.getImage(1)} />}
|
||||
<Column gap={ 1 } overflow="hidden" size={ 5 }>
|
||||
{ !!page.localization.getText(1) &&
|
||||
<Text center small overflow="auto">{ page.localization.getText(1) }</Text> }
|
||||
<Column grow gap={ 0 } overflow="hidden" position="relative">
|
||||
{ !!page.localization.getImage(1) &&
|
||||
<img alt="" className="!flex-grow" src={ page.localization.getImage(1) } /> }
|
||||
<CatalogAddOnBadgeWidgetView className="bg-muted rounded bottom-0 start-0" position="absolute" />
|
||||
<CatalogSimplePriceWidgetView />
|
||||
</Column>
|
||||
|
@ -16,8 +16,8 @@ import { CatalogLayoutProps } from './CatalogLayout.types';
|
||||
export const CatalogLayoutSoundMachineView: FC<CatalogLayoutProps> = props =>
|
||||
{
|
||||
const { page = null } = props;
|
||||
const [songId, setSongId] = useState(-1);
|
||||
const [officialSongId, setOfficialSongId] = useState('');
|
||||
const [ songId, setSongId ] = useState(-1);
|
||||
const [ officialSongId, setOfficialSongId ] = useState('');
|
||||
const { currentOffer = null, currentPage = null } = useCatalog();
|
||||
|
||||
const previewSong = (previewSongId: number) => GetSoundManager().musicController?.playSong(previewSongId, MusicPriorities.PRIORITY_PURCHASE_PREVIEW, 15, 0, 0, 0);
|
||||
@ -60,7 +60,7 @@ export const CatalogLayoutSoundMachineView: FC<CatalogLayoutProps> = props =>
|
||||
}
|
||||
|
||||
return () => GetSoundManager().musicController?.stop(MusicPriorities.PRIORITY_PURCHASE_PREVIEW);
|
||||
}, [currentOffer]);
|
||||
}, [ currentOffer ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -70,32 +70,32 @@ export const CatalogLayoutSoundMachineView: FC<CatalogLayoutProps> = props =>
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Column overflow="hidden" size={7}>
|
||||
{GetConfigurationValue('catalog.headers') &&
|
||||
<CatalogHeaderView imageUrl={currentPage.localization.getImage(0)} />}
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
{ GetConfigurationValue('catalog.headers') &&
|
||||
<CatalogHeaderView imageUrl={ currentPage.localization.getImage(0) } /> }
|
||||
<CatalogItemGridWidgetView />
|
||||
</Column>
|
||||
<Column center={!currentOffer} overflow="hidden" size={5}>
|
||||
{!currentOffer &&
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{!!page.localization.getImage(1) &&
|
||||
<LayoutImage imageUrl={page.localization.getImage(1)} />}
|
||||
<Text center dangerouslySetInnerHTML={{ __html: page.localization.getText(0) }} />
|
||||
</>}
|
||||
{currentOffer &&
|
||||
{ !!page.localization.getImage(1) &&
|
||||
<LayoutImage imageUrl={ page.localization.getImage(1) } /> }
|
||||
<Text center dangerouslySetInnerHTML={ { __html: page.localization.getText(0) } } />
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<div className="flex items-center justify-center overflow-hidden" style={{ height: 140 }}>
|
||||
{(currentOffer.product.productType !== ProductTypeEnum.BADGE) &&
|
||||
<div className="flex items-center justify-center overflow-hidden" style={ { height: 140 } }>
|
||||
{ (currentOffer.product.productType !== ProductTypeEnum.BADGE) &&
|
||||
<>
|
||||
<CatalogViewProductWidgetView />
|
||||
<CatalogAddOnBadgeWidgetView className="bg-muted rounded bottom-1 end-1" />
|
||||
</>}
|
||||
{(currentOffer.product.productType === ProductTypeEnum.BADGE) && <CatalogAddOnBadgeWidgetView className="scale-2" />}
|
||||
</> }
|
||||
{ (currentOffer.product.productType === ProductTypeEnum.BADGE) && <CatalogAddOnBadgeWidgetView className="scale-2" /> }
|
||||
</div>
|
||||
<Column grow gap={1}>
|
||||
<Column grow gap={ 1 }>
|
||||
<CatalogLimitedItemWidgetView />
|
||||
<Text grow truncate>{currentOffer.localizationName}</Text>
|
||||
{songId > -1 && <Button onClick={() => previewSong(songId)}>{LocalizeText('play_preview_button')}</Button>
|
||||
<Text grow truncate>{ currentOffer.localizationName }</Text>
|
||||
{ songId > -1 && <Button onClick={ () => previewSong(songId) }>{ LocalizeText('play_preview_button') }</Button>
|
||||
}
|
||||
<div className="flex justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
@ -105,7 +105,7 @@ export const CatalogLayoutSoundMachineView: FC<CatalogLayoutProps> = props =>
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</>}
|
||||
</> }
|
||||
</Column>
|
||||
</Grid>
|
||||
</>
|
||||
|
@ -15,32 +15,32 @@ export const CatalogLayoutSpacesView: FC<CatalogLayoutProps> = props =>
|
||||
useEffect(() =>
|
||||
{
|
||||
roomPreviewer.updatePreviewObjectBoundingRectangle();
|
||||
}, [roomPreviewer]);
|
||||
}, [ roomPreviewer ]);
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Column overflow="hidden" size={7}>
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
<CatalogSpacesWidgetView />
|
||||
</Column>
|
||||
<Column center={!currentOffer} overflow="hidden" size={5}>
|
||||
{!currentOffer &&
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{!!page.localization.getImage(1) && <img alt="" src={page.localization.getImage(1)} />}
|
||||
<Text center dangerouslySetInnerHTML={{ __html: page.localization.getText(0) }} />
|
||||
</>}
|
||||
{currentOffer &&
|
||||
{ !!page.localization.getImage(1) && <img alt="" src={ page.localization.getImage(1) } /> }
|
||||
<Text center dangerouslySetInnerHTML={ { __html: page.localization.getText(0) } } />
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<div className="relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
</div>
|
||||
<Column grow gap={1}>
|
||||
<Text grow truncate>{currentOffer.localizationName}</Text>
|
||||
<Column grow gap={ 1 }>
|
||||
<Text grow truncate>{ currentOffer.localizationName }</Text>
|
||||
<div className="flex justify-end">
|
||||
<CatalogTotalPriceWidget alignItems="end" />
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</>}
|
||||
</> }
|
||||
</Column>
|
||||
</Grid>
|
||||
);
|
||||
|
@ -10,7 +10,7 @@ import { CatalogLayoutProps } from './CatalogLayout.types';
|
||||
export const CatalogLayoutTrophiesView: FC<CatalogLayoutProps> = props =>
|
||||
{
|
||||
const { page = null } = props;
|
||||
const [trophyText, setTrophyText] = useState<string>('');
|
||||
const [ trophyText, setTrophyText ] = useState<string>('');
|
||||
const { currentOffer = null, setPurchaseOptions = null } = useCatalog();
|
||||
|
||||
useEffect(() =>
|
||||
@ -25,31 +25,31 @@ export const CatalogLayoutTrophiesView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, [currentOffer, trophyText, setPurchaseOptions]);
|
||||
}, [ currentOffer, trophyText, setPurchaseOptions ]);
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Column overflow="hidden" size={7}>
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
<CatalogItemGridWidgetView />
|
||||
<textarea className="!flex-grow form-control w-full" defaultValue={trophyText || ''} onChange={event => setTrophyText(event.target.value)} />
|
||||
<textarea className="!flex-grow form-control w-full" defaultValue={ trophyText || '' } onChange={ event => setTrophyText(event.target.value) } />
|
||||
</Column>
|
||||
<Column center={!currentOffer} overflow="hidden" size={5}>
|
||||
{!currentOffer &&
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{!!page.localization.getImage(1) && <img alt="" src={page.localization.getImage(1)} />}
|
||||
<Text center dangerouslySetInnerHTML={{ __html: page.localization.getText(0) }} />
|
||||
</>}
|
||||
{currentOffer &&
|
||||
{ !!page.localization.getImage(1) && <img alt="" src={ page.localization.getImage(1) } /> }
|
||||
<Text center dangerouslySetInnerHTML={ { __html: page.localization.getText(0) } } />
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<CatalogViewProductWidgetView />
|
||||
<Column grow gap={1}>
|
||||
<Text grow truncate>{currentOffer.localizationName}</Text>
|
||||
<Column grow gap={ 1 }>
|
||||
<Text grow truncate>{ currentOffer.localizationName }</Text>
|
||||
<div className="flex justify-end">
|
||||
<CatalogTotalPriceWidget alignItems="end" />
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</>}
|
||||
</> }
|
||||
</Column>
|
||||
</Grid>
|
||||
);
|
||||
|
@ -7,9 +7,9 @@ import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||
import { CatalogLayoutMarketplaceItemView, PUBLIC_OFFER } from './CatalogLayoutMarketplaceItemView';
|
||||
import { SearchFormView } from './CatalogLayoutMarketplaceSearchFormView';
|
||||
|
||||
const SORT_TYPES_VALUE = [1, 2];
|
||||
const SORT_TYPES_ACTIVITY = [3, 4, 5, 6];
|
||||
const SORT_TYPES_ADVANCED = [1, 2, 3, 4, 5, 6];
|
||||
const SORT_TYPES_VALUE = [ 1, 2 ];
|
||||
const SORT_TYPES_ACTIVITY = [ 3, 4, 5, 6 ];
|
||||
const SORT_TYPES_ADVANCED = [ 1, 2, 3, 4, 5, 6 ];
|
||||
export interface CatalogLayoutMarketplacePublicItemsViewProps extends CatalogLayoutProps
|
||||
{
|
||||
|
||||
@ -17,10 +17,10 @@ export interface CatalogLayoutMarketplacePublicItemsViewProps extends CatalogLay
|
||||
|
||||
export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplacePublicItemsViewProps> = props =>
|
||||
{
|
||||
const [searchType, setSearchType] = useState(MarketplaceSearchType.BY_ACTIVITY);
|
||||
const [totalItemsFound, setTotalItemsFound] = useState(0);
|
||||
const [offers, setOffers] = useState(new Map<number, MarketplaceOfferData>());
|
||||
const [lastSearch, setLastSearch] = useState<IMarketplaceSearchOptions>({ minPrice: -1, maxPrice: -1, query: '', type: 3 });
|
||||
const [ searchType, setSearchType ] = useState(MarketplaceSearchType.BY_ACTIVITY);
|
||||
const [ totalItemsFound, setTotalItemsFound ] = useState(0);
|
||||
const [ offers, setOffers ] = useState(new Map<number, MarketplaceOfferData>());
|
||||
const [ lastSearch, setLastSearch ] = useState<IMarketplaceSearchOptions>({ minPrice: -1, maxPrice: -1, query: '', type: 3 });
|
||||
const { getCurrencyAmount = null } = usePurse();
|
||||
const { simpleAlert = null, showConfirm = null } = useNotification();
|
||||
|
||||
@ -42,7 +42,7 @@ export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplac
|
||||
return SORT_TYPES_ADVANCED;
|
||||
}
|
||||
return [];
|
||||
}, [searchType]);
|
||||
}, [ searchType ]);
|
||||
|
||||
const purchaseItem = useCallback((offerData: MarketplaceOfferData) =>
|
||||
{
|
||||
@ -58,8 +58,8 @@ export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplac
|
||||
{
|
||||
SendMessageComposer(new BuyMarketplaceOfferMessageComposer(offerId));
|
||||
},
|
||||
null, null, null, LocalizeText('catalog.marketplace.confirm_title'));
|
||||
}, [getCurrencyAmount, simpleAlert, showConfirm]);
|
||||
null, null, null, LocalizeText('catalog.marketplace.confirm_title'));
|
||||
}, [ getCurrencyAmount, simpleAlert, showConfirm ]);
|
||||
|
||||
useMessageEvent<MarketPlaceOffersEvent>(MarketPlaceOffersEvent, event =>
|
||||
{
|
||||
@ -120,11 +120,11 @@ export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplac
|
||||
});
|
||||
|
||||
showConfirm(LocalizeText('catalog.marketplace.confirm_higher_header') +
|
||||
'\n' + LocalizeText('catalog.marketplace.confirm_price', ['price'], [parser.newPrice.toString()]), () =>
|
||||
'\n' + LocalizeText('catalog.marketplace.confirm_price', [ 'price' ], [ parser.newPrice.toString() ]), () =>
|
||||
{
|
||||
SendMessageComposer(new BuyMarketplaceOfferMessageComposer(parser.offerId));
|
||||
},
|
||||
null, null, null, LocalizeText('catalog.marketplace.confirm_higher_title'));
|
||||
null, null, null, LocalizeText('catalog.marketplace.confirm_higher_title'));
|
||||
break;
|
||||
case 4:
|
||||
simpleAlert(LocalizeText('catalog.alert.notenough.credits.description'), NotificationAlertType.DEFAULT, null, null, LocalizeText('catalog.alert.notenough.title'));
|
||||
@ -135,24 +135,24 @@ export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplac
|
||||
return (
|
||||
<>
|
||||
<div className="relative inline-flex align-middle">
|
||||
<Button active={(searchType === MarketplaceSearchType.BY_ACTIVITY)} onClick={() => setSearchType(MarketplaceSearchType.BY_ACTIVITY)}>
|
||||
{LocalizeText('catalog.marketplace.search_by_activity')}
|
||||
<Button active={ (searchType === MarketplaceSearchType.BY_ACTIVITY) } onClick={ () => setSearchType(MarketplaceSearchType.BY_ACTIVITY) }>
|
||||
{ LocalizeText('catalog.marketplace.search_by_activity') }
|
||||
</Button>
|
||||
<Button active={(searchType === MarketplaceSearchType.BY_VALUE)} onClick={() => setSearchType(MarketplaceSearchType.BY_VALUE)}>
|
||||
{LocalizeText('catalog.marketplace.search_by_value')}
|
||||
<Button active={ (searchType === MarketplaceSearchType.BY_VALUE) } onClick={ () => setSearchType(MarketplaceSearchType.BY_VALUE) }>
|
||||
{ LocalizeText('catalog.marketplace.search_by_value') }
|
||||
</Button>
|
||||
<Button active={(searchType === MarketplaceSearchType.ADVANCED)} onClick={() => setSearchType(MarketplaceSearchType.ADVANCED)}>
|
||||
{LocalizeText('catalog.marketplace.search_advanced')}
|
||||
<Button active={ (searchType === MarketplaceSearchType.ADVANCED) } onClick={ () => setSearchType(MarketplaceSearchType.ADVANCED) }>
|
||||
{ LocalizeText('catalog.marketplace.search_advanced') }
|
||||
</Button>
|
||||
</div>
|
||||
<SearchFormView searchType={searchType} sortTypes={getSortTypes} onSearch={requestOffers} />
|
||||
<Column gap={1} overflow="hidden">
|
||||
<SearchFormView searchType={ searchType } sortTypes={ getSortTypes } onSearch={ requestOffers } />
|
||||
<Column gap={ 1 } overflow="hidden">
|
||||
<Text shrink truncate fontWeight="bold">
|
||||
{LocalizeText('catalog.marketplace.items_found', ['count'], [offers.size.toString()])}
|
||||
{ LocalizeText('catalog.marketplace.items_found', [ 'count' ], [ offers.size.toString() ]) }
|
||||
</Text>
|
||||
<Column className="nitro-catalog-layout-marketplace-grid" overflow="auto">
|
||||
{
|
||||
Array.from(offers.values()).map((entry, index) => <CatalogLayoutMarketplaceItemView key={index} offerData={entry} type={PUBLIC_OFFER} onClick={purchaseItem} />)
|
||||
Array.from(offers.values()).map((entry, index) => <CatalogLayoutMarketplaceItemView key={ index } offerData={ entry } type={ PUBLIC_OFFER } onClick={ purchaseItem } />)
|
||||
}
|
||||
</Column>
|
||||
</Column>
|
||||
|
@ -13,17 +13,17 @@ export interface SearchFormViewProps
|
||||
export const SearchFormView: FC<SearchFormViewProps> = props =>
|
||||
{
|
||||
const { searchType = null, sortTypes = null, onSearch = null } = props;
|
||||
const [sortType, setSortType] = useState(sortTypes ? sortTypes[0] : 3); // first item of SORT_TYPES_ACTIVITY
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [min, setMin] = useState(0);
|
||||
const [max, setMax] = useState(0);
|
||||
const [ sortType, setSortType ] = useState(sortTypes ? sortTypes[0] : 3); // first item of SORT_TYPES_ACTIVITY
|
||||
const [ searchQuery, setSearchQuery ] = useState('');
|
||||
const [ min, setMin ] = useState(0);
|
||||
const [ max, setMax ] = useState(0);
|
||||
|
||||
const onSortTypeChange = useCallback((sortType: number) =>
|
||||
{
|
||||
setSortType(sortType);
|
||||
|
||||
if ((searchType === MarketplaceSearchType.BY_ACTIVITY) || (searchType === MarketplaceSearchType.BY_VALUE)) onSearch({ minPrice: -1, maxPrice: -1, query: '', type: sortType });
|
||||
}, [onSearch, searchType]);
|
||||
}, [ onSearch, searchType ]);
|
||||
|
||||
const onClickSearch = useCallback(() =>
|
||||
{
|
||||
@ -31,7 +31,7 @@ export const SearchFormView: FC<SearchFormViewProps> = props =>
|
||||
const maxPrice = ((max > 0) ? max : -1);
|
||||
|
||||
onSearch({ minPrice: minPrice, maxPrice: maxPrice, type: sortType, query: searchQuery })
|
||||
}, [max, min, onSearch, searchQuery, sortType]);
|
||||
}, [ max, min, onSearch, searchQuery, sortType ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -42,48 +42,45 @@ export const SearchFormView: FC<SearchFormViewProps> = props =>
|
||||
setSortType(sortType);
|
||||
|
||||
if (searchType === MarketplaceSearchType.BY_ACTIVITY || MarketplaceSearchType.BY_VALUE === searchType) onSearch({ minPrice: -1, maxPrice: -1, query: '', type: sortType });
|
||||
}, [onSearch, searchType, sortTypes]);
|
||||
}, [ onSearch, searchType, sortTypes ]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-1">
|
||||
<Text className="col-span-3">{LocalizeText('catalog.marketplace.sort_order')}</Text>
|
||||
<select className="form-select form-select-sm" value={sortType} onChange={event => onSortTypeChange(parseInt(event.target.value))}>
|
||||
{sortTypes.map(type => <option key={type} value={type}>{LocalizeText(`catalog.marketplace.sort.${type}`)}</option>)}
|
||||
<Text className="col-span-3">{ LocalizeText('catalog.marketplace.sort_order') }</Text>
|
||||
<select className="form-select form-select-sm" value={ sortType } onChange={ event => onSortTypeChange(parseInt(event.target.value)) }>
|
||||
{ sortTypes.map(type => <option key={ type } value={ type }>{ LocalizeText(`catalog.marketplace.sort.${ type }`) }</option>) }
|
||||
</select>
|
||||
</div>
|
||||
{searchType === MarketplaceSearchType.ADVANCED &&
|
||||
{ searchType === MarketplaceSearchType.ADVANCED &&
|
||||
<>
|
||||
<div className="flex items-center gap-1">
|
||||
<Text className="col-span-3">{LocalizeText('catalog.marketplace.search_name')}</Text>
|
||||
|
||||
<Text className="col-span-3">{ LocalizeText('catalog.marketplace.search_name') }</Text>
|
||||
<NitroInput
|
||||
value={searchQuery}
|
||||
onChange={event => setSearchQuery(event.target.value)} />
|
||||
value={ searchQuery }
|
||||
onChange={ event => setSearchQuery(event.target.value) } />
|
||||
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Text className="col-span-3">{LocalizeText('catalog.marketplace.search_price')}</Text>
|
||||
<Text className="col-span-3">{ LocalizeText('catalog.marketplace.search_price') }</Text>
|
||||
<div className="flex w-full gap-1">
|
||||
|
||||
<NitroInput
|
||||
min={ 0 }
|
||||
type="number"
|
||||
min={0}
|
||||
value={min}
|
||||
onChange={event => setMin(event.target.valueAsNumber)} />
|
||||
|
||||
|
||||
value={ min }
|
||||
onChange={ event => setMin(event.target.valueAsNumber) } />
|
||||
<NitroInput
|
||||
min={ 0 }
|
||||
type="number"
|
||||
min={0}
|
||||
value={max}
|
||||
onChange={event => setMax(event.target.valueAsNumber)} />
|
||||
value={ max }
|
||||
onChange={ event => setMax(event.target.valueAsNumber) } />
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<Button className="mx-auto" variant="secondary" onClick={onClickSearch}>{LocalizeText('generic.search')}</Button>
|
||||
</>}
|
||||
<Button className="mx-auto" variant="secondary" onClick={ onClickSearch }>{ LocalizeText('generic.search') }</Button>
|
||||
</> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ import { NitroInput } from '../../../../../../layout';
|
||||
|
||||
export const MarketplacePostOfferView: FC<{}> = props =>
|
||||
{
|
||||
const [item, setItem] = useState<FurnitureItem>(null);
|
||||
const [askingPrice, setAskingPrice] = useState(0);
|
||||
const [tempAskingPrice, setTempAskingPrice] = useState('0');
|
||||
const [ item, setItem ] = useState<FurnitureItem>(null);
|
||||
const [ askingPrice, setAskingPrice ] = useState(0);
|
||||
const [ tempAskingPrice, setTempAskingPrice ] = useState('0');
|
||||
const { catalogOptions = null, setCatalogOptions = null } = useCatalog();
|
||||
const { marketplaceConfiguration = null } = catalogOptions;
|
||||
const { showConfirm = null } = useNotification();
|
||||
@ -47,14 +47,14 @@ export const MarketplacePostOfferView: FC<{}> = props =>
|
||||
if (!item || marketplaceConfiguration) return;
|
||||
|
||||
SendMessageComposer(new GetMarketplaceConfigurationMessageComposer());
|
||||
}, [item, marketplaceConfiguration]);
|
||||
}, [ item, marketplaceConfiguration ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (!item) return;
|
||||
|
||||
return () => setAskingPrice(0);
|
||||
}, [item]);
|
||||
}, [ item ]);
|
||||
|
||||
if (!marketplaceConfiguration || !item) return null;
|
||||
|
||||
@ -67,54 +67,53 @@ export const MarketplacePostOfferView: FC<{}> = props =>
|
||||
{
|
||||
if (!item || (askingPrice < marketplaceConfiguration.minimumPrice)) return;
|
||||
|
||||
showConfirm(LocalizeText('inventory.marketplace.confirm_offer.info', ['furniname', 'price'], [getFurniTitle, askingPrice.toString()]), () =>
|
||||
showConfirm(LocalizeText('inventory.marketplace.confirm_offer.info', [ 'furniname', 'price' ], [ getFurniTitle, askingPrice.toString() ]), () =>
|
||||
{
|
||||
SendMessageComposer(new MakeOfferMessageComposer(askingPrice, item.isWallItem ? 2 : 1, item.id));
|
||||
setItem(null);
|
||||
},
|
||||
() =>
|
||||
{
|
||||
setItem(null)
|
||||
}, null, null, LocalizeText('inventory.marketplace.confirm_offer.title'));
|
||||
() =>
|
||||
{
|
||||
setItem(null)
|
||||
}, null, null, LocalizeText('inventory.marketplace.confirm_offer.title'));
|
||||
}
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-catalog-layout-marketplace-post-offer" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={LocalizeText('inventory.marketplace.make_offer.title')} onCloseClick={event => setItem(null)} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText('inventory.marketplace.make_offer.title') } onCloseClick={ event => setItem(null) } />
|
||||
<NitroCardContentView overflow="hidden">
|
||||
<Grid fullHeight>
|
||||
<Column center className="bg-muted rounded p-2" overflow="hidden" size={4}>
|
||||
<LayoutFurniImageView extraData={item.extra.toString()} productClassId={item.type} productType={item.isWallItem ? ProductTypeEnum.WALL : ProductTypeEnum.FLOOR} />
|
||||
<Column center className="bg-muted rounded p-2" overflow="hidden" size={ 4 }>
|
||||
<LayoutFurniImageView extraData={ item.extra.toString() } productClassId={ item.type } productType={ item.isWallItem ? ProductTypeEnum.WALL : ProductTypeEnum.FLOOR } />
|
||||
</Column>
|
||||
<Column justifyContent="between" overflow="hidden" size={8}>
|
||||
<Column grow gap={1}>
|
||||
<Text fontWeight="bold">{getFurniTitle}</Text>
|
||||
<Text shrink truncate>{getFurniDescription}</Text>
|
||||
<Column justifyContent="between" overflow="hidden" size={ 8 }>
|
||||
<Column grow gap={ 1 }>
|
||||
<Text fontWeight="bold">{ getFurniTitle }</Text>
|
||||
<Text shrink truncate>{ getFurniDescription }</Text>
|
||||
</Column>
|
||||
<Column overflow="auto">
|
||||
<Text italics>
|
||||
{LocalizeText('inventory.marketplace.make_offer.expiration_info', ['time'], [marketplaceConfiguration.offerTime.toString()])}
|
||||
{ LocalizeText('inventory.marketplace.make_offer.expiration_info', [ 'time' ], [ marketplaceConfiguration.offerTime.toString() ]) }
|
||||
</Text>
|
||||
<div className="input-group has-validation">
|
||||
|
||||
|
||||
<NitroInput min={0} placeholder={LocalizeText('inventory.marketplace.make_offer.price_request')} type="number" value={tempAskingPrice} onChange={event => updateAskingPrice(event.target.value)} />
|
||||
|
||||
{((askingPrice < marketplaceConfiguration.minimumPrice) || isNaN(askingPrice)) &&
|
||||
<NitroInput min={ 0 } placeholder={ LocalizeText('inventory.marketplace.make_offer.price_request') } type="number" value={ tempAskingPrice } onChange={ event => updateAskingPrice(event.target.value) } />
|
||||
{ ((askingPrice < marketplaceConfiguration.minimumPrice) || isNaN(askingPrice)) &&
|
||||
<div className="invalid-feedback d-block">
|
||||
{LocalizeText('inventory.marketplace.make_offer.min_price', ['minprice'], [marketplaceConfiguration.minimumPrice.toString()])}
|
||||
</div>}
|
||||
{((askingPrice > marketplaceConfiguration.maximumPrice) && !isNaN(askingPrice)) &&
|
||||
{ LocalizeText('inventory.marketplace.make_offer.min_price', [ 'minprice' ], [ marketplaceConfiguration.minimumPrice.toString() ]) }
|
||||
</div> }
|
||||
{ ((askingPrice > marketplaceConfiguration.maximumPrice) && !isNaN(askingPrice)) &&
|
||||
<div className="invalid-feedback d-block">
|
||||
{LocalizeText('inventory.marketplace.make_offer.max_price', ['maxprice'], [marketplaceConfiguration.maximumPrice.toString()])}
|
||||
</div>}
|
||||
{(!((askingPrice < marketplaceConfiguration.minimumPrice) || (askingPrice > marketplaceConfiguration.maximumPrice) || isNaN(askingPrice))) &&
|
||||
{ LocalizeText('inventory.marketplace.make_offer.max_price', [ 'maxprice' ], [ marketplaceConfiguration.maximumPrice.toString() ]) }
|
||||
</div> }
|
||||
{ (!((askingPrice < marketplaceConfiguration.minimumPrice) || (askingPrice > marketplaceConfiguration.maximumPrice) || isNaN(askingPrice))) &&
|
||||
<div className="invalid-feedback d-block">
|
||||
{LocalizeText('inventory.marketplace.make_offer.final_price', ['commission', 'finalprice'], [getCommission().toString(), (askingPrice + getCommission()).toString()])}
|
||||
</div>}
|
||||
{ LocalizeText('inventory.marketplace.make_offer.final_price', [ 'commission', 'finalprice' ], [ getCommission().toString(), (askingPrice + getCommission()).toString() ]) }
|
||||
</div> }
|
||||
</div>
|
||||
<Button disabled={((askingPrice < marketplaceConfiguration.minimumPrice) || (askingPrice > marketplaceConfiguration.maximumPrice) || isNaN(askingPrice))} onClick={postItem}>
|
||||
{LocalizeText('inventory.marketplace.make_offer.post')}
|
||||
<Button disabled={ ((askingPrice < marketplaceConfiguration.minimumPrice) || (askingPrice > marketplaceConfiguration.maximumPrice) || isNaN(askingPrice)) } onClick={ postItem }>
|
||||
{ LocalizeText('inventory.marketplace.make_offer.post') }
|
||||
</Button>
|
||||
</Column>
|
||||
</Column>
|
||||
|
@ -14,15 +14,15 @@ import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||
export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||
{
|
||||
const { page = null } = props;
|
||||
const [petIndex, setPetIndex] = useState(-1);
|
||||
const [sellablePalettes, setSellablePalettes] = useState<SellablePetPaletteData[]>([]);
|
||||
const [selectedPaletteIndex, setSelectedPaletteIndex] = useState(-1);
|
||||
const [sellableColors, setSellableColors] = useState<number[][]>([]);
|
||||
const [selectedColorIndex, setSelectedColorIndex] = useState(-1);
|
||||
const [colorsShowing, setColorsShowing] = useState(false);
|
||||
const [petName, setPetName] = useState('');
|
||||
const [approvalPending, setApprovalPending] = useState(true);
|
||||
const [approvalResult, setApprovalResult] = useState(-1);
|
||||
const [ petIndex, setPetIndex ] = useState(-1);
|
||||
const [ sellablePalettes, setSellablePalettes ] = useState<SellablePetPaletteData[]>([]);
|
||||
const [ selectedPaletteIndex, setSelectedPaletteIndex ] = useState(-1);
|
||||
const [ sellableColors, setSellableColors ] = useState<number[][]>([]);
|
||||
const [ selectedColorIndex, setSelectedColorIndex ] = useState(-1);
|
||||
const [ colorsShowing, setColorsShowing ] = useState(false);
|
||||
const [ petName, setPetName ] = useState('');
|
||||
const [ approvalPending, setApprovalPending ] = useState(true);
|
||||
const [ approvalResult, setApprovalResult ] = useState(-1);
|
||||
const { currentOffer = null, setCurrentOffer = null, setPurchaseOptions = null, catalogOptions = null, roomPreviewer = null } = useCatalog();
|
||||
const { petPalettes = null } = catalogOptions;
|
||||
|
||||
@ -31,14 +31,14 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||
if (!sellableColors.length || (selectedColorIndex === -1)) return 0xFFFFFF;
|
||||
|
||||
return sellableColors[selectedColorIndex][0];
|
||||
}, [sellableColors, selectedColorIndex]);
|
||||
}, [ sellableColors, selectedColorIndex ]);
|
||||
|
||||
const petBreedName = useMemo(() =>
|
||||
{
|
||||
if ((petIndex === -1) || !sellablePalettes.length || (selectedPaletteIndex === -1)) return '';
|
||||
|
||||
return LocalizeText(`pet.breed.${petIndex}.${sellablePalettes[selectedPaletteIndex].breedId}`);
|
||||
}, [petIndex, sellablePalettes, selectedPaletteIndex]);
|
||||
return LocalizeText(`pet.breed.${ petIndex }.${ sellablePalettes[selectedPaletteIndex].breedId }`);
|
||||
}, [ petIndex, sellablePalettes, selectedPaletteIndex ]);
|
||||
|
||||
const petPurchaseString = useMemo(() =>
|
||||
{
|
||||
@ -59,8 +59,8 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
while (colorString.length < 6) colorString = ('0' + colorString);
|
||||
|
||||
return `${paletteId}\n${colorString}`;
|
||||
}, [sellablePalettes, selectedPaletteIndex, petIndex, sellableColors, selectedColorIndex]);
|
||||
return `${ paletteId }\n${ colorString }`;
|
||||
}, [ sellablePalettes, selectedPaletteIndex, petIndex, sellableColors, selectedColorIndex ]);
|
||||
|
||||
const validationErrorMessage = useMemo(() =>
|
||||
{
|
||||
@ -85,7 +85,7 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||
if (!key || !key.length) return '';
|
||||
|
||||
return LocalizeText(key);
|
||||
}, [approvalResult]);
|
||||
}, [ approvalResult ]);
|
||||
|
||||
const purchasePet = useCallback(() =>
|
||||
{
|
||||
@ -98,11 +98,11 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
if (approvalResult === 0)
|
||||
{
|
||||
SendMessageComposer(new PurchaseFromCatalogComposer(page.pageId, currentOffer.offerId, `${petName}\n${petPurchaseString}`, 1));
|
||||
SendMessageComposer(new PurchaseFromCatalogComposer(page.pageId, currentOffer.offerId, `${ petName }\n${ petPurchaseString }`, 1));
|
||||
|
||||
return;
|
||||
}
|
||||
}, [page, currentOffer, petName, petPurchaseString, approvalResult]);
|
||||
}, [ page, currentOffer, petName, petPurchaseString, approvalResult ]);
|
||||
|
||||
useMessageEvent<ApproveNameMessageEvent>(ApproveNameMessageEvent, event =>
|
||||
{
|
||||
@ -123,7 +123,7 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||
setCurrentOffer(offer);
|
||||
setPetIndex(GetPetIndexFromLocalization(offer.localizationId));
|
||||
setColorsShowing(false);
|
||||
}, [page, setCurrentOffer]);
|
||||
}, [ page, setCurrentOffer ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -159,7 +159,7 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||
setSellablePalettes([]);
|
||||
|
||||
SendMessageComposer(new GetSellablePetPalettesComposer(productData.type));
|
||||
}, [currentOffer, petPalettes]);
|
||||
}, [ currentOffer, petPalettes ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -169,7 +169,7 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
setSelectedColorIndex((colors.length ? 0 : -1));
|
||||
setSellableColors(colors);
|
||||
}, [petIndex, sellablePalettes]);
|
||||
}, [ petIndex, sellablePalettes ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -179,64 +179,64 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
if ((petIndex === -1) || !sellablePalettes.length || (selectedPaletteIndex === -1)) return;
|
||||
|
||||
let petFigureString = `${petIndex} ${sellablePalettes[selectedPaletteIndex].paletteId}`;
|
||||
let petFigureString = `${ petIndex } ${ sellablePalettes[selectedPaletteIndex].paletteId }`;
|
||||
|
||||
if (petIndex <= 7) petFigureString += ` ${getColor.toString(16)}`;
|
||||
if (petIndex <= 7) petFigureString += ` ${ getColor.toString(16) }`;
|
||||
|
||||
roomPreviewer.addPetIntoRoom(petFigureString);
|
||||
}, [roomPreviewer, petIndex, sellablePalettes, selectedPaletteIndex, getColor]);
|
||||
}, [ roomPreviewer, petIndex, sellablePalettes, selectedPaletteIndex, getColor ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setApprovalResult(-1);
|
||||
}, [petName]);
|
||||
}, [ petName ]);
|
||||
|
||||
if (!currentOffer) return null;
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Column overflow="hidden" size={7}>
|
||||
<AutoGrid columnCount={5}>
|
||||
{!colorsShowing && (sellablePalettes.length > 0) && sellablePalettes.map((palette, index) =>
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
<AutoGrid columnCount={ 5 }>
|
||||
{ !colorsShowing && (sellablePalettes.length > 0) && sellablePalettes.map((palette, index) =>
|
||||
{
|
||||
return (
|
||||
<LayoutGridItem key={index} itemActive={(selectedPaletteIndex === index)} onClick={event => setSelectedPaletteIndex(index)}>
|
||||
<LayoutPetImageView direction={2} headOnly={true} paletteId={palette.paletteId} typeId={petIndex} />
|
||||
<LayoutGridItem key={ index } itemActive={ (selectedPaletteIndex === index) } onClick={ event => setSelectedPaletteIndex(index) }>
|
||||
<LayoutPetImageView direction={ 2 } headOnly={ true } paletteId={ palette.paletteId } typeId={ petIndex } />
|
||||
</LayoutGridItem>
|
||||
);
|
||||
})}
|
||||
{colorsShowing && (sellableColors.length > 0) && sellableColors.map((colorSet, index) => <LayoutGridItem key={index} itemHighlight className="clear-bg" itemActive={(selectedColorIndex === index)} itemColor={ColorConverter.int2rgb(colorSet[0])} onClick={event => setSelectedColorIndex(index)} />)}
|
||||
}) }
|
||||
{ colorsShowing && (sellableColors.length > 0) && sellableColors.map((colorSet, index) => <LayoutGridItem key={ index } itemHighlight className="clear-bg" itemActive={ (selectedColorIndex === index) } itemColor={ ColorConverter.int2rgb(colorSet[0]) } onClick={ event => setSelectedColorIndex(index) } />) }
|
||||
</AutoGrid>
|
||||
</Column>
|
||||
<Column center={!currentOffer} overflow="hidden" size={5}>
|
||||
{!currentOffer &&
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{!!page.localization.getImage(1) && <img alt="" src={page.localization.getImage(1)} />}
|
||||
<Text center dangerouslySetInnerHTML={{ __html: page.localization.getText(0) }} />
|
||||
</>}
|
||||
{currentOffer &&
|
||||
{ !!page.localization.getImage(1) && <img alt="" src={ page.localization.getImage(1) } /> }
|
||||
<Text center dangerouslySetInnerHTML={ { __html: page.localization.getText(0) } } />
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<div className="relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
<CatalogAddOnBadgeWidgetView className="bg-muted rounded bottom-1 end-1" position="absolute" />
|
||||
{((petIndex > -1) && (petIndex <= 7)) &&
|
||||
<Button className="bottom-1 start-1" position="absolute" onClick={event => setColorsShowing(!colorsShowing)}>
|
||||
{ ((petIndex > -1) && (petIndex <= 7)) &&
|
||||
<Button className="bottom-1 start-1" position="absolute" onClick={ event => setColorsShowing(!colorsShowing) }>
|
||||
<FaFillDrip className="fa-icon" />
|
||||
</Button>}
|
||||
</Button> }
|
||||
</div>
|
||||
<Column grow gap={1}>
|
||||
<Text truncate>{petBreedName}</Text>
|
||||
<Column grow gap={1}>
|
||||
<input className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] form-control-sm w-full" placeholder={LocalizeText('widgets.petpackage.name.title')} type="text" value={petName} onChange={event => setPetName(event.target.value)} />
|
||||
{(approvalResult > 0) &&
|
||||
<div className="invalid-feedback d-block m-0">{validationErrorMessage}</div>}
|
||||
<Column grow gap={ 1 }>
|
||||
<Text truncate>{ petBreedName }</Text>
|
||||
<Column grow gap={ 1 }>
|
||||
<input className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] form-control-sm w-full" placeholder={ LocalizeText('widgets.petpackage.name.title') } type="text" value={ petName } onChange={ event => setPetName(event.target.value) } />
|
||||
{ (approvalResult > 0) &&
|
||||
<div className="invalid-feedback d-block m-0">{ validationErrorMessage }</div> }
|
||||
</Column>
|
||||
<div className="flex justify-end">
|
||||
<CatalogTotalPriceWidget alignItems="end" justifyContent="end" />
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView purchaseCallback={purchasePet} />
|
||||
<CatalogPurchaseWidgetView purchaseCallback={ purchasePet } />
|
||||
</Column>
|
||||
</>}
|
||||
</> }
|
||||
</Column>
|
||||
</Grid>
|
||||
);
|
||||
|
@ -6,7 +6,7 @@ import { useCatalog } from '../../../../../hooks';
|
||||
|
||||
export const CatalogGuildSelectorWidgetView: FC<{}> = props =>
|
||||
{
|
||||
const [selectedGroupIndex, setSelectedGroupIndex] = useState<number>(0);
|
||||
const [ selectedGroupIndex, setSelectedGroupIndex ] = useState<number>(0);
|
||||
const { currentOffer = null, catalogOptions = null, setPurchaseOptions = null } = useCatalog();
|
||||
const { groups = null } = catalogOptions;
|
||||
|
||||
@ -20,10 +20,10 @@ export const CatalogGuildSelectorWidgetView: FC<{}> = props =>
|
||||
|
||||
const stuffData = new StringDataType();
|
||||
|
||||
stuffData.setValue(['0', group.groupId.toString(), group.badgeCode, group.colorA, group.colorB]);
|
||||
stuffData.setValue([ '0', group.groupId.toString(), group.badgeCode, group.colorA, group.colorB ]);
|
||||
|
||||
return stuffData;
|
||||
}, [selectedGroupIndex, groups]);
|
||||
}, [ selectedGroupIndex, groups ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -39,7 +39,7 @@ export const CatalogGuildSelectorWidgetView: FC<{}> = props =>
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, [currentOffer, previewStuffData, setPurchaseOptions]);
|
||||
}, [ currentOffer, previewStuffData, setPurchaseOptions ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -50,9 +50,9 @@ export const CatalogGuildSelectorWidgetView: FC<{}> = props =>
|
||||
{
|
||||
return (
|
||||
<div className="bg-muted rounded p-1 text-black text-center">
|
||||
{LocalizeText('catalog.guild_selector.members_only')}
|
||||
{ LocalizeText('catalog.guild_selector.members_only') }
|
||||
<Button className="mt-1">
|
||||
{LocalizeText('catalog.guild_selector.find_groups')}
|
||||
{ LocalizeText('catalog.guild_selector.find_groups') }
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
@ -62,13 +62,13 @@ export const CatalogGuildSelectorWidgetView: FC<{}> = props =>
|
||||
|
||||
return (
|
||||
<div className="flex gap-1">
|
||||
{!!selectedGroup &&
|
||||
{ !!selectedGroup &&
|
||||
<Flex className="rounded border" overflow="hidden">
|
||||
<div className="h-full" style={{ width: '20px', backgroundColor: '#' + selectedGroup.colorA }} />
|
||||
<div className="h-full" style={{ width: '20px', backgroundColor: '#' + selectedGroup.colorB }} />
|
||||
</Flex>}
|
||||
<select className="form-select form-select-sm" value={selectedGroupIndex} onChange={event => setSelectedGroupIndex(parseInt(event.target.value))}>
|
||||
{groups.map((group, index) => <option key={index} value={index}>{group.groupName}</option>)}
|
||||
<div className="h-full" style={ { width: '20px', backgroundColor: '#' + selectedGroup.colorA } } />
|
||||
<div className="h-full" style={ { width: '20px', backgroundColor: '#' + selectedGroup.colorB } } />
|
||||
</Flex> }
|
||||
<select className="form-select form-select-sm" value={ selectedGroupIndex } onChange={ event => setSelectedGroupIndex(parseInt(event.target.value)) }>
|
||||
{ groups.map((group, index) => <option key={ index } value={ index }>{ group.groupName }</option>) }
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
|
@ -11,7 +11,7 @@ export const CatalogLimitedItemWidgetView: FC = props =>
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<LayoutLimitedEditionCompletePlateView className="mx-auto" uniqueLimitedItemsLeft={currentOffer.product.uniqueLimitedItemsLeft} uniqueLimitedSeriesSize={currentOffer.product.uniqueLimitedItemSeriesSize} />
|
||||
<LayoutLimitedEditionCompletePlateView className="mx-auto" uniqueLimitedItemsLeft={ currentOffer.product.uniqueLimitedItemsLeft } uniqueLimitedSeriesSize={ currentOffer.product.uniqueLimitedItemSeriesSize } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -9,14 +9,14 @@ interface CatalogSpacesWidgetViewProps extends AutoGridProps
|
||||
|
||||
}
|
||||
|
||||
const SPACES_GROUP_NAMES = ['floors', 'walls', 'views'];
|
||||
const SPACES_GROUP_NAMES = [ 'floors', 'walls', 'views' ];
|
||||
|
||||
export const CatalogSpacesWidgetView: FC<CatalogSpacesWidgetViewProps> = props =>
|
||||
{
|
||||
const { columnCount = 5, children = null, ...rest } = props;
|
||||
const [groupedOffers, setGroupedOffers] = useState<IPurchasableOffer[][]>(null);
|
||||
const [selectedGroupIndex, setSelectedGroupIndex] = useState(-1);
|
||||
const [selectedOfferForGroup, setSelectedOfferForGroup] = useState<IPurchasableOffer[]>(null);
|
||||
const [ groupedOffers, setGroupedOffers ] = useState<IPurchasableOffer[][]>(null);
|
||||
const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(-1);
|
||||
const [ selectedOfferForGroup, setSelectedOfferForGroup ] = useState<IPurchasableOffer[]>(null);
|
||||
const { currentPage = null, currentOffer = null, setCurrentOffer = null, setPurchaseOptions = null } = useCatalog();
|
||||
const elementRef = useRef<HTMLDivElement>();
|
||||
|
||||
@ -26,7 +26,7 @@ export const CatalogSpacesWidgetView: FC<CatalogSpacesWidgetViewProps> = props =
|
||||
|
||||
setSelectedOfferForGroup(prevValue =>
|
||||
{
|
||||
const newValue = [...prevValue];
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
newValue[selectedGroupIndex] = offer;
|
||||
|
||||
@ -38,7 +38,7 @@ export const CatalogSpacesWidgetView: FC<CatalogSpacesWidgetViewProps> = props =
|
||||
{
|
||||
if (!currentPage) return;
|
||||
|
||||
const groupedOffers: IPurchasableOffer[][] = [[], [], []];
|
||||
const groupedOffers: IPurchasableOffer[][] = [ [], [], [] ];
|
||||
|
||||
for (const offer of currentPage.offers)
|
||||
{
|
||||
@ -66,8 +66,8 @@ export const CatalogSpacesWidgetView: FC<CatalogSpacesWidgetViewProps> = props =
|
||||
|
||||
setGroupedOffers(groupedOffers);
|
||||
setSelectedGroupIndex(0);
|
||||
setSelectedOfferForGroup([groupedOffers[0][0], groupedOffers[1][0], groupedOffers[2][0]]);
|
||||
}, [currentPage]);
|
||||
setSelectedOfferForGroup([ groupedOffers[0][0], groupedOffers[1][0], groupedOffers[2][0] ]);
|
||||
}, [ currentPage ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -75,7 +75,7 @@ export const CatalogSpacesWidgetView: FC<CatalogSpacesWidgetViewProps> = props =
|
||||
|
||||
setCurrentOffer(selectedOfferForGroup[selectedGroupIndex]);
|
||||
|
||||
}, [selectedGroupIndex, selectedOfferForGroup, setCurrentOffer]);
|
||||
}, [ selectedGroupIndex, selectedOfferForGroup, setCurrentOffer ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -90,12 +90,12 @@ export const CatalogSpacesWidgetView: FC<CatalogSpacesWidgetViewProps> = props =
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, [currentOffer, selectedGroupIndex, selectedOfferForGroup, setPurchaseOptions]);
|
||||
}, [ currentOffer, selectedGroupIndex, selectedOfferForGroup, setPurchaseOptions ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (elementRef && elementRef.current) elementRef.current.scrollTop = 0;
|
||||
}, [selectedGroupIndex]);
|
||||
}, [ selectedGroupIndex ]);
|
||||
|
||||
if (!groupedOffers || (selectedGroupIndex === -1)) return null;
|
||||
|
||||
@ -104,11 +104,11 @@ export const CatalogSpacesWidgetView: FC<CatalogSpacesWidgetViewProps> = props =
|
||||
return (
|
||||
<>
|
||||
<div className="relative inline-flex align-middle">
|
||||
{SPACES_GROUP_NAMES.map((name, index) => <Button key={index} active={(selectedGroupIndex === index)} onClick={event => setSelectedGroupIndex(index)}>{LocalizeText(`catalog.spaces.tab.${name}`)}</Button>)}
|
||||
{ SPACES_GROUP_NAMES.map((name, index) => <Button key={ index } active={ (selectedGroupIndex === index) } onClick={ event => setSelectedGroupIndex(index) }>{ LocalizeText(`catalog.spaces.tab.${ name }`) }</Button>) }
|
||||
</div>
|
||||
<AutoGrid columnCount={columnCount} innerRef={elementRef} {...rest}>
|
||||
{offers && (offers.length > 0) && offers.map((offer, index) => <CatalogGridOfferView key={index} itemActive={(currentOffer && (currentOffer === offer))} offer={offer} selectOffer={offer => setSelectedOffer(offer)} />)}
|
||||
{children}
|
||||
<AutoGrid columnCount={ columnCount } innerRef={ elementRef } { ...rest }>
|
||||
{ offers && (offers.length > 0) && offers.map((offer, index) => <CatalogGridOfferView key={ index } itemActive={ (currentOffer && (currentOffer === offer)) } offer={ offer } selectOffer={ offer => setSelectedOffer(offer) } />) }
|
||||
{ children }
|
||||
</AutoGrid>
|
||||
</>
|
||||
);
|
||||
|
@ -35,11 +35,11 @@ export const CatalogSpinnerWidgetView: FC<{}> = props =>
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text>{LocalizeText('catalog.bundlewidget.spinner.select.amount')}</Text>
|
||||
<Text>{ LocalizeText('catalog.bundlewidget.spinner.select.amount') }</Text>
|
||||
<div className="flex items-center gap-1">
|
||||
<FaCaretLeft className="text-black cursor-pointer fa-icon" onClick={event => updateQuantity(quantity - 1)} />
|
||||
<input className="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none min-h-[17px] h-[17px] w-[28px] px-[4px] py-[0] text-right rounded-[.2rem]" type="number" value={quantity} onChange={event => updateQuantity(event.target.valueAsNumber)} />
|
||||
<FaCaretRight className="text-black cursor-pointer fa-icon" onClick={event => updateQuantity(quantity + 1)} />
|
||||
<FaCaretLeft className="text-black cursor-pointer fa-icon" onClick={ event => updateQuantity(quantity - 1) } />
|
||||
<input className="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none min-h-[17px] h-[17px] w-[28px] px-[4px] py-[0] text-right rounded-[.2rem]" type="number" value={ quantity } onChange={ event => updateQuantity(event.target.valueAsNumber) } />
|
||||
<FaCaretRight className="text-black cursor-pointer fa-icon" onClick={ event => updateQuantity(quantity + 1) } />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -10,7 +10,7 @@ export const OfferWindowView = (props: { offer: TargetedOfferData, setOpen: Disp
|
||||
|
||||
const { getCurrencyAmount } = usePurse();
|
||||
|
||||
const [amount, setAmount] = useState<number>(1);
|
||||
const [ amount, setAmount ] = useState<number>(1);
|
||||
|
||||
const canPurchase = useMemo(() =>
|
||||
{
|
||||
@ -26,7 +26,7 @@ export const OfferWindowView = (props: { offer: TargetedOfferData, setOpen: Disp
|
||||
if (offer.purchaseLimit > 0) limit = true;
|
||||
|
||||
return (credits && points && limit);
|
||||
}, [offer, getCurrencyAmount])
|
||||
}, [ offer, getCurrencyAmount ])
|
||||
|
||||
const expirationTime = () =>
|
||||
{
|
||||
@ -44,41 +44,41 @@ export const OfferWindowView = (props: { offer: TargetedOfferData, setOpen: Disp
|
||||
if (!offer) return;
|
||||
|
||||
return <NitroCardView className="nitro-targeted-offer" theme="primary-slim" uniqueKey="targeted-offer">
|
||||
<NitroCardHeaderView headerText={LocalizeText(offer.title)} onCloseClick={event => setOpen(false)} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText(offer.title) } onCloseClick={ event => setOpen(false) } />
|
||||
<div className="container-fluid p-1 relative justify-center items-center cursor-pointer gap-3 bg-danger">
|
||||
{LocalizeText('targeted.offer.timeleft', ['timeleft'], [expirationTime()])}
|
||||
{ LocalizeText('targeted.offer.timeleft', [ 'timeleft' ], [ expirationTime() ]) }
|
||||
</div>
|
||||
<NitroCardContentView gap={1}>
|
||||
<Flex fullHeight gap={1}>
|
||||
<Flex column className="w-75 text-black" gap={1}>
|
||||
<NitroCardContentView gap={ 1 }>
|
||||
<Flex fullHeight gap={ 1 }>
|
||||
<Flex column className="w-75 text-black" gap={ 1 }>
|
||||
<Column fullHeight className="bg-warning p-2">
|
||||
<h4>
|
||||
{LocalizeText(offer.title)}
|
||||
{ LocalizeText(offer.title) }
|
||||
</h4>
|
||||
<div dangerouslySetInnerHTML={{ __html: offer.description }} />
|
||||
<div dangerouslySetInnerHTML={ { __html: offer.description } } />
|
||||
</Column>
|
||||
<Flex alignItems="center" alignSelf="center" gap={2} justifyContent="center">
|
||||
{offer.purchaseLimit > 1 &&
|
||||
<Flex alignItems="center" alignSelf="center" gap={ 2 } justifyContent="center">
|
||||
{ offer.purchaseLimit > 1 &&
|
||||
<div className="flex gap-1">
|
||||
<Text variant="muted">{LocalizeText('catalog.bundlewidget.quantity')}</Text>
|
||||
<input max={offer.purchaseLimit} min={1} type="number" value={amount} onChange={evt => setAmount(parseInt(evt.target.value))} />
|
||||
</div>}
|
||||
<Button disabled={!canPurchase} variant="primary" onClick={() => buyOffer()}>{LocalizeText('targeted.offer.button.buy')}</Button>
|
||||
<Text variant="muted">{ LocalizeText('catalog.bundlewidget.quantity') }</Text>
|
||||
<input max={ offer.purchaseLimit } min={ 1 } type="number" value={ amount } onChange={ evt => setAmount(parseInt(evt.target.value)) } />
|
||||
</div> }
|
||||
<Button disabled={ !canPurchase } variant="primary" onClick={ () => buyOffer() }>{ LocalizeText('targeted.offer.button.buy') }</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<div className="w-50 h-full" style={{ background: `url(${GetConfigurationValue('image.library.url') + offer.imageUrl}) no-repeat center` }} />
|
||||
<div className="w-50 h-full" style={ { background: `url(${ GetConfigurationValue('image.library.url') + offer.imageUrl }) no-repeat center` } } />
|
||||
</Flex>
|
||||
<Flex column alignItems="center" className="price-ray absolute" justifyContent="center">
|
||||
<Text>{LocalizeText('targeted.offer.price.label')}</Text>
|
||||
{offer.priceInCredits > 0 &&
|
||||
<Text>{ LocalizeText('targeted.offer.price.label') }</Text>
|
||||
{ offer.priceInCredits > 0 &&
|
||||
<div className="flex gap-1">
|
||||
<Text variant="light">{offer.priceInCredits}</Text>
|
||||
<LayoutCurrencyIcon type={-1} />
|
||||
</div>}
|
||||
{offer.priceInActivityPoints > 0 &&
|
||||
<Text variant="light">{ offer.priceInCredits }</Text>
|
||||
<LayoutCurrencyIcon type={ -1 } />
|
||||
</div> }
|
||||
{ offer.priceInActivityPoints > 0 &&
|
||||
<div className="flex gap-1">
|
||||
<Text className="ubuntu-bold" variant="light">+{offer.priceInActivityPoints}</Text> <LayoutCurrencyIcon type={offer.activityPointType} />
|
||||
</div>}
|
||||
<Text className="ubuntu-bold" variant="light">+{ offer.priceInActivityPoints }</Text> <LayoutCurrencyIcon type={ offer.activityPointType } />
|
||||
</div> }
|
||||
</Flex>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>;
|
||||
|
@ -3,11 +3,12 @@ import { FC, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { ChatEntryType, LocalizeText } from '../../api';
|
||||
import { Flex, InfiniteScroll, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common';
|
||||
import { useChatHistory } from '../../hooks';
|
||||
import { NitroInput } from '../../layout';
|
||||
|
||||
export const ChatHistoryView: FC<{}> = props =>
|
||||
{
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [searchText, setSearchText] = useState<string>('');
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const [ searchText, setSearchText ] = useState<string>('');
|
||||
const { chatHistory = [] } = useChatHistory();
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -18,12 +19,12 @@ export const ChatHistoryView: FC<{}> = props =>
|
||||
let text = searchText.toLowerCase();
|
||||
|
||||
return chatHistory.filter(entry => ((entry.message && entry.message.toLowerCase().includes(text))) || (entry.name && entry.name.toLowerCase().includes(text)));
|
||||
}, [chatHistory, searchText]);
|
||||
}, [ chatHistory, searchText ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (elementRef && elementRef.current && isVisible) elementRef.current.scrollTop = elementRef.current.scrollHeight;
|
||||
}, [isVisible]);
|
||||
}, [ isVisible ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -59,37 +60,37 @@ export const ChatHistoryView: FC<{}> = props =>
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-chat-history" theme="primary-slim" uniqueKey="chat-history">
|
||||
<NitroCardHeaderView headerText={LocalizeText('room.chathistory.button.text')} onCloseClick={event => setIsVisible(false)} />
|
||||
<NitroCardContentView gap={2} innerRef={elementRef} overflow="hidden">
|
||||
<NitroInput placeholder={LocalizeText('generic.search')} type="text" value={searchText} onChange={event => setSearchText(event.target.value)} />
|
||||
<InfiniteScroll rowRender={row =>
|
||||
<NitroCardHeaderView headerText={ LocalizeText('room.chathistory.button.text') } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardContentView gap={ 2 } innerRef={ elementRef } overflow="hidden">
|
||||
<NitroInput placeholder={ LocalizeText('generic.search') } type="text" value={ searchText } onChange={ event => setSearchText(event.target.value) } />
|
||||
<InfiniteScroll rowRender={ row =>
|
||||
{
|
||||
return (
|
||||
<Flex alignItems="center" className="p-1" gap={2}>
|
||||
<Text variant="muted">{row.timestamp}</Text>
|
||||
{(row.type === ChatEntryType.TYPE_CHAT) &&
|
||||
<div className="bubble-container" style={{ position: 'relative' }}>
|
||||
{(row.style === 0) &&
|
||||
<div className="user-container-bg" style={{ backgroundColor: row.color }} />}
|
||||
<div className={`chat-bubble bubble-${row.style} type-${row.chatType}`} style={{ maxWidth: '100%' }}>
|
||||
<Flex alignItems="center" className="p-1" gap={ 2 }>
|
||||
<Text variant="muted">{ row.timestamp }</Text>
|
||||
{ (row.type === ChatEntryType.TYPE_CHAT) &&
|
||||
<div className="bubble-container" style={ { position: 'relative' } }>
|
||||
{ (row.style === 0) &&
|
||||
<div className="user-container-bg" style={ { backgroundColor: row.color } } /> }
|
||||
<div className={ `chat-bubble bubble-${ row.style } type-${ row.chatType }` } style={ { maxWidth: '100%' } }>
|
||||
<div className="user-container">
|
||||
{row.imageUrl && (row.imageUrl.length > 0) &&
|
||||
<div className="user-image" style={{ backgroundImage: `url(${row.imageUrl})` }} />}
|
||||
{ row.imageUrl && (row.imageUrl.length > 0) &&
|
||||
<div className="user-image" style={ { backgroundImage: `url(${ row.imageUrl })` } } /> }
|
||||
</div>
|
||||
<div className="chat-content">
|
||||
<b className="username mr-1" dangerouslySetInnerHTML={{ __html: `${row.name}: ` }} />
|
||||
<span className="message" dangerouslySetInnerHTML={{ __html: `${row.message}` }} />
|
||||
<b className="mr-1 username" dangerouslySetInnerHTML={ { __html: `${ row.name }: ` } } />
|
||||
<span className="message" dangerouslySetInnerHTML={ { __html: `${ row.message }` } } />
|
||||
</div>
|
||||
</div>
|
||||
</div>}
|
||||
{(row.type === ChatEntryType.TYPE_ROOM_INFO) &&
|
||||
</div> }
|
||||
{ (row.type === ChatEntryType.TYPE_ROOM_INFO) &&
|
||||
<>
|
||||
<i className="nitro-icon icon-small-room" />
|
||||
<Text grow textBreak wrap>{row.name}</Text>
|
||||
</>}
|
||||
<Text grow textBreak wrap>{ row.name }</Text>
|
||||
</> }
|
||||
</Flex>
|
||||
)
|
||||
}} rows={filteredChatHistory} scrollToBottom={true} />
|
||||
} } rows={ filteredChatHistory } scrollToBottom={ true } />
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
|
@ -14,18 +14,18 @@ import { FloorplanOptionsView } from './views/FloorplanOptionsView';
|
||||
|
||||
export const FloorplanEditorView: FC<{}> = props =>
|
||||
{
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [importExportVisible, setImportExportVisible] = useState(false);
|
||||
const [originalFloorplanSettings, setOriginalFloorplanSettings] = useState<IFloorplanSettings>({
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const [ importExportVisible, setImportExportVisible ] = useState(false);
|
||||
const [ originalFloorplanSettings, setOriginalFloorplanSettings ] = useState<IFloorplanSettings>({
|
||||
tilemap: '',
|
||||
reservedTiles: [],
|
||||
entryPoint: [0, 0],
|
||||
entryPoint: [ 0, 0 ],
|
||||
entryPointDir: 2,
|
||||
wallHeight: -1,
|
||||
thicknessWall: 1,
|
||||
thicknessFloor: 1
|
||||
});
|
||||
const [visualizationSettings, setVisualizationSettings] = useState<IVisualizationSettings>({
|
||||
const [ visualizationSettings, setVisualizationSettings ] = useState<IVisualizationSettings>({
|
||||
entryPointDir: 2,
|
||||
wallHeight: -1,
|
||||
thicknessWall: 1,
|
||||
@ -136,25 +136,25 @@ export const FloorplanEditorView: FC<{}> = props =>
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<FloorplanEditorContextProvider value={{ originalFloorplanSettings: originalFloorplanSettings, setOriginalFloorplanSettings: setOriginalFloorplanSettings, visualizationSettings: visualizationSettings, setVisualizationSettings: setVisualizationSettings }}>
|
||||
{isVisible &&
|
||||
<FloorplanEditorContextProvider value={ { originalFloorplanSettings: originalFloorplanSettings, setOriginalFloorplanSettings: setOriginalFloorplanSettings, visualizationSettings: visualizationSettings, setVisualizationSettings: setVisualizationSettings } }>
|
||||
{ isVisible &&
|
||||
<NitroCardView className="w-[760px] h-[500px]" theme="primary-slim" uniqueKey="floorpan-editor">
|
||||
<NitroCardHeaderView headerText={LocalizeText('floor.plan.editor.title')} onCloseClick={() => setIsVisible(false)} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText('floor.plan.editor.title') } onCloseClick={ () => setIsVisible(false) } />
|
||||
<NitroCardContentView overflow="hidden">
|
||||
<FloorplanOptionsView />
|
||||
<FloorplanCanvasView overflow="hidden" />
|
||||
<div className="flex justify-between">
|
||||
<Button onClick={revertChanges}>{LocalizeText('floor.plan.editor.reload')}</Button>
|
||||
<Button onClick={ revertChanges }>{ LocalizeText('floor.plan.editor.reload') }</Button>
|
||||
<div className="relative inline-flex align-middle">
|
||||
<Button disabled={true}>{LocalizeText('floor.plan.editor.preview')}</Button>
|
||||
<Button onClick={event => setImportExportVisible(true)}>{LocalizeText('floor.plan.editor.import.export')}</Button>
|
||||
<Button onClick={saveFloorChanges}>{LocalizeText('floor.plan.editor.save')}</Button>
|
||||
<Button disabled={ true }>{ LocalizeText('floor.plan.editor.preview') }</Button>
|
||||
<Button onClick={ event => setImportExportVisible(true) }>{ LocalizeText('floor.plan.editor.import.export') }</Button>
|
||||
<Button onClick={ saveFloorChanges }>{ LocalizeText('floor.plan.editor.save') }</Button>
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>}
|
||||
{importExportVisible &&
|
||||
<FloorplanImportExportView onCloseClick={() => setImportExportVisible(false)} />}
|
||||
</NitroCardView> }
|
||||
{ importExportVisible &&
|
||||
<FloorplanImportExportView onCloseClick={ () => setImportExportVisible(false) } /> }
|
||||
</FloorplanEditorContextProvider>
|
||||
);
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ import { FloorplanEditor } from '../common/FloorplanEditor';
|
||||
export const FloorplanCanvasView: FC<ColumnProps> = props =>
|
||||
{
|
||||
const { gap = 1, children = null, ...rest } = props;
|
||||
const [occupiedTilesReceived, setOccupiedTilesReceived] = useState(false);
|
||||
const [entryTileReceived, setEntryTileReceived] = useState(false);
|
||||
const [ occupiedTilesReceived, setOccupiedTilesReceived ] = useState(false);
|
||||
const [ entryTileReceived, setEntryTileReceived ] = useState(false);
|
||||
const { originalFloorplanSettings = null, setOriginalFloorplanSettings = null, setVisualizationSettings = null } = useFloorplanEditorContext();
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -43,7 +43,7 @@ export const FloorplanCanvasView: FC<ColumnProps> = props =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.entryPoint = [parser.x, parser.y];
|
||||
newValue.entryPoint = [ parser.x, parser.y ];
|
||||
newValue.entryPointDir = parser.direction;
|
||||
|
||||
return newValue;
|
||||
@ -121,14 +121,14 @@ export const FloorplanCanvasView: FC<ColumnProps> = props =>
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [originalFloorplanSettings.thicknessFloor, originalFloorplanSettings.thicknessWall, originalFloorplanSettings.wallHeight, setVisualizationSettings]);
|
||||
}, [ originalFloorplanSettings.thicknessFloor, originalFloorplanSettings.thicknessWall, originalFloorplanSettings.wallHeight, setVisualizationSettings ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (!entryTileReceived || !occupiedTilesReceived) return;
|
||||
|
||||
FloorplanEditor.instance.renderTiles();
|
||||
}, [entryTileReceived, occupiedTilesReceived]);
|
||||
}, [ entryTileReceived, occupiedTilesReceived ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -167,33 +167,33 @@ export const FloorplanCanvasView: FC<ColumnProps> = props =>
|
||||
const isSmallScreen = () => window.innerWidth < 768;
|
||||
|
||||
return (
|
||||
<Column gap={gap} {...rest}>
|
||||
<Grid gap={1} overflow="hidden">
|
||||
<Column center className="hidden" size={1}>
|
||||
<Button className="hidden" onClick={event => onClickArrowButton('left')}>
|
||||
<Column gap={ gap } { ...rest }>
|
||||
<Grid gap={ 1 } overflow="hidden">
|
||||
<Column center className="hidden" size={ 1 }>
|
||||
<Button className="hidden" onClick={ event => onClickArrowButton('left') }>
|
||||
<FaArrowLeft className="fa-icon" />
|
||||
</Button>
|
||||
</Column>
|
||||
<Column gap={1} overflow="hidden" size={isSmallScreen() ? 10 : 12}>
|
||||
<Column gap={ 1 } overflow="hidden" size={ isSmallScreen() ? 10 : 12 }>
|
||||
<div className="flex hidden justify-content-enter" >
|
||||
<Button shrink onClick={event => onClickArrowButton('up')}>
|
||||
<Button shrink onClick={ event => onClickArrowButton('up') }>
|
||||
<FaArrowUp className="fa-icon" />
|
||||
</Button>
|
||||
</div>
|
||||
<div ref={elementRef} className="overflow-auto" />
|
||||
<div ref={ elementRef } className="overflow-auto" />
|
||||
<div className="flex hidden justify-center">
|
||||
<Button shrink onClick={event => onClickArrowButton('down')}>
|
||||
<Button shrink onClick={ event => onClickArrowButton('down') }>
|
||||
<FaArrowDown className="fa-icon" />
|
||||
</Button>
|
||||
</div>
|
||||
</Column>
|
||||
<Column center className="hidden" size={1}>
|
||||
<Button className="hidden" onClick={event => onClickArrowButton('right')}>
|
||||
<Column center className="hidden" size={ 1 }>
|
||||
<Button className="hidden" onClick={ event => onClickArrowButton('right') }>
|
||||
<FaArrowRight className="fa-icon" />
|
||||
</Button>
|
||||
</Column>
|
||||
</Grid>
|
||||
{children}
|
||||
{ children }
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ interface FloorplanImportExportViewProps
|
||||
export const FloorplanImportExportView: FC<FloorplanImportExportViewProps> = props =>
|
||||
{
|
||||
const { onCloseClick = null } = props;
|
||||
const [map, setMap] = useState<string>('');
|
||||
const [ map, setMap ] = useState<string>('');
|
||||
const { originalFloorplanSettings = null } = useFloorplanEditorContext();
|
||||
|
||||
const saveFloorChanges = () =>
|
||||
@ -38,15 +38,15 @@ export const FloorplanImportExportView: FC<FloorplanImportExportViewProps> = pro
|
||||
|
||||
return (
|
||||
<NitroCardView className="floorplan-import-export" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={LocalizeText('floor.plan.editor.import.export')} onCloseClick={onCloseClick} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText('floor.plan.editor.import.export') } onCloseClick={ onCloseClick } />
|
||||
<NitroCardContentView>
|
||||
<textarea className="h-full" value={map} onChange={event => setMap(event.target.value)} />
|
||||
<textarea className="h-full" value={ map } onChange={ event => setMap(event.target.value) } />
|
||||
<div className="flex justify-between">
|
||||
<Button onClick={event => setMap(ConvertTileMapToString(originalFloorplanSettings.tilemap))}>
|
||||
{LocalizeText('floor.plan.editor.revert.to.last.received.map')}
|
||||
<Button onClick={ event => setMap(ConvertTileMapToString(originalFloorplanSettings.tilemap)) }>
|
||||
{ LocalizeText('floor.plan.editor.revert.to.last.received.map') }
|
||||
</Button>
|
||||
<Button onClick={saveFloorChanges}>
|
||||
{LocalizeText('floor.plan.editor.save')}
|
||||
<Button onClick={ saveFloorChanges }>
|
||||
{ LocalizeText('floor.plan.editor.save') }
|
||||
</Button>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
|
@ -16,8 +16,8 @@ const MAX_FLOOR_HEIGHT: number = 26;
|
||||
export const FloorplanOptionsView: FC<{}> = props =>
|
||||
{
|
||||
const { visualizationSettings = null, setVisualizationSettings = null } = useFloorplanEditorContext();
|
||||
const [floorAction, setFloorAction] = useState(FloorAction.SET);
|
||||
const [floorHeight, setFloorHeight] = useState(0);
|
||||
const [ floorAction, setFloorAction ] = useState(FloorAction.SET);
|
||||
const [ floorHeight, setFloorHeight ] = useState(0);
|
||||
|
||||
const selectAction = (action: number) =>
|
||||
{
|
||||
@ -117,69 +117,69 @@ export const FloorplanOptionsView: FC<{}> = props =>
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<Grid>
|
||||
<Column gap={1} size={5}>
|
||||
<Text bold>{LocalizeText('floor.plan.editor.draw.mode')}</Text>
|
||||
<Flex gap={3}>
|
||||
<Column gap={ 1 } size={ 5 }>
|
||||
<Text bold>{ LocalizeText('floor.plan.editor.draw.mode') }</Text>
|
||||
<Flex gap={ 3 }>
|
||||
<div className="flex gap-1">
|
||||
<LayoutGridItem itemActive={(floorAction === FloorAction.SET)} onClick={event => selectAction(FloorAction.SET)}>
|
||||
<LayoutGridItem itemActive={ (floorAction === FloorAction.SET) } onClick={ event => selectAction(FloorAction.SET) }>
|
||||
<i className="nitro-icon icon-set-tile" />
|
||||
</LayoutGridItem>
|
||||
<LayoutGridItem itemActive={(floorAction === FloorAction.UNSET)} onClick={event => selectAction(FloorAction.UNSET)}>
|
||||
<LayoutGridItem itemActive={ (floorAction === FloorAction.UNSET) } onClick={ event => selectAction(FloorAction.UNSET) }>
|
||||
<i className="nitro-icon icon-unset-tile" />
|
||||
</LayoutGridItem>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
<LayoutGridItem itemActive={(floorAction === FloorAction.UP)} onClick={event => selectAction(FloorAction.UP)}>
|
||||
<LayoutGridItem itemActive={ (floorAction === FloorAction.UP) } onClick={ event => selectAction(FloorAction.UP) }>
|
||||
<i className="nitro-icon icon-increase-height" />
|
||||
</LayoutGridItem>
|
||||
<LayoutGridItem itemActive={(floorAction === FloorAction.DOWN)} onClick={event => selectAction(FloorAction.DOWN)}>
|
||||
<LayoutGridItem itemActive={ (floorAction === FloorAction.DOWN) } onClick={ event => selectAction(FloorAction.DOWN) }>
|
||||
<i className="nitro-icon icon-decrease-height" />
|
||||
</LayoutGridItem>
|
||||
</div>
|
||||
<LayoutGridItem itemActive={(floorAction === FloorAction.DOOR)} onClick={event => selectAction(FloorAction.DOOR)}>
|
||||
<LayoutGridItem itemActive={ (floorAction === FloorAction.DOOR) } onClick={ event => selectAction(FloorAction.DOOR) }>
|
||||
<i className="nitro-icon icon-set-door" />
|
||||
</LayoutGridItem>
|
||||
</Flex>
|
||||
</Column>
|
||||
<Column alignItems="center" size={4}>
|
||||
<Text bold>{LocalizeText('floor.plan.editor.enter.direction')}</Text>
|
||||
<i className={`nitro-icon icon-door-direction-${visualizationSettings.entryPointDir} cursor-pointer`} onClick={changeDoorDirection} />
|
||||
<Column alignItems="center" size={ 4 }>
|
||||
<Text bold>{ LocalizeText('floor.plan.editor.enter.direction') }</Text>
|
||||
<i className={ `nitro-icon icon-door-direction-${ visualizationSettings.entryPointDir } cursor-pointer` } onClick={ changeDoorDirection } />
|
||||
</Column>
|
||||
<Column size={3}>
|
||||
<Text bold>{LocalizeText('floor.editor.wall.height')}</Text>
|
||||
<Column size={ 3 }>
|
||||
<Text bold>{ LocalizeText('floor.editor.wall.height') }</Text>
|
||||
<div className="flex items-center gap-1">
|
||||
<FaCaretLeft className="cursor-pointer fa-icon" onClick={decreaseWallHeight} />
|
||||
<input className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] form-control-sm quantity-input" type="number" value={visualizationSettings.wallHeight} onChange={event => onWallHeightChange(event.target.valueAsNumber)} />
|
||||
<FaCaretRight className="cursor-pointer fa-icon" onClick={increaseWallHeight} />
|
||||
<FaCaretLeft className="cursor-pointer fa-icon" onClick={ decreaseWallHeight } />
|
||||
<input className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] form-control-sm quantity-input" type="number" value={ visualizationSettings.wallHeight } onChange={ event => onWallHeightChange(event.target.valueAsNumber) } />
|
||||
<FaCaretRight className="cursor-pointer fa-icon" onClick={ increaseWallHeight } />
|
||||
</div>
|
||||
</Column>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Column size={6}>
|
||||
<Text bold>{LocalizeText('floor.plan.editor.tile.height')}: {floorHeight}</Text>
|
||||
<Column size={ 6 }>
|
||||
<Text bold>{ LocalizeText('floor.plan.editor.tile.height') }: { floorHeight }</Text>
|
||||
<ReactSlider
|
||||
className="nitro-slider"
|
||||
max={MAX_FLOOR_HEIGHT}
|
||||
min={MIN_FLOOR_HEIGHT}
|
||||
renderThumb={({ style, ...rest }, state) => <div style={{ backgroundColor: `#${COLORMAP[state.valueNow.toString(33)]}`, ...style }} {...rest}>{state.valueNow}</div>}
|
||||
step={1}
|
||||
value={floorHeight}
|
||||
onChange={event => onFloorHeightChange(event)} />
|
||||
max={ MAX_FLOOR_HEIGHT }
|
||||
min={ MIN_FLOOR_HEIGHT }
|
||||
renderThumb={ ({ style, ...rest }, state) => <div style={ { backgroundColor: `#${ COLORMAP[state.valueNow.toString(33)] }`, ...style } } { ...rest }>{ state.valueNow }</div> }
|
||||
step={ 1 }
|
||||
value={ floorHeight }
|
||||
onChange={ event => onFloorHeightChange(event) } />
|
||||
</Column>
|
||||
<Column size={6}>
|
||||
<Text bold>{LocalizeText('floor.plan.editor.room.options')}</Text>
|
||||
<Column size={ 6 }>
|
||||
<Text bold>{ LocalizeText('floor.plan.editor.room.options') }</Text>
|
||||
<Flex className="items-center">
|
||||
<select className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] form-control-sm" value={visualizationSettings.thicknessWall} onChange={event => onWallThicknessChange(parseInt(event.target.value))}>
|
||||
<option value={0}>{LocalizeText('navigator.roomsettings.wall_thickness.thinnest')}</option>
|
||||
<option value={1}>{LocalizeText('navigator.roomsettings.wall_thickness.thin')}</option>
|
||||
<option value={2}>{LocalizeText('navigator.roomsettings.wall_thickness.normal')}</option>
|
||||
<option value={3}>{LocalizeText('navigator.roomsettings.wall_thickness.thick')}</option>
|
||||
<select className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] form-control-sm" value={ visualizationSettings.thicknessWall } onChange={ event => onWallThicknessChange(parseInt(event.target.value)) }>
|
||||
<option value={ 0 }>{ LocalizeText('navigator.roomsettings.wall_thickness.thinnest') }</option>
|
||||
<option value={ 1 }>{ LocalizeText('navigator.roomsettings.wall_thickness.thin') }</option>
|
||||
<option value={ 2 }>{ LocalizeText('navigator.roomsettings.wall_thickness.normal') }</option>
|
||||
<option value={ 3 }>{ LocalizeText('navigator.roomsettings.wall_thickness.thick') }</option>
|
||||
</select>
|
||||
<select className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] form-control-sm" value={visualizationSettings.thicknessFloor} onChange={event => onFloorThicknessChange(parseInt(event.target.value))}>
|
||||
<option value={0}>{LocalizeText('navigator.roomsettings.floor_thickness.thinnest')}</option>
|
||||
<option value={1}>{LocalizeText('navigator.roomsettings.floor_thickness.thin')}</option>
|
||||
<option value={2}>{LocalizeText('navigator.roomsettings.floor_thickness.normal')}</option>
|
||||
<option value={3}>{LocalizeText('navigator.roomsettings.floor_thickness.thick')}</option>
|
||||
<select className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] form-control-sm" value={ visualizationSettings.thicknessFloor } onChange={ event => onFloorThicknessChange(parseInt(event.target.value)) }>
|
||||
<option value={ 0 }>{ LocalizeText('navigator.roomsettings.floor_thickness.thinnest') }</option>
|
||||
<option value={ 1 }>{ LocalizeText('navigator.roomsettings.floor_thickness.thin') }</option>
|
||||
<option value={ 2 }>{ LocalizeText('navigator.roomsettings.floor_thickness.normal') }</option>
|
||||
<option value={ 3 }>{ LocalizeText('navigator.roomsettings.floor_thickness.thick') }</option>
|
||||
</select>
|
||||
</Flex>
|
||||
</Column>
|
||||
|
@ -7,7 +7,7 @@ import { useFriends } from '../../../../hooks';
|
||||
export const FriendBarItemView: FC<{ friend: MessengerFriend }> = props =>
|
||||
{
|
||||
const { friend = null } = props;
|
||||
const [isVisible, setVisible] = useState(false);
|
||||
const [ isVisible, setVisible ] = useState(false);
|
||||
const { followFriend = null } = useFriends();
|
||||
const elementRef = useRef<HTMLDivElement>();
|
||||
|
||||
@ -33,27 +33,27 @@ export const FriendBarItemView: FC<{ friend: MessengerFriend }> = props =>
|
||||
if (!friend)
|
||||
{
|
||||
return (
|
||||
<Button size='md' justifyContent='start' className="border w-[130px] mx-[3px] my-[0] z-0 relative pl-[37px] text-left friend-bar-search">
|
||||
<Button className="border w-[130px] mx-[3px] my-[0] z-0 relative pl-[37px] text-left friend-bar-search" justifyContent="start" size="md">
|
||||
<div className="absolute -top-[3px] left-[5px] w-[31px] h-[34px] bg-[url('@/assets/images/toolbar/friend-search.png')]" />
|
||||
<div className="truncate">{LocalizeText('friend.bar.find.title')}</div>
|
||||
<div className="truncate">{ LocalizeText('friend.bar.find.title') }</div>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button size='md' justifyContent='start' variant={'success'} className={' block w-[130px] mx-[3px] my-[0] z-0 relative pl-[37px] text-left' + (isVisible ? 'mb-[21px]' : '')} onClick={event => setVisible(prevValue => !prevValue)}>
|
||||
<div className={`friend-bar-item-head absolute ${friend.id > 0 ? '-top-[30px] -left-[30px]' : '-top-[5px] -left-[3.5px]'}`}>
|
||||
{(friend.id > 0) && <LayoutAvatarImageView direction={2} figure={friend.figure} headOnly={true} />}
|
||||
{(friend.id <= 0) && <LayoutBadgeImageView badgeCode={friend.figure} isGroup={true} />}
|
||||
<Button className={ ' block w-[130px] mx-[3px] my-[0] z-0 relative pl-[37px] text-left' + (isVisible ? 'mb-[21px]' : '') } justifyContent="start" size="md" variant={ 'success' } onClick={ event => setVisible(prevValue => !prevValue) }>
|
||||
<div className={ `friend-bar-item-head absolute ${ friend.id > 0 ? '-top-[30px] -left-[30px]' : '-top-[5px] -left-[3.5px]' }` }>
|
||||
{ (friend.id > 0) && <LayoutAvatarImageView direction={ 2 } figure={ friend.figure } headOnly={ true } /> }
|
||||
{ (friend.id <= 0) && <LayoutBadgeImageView badgeCode={ friend.figure } isGroup={ true } /> }
|
||||
</div>
|
||||
<div className="truncate">{friend.name}</div>
|
||||
{isVisible &&
|
||||
<div className="truncate">{ friend.name }</div>
|
||||
{ isVisible &&
|
||||
<div className="flex justify-between">
|
||||
<div className="cursor-pointer nitro-friends-spritesheet icon-friendbar-chat" onClick={event => OpenMessengerChat(friend.id)} />
|
||||
{friend.followingAllowed &&
|
||||
<div className="cursor-pointer nitro-friends-spritesheet icon-friendbar-visit" onClick={event => followFriend(friend)} />}
|
||||
<div className="cursor-pointer nitro-friends-spritesheet icon-profile" onClick={event => GetUserProfile(friend.id)} />
|
||||
</div>}
|
||||
<div className="cursor-pointer nitro-friends-spritesheet icon-friendbar-chat" onClick={ event => OpenMessengerChat(friend.id) } />
|
||||
{ friend.followingAllowed &&
|
||||
<div className="cursor-pointer nitro-friends-spritesheet icon-friendbar-visit" onClick={ event => followFriend(friend) } /> }
|
||||
<div className="cursor-pointer nitro-friends-spritesheet icon-profile" onClick={ event => GetUserProfile(friend.id) } />
|
||||
</div> }
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
@ -9,16 +9,16 @@ const MAX_DISPLAY_COUNT = 3;
|
||||
export const FriendBarView: FC<{ onlineFriends: MessengerFriend[] }> = props =>
|
||||
{
|
||||
const { onlineFriends = null } = props;
|
||||
const [indexOffset, setIndexOffset] = useState(0);
|
||||
const [ indexOffset, setIndexOffset ] = useState(0);
|
||||
const elementRef = useRef<HTMLDivElement>();
|
||||
|
||||
return (
|
||||
<div ref={elementRef} className="flex items-center ">
|
||||
<Button className="z-[2] cursor-pointer" disabled={(indexOffset <= 0)} variant="black" onClick={event => setIndexOffset(indexOffset - 1)}>
|
||||
<div ref={ elementRef } className="flex items-center ">
|
||||
<Button className="z-[2] cursor-pointer" disabled={ (indexOffset <= 0) } variant="black" onClick={ event => setIndexOffset(indexOffset - 1) }>
|
||||
<FaChevronLeft className="fa-icon" />
|
||||
</Button>
|
||||
{Array.from(Array(MAX_DISPLAY_COUNT), (e, i) => <FriendBarItemView key={i} friend={(onlineFriends[indexOffset + i] || null)} />)}
|
||||
<Button className="z-[2] cursor-pointer" disabled={!((onlineFriends.length > MAX_DISPLAY_COUNT) && ((indexOffset + MAX_DISPLAY_COUNT) <= (onlineFriends.length - 1)))} variant="black" onClick={event => setIndexOffset(indexOffset + 1)}>
|
||||
{ Array.from(Array(MAX_DISPLAY_COUNT), (e, i) => <FriendBarItemView key={ i } friend={ (onlineFriends[indexOffset + i] || null) } />) }
|
||||
<Button className="z-[2] cursor-pointer" disabled={ !((onlineFriends.length > MAX_DISPLAY_COUNT) && ((indexOffset + MAX_DISPLAY_COUNT) <= (onlineFriends.length - 1))) } variant="black" onClick={ event => setIndexOffset(indexOffset + 1) }>
|
||||
<FaChevronRight className="fa-icon" />
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -12,18 +12,18 @@ interface FriendsRoomInviteViewProps
|
||||
export const FriendsRoomInviteView: FC<FriendsRoomInviteViewProps> = props =>
|
||||
{
|
||||
const { selectedFriendsIds = null, onCloseClick = null, sendRoomInvite = null } = props;
|
||||
const [roomInviteMessage, setRoomInviteMessage] = useState<string>('');
|
||||
const [ roomInviteMessage, setRoomInviteMessage ] = useState<string>('');
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-friends-room-invite" theme="primary-slim" uniqueKey="nitro-friends-room-invite">
|
||||
<NitroCardHeaderView headerText={LocalizeText('friendlist.invite.title')} onCloseClick={onCloseClick} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText('friendlist.invite.title') } onCloseClick={ onCloseClick } />
|
||||
<NitroCardContentView className="text-black">
|
||||
{LocalizeText('friendlist.invite.summary', ['count'], [selectedFriendsIds.length.toString()])}
|
||||
<textarea className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem]" maxLength={255} value={roomInviteMessage} onChange={event => setRoomInviteMessage(event.target.value)}></textarea>
|
||||
<Text center className="bg-muted rounded p-1">{LocalizeText('friendlist.invite.note')}</Text>
|
||||
{ LocalizeText('friendlist.invite.summary', [ 'count' ], [ selectedFriendsIds.length.toString() ]) }
|
||||
<textarea className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem]" maxLength={ 255 } value={ roomInviteMessage } onChange={ event => setRoomInviteMessage(event.target.value) }></textarea>
|
||||
<Text center className="bg-muted rounded p-1">{ LocalizeText('friendlist.invite.note') }</Text>
|
||||
<div className="flex gap-1">
|
||||
<Button fullWidth disabled={((roomInviteMessage.length === 0) || (selectedFriendsIds.length === 0))} variant="success" onClick={() => sendRoomInvite(roomInviteMessage)}>{LocalizeText('friendlist.invite.send')}</Button>
|
||||
<Button fullWidth onClick={onCloseClick}>{LocalizeText('generic.cancel')}</Button>
|
||||
<Button fullWidth disabled={ ((roomInviteMessage.length === 0) || (selectedFriendsIds.length === 0)) } variant="success" onClick={ () => sendRoomInvite(roomInviteMessage) }>{ LocalizeText('friendlist.invite.send') }</Button>
|
||||
<Button fullWidth onClick={ onCloseClick }>{ LocalizeText('generic.cancel') }</Button>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
|
@ -12,9 +12,9 @@ interface FriendsSearchViewProps extends NitroCardAccordionSetViewProps
|
||||
export const FriendsSearchView: FC<FriendsSearchViewProps> = props =>
|
||||
{
|
||||
const { ...rest } = props;
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const [friendResults, setFriendResults] = useState<HabboSearchResultData[]>(null);
|
||||
const [otherResults, setOtherResults] = useState<HabboSearchResultData[]>(null);
|
||||
const [ searchValue, setSearchValue ] = useState('');
|
||||
const [ friendResults, setFriendResults ] = useState<HabboSearchResultData[]>(null);
|
||||
const [ otherResults, setOtherResults ] = useState<HabboSearchResultData[]>(null);
|
||||
const { canRequestFriend = null, requestFriend = null } = useFriends();
|
||||
|
||||
useMessageEvent<HabboSearchResultEvent>(HabboSearchResultEvent, event =>
|
||||
@ -37,66 +37,66 @@ export const FriendsSearchView: FC<FriendsSearchViewProps> = props =>
|
||||
}, 500);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [searchValue]);
|
||||
}, [ searchValue ]);
|
||||
|
||||
return (
|
||||
<NitroCardAccordionSetView {...rest}>
|
||||
<input className="search-input form-control form-control-sm w-full rounded-0" maxLength={50} placeholder={LocalizeText('generic.search')} type="text" value={searchValue} onChange={event => setSearchValue(event.target.value)} />
|
||||
<NitroCardAccordionSetView { ...rest }>
|
||||
<input className="search-input form-control form-control-sm w-full rounded-0" maxLength={ 50 } placeholder={ LocalizeText('generic.search') } type="text" value={ searchValue } onChange={ event => setSearchValue(event.target.value) } />
|
||||
<div className="flex flex-col">
|
||||
{friendResults &&
|
||||
{ friendResults &&
|
||||
<>
|
||||
{(friendResults.length === 0) &&
|
||||
<Text bold small className="px-2 py-1">{LocalizeText('friendlist.search.nofriendsfound')}</Text>}
|
||||
{(friendResults.length > 0) &&
|
||||
<Column gap={0}>
|
||||
<Text bold small className="px-2 py-1">{LocalizeText('friendlist.search.friendscaption', ['cnt'], [friendResults.length.toString()])}</Text>
|
||||
{ (friendResults.length === 0) &&
|
||||
<Text bold small className="px-2 py-1">{ LocalizeText('friendlist.search.nofriendsfound') }</Text> }
|
||||
{ (friendResults.length > 0) &&
|
||||
<Column gap={ 0 }>
|
||||
<Text bold small className="px-2 py-1">{ LocalizeText('friendlist.search.friendscaption', [ 'cnt' ], [ friendResults.length.toString() ]) }</Text>
|
||||
<hr className="mx-2 mt-0 mb-1 text-black" />
|
||||
<Column gap={0}>
|
||||
{friendResults.map(result =>
|
||||
<Column gap={ 0 }>
|
||||
{ friendResults.map(result =>
|
||||
{
|
||||
return (
|
||||
<NitroCardAccordionItemView key={result.avatarId} className="px-2 py-1" justifyContent="between">
|
||||
<NitroCardAccordionItemView key={ result.avatarId } className="px-2 py-1" justifyContent="between">
|
||||
<div className="flex items-center gap-1">
|
||||
<UserProfileIconView userId={result.avatarId} />
|
||||
<div>{result.avatarName}</div>
|
||||
<UserProfileIconView userId={ result.avatarId } />
|
||||
<div>{ result.avatarName }</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
{result.isAvatarOnline &&
|
||||
<div className="nitro-friends-spritesheet icon-chat cursor-pointer" title={LocalizeText('friendlist.tip.im')} onClick={event => OpenMessengerChat(result.avatarId)} />}
|
||||
{ result.isAvatarOnline &&
|
||||
<div className="nitro-friends-spritesheet icon-chat cursor-pointer" title={ LocalizeText('friendlist.tip.im') } onClick={ event => OpenMessengerChat(result.avatarId) } /> }
|
||||
</div>
|
||||
</NitroCardAccordionItemView>
|
||||
)
|
||||
})}
|
||||
}) }
|
||||
</Column>
|
||||
</Column>}
|
||||
</>}
|
||||
{otherResults &&
|
||||
</Column> }
|
||||
</> }
|
||||
{ otherResults &&
|
||||
<>
|
||||
{(otherResults.length === 0) &&
|
||||
<Text bold small className="px-2 py-1">{LocalizeText('friendlist.search.noothersfound')}</Text>}
|
||||
{(otherResults.length > 0) &&
|
||||
<Column gap={0}>
|
||||
<Text bold small className="px-2 py-1">{LocalizeText('friendlist.search.otherscaption', ['cnt'], [otherResults.length.toString()])}</Text>
|
||||
{ (otherResults.length === 0) &&
|
||||
<Text bold small className="px-2 py-1">{ LocalizeText('friendlist.search.noothersfound') }</Text> }
|
||||
{ (otherResults.length > 0) &&
|
||||
<Column gap={ 0 }>
|
||||
<Text bold small className="px-2 py-1">{ LocalizeText('friendlist.search.otherscaption', [ 'cnt' ], [ otherResults.length.toString() ]) }</Text>
|
||||
<hr className="mx-2 mt-0 mb-1 text-black" />
|
||||
<Column gap={0}>
|
||||
{otherResults.map(result =>
|
||||
<Column gap={ 0 }>
|
||||
{ otherResults.map(result =>
|
||||
{
|
||||
return (
|
||||
<NitroCardAccordionItemView key={result.avatarId} className="px-2 py-1" justifyContent="between">
|
||||
<NitroCardAccordionItemView key={ result.avatarId } className="px-2 py-1" justifyContent="between">
|
||||
<div className="flex items-center gap-1">
|
||||
<UserProfileIconView userId={result.avatarId} />
|
||||
<div>{result.avatarName}</div>
|
||||
<UserProfileIconView userId={ result.avatarId } />
|
||||
<div>{ result.avatarName }</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
{canRequestFriend(result.avatarId) &&
|
||||
<div className="nitro-friends-spritesheet icon-add cursor-pointer" title={LocalizeText('friendlist.tip.addfriend')} onClick={event => requestFriend(result.avatarId, result.avatarName)} />}
|
||||
{ canRequestFriend(result.avatarId) &&
|
||||
<div className="nitro-friends-spritesheet icon-add cursor-pointer" title={ LocalizeText('friendlist.tip.addfriend') } onClick={ event => requestFriend(result.avatarId, result.avatarName) } /> }
|
||||
</div>
|
||||
</NitroCardAccordionItemView>
|
||||
)
|
||||
})}
|
||||
}) }
|
||||
</Column>
|
||||
</Column>}
|
||||
</>}
|
||||
</Column> }
|
||||
</> }
|
||||
</div>
|
||||
</NitroCardAccordionSetView>
|
||||
);
|
||||
|
@ -9,9 +9,9 @@ import { FriendsMessengerThreadView } from './messenger-thread/FriendsMessengerT
|
||||
|
||||
export const FriendsMessengerView: FC<{}> = props =>
|
||||
{
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [lastThreadId, setLastThreadId] = useState(-1);
|
||||
const [messageText, setMessageText] = useState('');
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const [ lastThreadId, setLastThreadId ] = useState(-1);
|
||||
const [ messageText, setMessageText ] = useState('');
|
||||
const { visibleThreads = [], activeThread = null, getMessageThread = null, sendMessage = null, setActiveThreadId = null, closeThread = null } = useMessenger();
|
||||
const { report = null } = useHelp();
|
||||
const messagesBox = useRef<HTMLDivElement>();
|
||||
@ -72,14 +72,14 @@ export const FriendsMessengerView: FC<{}> = props =>
|
||||
AddLinkEventTracker(linkTracker);
|
||||
|
||||
return () => RemoveLinkEventTracker(linkTracker);
|
||||
}, [getMessageThread, setActiveThreadId]);
|
||||
}, [ getMessageThread, setActiveThreadId ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (!isVisible || !activeThread) return;
|
||||
|
||||
messagesBox.current.scrollTop = messagesBox.current.scrollHeight;
|
||||
}, [isVisible, activeThread]);
|
||||
}, [ isVisible, activeThread ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -102,74 +102,74 @@ export const FriendsMessengerView: FC<{}> = props =>
|
||||
setLastThreadId(activeThread.threadId);
|
||||
setActiveThreadId(-1);
|
||||
}
|
||||
}, [isVisible, activeThread, lastThreadId, visibleThreads, setActiveThreadId]);
|
||||
}, [ isVisible, activeThread, lastThreadId, visibleThreads, setActiveThreadId ]);
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-friends-messenger" theme="primary-slim" uniqueKey="nitro-friends-messenger">
|
||||
<NitroCardHeaderView headerText={LocalizeText('messenger.window.title', ['OPEN_CHAT_COUNT'], [visibleThreads.length.toString()])} onCloseClick={event => setIsVisible(false)} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText('messenger.window.title', [ 'OPEN_CHAT_COUNT' ], [ visibleThreads.length.toString() ]) } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardContentView>
|
||||
<Grid overflow="hidden">
|
||||
<Column overflow="hidden" size={4}>
|
||||
<Text bold>{LocalizeText('toolbar.icon.label.messenger')}</Text>
|
||||
<Column overflow="hidden" size={ 4 }>
|
||||
<Text bold>{ LocalizeText('toolbar.icon.label.messenger') }</Text>
|
||||
<Column fit overflow="auto">
|
||||
<div className="flex flex-col">
|
||||
{visibleThreads && (visibleThreads.length > 0) && visibleThreads.map(thread =>
|
||||
{ visibleThreads && (visibleThreads.length > 0) && visibleThreads.map(thread =>
|
||||
{
|
||||
return (
|
||||
<LayoutGridItem key={thread.threadId} itemActive={(activeThread === thread)} onClick={event => setActiveThreadId(thread.threadId)}>
|
||||
{thread.unread &&
|
||||
<LayoutItemCountView count={thread.unreadCount} />}
|
||||
<LayoutGridItem key={ thread.threadId } itemActive={ (activeThread === thread) } onClick={ event => setActiveThreadId(thread.threadId) }>
|
||||
{ thread.unread &&
|
||||
<LayoutItemCountView count={ thread.unreadCount } /> }
|
||||
<div className="flex w-full items-center gap-1">
|
||||
<div className="flex items-center friend-head px-1">
|
||||
{(thread.participant.id > 0) &&
|
||||
<LayoutAvatarImageView direction={3} figure={thread.participant.figure} headOnly={true} />}
|
||||
{(thread.participant.id <= 0) &&
|
||||
<LayoutBadgeImageView badgeCode={thread.participant.figure} isGroup={true} />}
|
||||
{ (thread.participant.id > 0) &&
|
||||
<LayoutAvatarImageView direction={ 3 } figure={ thread.participant.figure } headOnly={ true } /> }
|
||||
{ (thread.participant.id <= 0) &&
|
||||
<LayoutBadgeImageView badgeCode={ thread.participant.figure } isGroup={ true } /> }
|
||||
</div>
|
||||
<Text grow truncate>{thread.participant.name}</Text>
|
||||
<Text grow truncate>{ thread.participant.name }</Text>
|
||||
</div>
|
||||
</LayoutGridItem>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</div>
|
||||
</Column>
|
||||
</Column>
|
||||
<Column overflow="hidden" size={8}>
|
||||
{activeThread &&
|
||||
<Column overflow="hidden" size={ 8 }>
|
||||
{ activeThread &&
|
||||
<>
|
||||
<Text bold center>{LocalizeText('messenger.window.separator', ['FRIEND_NAME'], [activeThread.participant.name])}</Text>
|
||||
<Flex alignItems="center" gap={1} justifyContent="between">
|
||||
<Text bold center>{ LocalizeText('messenger.window.separator', [ 'FRIEND_NAME' ], [ activeThread.participant.name ]) }</Text>
|
||||
<Flex alignItems="center" gap={ 1 } justifyContent="between">
|
||||
<div className="flex gap-1">
|
||||
<div className="relative inline-flex align-middle">
|
||||
<Button onClick={followFriend}>
|
||||
<Button onClick={ followFriend }>
|
||||
<div className="nitro-friends-spritesheet icon-follow" />
|
||||
</Button>
|
||||
<Button onClick={openProfile}>
|
||||
<Button onClick={ openProfile }>
|
||||
<div className="nitro-friends-spritesheet icon-profile-sm" />
|
||||
</Button>
|
||||
</div>
|
||||
<Button variant="danger" onClick={() => report(ReportType.IM, { reportedUserId: activeThread.participant.id })}>
|
||||
{LocalizeText('messenger.window.button.report')}
|
||||
<Button variant="danger" onClick={ () => report(ReportType.IM, { reportedUserId: activeThread.participant.id }) }>
|
||||
{ LocalizeText('messenger.window.button.report') }
|
||||
</Button>
|
||||
</div>
|
||||
<Button onClick={event => closeThread(activeThread.threadId)}>
|
||||
<Button onClick={ event => closeThread(activeThread.threadId) }>
|
||||
<FaTimes className="fa-icon" />
|
||||
</Button>
|
||||
</Flex>
|
||||
<Column fit className="bg-muted p-2 rounded chat-messages">
|
||||
<Column innerRef={messagesBox} overflow="auto">
|
||||
<FriendsMessengerThreadView thread={activeThread} />
|
||||
<Column innerRef={ messagesBox } overflow="auto">
|
||||
<FriendsMessengerThreadView thread={ activeThread } />
|
||||
</Column>
|
||||
</Column>
|
||||
<div className="flex gap-1">
|
||||
<NitroInput maxLength={255} placeholder={LocalizeText('messenger.window.input.default', ['FRIEND_NAME'], [activeThread.participant.name])} type="text" value={messageText} onChange={event => setMessageText(event.target.value)} onKeyDown={onKeyDown} />
|
||||
<Button variant="success" onClick={send}>
|
||||
{LocalizeText('widgets.chatinput.say')}
|
||||
<NitroInput maxLength={ 255 } placeholder={ LocalizeText('messenger.window.input.default', [ 'FRIEND_NAME' ], [ activeThread.participant.name ]) } type="text" value={ messageText } onChange={ event => setMessageText(event.target.value) } onKeyDown={ onKeyDown } />
|
||||
<Button variant="success" onClick={ send }>
|
||||
{ LocalizeText('widgets.chatinput.say') }
|
||||
</Button>
|
||||
</div>
|
||||
</>}
|
||||
</> }
|
||||
</Column>
|
||||
</Grid>
|
||||
</NitroCardContentView>
|
||||
|
@ -7,7 +7,7 @@ export const FriendsMessengerThreadGroup: FC<{ thread: MessengerThread, group: M
|
||||
{
|
||||
const { thread = null, group = null } = props;
|
||||
|
||||
const groupChatData = useMemo(() => ((group.type === MessengerGroupType.GROUP_CHAT) && GetGroupChatData(group.chats[0].extraData)), [group]);
|
||||
const groupChatData = useMemo(() => ((group.type === MessengerGroupType.GROUP_CHAT) && GetGroupChatData(group.chats[0].extraData)), [ group ]);
|
||||
|
||||
const isOwnChat = useMemo(() =>
|
||||
{
|
||||
@ -18,7 +18,7 @@ export const FriendsMessengerThreadGroup: FC<{ thread: MessengerThread, group: M
|
||||
if (groupChatData && group.chats.length && (groupChatData.userId === GetSessionDataManager().userId)) return true;
|
||||
|
||||
return false;
|
||||
}, [thread, group, groupChatData]);
|
||||
}, [ thread, group, groupChatData ]);
|
||||
|
||||
if (!thread || !group) return null;
|
||||
|
||||
@ -26,48 +26,48 @@ export const FriendsMessengerThreadGroup: FC<{ thread: MessengerThread, group: M
|
||||
{
|
||||
return (
|
||||
<>
|
||||
{group.chats.map((chat, index) =>
|
||||
{ group.chats.map((chat, index) =>
|
||||
{
|
||||
return (
|
||||
<Flex key={index} fullWidth gap={2} justifyContent="start">
|
||||
<Flex key={ index } fullWidth gap={ 2 } justifyContent="start">
|
||||
<Base className="w-full text-break">
|
||||
{(chat.type === MessengerThreadChat.SECURITY_NOTIFICATION) &&
|
||||
<Flex alignItems="center" className="bg-light rounded mb-2 px-2 py-1 small text-muted" gap={2}>
|
||||
{ (chat.type === MessengerThreadChat.SECURITY_NOTIFICATION) &&
|
||||
<Flex alignItems="center" className="bg-light rounded mb-2 px-2 py-1 small text-muted" gap={ 2 }>
|
||||
<Base className="nitro-friends-spritesheet icon-warning flex-shrink-0" />
|
||||
<Base>{chat.message}</Base>
|
||||
</Flex>}
|
||||
{(chat.type === MessengerThreadChat.ROOM_INVITE) &&
|
||||
<Flex alignItems="center" className="bg-light rounded mb-2 px-2 py-1 small text-black" gap={2}>
|
||||
<Base>{ chat.message }</Base>
|
||||
</Flex> }
|
||||
{ (chat.type === MessengerThreadChat.ROOM_INVITE) &&
|
||||
<Flex alignItems="center" className="bg-light rounded mb-2 px-2 py-1 small text-black" gap={ 2 }>
|
||||
<Base className="messenger-notification-icon flex-shrink-0" />
|
||||
<Base>{(LocalizeText('messenger.invitation') + ' ')}{chat.message}</Base>
|
||||
</Flex>}
|
||||
<Base>{ (LocalizeText('messenger.invitation') + ' ') }{ chat.message }</Base>
|
||||
</Flex> }
|
||||
</Base>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex fullWidth gap={2} justifyContent={isOwnChat ? 'end' : 'start'}>
|
||||
<Flex fullWidth gap={ 2 } justifyContent={ isOwnChat ? 'end' : 'start' }>
|
||||
<Base shrink className="message-avatar">
|
||||
{((group.type === MessengerGroupType.PRIVATE_CHAT) && !isOwnChat) &&
|
||||
<LayoutAvatarImageView direction={2} figure={thread.participant.figure} />}
|
||||
{(groupChatData && !isOwnChat) &&
|
||||
<LayoutAvatarImageView direction={2} figure={groupChatData.figure} />}
|
||||
{ ((group.type === MessengerGroupType.PRIVATE_CHAT) && !isOwnChat) &&
|
||||
<LayoutAvatarImageView direction={ 2 } figure={ thread.participant.figure } /> }
|
||||
{ (groupChatData && !isOwnChat) &&
|
||||
<LayoutAvatarImageView direction={ 2 } figure={ groupChatData.figure } /> }
|
||||
</Base>
|
||||
<Base className={'bg-light text-black border-radius mb-2 rounded py-1 px-2 messages-group-' + (isOwnChat ? 'right' : 'left')}>
|
||||
<Base className={ 'bg-light text-black border-radius mb-2 rounded py-1 px-2 messages-group-' + (isOwnChat ? 'right' : 'left') }>
|
||||
<Base className="font-bold ">
|
||||
{isOwnChat && GetSessionDataManager().userName}
|
||||
{!isOwnChat && (groupChatData ? groupChatData.username : thread.participant.name)}
|
||||
{ isOwnChat && GetSessionDataManager().userName }
|
||||
{ !isOwnChat && (groupChatData ? groupChatData.username : thread.participant.name) }
|
||||
</Base>
|
||||
{group.chats.map((chat, index) => <Base key={index} className="text-break">{chat.message}</Base>)}
|
||||
{ group.chats.map((chat, index) => <Base key={ index } className="text-break">{ chat.message }</Base>) }
|
||||
</Base>
|
||||
{isOwnChat &&
|
||||
{ isOwnChat &&
|
||||
<Base shrink className="message-avatar">
|
||||
<LayoutAvatarImageView direction={4} figure={GetSessionDataManager().figure} />
|
||||
</Base>}
|
||||
<LayoutAvatarImageView direction={ 4 } figure={ GetSessionDataManager().figure } />
|
||||
</Base> }
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ export const GameListView = () =>
|
||||
|
||||
const getClasses = (game: GameConfigurationData) =>
|
||||
{
|
||||
let classes = ['game-icon'];
|
||||
let classes = [ 'game-icon' ];
|
||||
|
||||
if (selectedGame === game) classes.push('selected');
|
||||
|
||||
@ -17,15 +17,15 @@ export const GameListView = () =>
|
||||
|
||||
const getIconImage = (game: GameConfigurationData): string =>
|
||||
{
|
||||
return `url(${game.assetUrl}${game.gameNameId}_icon.png)`
|
||||
return `url(${ game.assetUrl }${ game.gameNameId }_icon.png)`
|
||||
}
|
||||
|
||||
return <div className="gameList-container bg-dark p-1 w-full">
|
||||
{LocalizeText('gamecenter.game_list_title')}
|
||||
{ LocalizeText('gamecenter.game_list_title') }
|
||||
<div className="flex gap-3">
|
||||
{games && games.map((game, index) =>
|
||||
<div key={index} className={getClasses(game)} style={{ backgroundImage: getIconImage(game) }} onClick={evt => setSelectedGame(game)} />
|
||||
)}
|
||||
{ games && games.map((game, index) =>
|
||||
<div key={ index } className={ getClasses(game) } style={ { backgroundImage: getIconImage(game) } } onClick={ evt => setSelectedGame(game) } />
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import { useGameCenter } from '../../../hooks';
|
||||
export const GameStageView = () =>
|
||||
{
|
||||
const { gameURL, setGameURL } = useGameCenter();
|
||||
const [loadTimes, setLoadTimes] = useState<number>(0);
|
||||
const [ loadTimes, setLoadTimes ] = useState<number>(0);
|
||||
const ref = useRef<HTMLDivElement>();
|
||||
|
||||
useEffect(() =>
|
||||
@ -29,7 +29,7 @@ export const GameStageView = () =>
|
||||
ref.current.innerHTML = '';
|
||||
ref.current.appendChild(frame);
|
||||
|
||||
}, [ref, gameURL]);
|
||||
}, [ ref, gameURL ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -38,9 +38,9 @@ export const GameStageView = () =>
|
||||
setGameURL(null);
|
||||
SendMessageComposer(new Game2ExitGameMessageComposer());
|
||||
}
|
||||
}, [loadTimes, setGameURL])
|
||||
}, [ loadTimes, setGameURL ])
|
||||
|
||||
if (!gameURL) return null;
|
||||
|
||||
return <div ref={ref} className="game-center-stage" />
|
||||
return <div ref={ ref } className="game-center-stage" />
|
||||
}
|
||||
|
@ -10,17 +10,17 @@ interface GroupBadgeCreatorViewProps
|
||||
setBadgeParts: Dispatch<SetStateAction<GroupBadgePart[]>>;
|
||||
}
|
||||
|
||||
const POSITIONS: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||
const POSITIONS: number[] = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
|
||||
|
||||
export const GroupBadgeCreatorView: FC<GroupBadgeCreatorViewProps> = props =>
|
||||
{
|
||||
const { badgeParts = [], setBadgeParts = null } = props;
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(-1);
|
||||
const [ selectedIndex, setSelectedIndex ] = useState<number>(-1);
|
||||
const { groupCustomize = null } = useGroup();
|
||||
|
||||
const setPartProperty = (partIndex: number, property: string, value: number) =>
|
||||
{
|
||||
const newBadgeParts = [...badgeParts];
|
||||
const newBadgeParts = [ ...badgeParts ];
|
||||
|
||||
newBadgeParts[partIndex][property] = value;
|
||||
|
||||
@ -33,51 +33,51 @@ export const GroupBadgeCreatorView: FC<GroupBadgeCreatorViewProps> = props =>
|
||||
|
||||
return (
|
||||
<>
|
||||
{((selectedIndex < 0) && badgeParts && (badgeParts.length > 0)) && badgeParts.map((part, index) =>
|
||||
{ ((selectedIndex < 0) && badgeParts && (badgeParts.length > 0)) && badgeParts.map((part, index) =>
|
||||
{
|
||||
return (
|
||||
<Flex key={index} alignItems="center" className="bg-muted rounded px-2 py-1" gap={2} justifyContent="between">
|
||||
<Flex center pointer className="bg-muted rounded p-1" onClick={event => setSelectedIndex(index)}>
|
||||
{(badgeParts[index].code && (badgeParts[index].code.length > 0)) &&
|
||||
<LayoutBadgeImageView badgeCode={badgeParts[index].code} isGroup={true} />}
|
||||
{(!badgeParts[index].code || !badgeParts[index].code.length) &&
|
||||
<Flex key={ index } alignItems="center" className="bg-muted rounded px-2 py-1" gap={ 2 } justifyContent="between">
|
||||
<Flex center pointer className="bg-muted rounded p-1" onClick={ event => setSelectedIndex(index) }>
|
||||
{ (badgeParts[index].code && (badgeParts[index].code.length > 0)) &&
|
||||
<LayoutBadgeImageView badgeCode={ badgeParts[index].code } isGroup={ true } /> }
|
||||
{ (!badgeParts[index].code || !badgeParts[index].code.length) &&
|
||||
<Flex center className="relative w-[40px] h-[40px] bg-no-repeat bg-center group-badge">
|
||||
<FaPlus className="fa-icon" />
|
||||
</Flex>}
|
||||
</Flex> }
|
||||
</Flex>
|
||||
{(part.type !== GroupBadgePart.BASE) &&
|
||||
<Grid columnCount={3} gap={1}>
|
||||
{POSITIONS.map((position, posIndex) =>
|
||||
{ (part.type !== GroupBadgePart.BASE) &&
|
||||
<Grid columnCount={ 3 } gap={ 1 }>
|
||||
{ POSITIONS.map((position, posIndex) =>
|
||||
{
|
||||
return <div key={posIndex} className={`relative rounded-[.25rem] w-[16px] h-[16px] bg-[#fff] border-[2px] border-[solid] border-[#fff] [box-shadow:inset_3px_3px_#0000001a] cursor-pointer ${(badgeParts[index].position === position) ? 'bg-primary [box-shadow:none]' : ''}`} onClick={event => setPartProperty(index, 'position', position)} />
|
||||
})}
|
||||
</Grid>}
|
||||
<Grid columnCount={8} gap={1}>
|
||||
{(groupCustomize.badgePartColors.length > 0) && groupCustomize.badgePartColors.map((item, colorIndex) =>
|
||||
return <div key={ posIndex } className={ `relative rounded-[.25rem] w-[16px] h-[16px] bg-[#fff] border-[2px] border-[solid] border-[#fff] [box-shadow:inset_3px_3px_#0000001a] cursor-pointer ${ (badgeParts[index].position === position) ? 'bg-primary [box-shadow:none]' : '' }` } onClick={ event => setPartProperty(index, 'position', position) } />
|
||||
}) }
|
||||
</Grid> }
|
||||
<Grid columnCount={ 8 } gap={ 1 }>
|
||||
{ (groupCustomize.badgePartColors.length > 0) && groupCustomize.badgePartColors.map((item, colorIndex) =>
|
||||
{
|
||||
return <div key={colorIndex} className={`relative [box-shadow:inset_2px_2px_#0003] rounded-[.25rem] w-[16px] h-[16px] bg-[#fff] border-[2px] border-[solid] border-[#fff] [box-shadow:inset_3px_3px_#0000001a]cursor-pointer ${(badgeParts[index].color === (colorIndex + 1)) ? 'bg-primary [box-shadow:none]' : ''}`} style={{ backgroundColor: '#' + item.color }} onClick={event => setPartProperty(index, 'color', (colorIndex + 1))} />
|
||||
})}
|
||||
return <div key={ colorIndex } className={ `relative [box-shadow:inset_2px_2px_#0003] rounded-[.25rem] w-[16px] h-[16px] bg-[#fff] border-[2px] border-[solid] border-[#fff] [box-shadow:inset_3px_3px_#0000001a]cursor-pointer ${ (badgeParts[index].color === (colorIndex + 1)) ? 'bg-primary [box-shadow:none]' : '' }` } style={ { backgroundColor: '#' + item.color } } onClick={ event => setPartProperty(index, 'color', (colorIndex + 1)) } />
|
||||
}) }
|
||||
</Grid>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
{(selectedIndex >= 0) &&
|
||||
<Grid columnCount={5} gap={1}>
|
||||
{(badgeParts[selectedIndex].type === GroupBadgePart.SYMBOL) &&
|
||||
<Column center pointer className="bg-muted rounded p-1" onClick={event => setPartProperty(selectedIndex, 'key', 0)}>
|
||||
}) }
|
||||
{ (selectedIndex >= 0) &&
|
||||
<Grid columnCount={ 5 } gap={ 1 }>
|
||||
{ (badgeParts[selectedIndex].type === GroupBadgePart.SYMBOL) &&
|
||||
<Column center pointer className="bg-muted rounded p-1" onClick={ event => setPartProperty(selectedIndex, 'key', 0) }>
|
||||
<Flex center className="relative w-[40px] h-[40px] bg-no-repeat bg-center group-badge">
|
||||
<FaTimes className="fa-icon" />
|
||||
</Flex>
|
||||
</Column>}
|
||||
{((badgeParts[selectedIndex].type === GroupBadgePart.BASE) ? groupCustomize.badgeBases : groupCustomize.badgeSymbols).map((item, index) =>
|
||||
</Column> }
|
||||
{ ((badgeParts[selectedIndex].type === GroupBadgePart.BASE) ? groupCustomize.badgeBases : groupCustomize.badgeSymbols).map((item, index) =>
|
||||
{
|
||||
return (
|
||||
<Column key={index} center pointer className="bg-muted rounded p-1" onClick={event => setPartProperty(selectedIndex, 'key', item.id)}>
|
||||
<LayoutBadgeImageView badgeCode={GroupBadgePart.getCode(badgeParts[selectedIndex].type, item.id, badgeParts[selectedIndex].color, 4)} isGroup={true} />
|
||||
<Column key={ index } center pointer className="bg-muted rounded p-1" onClick={ event => setPartProperty(selectedIndex, 'key', item.id) }>
|
||||
<LayoutBadgeImageView badgeCode={ GroupBadgePart.getCode(badgeParts[selectedIndex].type, item.id, badgeParts[selectedIndex].color, 4) } isGroup={ true } />
|
||||
</Column>
|
||||
);
|
||||
})}
|
||||
</Grid>}
|
||||
}) }
|
||||
</Grid> }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -13,16 +13,16 @@ interface GroupCreatorViewProps
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const TABS: number[] = [1, 2, 3, 4];
|
||||
const TABS: number[] = [ 1, 2, 3, 4 ];
|
||||
|
||||
export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
|
||||
{
|
||||
const { onClose = null } = props;
|
||||
const [currentTab, setCurrentTab] = useState<number>(1);
|
||||
const [closeAction, setCloseAction] = useState<{ action: () => boolean }>(null);
|
||||
const [groupData, setGroupData] = useState<IGroupData>(null);
|
||||
const [availableRooms, setAvailableRooms] = useState<{ id: number, name: string }[]>(null);
|
||||
const [purchaseCost, setPurchaseCost] = useState<number>(0);
|
||||
const [ currentTab, setCurrentTab ] = useState<number>(1);
|
||||
const [ closeAction, setCloseAction ] = useState<{ action: () => boolean }>(null);
|
||||
const [ groupData, setGroupData ] = useState<IGroupData>(null);
|
||||
const [ availableRooms, setAvailableRooms ] = useState<{ id: number, name: string }[]>(null);
|
||||
const [ purchaseCost, setPurchaseCost ] = useState<number>(0);
|
||||
|
||||
const onCloseClose = () =>
|
||||
{
|
||||
@ -113,49 +113,49 @@ export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
|
||||
});
|
||||
|
||||
SendMessageComposer(new GroupBuyDataComposer());
|
||||
}, [setGroupData]);
|
||||
}, [ setGroupData ]);
|
||||
|
||||
if (!groupData) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView className="h-[355px] w-[390px] border-[1px] border-[solid] border-[#283F5D] " theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={LocalizeText('group.create.title')} onCloseClick={onCloseClose} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText('group.create.title') } onCloseClick={ onCloseClose } />
|
||||
<NitroCardContentView>
|
||||
<div className="flex items-center justify-center creator-tabs">
|
||||
{TABS.map((tab, index) =>
|
||||
{ TABS.map((tab, index) =>
|
||||
{
|
||||
return (
|
||||
<Flex key={index} center className={`relative -ml-[6px] bg-[url('@/assets/images/groups/creator_tabs.png')] bg-no-repeat ${((tab === 1) ? 'w-[84px] h-[24px] bg-[0px_0px]' : (tab === 4) ? 'w-[133px] h-[28px] bg-[0px_-104px]' : 'w-[83px] h-[24px] bg-[0px_-52px]')} ${(currentTab === tab) ? 'active' : ''}`}>
|
||||
<Text variant="white">{LocalizeText(`group.create.steplabel.${tab}`)}</Text>
|
||||
<Flex key={ index } center className={ `relative -ml-[6px] bg-[url('@/assets/images/groups/creator_tabs.png')] bg-no-repeat ${ ((tab === 1) ? 'w-[84px] h-[24px] bg-[0px_0px]' : (tab === 4) ? 'w-[133px] h-[28px] bg-[0px_-104px]' : 'w-[83px] h-[24px] bg-[0px_-52px]') } ${ (currentTab === tab) ? 'active' : '' }` }>
|
||||
<Text variant="white">{ LocalizeText(`group.create.steplabel.${ tab }`) }</Text>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</div>
|
||||
<Column overflow="hidden">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={`bg-no-repeat w-[122px] h-[68px] bg-[url('@/assets/images/groups/creator_images.png')] ${currentTab === 1 && 'bg-[0px_0px] !w-[99px] !h-[50px]'}
|
||||
${currentTab == 2 && '!bg-[-99px_0px] !w-[98px] !h-[62px]'} ${currentTab === 3 && '!bg-[0px_-50px] !w-[96px] !h-[45px]'} ${currentTab === 4 || currentTab === 5 && '!bg-[0px_-95px] !w-[114px] !h-[61px]'} `} />
|
||||
<Column grow gap={0}>
|
||||
<Text bold fontSize={4}>{LocalizeText(`group.create.stepcaption.${currentTab}`)}</Text>
|
||||
<Text>{LocalizeText(`group.create.stepdesc.${currentTab}`)}</Text>
|
||||
<div className={ `bg-no-repeat w-[122px] h-[68px] bg-[url('@/assets/images/groups/creator_images.png')] ${ currentTab === 1 && 'bg-[0px_0px] !w-[99px] !h-[50px]' }
|
||||
${ currentTab == 2 && '!bg-[-99px_0px] !w-[98px] !h-[62px]' } ${ currentTab === 3 && '!bg-[0px_-50px] !w-[96px] !h-[45px]' } ${ currentTab === 4 || currentTab === 5 && '!bg-[0px_-95px] !w-[114px] !h-[61px]' } ` } />
|
||||
<Column grow gap={ 0 }>
|
||||
<Text bold fontSize={ 4 }>{ LocalizeText(`group.create.stepcaption.${ currentTab }`) }</Text>
|
||||
<Text>{ LocalizeText(`group.create.stepdesc.${ currentTab }`) }</Text>
|
||||
</Column>
|
||||
</div>
|
||||
<Column overflow="hidden">
|
||||
{(currentTab === 1) &&
|
||||
<GroupTabIdentityView availableRooms={availableRooms} groupData={groupData} isCreator={true} setCloseAction={setCloseAction} setGroupData={setGroupData} onClose={null} />}
|
||||
{(currentTab === 2) &&
|
||||
<GroupTabBadgeView groupData={groupData} setCloseAction={setCloseAction} setGroupData={setGroupData} />}
|
||||
{(currentTab === 3) &&
|
||||
<GroupTabColorsView groupData={groupData} setCloseAction={setCloseAction} setGroupData={setGroupData} />}
|
||||
{(currentTab === 4) &&
|
||||
<GroupTabCreatorConfirmationView groupData={groupData} purchaseCost={purchaseCost} setGroupData={setGroupData} />}
|
||||
{ (currentTab === 1) &&
|
||||
<GroupTabIdentityView availableRooms={ availableRooms } groupData={ groupData } isCreator={ true } setCloseAction={ setCloseAction } setGroupData={ setGroupData } onClose={ null } /> }
|
||||
{ (currentTab === 2) &&
|
||||
<GroupTabBadgeView groupData={ groupData } setCloseAction={ setCloseAction } setGroupData={ setGroupData } /> }
|
||||
{ (currentTab === 3) &&
|
||||
<GroupTabColorsView groupData={ groupData } setCloseAction={ setCloseAction } setGroupData={ setGroupData } /> }
|
||||
{ (currentTab === 4) &&
|
||||
<GroupTabCreatorConfirmationView groupData={ groupData } purchaseCost={ purchaseCost } setGroupData={ setGroupData } /> }
|
||||
</Column>
|
||||
<div className="flex justify-between">
|
||||
<Button className="text-black" variant="link" onClick={previousStep}>
|
||||
{LocalizeText(currentTab === 1 ? 'generic.cancel' : 'group.create.previousstep')}
|
||||
<Button className="text-black" variant="link" onClick={ previousStep }>
|
||||
{ LocalizeText(currentTab === 1 ? 'generic.cancel' : 'group.create.previousstep') }
|
||||
</Button>
|
||||
<Button disabled={((currentTab === 4) && !HasHabboClub())} variant={((currentTab === 4) ? HasHabboClub() ? 'success' : 'danger' : 'primary')} onClick={nextStep}>
|
||||
{LocalizeText((currentTab === 4) ? HasHabboClub() ? 'group.create.confirm.buy' : 'group.create.confirm.viprequired' : 'group.create.nextstep')}
|
||||
<Button disabled={ ((currentTab === 4) && !HasHabboClub()) } variant={ ((currentTab === 4) ? HasHabboClub() ? 'success' : 'danger' : 'primary') } onClick={ nextStep }>
|
||||
{ LocalizeText((currentTab === 4) ? HasHabboClub() ? 'group.create.confirm.buy' : 'group.create.confirm.viprequired' : 'group.create.nextstep') }
|
||||
</Button>
|
||||
</div>
|
||||
</Column>
|
||||
|
@ -4,7 +4,7 @@ import { CatalogPageName, GetGroupManager, GetGroupMembers, GroupMembershipType,
|
||||
import { Button, Column, Grid, GridProps, LayoutBadgeImageView, Text } from '../../../common';
|
||||
import { useNotification } from '../../../hooks';
|
||||
|
||||
const STATES: string[] = ['regular', 'exclusive', 'private'];
|
||||
const STATES: string[] = [ 'regular', 'exclusive', 'private' ];
|
||||
|
||||
interface GroupInformationViewProps extends GridProps
|
||||
{
|
||||
@ -36,11 +36,11 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
|
||||
{
|
||||
if (groupInformation.membershipType === GroupMembershipType.NOT_MEMBER || groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING) return null;
|
||||
|
||||
if (isRealOwner) return <i className="nitro-icon icon-group-owner" title={LocalizeText('group.youareowner')} />;
|
||||
if (isRealOwner) return <i className="nitro-icon icon-group-owner" title={ LocalizeText('group.youareowner') } />;
|
||||
|
||||
if (groupInformation.isAdmin) return <i className="nitro-icon icon-group-admin" title={LocalizeText('group.youareadmin')} />;
|
||||
if (groupInformation.isAdmin) return <i className="nitro-icon icon-group-admin" title={ LocalizeText('group.youareadmin') } />;
|
||||
|
||||
return <i className="nitro-icon icon-group-member" title={LocalizeText('group.youaremember')} />;
|
||||
return <i className="nitro-icon icon-group-member" title={ LocalizeText('group.youaremember') } />;
|
||||
}
|
||||
|
||||
const getButtonText = () =>
|
||||
@ -100,45 +100,45 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
|
||||
if (!groupInformation) return null;
|
||||
|
||||
return (
|
||||
<Grid overflow={overflow} {...rest}>
|
||||
<Column center overflow="hidden" size={3}>
|
||||
<Grid overflow={ overflow } { ...rest }>
|
||||
<Column center overflow="hidden" size={ 3 }>
|
||||
<div className="flex items-center overflow-hidden group-badge">
|
||||
<LayoutBadgeImageView badgeCode={groupInformation.badge} isGroup={true} scale={2} />
|
||||
<LayoutBadgeImageView badgeCode={ groupInformation.badge } isGroup={ true } scale={ 2 } />
|
||||
</div>
|
||||
<Column alignItems="center" gap={1}>
|
||||
<Text pointer small underline onClick={() => handleAction('members')}>{LocalizeText('group.membercount', ['totalMembers'], [groupInformation.membersCount.toString()])}</Text>
|
||||
{(groupInformation.pendingRequestsCount > 0) &&
|
||||
<Text pointer small underline onClick={() => handleAction('members_pending')}>{LocalizeText('group.pendingmembercount', ['amount'], [groupInformation.pendingRequestsCount.toString()])}</Text>}
|
||||
{groupInformation.isOwner &&
|
||||
<Text pointer small underline onClick={() => handleAction('manage')}>{LocalizeText('group.manage')}</Text>}
|
||||
<Column alignItems="center" gap={ 1 }>
|
||||
<Text pointer small underline onClick={ () => handleAction('members') }>{ LocalizeText('group.membercount', [ 'totalMembers' ], [ groupInformation.membersCount.toString() ]) }</Text>
|
||||
{ (groupInformation.pendingRequestsCount > 0) &&
|
||||
<Text pointer small underline onClick={ () => handleAction('members_pending') }>{ LocalizeText('group.pendingmembercount', [ 'amount' ], [ groupInformation.pendingRequestsCount.toString() ]) }</Text> }
|
||||
{ groupInformation.isOwner &&
|
||||
<Text pointer small underline onClick={ () => handleAction('manage') }>{ LocalizeText('group.manage') }</Text> }
|
||||
</Column>
|
||||
{getRoleIcon()}
|
||||
{ getRoleIcon() }
|
||||
</Column>
|
||||
<div className="flex flex-col justify-between overflow-auto col-span-9">
|
||||
<div className="flex flex-col overflow-hidden">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<Text bold>{groupInformation.title}</Text>
|
||||
<Text bold>{ groupInformation.title }</Text>
|
||||
<div className="flex gap-1">
|
||||
<i className={'nitro-icon icon-group-type-' + groupInformation.type} title={LocalizeText(`group.edit.settings.type.${STATES[groupInformation.type]}.help`)} />
|
||||
{groupInformation.canMembersDecorate &&
|
||||
<i className="nitro-icon icon-group-decorate" title={LocalizeText('group.memberscandecorate')} />}
|
||||
<i className={ 'nitro-icon icon-group-type-' + groupInformation.type } title={ LocalizeText(`group.edit.settings.type.${ STATES[groupInformation.type] }.help`) } />
|
||||
{ groupInformation.canMembersDecorate &&
|
||||
<i className="nitro-icon icon-group-decorate" title={ LocalizeText('group.memberscandecorate') } /> }
|
||||
</div>
|
||||
</div>
|
||||
<Text small>{LocalizeText('group.created', ['date', 'owner'], [groupInformation.createdAt, groupInformation.ownerName])}</Text>
|
||||
<Text small>{ LocalizeText('group.created', [ 'date', 'owner' ], [ groupInformation.createdAt, groupInformation.ownerName ]) }</Text>
|
||||
</div>
|
||||
<Text small className="group-description" overflow="auto">{groupInformation.description}</Text>
|
||||
<Text small className="group-description" overflow="auto">{ groupInformation.description }</Text>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text pointer small underline onClick={() => handleAction('homeroom')}>{LocalizeText('group.linktobase')}</Text>
|
||||
<Text pointer small underline onClick={() => handleAction('furniture')}>{LocalizeText('group.buyfurni')}</Text>
|
||||
<Text pointer small underline onClick={() => handleAction('popular_groups')}>{LocalizeText('group.showgroups')}</Text>
|
||||
<Text pointer small underline onClick={ () => handleAction('homeroom') }>{ LocalizeText('group.linktobase') }</Text>
|
||||
<Text pointer small underline onClick={ () => handleAction('furniture') }>{ LocalizeText('group.buyfurni') }</Text>
|
||||
<Text pointer small underline onClick={ () => handleAction('popular_groups') }>{ LocalizeText('group.showgroups') }</Text>
|
||||
</div>
|
||||
{(groupInformation.type !== GroupType.PRIVATE || groupInformation.type === GroupType.PRIVATE && groupInformation.membershipType === GroupMembershipType.MEMBER) &&
|
||||
<Button disabled={(groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING) || isRealOwner} onClick={handleButtonClick}>
|
||||
{LocalizeText(getButtonText())}
|
||||
</Button>}
|
||||
{ (groupInformation.type !== GroupType.PRIVATE || groupInformation.type === GroupType.PRIVATE && groupInformation.membershipType === GroupMembershipType.MEMBER) &&
|
||||
<Button disabled={ (groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING) || isRealOwner } onClick={ handleButtonClick }>
|
||||
{ LocalizeText(getButtonText()) }
|
||||
</Button> }
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
|
@ -8,13 +8,13 @@ import { classNames } from '../../../layout';
|
||||
|
||||
export const GroupMembersView: FC<{}> = props =>
|
||||
{
|
||||
const [groupId, setGroupId] = useState<number>(-1);
|
||||
const [levelId, setLevelId] = useState<number>(-1);
|
||||
const [membersData, setMembersData] = useState<GroupMembersParser>(null);
|
||||
const [pageId, setPageId] = useState<number>(-1);
|
||||
const [totalPages, setTotalPages] = useState<number>(0);
|
||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||
const [removingMemberName, setRemovingMemberName] = useState<string>(null);
|
||||
const [ groupId, setGroupId ] = useState<number>(-1);
|
||||
const [ levelId, setLevelId ] = useState<number>(-1);
|
||||
const [ membersData, setMembersData ] = useState<GroupMembersParser>(null);
|
||||
const [ pageId, setPageId ] = useState<number>(-1);
|
||||
const [ totalPages, setTotalPages ] = useState<number>(0);
|
||||
const [ searchQuery, setSearchQuery ] = useState<string>('');
|
||||
const [ removingMemberName, setRemovingMemberName ] = useState<string>(null);
|
||||
const { showConfirm = null } = useNotification();
|
||||
|
||||
const getRankDescription = (member: GroupMemberParser) =>
|
||||
@ -36,7 +36,7 @@ export const GroupMembersView: FC<{}> = props =>
|
||||
if ((groupId === -1) || (levelId === -1) || (pageId === -1)) return;
|
||||
|
||||
SendMessageComposer(new GroupMembersComposer(groupId, pageId, searchQuery, levelId));
|
||||
}, [groupId, levelId, pageId, searchQuery]);
|
||||
}, [ groupId, levelId, pageId, searchQuery ]);
|
||||
|
||||
const toggleAdmin = (member: GroupMemberParser) =>
|
||||
{
|
||||
@ -87,7 +87,7 @@ export const GroupMembersView: FC<{}> = props =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
showConfirm(LocalizeText(((parser.furnitureCount > 0) ? 'group.kickconfirm.desc' : 'group.kickconfirm_nofurni.desc'), ['user', 'amount'], [removingMemberName, parser.furnitureCount.toString()]), () =>
|
||||
showConfirm(LocalizeText(((parser.furnitureCount > 0) ? 'group.kickconfirm.desc' : 'group.kickconfirm_nofurni.desc'), [ 'user', 'amount' ], [ removingMemberName, parser.furnitureCount.toString() ]), () =>
|
||||
{
|
||||
SendMessageComposer(new GroupRemoveMemberComposer(membersData.groupId, parser.userId));
|
||||
|
||||
@ -124,14 +124,14 @@ export const GroupMembersView: FC<{}> = props =>
|
||||
useEffect(() =>
|
||||
{
|
||||
setPageId(0);
|
||||
}, [groupId, levelId, searchQuery]);
|
||||
}, [ groupId, levelId, searchQuery ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if ((groupId === -1) || (levelId === -1) || (pageId === -1)) return;
|
||||
|
||||
SendMessageComposer(new GroupMembersComposer(groupId, pageId, searchQuery, levelId));
|
||||
}, [groupId, levelId, pageId, searchQuery]);
|
||||
}, [ groupId, levelId, pageId, searchQuery ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -142,66 +142,66 @@ export const GroupMembersView: FC<{}> = props =>
|
||||
setTotalPages(0);
|
||||
setSearchQuery('');
|
||||
setRemovingMemberName(null);
|
||||
}, [groupId]);
|
||||
}, [ groupId ]);
|
||||
|
||||
if ((groupId === -1) || !membersData) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView className="w-[400px] max-h-[380px] " theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={LocalizeText('group.members.title', ['groupName'], [membersData ? membersData.groupTitle : ''])} onCloseClick={event => setGroupId(-1)} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText('group.members.title', [ 'groupName' ], [ membersData ? membersData.groupTitle : '' ]) } onCloseClick={ event => setGroupId(-1) } />
|
||||
<NitroCardContentView overflow="hidden">
|
||||
<div className="flex gap-2">
|
||||
<Flex center className="group-badge">
|
||||
<LayoutBadgeImageView badgeCode={membersData.badge} className="mx-auto block" isGroup={true} />
|
||||
<LayoutBadgeImageView badgeCode={ membersData.badge } className="mx-auto block" isGroup={ true } />
|
||||
</Flex>
|
||||
<Column fullWidth gap={1}>
|
||||
<input className="min-h-[calc(1.5em+.5rem+2px)] px-[.5rem] py-[.25rem] text-[.7875rem] rounded-[.2rem] w-full" placeholder={LocalizeText('group.members.searchinfo')} type="text" value={searchQuery} onChange={event => setSearchQuery(event.target.value)} />
|
||||
<select className="form-select form-select-sm w-full" value={levelId} onChange={event => setLevelId(parseInt(event.target.value))}>
|
||||
<option value="0">{LocalizeText('group.members.search.all')}</option>
|
||||
<option value="1">{LocalizeText('group.members.search.admins')}</option>
|
||||
<option value="2">{LocalizeText('group.members.search.pending')}</option>
|
||||
<Column fullWidth gap={ 1 }>
|
||||
<input className="min-h-[calc(1.5em+.5rem+2px)] px-[.5rem] py-[.25rem] text-[.7875rem] rounded-[.2rem] w-full" placeholder={ LocalizeText('group.members.searchinfo') } type="text" value={ searchQuery } onChange={ event => setSearchQuery(event.target.value) } />
|
||||
<select className="form-select form-select-sm w-full" value={ levelId } onChange={ event => setLevelId(parseInt(event.target.value)) }>
|
||||
<option value="0">{ LocalizeText('group.members.search.all') }</option>
|
||||
<option value="1">{ LocalizeText('group.members.search.admins') }</option>
|
||||
<option value="2">{ LocalizeText('group.members.search.pending') }</option>
|
||||
</select>
|
||||
</Column>
|
||||
</div>
|
||||
<Grid className="nitro-group-members-list-grid" columnCount={2} overflow="auto">
|
||||
{membersData.result.map((member, index) =>
|
||||
<Grid className="nitro-group-members-list-grid" columnCount={ 2 } overflow="auto">
|
||||
{ membersData.result.map((member, index) =>
|
||||
{
|
||||
return (
|
||||
<Flex key={index} alignItems="center" className="p-2 bg-white rounded h-[50px] max-h-[50px]" gap={2} overflow="hidden">
|
||||
<div className="cursor-pointer relative overflow-hidden w-[40px] h-[50px]" onClick={() => GetUserProfile(member.id)}>
|
||||
<LayoutAvatarImageView className='absolute -left-[25px] -top-[20px]' direction={2} figure={member.figure} headOnly={true} />
|
||||
<Flex key={ index } alignItems="center" className="p-2 bg-white rounded h-[50px] max-h-[50px]" gap={ 2 } overflow="hidden">
|
||||
<div className="cursor-pointer relative overflow-hidden w-[40px] h-[50px]" onClick={ () => GetUserProfile(member.id) }>
|
||||
<LayoutAvatarImageView className="absolute -left-[25px] -top-[20px]" direction={ 2 } figure={ member.figure } headOnly={ true } />
|
||||
</div>
|
||||
<Column grow gap={1}>
|
||||
<Text bold pointer small onClick={event => GetUserProfile(member.id)}>{member.name}</Text>
|
||||
{(member.rank !== GroupRank.REQUESTED) &&
|
||||
<Text italics small variant="muted">{LocalizeText('group.members.since', ['date'], [member.joinedAt])}</Text>}
|
||||
<Column grow gap={ 1 }>
|
||||
<Text bold pointer small onClick={ event => GetUserProfile(member.id) }>{ member.name }</Text>
|
||||
{ (member.rank !== GroupRank.REQUESTED) &&
|
||||
<Text italics small variant="muted">{ LocalizeText('group.members.since', [ 'date' ], [ member.joinedAt ]) }</Text> }
|
||||
</Column>
|
||||
<div className="flex flex-col gap-1">
|
||||
{(member.rank !== GroupRank.REQUESTED) &&
|
||||
{ (member.rank !== GroupRank.REQUESTED) &&
|
||||
<div className="flex items-center justify-center">
|
||||
<div className={classNames(`nitro-icon icon-group-small-${((member.rank === GroupRank.OWNER) ? 'owner' : (member.rank === GroupRank.ADMIN) ? 'admin' : (membersData.admin && (member.rank === GroupRank.MEMBER)) ? 'not-admin' : '')}`, membersData.admin && 'cursor-pointer')} title={LocalizeText(getRankDescription(member))} onClick={event => toggleAdmin(member)} />
|
||||
</div>}
|
||||
{membersData.admin && (member.rank === GroupRank.REQUESTED) &&
|
||||
<div className={ classNames(`nitro-icon icon-group-small-${ ((member.rank === GroupRank.OWNER) ? 'owner' : (member.rank === GroupRank.ADMIN) ? 'admin' : (membersData.admin && (member.rank === GroupRank.MEMBER)) ? 'not-admin' : '') }`, membersData.admin && 'cursor-pointer') } title={ LocalizeText(getRankDescription(member)) } onClick={ event => toggleAdmin(member) } />
|
||||
</div> }
|
||||
{ membersData.admin && (member.rank === GroupRank.REQUESTED) &&
|
||||
<Flex alignItems="center">
|
||||
<div className="cursor-pointer nitro-friends-spritesheet icon-accept" title={LocalizeText('group.members.accept')} onClick={event => acceptMembership(member)} />
|
||||
</Flex>}
|
||||
{membersData.admin && (member.rank !== GroupRank.OWNER) && (member.id !== GetSessionDataManager().userId) &&
|
||||
<div className="cursor-pointer nitro-friends-spritesheet icon-accept" title={ LocalizeText('group.members.accept') } onClick={ event => acceptMembership(member) } />
|
||||
</Flex> }
|
||||
{ membersData.admin && (member.rank !== GroupRank.OWNER) && (member.id !== GetSessionDataManager().userId) &&
|
||||
<Flex alignItems="center">
|
||||
<div className="cursor-pointer nitro-friends-spritesheet icon-deny" title={LocalizeText(member.rank === GroupRank.REQUESTED ? 'group.members.reject' : 'group.members.kick')} onClick={event => removeMemberOrDeclineMembership(member)} />
|
||||
</Flex>}
|
||||
<div className="cursor-pointer nitro-friends-spritesheet icon-deny" title={ LocalizeText(member.rank === GroupRank.REQUESTED ? 'group.members.reject' : 'group.members.kick') } onClick={ event => removeMemberOrDeclineMembership(member) } />
|
||||
</Flex> }
|
||||
</div>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</Grid>
|
||||
<Flex alignItems="center" gap={1} justifyContent="between">
|
||||
<Button disabled={(membersData.pageIndex === 0)} onClick={event => setPageId(prevValue => (prevValue - 1))}>
|
||||
<Flex alignItems="center" gap={ 1 } justifyContent="between">
|
||||
<Button disabled={ (membersData.pageIndex === 0) } onClick={ event => setPageId(prevValue => (prevValue - 1)) }>
|
||||
<FaChevronLeft className="fa-icon" />
|
||||
</Button>
|
||||
<Text small>
|
||||
{LocalizeText('group.members.pageinfo', ['amount', 'page', 'totalPages'], [membersData.totalMembersCount.toString(), (membersData.pageIndex + 1).toString(), totalPages.toString()])}
|
||||
{ LocalizeText('group.members.pageinfo', [ 'amount', 'page', 'totalPages' ], [ membersData.totalMembersCount.toString(), (membersData.pageIndex + 1).toString(), totalPages.toString() ]) }
|
||||
</Text>
|
||||
<Button disabled={(membersData.pageIndex === (totalPages - 1))} onClick={event => setPageId(prevValue => (prevValue + 1))}>
|
||||
<Button disabled={ (membersData.pageIndex === (totalPages - 1)) } onClick={ event => setPageId(prevValue => (prevValue + 1)) }>
|
||||
<FaChevronRight className="fa-icon" />
|
||||
</Button>
|
||||
</Flex>
|
||||
|
@ -7,9 +7,9 @@ import { useMessageEvent, useNotification } from '../../../hooks';
|
||||
|
||||
export const GroupRoomInformationView: FC<{}> = props =>
|
||||
{
|
||||
const [expectedGroupId, setExpectedGroupId] = useState<number>(0);
|
||||
const [groupInformation, setGroupInformation] = useState<GroupInformationParser>(null);
|
||||
const [isOpen, setIsOpen] = useState<boolean>(true);
|
||||
const [ expectedGroupId, setExpectedGroupId ] = useState<number>(0);
|
||||
const [ groupInformation, setGroupInformation ] = useState<GroupInformationParser>(null);
|
||||
const [ isOpen, setIsOpen ] = useState<boolean>(true);
|
||||
const { showConfirm = null } = useNotification();
|
||||
|
||||
useMessageEvent<DesktopViewEvent>(DesktopViewEvent, event =>
|
||||
@ -107,25 +107,25 @@ export const GroupRoomInformationView: FC<{}> = props =>
|
||||
return (
|
||||
<div className="pointer-events-auto px-[5px] py-[6px] [box-shadow:inset_0_5px_#22222799,_inset_0_-4px_#12121599] bg-[#1c1c20f2] rounded text-sm">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Flex pointer alignItems="center" justifyContent="between" onClick={event => setIsOpen(value => !value)}>
|
||||
<Text variant="white">{LocalizeText('group.homeroominfo.title')}</Text>
|
||||
{isOpen && <FaChevronUp className="fa-icon" />}
|
||||
{!isOpen && <FaChevronDown className="fa-icon" />}
|
||||
<Flex pointer alignItems="center" justifyContent="between" onClick={ event => setIsOpen(value => !value) }>
|
||||
<Text variant="white">{ LocalizeText('group.homeroominfo.title') }</Text>
|
||||
{ isOpen && <FaChevronUp className="fa-icon" /> }
|
||||
{ !isOpen && <FaChevronDown className="fa-icon" /> }
|
||||
</Flex>
|
||||
{isOpen &&
|
||||
{ isOpen &&
|
||||
<>
|
||||
<Flex pointer alignItems="center" gap={2} onClick={event => GetGroupInformation(groupInformation.id)}>
|
||||
<Flex pointer alignItems="center" gap={ 2 } onClick={ event => GetGroupInformation(groupInformation.id) }>
|
||||
<div className="group-badge">
|
||||
<LayoutBadgeImageView badgeCode={groupInformation.badge} isGroup={true} />
|
||||
<LayoutBadgeImageView badgeCode={ groupInformation.badge } isGroup={ true } />
|
||||
</div>
|
||||
<Text variant="white">{groupInformation.title}</Text>
|
||||
<Text variant="white">{ groupInformation.title }</Text>
|
||||
</Flex>
|
||||
{(groupInformation.type !== GroupType.PRIVATE || isRealOwner) &&
|
||||
<Button fullWidth disabled={(groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING)} variant="success" onClick={handleButtonClick}>
|
||||
{LocalizeText(getButtonText())}
|
||||
{ (groupInformation.type !== GroupType.PRIVATE || isRealOwner) &&
|
||||
<Button fullWidth disabled={ (groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING) } variant="success" onClick={ handleButtonClick }>
|
||||
{ LocalizeText(getButtonText()) }
|
||||
</Button>
|
||||
}
|
||||
</>}
|
||||
</> }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -15,7 +15,7 @@ interface GroupTabColorsViewProps
|
||||
export const GroupTabColorsView: FC<GroupTabColorsViewProps> = props =>
|
||||
{
|
||||
const { groupData = null, setGroupData = null, setCloseAction = null } = props;
|
||||
const [colors, setColors] = useState<number[]>(null);
|
||||
const [ colors, setColors ] = useState<number[]>(null);
|
||||
const { groupCustomize = null } = useGroup();
|
||||
|
||||
const getGroupColor = (colorIndex: number) =>
|
||||
@ -29,7 +29,7 @@ export const GroupTabColorsView: FC<GroupTabColorsViewProps> = props =>
|
||||
{
|
||||
setColors(prevValue =>
|
||||
{
|
||||
const newColors = [...prevValue];
|
||||
const newColors = [ ...prevValue ];
|
||||
|
||||
newColors[colorIndex] = colorId;
|
||||
|
||||
@ -49,7 +49,7 @@ export const GroupTabColorsView: FC<GroupTabColorsViewProps> = props =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.groupColors = [...colors];
|
||||
newValue.groupColors = [ ...colors ];
|
||||
|
||||
return newValue;
|
||||
});
|
||||
@ -60,67 +60,67 @@ export const GroupTabColorsView: FC<GroupTabColorsViewProps> = props =>
|
||||
SendMessageComposer(new GroupSaveColorsComposer(groupData.groupId, colors[0], colors[1]));
|
||||
|
||||
return true;
|
||||
}, [groupData, colors, setGroupData]);
|
||||
}, [ groupData, colors, setGroupData ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (!groupCustomize.groupColorsA || !groupCustomize.groupColorsB || groupData.groupColors) return;
|
||||
|
||||
const groupColors = [groupCustomize.groupColorsA[0].id, groupCustomize.groupColorsB[0].id];
|
||||
const groupColors = [ groupCustomize.groupColorsA[0].id, groupCustomize.groupColorsB[0].id ];
|
||||
|
||||
setGroupData(prevValue =>
|
||||
{
|
||||
return { ...prevValue, groupColors };
|
||||
});
|
||||
}, [groupCustomize, groupData.groupColors, setGroupData]);
|
||||
}, [ groupCustomize, groupData.groupColors, setGroupData ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (groupData.groupId <= 0)
|
||||
{
|
||||
setColors(groupData.groupColors ? [...groupData.groupColors] : null);
|
||||
setColors(groupData.groupColors ? [ ...groupData.groupColors ] : null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setColors(groupData.groupColors);
|
||||
}, [groupData]);
|
||||
}, [ groupData ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setCloseAction({ action: saveColors });
|
||||
|
||||
return () => setCloseAction(null);
|
||||
}, [setCloseAction, saveColors]);
|
||||
}, [ setCloseAction, saveColors ]);
|
||||
|
||||
if (!colors) return null;
|
||||
|
||||
return (
|
||||
<Grid overflow="hidden">
|
||||
<Column gap={1} size={2}>
|
||||
<Text bold>{LocalizeText('group.edit.color.guild.color')}</Text>
|
||||
{groupData.groupColors && (groupData.groupColors.length > 0) &&
|
||||
<Column gap={ 1 } size={ 2 }>
|
||||
<Text bold>{ LocalizeText('group.edit.color.guild.color') }</Text>
|
||||
{ groupData.groupColors && (groupData.groupColors.length > 0) &&
|
||||
<div className="flex overflow-hidden border rounded">
|
||||
<div className="w-[30px] h-[40px]" style={{ backgroundColor: '#' + getGroupColor(0) }} />
|
||||
<div className="w-[30px] h-[40px]" style={{ backgroundColor: '#' + getGroupColor(1) }} />
|
||||
</div>}
|
||||
<div className="w-[30px] h-[40px]" style={ { backgroundColor: '#' + getGroupColor(0) } } />
|
||||
<div className="w-[30px] h-[40px]" style={ { backgroundColor: '#' + getGroupColor(1) } } />
|
||||
</div> }
|
||||
</Column>
|
||||
<Column gap={1} overflow="hidden" size={5}>
|
||||
<Text bold>{LocalizeText('group.edit.color.primary.color')}</Text>
|
||||
<AutoGrid columnCount={7} columnMinHeight={16} columnMinWidth={16} gap={1}>
|
||||
{groupData.groupColors && groupCustomize.groupColorsA && groupCustomize.groupColorsA.map((item, index) =>
|
||||
<Column gap={ 1 } overflow="hidden" size={ 5 }>
|
||||
<Text bold>{ LocalizeText('group.edit.color.primary.color') }</Text>
|
||||
<AutoGrid columnCount={ 7 } columnMinHeight={ 16 } columnMinWidth={ 16 } gap={ 1 }>
|
||||
{ groupData.groupColors && groupCustomize.groupColorsA && groupCustomize.groupColorsA.map((item, index) =>
|
||||
{
|
||||
return <div key={index} className={classNames('relative rounded-[.25rem] w-[16px] h-[16px] bg-[#fff] border-[2px] border-[solid] border-[#fff] [box-shadow:inset_3px_3px_#0000001a] [box-shadow:inset_2px_2px_#0003] cursor-pointer', ((groupData.groupColors[0] === item.id) && 'bg-primary [box-shadow:none]'))} style={{ backgroundColor: '#' + item.color }} onClick={() => selectColor(0, item.id)}></div>
|
||||
})}
|
||||
return <div key={ index } className={ classNames('relative rounded-[.25rem] w-[16px] h-[16px] bg-[#fff] border-[2px] border-[solid] border-[#fff] [box-shadow:inset_3px_3px_#0000001a] [box-shadow:inset_2px_2px_#0003] cursor-pointer', ((groupData.groupColors[0] === item.id) && 'bg-primary [box-shadow:none]')) } style={ { backgroundColor: '#' + item.color } } onClick={ () => selectColor(0, item.id) }></div>
|
||||
}) }
|
||||
</AutoGrid>
|
||||
</Column>
|
||||
<Column gap={1} overflow="hidden" size={5}>
|
||||
<Text bold>{LocalizeText('group.edit.color.secondary.color')}</Text>
|
||||
<AutoGrid columnCount={7} columnMinHeight={16} columnMinWidth={16} gap={1}>
|
||||
{groupData.groupColors && groupCustomize.groupColorsB && groupCustomize.groupColorsB.map((item, index) =>
|
||||
<Column gap={ 1 } overflow="hidden" size={ 5 }>
|
||||
<Text bold>{ LocalizeText('group.edit.color.secondary.color') }</Text>
|
||||
<AutoGrid columnCount={ 7 } columnMinHeight={ 16 } columnMinWidth={ 16 } gap={ 1 }>
|
||||
{ groupData.groupColors && groupCustomize.groupColorsB && groupCustomize.groupColorsB.map((item, index) =>
|
||||
{
|
||||
return <div key={index} className={classNames('relative rounded-[.25rem] w-[16px] h-[16px] bg-[#fff] border-[2px] border-[solid] border-[#fff] [box-shadow:inset_3px_3px_#0000001a] [box-shadow:inset_2px_2px_#0003] cursor-pointer', ((groupData.groupColors[1] === item.id) && 'bg-primary [box-shadow:none]'))} style={{ backgroundColor: '#' + item.color }} onClick={() => selectColor(1, item.id)}></div>
|
||||
})}
|
||||
return <div key={ index } className={ classNames('relative rounded-[.25rem] w-[16px] h-[16px] bg-[#fff] border-[2px] border-[solid] border-[#fff] [box-shadow:inset_3px_3px_#0000001a] [box-shadow:inset_2px_2px_#0003] cursor-pointer', ((groupData.groupColors[1] === item.id) && 'bg-primary [box-shadow:none]')) } style={ { backgroundColor: '#' + item.color } } onClick={ () => selectColor(1, item.id) }></div>
|
||||
}) }
|
||||
</AutoGrid>
|
||||
</Column>
|
||||
</Grid>
|
||||
|
@ -36,30 +36,30 @@ export const GroupTabCreatorConfirmationView: FC<GroupTabCreatorConfirmationView
|
||||
if (!groupData) return null;
|
||||
|
||||
return (
|
||||
<Grid gap={1} overflow="hidden">
|
||||
<Column size={3}>
|
||||
<Column center className="bg-muted rounded p-1" gap={2}>
|
||||
<Text bold center>{LocalizeText('group.create.confirm.guildbadge')}</Text>
|
||||
<LayoutBadgeImageView badgeCode={getCompleteBadgeCode()} isGroup={true} />
|
||||
<Grid gap={ 1 } overflow="hidden">
|
||||
<Column size={ 3 }>
|
||||
<Column center className="bg-muted rounded p-1" gap={ 2 }>
|
||||
<Text bold center>{ LocalizeText('group.create.confirm.guildbadge') }</Text>
|
||||
<LayoutBadgeImageView badgeCode={ getCompleteBadgeCode() } isGroup={ true } />
|
||||
</Column>
|
||||
<Column center className="bg-muted rounded p-1" gap={2}>
|
||||
<Text bold center>{LocalizeText('group.edit.color.guild.color')}</Text>
|
||||
<Column center className="bg-muted rounded p-1" gap={ 2 }>
|
||||
<Text bold center>{ LocalizeText('group.edit.color.guild.color') }</Text>
|
||||
<Flex className="rounded border" overflow="hidden">
|
||||
<div className="w-[30px] h-[40px]" style={{ backgroundColor: '#' + getGroupColor(0) }} />
|
||||
<div className="w-[30px] h-[40px]" style={{ backgroundColor: '#' + getGroupColor(1) }} />
|
||||
<div className="w-[30px] h-[40px]" style={ { backgroundColor: '#' + getGroupColor(0) } } />
|
||||
<div className="w-[30px] h-[40px]" style={ { backgroundColor: '#' + getGroupColor(1) } } />
|
||||
</Flex>
|
||||
</Column>
|
||||
</Column>
|
||||
<Column justifyContent="between" size={9}>
|
||||
<Column justifyContent="between" size={ 9 }>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>{groupData.groupName}</Text>
|
||||
<Text>{groupData.groupDescription}</Text>
|
||||
<Text bold>{ groupData.groupName }</Text>
|
||||
<Text>{ groupData.groupDescription }</Text>
|
||||
</div>
|
||||
<Text overflow="auto">{LocalizeText('group.create.confirm.info')}</Text>
|
||||
<Text overflow="auto">{ LocalizeText('group.create.confirm.info') }</Text>
|
||||
</div>
|
||||
<Text center className="bg-primary rounded p-1" variant="white">
|
||||
{LocalizeText('group.create.confirm.buyinfo', ['amount'], [purchaseCost.toString()])}
|
||||
{ LocalizeText('group.create.confirm.buyinfo', [ 'amount' ], [ purchaseCost.toString() ]) }
|
||||
</Text>
|
||||
</Column>
|
||||
</Grid>
|
||||
|
@ -18,9 +18,9 @@ interface GroupTabIdentityViewProps
|
||||
export const GroupTabIdentityView: FC<GroupTabIdentityViewProps> = props =>
|
||||
{
|
||||
const { groupData = null, setGroupData = null, setCloseAction = null, onClose = null, isCreator = false, availableRooms = [] } = props;
|
||||
const [groupName, setGroupName] = useState<string>('');
|
||||
const [groupDescription, setGroupDescription] = useState<string>('');
|
||||
const [groupHomeroomId, setGroupHomeroomId] = useState<number>(-1);
|
||||
const [ groupName, setGroupName ] = useState<string>('');
|
||||
const [ groupDescription, setGroupDescription ] = useState<string>('');
|
||||
const [ groupHomeroomId, setGroupHomeroomId ] = useState<number>(-1);
|
||||
const { showConfirm = null } = useNotification();
|
||||
|
||||
const deleteGroup = () =>
|
||||
@ -62,21 +62,21 @@ export const GroupTabIdentityView: FC<GroupTabIdentityViewProps> = props =>
|
||||
SendMessageComposer(new GroupSaveInformationComposer(groupData.groupId, groupName, (groupDescription || '')));
|
||||
|
||||
return true;
|
||||
}, [groupData, groupName, groupDescription, groupHomeroomId, setGroupData]);
|
||||
}, [ groupData, groupName, groupDescription, groupHomeroomId, setGroupData ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setGroupName(groupData.groupName || '');
|
||||
setGroupDescription(groupData.groupDescription || '');
|
||||
setGroupHomeroomId(groupData.groupHomeroomId);
|
||||
}, [groupData]);
|
||||
}, [ groupData ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setCloseAction({ action: saveIdentity });
|
||||
|
||||
return () => setCloseAction(null);
|
||||
}, [setCloseAction, saveIdentity]);
|
||||
}, [ setCloseAction, saveIdentity ]);
|
||||
|
||||
if (!groupData) return null;
|
||||
|
||||
@ -84,34 +84,34 @@ export const GroupTabIdentityView: FC<GroupTabIdentityViewProps> = props =>
|
||||
<Column justifyContent="between" overflow="auto">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-1">
|
||||
<Text center className="col-span-3">{LocalizeText('group.edit.name')}</Text>
|
||||
<NitroInput maxLength={29} type="text" value={groupName} onChange={event => setGroupName(event.target.value)} />
|
||||
<Text center className="col-span-3">{ LocalizeText('group.edit.name') }</Text>
|
||||
<NitroInput maxLength={ 29 } type="text" value={ groupName } onChange={ event => setGroupName(event.target.value) } />
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Text center className="col-span-3">{LocalizeText('group.edit.desc')}</Text>
|
||||
<textarea className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] form-control-sm" maxLength={254} value={groupDescription} onChange={event => setGroupDescription(event.target.value)} />
|
||||
<Text center className="col-span-3">{ LocalizeText('group.edit.desc') }</Text>
|
||||
<textarea className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] form-control-sm" maxLength={ 254 } value={ groupDescription } onChange={ event => setGroupDescription(event.target.value) } />
|
||||
</div>
|
||||
{isCreator &&
|
||||
{ isCreator &&
|
||||
<>
|
||||
<div className="flex items-center gap-1">
|
||||
<Text center className="col-span-3">{LocalizeText('group.edit.base')}</Text>
|
||||
<Column fullWidth gap={1}>
|
||||
<select className="form-select form-select-sm" value={groupHomeroomId} onChange={event => setGroupHomeroomId(parseInt(event.target.value))}>
|
||||
<option disabled value={-1}>{LocalizeText('group.edit.base.select.room')}</option>
|
||||
{availableRooms && availableRooms.map((room, index) => <option key={index} value={room.id}>{room.name}</option>)}
|
||||
<Text center className="col-span-3">{ LocalizeText('group.edit.base') }</Text>
|
||||
<Column fullWidth gap={ 1 }>
|
||||
<select className="form-select form-select-sm" value={ groupHomeroomId } onChange={ event => setGroupHomeroomId(parseInt(event.target.value)) }>
|
||||
<option disabled value={ -1 }>{ LocalizeText('group.edit.base.select.room') }</option>
|
||||
{ availableRooms && availableRooms.map((room, index) => <option key={ index } value={ room.id }>{ room.name }</option>) }
|
||||
</select>
|
||||
</Column>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
<div className="col-span-3"> </div>
|
||||
<Text small>{LocalizeText('group.edit.base.warning')}</Text>
|
||||
<Text small>{ LocalizeText('group.edit.base.warning') }</Text>
|
||||
</div>
|
||||
</>}
|
||||
</> }
|
||||
</div>
|
||||
{!isCreator &&
|
||||
<Button variant="danger" onClick={deleteGroup}>{LocalizeText('group.delete')}</Button>}
|
||||
{isCreator &&
|
||||
<Text center fullWidth pointer underline onClick={event => CreateLinkEvent('navigator/create')}>{LocalizeText('group.createroom')}</Text>}
|
||||
{ !isCreator &&
|
||||
<Button variant="danger" onClick={ deleteGroup }>{ LocalizeText('group.delete') }</Button> }
|
||||
{ isCreator &&
|
||||
<Text center fullWidth pointer underline onClick={ event => CreateLinkEvent('navigator/create') }>{ LocalizeText('group.createroom') }</Text> }
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
@ -35,41 +35,41 @@ export const GuideToolMenuView: FC<GuideToolMenuViewProps> = props =>
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<Flex alignItems="center" className="bg-muted p-2 rounded" gap={2}>
|
||||
<div className={'duty-switch' + (isOnDuty ? '' : ' off')} onClick={event => processAction('toggle_duty')} />
|
||||
<Column gap={0}>
|
||||
<Text bold>{LocalizeText('guide.help.guide.tool.yourstatus')}</Text>
|
||||
<Text>{LocalizeText(`guide.help.guide.tool.duty.${(isOnDuty ? 'on' : 'off')}`)}</Text>
|
||||
<Flex alignItems="center" className="bg-muted p-2 rounded" gap={ 2 }>
|
||||
<div className={ 'duty-switch' + (isOnDuty ? '' : ' off') } onClick={ event => processAction('toggle_duty') } />
|
||||
<Column gap={ 0 }>
|
||||
<Text bold>{ LocalizeText('guide.help.guide.tool.yourstatus') }</Text>
|
||||
<Text>{ LocalizeText(`guide.help.guide.tool.duty.${ (isOnDuty ? 'on' : 'off') }`) }</Text>
|
||||
</Column>
|
||||
</Flex>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>{LocalizeText('guide.help.guide.tool.tickettypeselection.caption')}</Text>
|
||||
<Text bold>{ LocalizeText('guide.help.guide.tool.tickettypeselection.caption') }</Text>
|
||||
<div className="flex items-center gap-1">
|
||||
<input checked={isHandlingGuideRequests} className="form-check-input" disabled={isOnDuty} type="checkbox" onChange={event => setIsHandlingGuideRequests(event.target.checked)} />
|
||||
<Text>{LocalizeText('guide.help.guide.tool.tickettypeselection.guiderequests')}</Text>
|
||||
<input checked={ isHandlingGuideRequests } className="form-check-input" disabled={ isOnDuty } type="checkbox" onChange={ event => setIsHandlingGuideRequests(event.target.checked) } />
|
||||
<Text>{ LocalizeText('guide.help.guide.tool.tickettypeselection.guiderequests') }</Text>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<input checked={isHandlingHelpRequests} className="form-check-input" disabled={isOnDuty} type="checkbox" onChange={event => setIsHandlingHelpRequests(event.target.checked)} />
|
||||
<Text>{LocalizeText('guide.help.guide.tool.tickettypeselection.onlyhelprequests')}</Text>
|
||||
<input checked={ isHandlingHelpRequests } className="form-check-input" disabled={ isOnDuty } type="checkbox" onChange={ event => setIsHandlingHelpRequests(event.target.checked) } />
|
||||
<Text>{ LocalizeText('guide.help.guide.tool.tickettypeselection.onlyhelprequests') }</Text>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<input checked={isHandlingBullyReports} className="form-check-input" disabled={isOnDuty} type="checkbox" onChange={event => setIsHandlingBullyReports(event.target.checked)} />
|
||||
<Text>{LocalizeText('guide.help.guide.tool.tickettypeselection.bullyreports')}</Text>
|
||||
<input checked={ isHandlingBullyReports } className="form-check-input" disabled={ isOnDuty } type="checkbox" onChange={ event => setIsHandlingBullyReports(event.target.checked) } />
|
||||
<Text>{ LocalizeText('guide.help.guide.tool.tickettypeselection.bullyreports') }</Text>
|
||||
</div>
|
||||
</div>
|
||||
<hr className="bg-dark m-0" />
|
||||
<div className="flex justify-enter items-center gap-2">
|
||||
<div className="info-icon" />
|
||||
<div className="flex flex-col gap-1">
|
||||
<div dangerouslySetInnerHTML={{ __html: LocalizeText('guide.help.guide.tool.guidesonduty', ['amount'], [guidesOnDuty.toString()]) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: LocalizeText('guide.help.guide.tool.helpersonduty', ['amount'], [helpersOnDuty.toString()]) }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: LocalizeText('guide.help.guide.tool.guardiansonduty', ['amount'], [guardiansOnDuty.toString()]) }} />
|
||||
<div dangerouslySetInnerHTML={ { __html: LocalizeText('guide.help.guide.tool.guidesonduty', [ 'amount' ], [ guidesOnDuty.toString() ]) } } />
|
||||
<div dangerouslySetInnerHTML={ { __html: LocalizeText('guide.help.guide.tool.helpersonduty', [ 'amount' ], [ helpersOnDuty.toString() ]) } } />
|
||||
<div dangerouslySetInnerHTML={ { __html: LocalizeText('guide.help.guide.tool.guardiansonduty', [ 'amount' ], [ guardiansOnDuty.toString() ]) } } />
|
||||
</div>
|
||||
</div>
|
||||
<hr className="bg-dark m-0" />
|
||||
<Flex gap={2} justifyContent="between">
|
||||
<Button disabled onClick={event => processAction('forum_link')}>{LocalizeText('guide.help.guide.tool.forum.link')}</Button>
|
||||
<Button disabled>{LocalizeText('guide.help.guide.tool.skill.link')}</Button>
|
||||
<Flex gap={ 2 } justifyContent="between">
|
||||
<Button disabled onClick={ event => processAction('forum_link') }>{ LocalizeText('guide.help.guide.tool.forum.link') }</Button>
|
||||
<Button disabled>{ LocalizeText('guide.help.guide.tool.skill.link') }</Button>
|
||||
</Flex>
|
||||
</div>
|
||||
);
|
||||
|
@ -3,7 +3,7 @@ import { FC, KeyboardEvent, useCallback, useEffect, useRef, useState } from 'rea
|
||||
import { GuideToolMessageGroup, LocalizeText, SendMessageComposer, TryVisitRoom } from '../../../api';
|
||||
import { Button, Column, Flex, LayoutAvatarImageView, Text } from '../../../common';
|
||||
import { useMessageEvent } from '../../../hooks';
|
||||
import { classNames } from '../../../layout';
|
||||
import { NitroInput, classNames } from '../../../layout';
|
||||
|
||||
interface GuideToolOngoingViewProps
|
||||
{
|
||||
@ -21,13 +21,13 @@ export const GuideToolOngoingView: FC<GuideToolOngoingViewProps> = props =>
|
||||
|
||||
const { isGuide = false, userId = 0, userName = null, userFigure = null, isTyping = false, messageGroups = [] } = props;
|
||||
|
||||
const [messageText, setMessageText] = useState<string>('');
|
||||
const [ messageText, setMessageText ] = useState<string>('');
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
scrollDiv.current?.scrollIntoView({ block: 'end', behavior: 'smooth' });
|
||||
|
||||
}, [messageGroups]);
|
||||
}, [ messageGroups ]);
|
||||
|
||||
const visit = useCallback(() =>
|
||||
{
|
||||
@ -57,14 +57,14 @@ export const GuideToolOngoingView: FC<GuideToolOngoingViewProps> = props =>
|
||||
|
||||
SendMessageComposer(new GuideSessionMessageMessageComposer(messageText));
|
||||
setMessageText('');
|
||||
}, [messageText]);
|
||||
}, [ messageText ]);
|
||||
|
||||
const onKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) =>
|
||||
{
|
||||
if (event.key !== 'Enter') return;
|
||||
|
||||
sendMessage();
|
||||
}, [sendMessage]);
|
||||
}, [ sendMessage ]);
|
||||
|
||||
const isOwnChat = useCallback((userId: number) =>
|
||||
{
|
||||
@ -73,58 +73,58 @@ export const GuideToolOngoingView: FC<GuideToolOngoingViewProps> = props =>
|
||||
|
||||
return (
|
||||
<Column fullHeight>
|
||||
<Flex alignItems="center" className="p-2 rounded bg-muted" gap={1} justifyContent="between">
|
||||
{isGuide &&
|
||||
<Flex alignItems="center" className="p-2 rounded bg-muted" gap={ 1 } justifyContent="between">
|
||||
{ isGuide &&
|
||||
<div className="relative inline-flex align-middle">
|
||||
<Button onClick={visit}>{LocalizeText('guide.help.request.guide.ongoing.visit.button')}</Button>
|
||||
<Button onClick={invite}>{LocalizeText('guide.help.request.guide.ongoing.invite.button')}</Button>
|
||||
</div>}
|
||||
{!isGuide &&
|
||||
<Column gap={0}>
|
||||
<Text bold>{userName}</Text>
|
||||
<Text>{LocalizeText('guide.help.request.user.ongoing.guide.desc')}</Text>
|
||||
</Column>}
|
||||
<Button disabled variant="danger">{LocalizeText('guide.help.common.report.link')}</Button>
|
||||
<Button onClick={ visit }>{ LocalizeText('guide.help.request.guide.ongoing.visit.button') }</Button>
|
||||
<Button onClick={ invite }>{ LocalizeText('guide.help.request.guide.ongoing.invite.button') }</Button>
|
||||
</div> }
|
||||
{ !isGuide &&
|
||||
<Column gap={ 0 }>
|
||||
<Text bold>{ userName }</Text>
|
||||
<Text>{ LocalizeText('guide.help.request.user.ongoing.guide.desc') }</Text>
|
||||
</Column> }
|
||||
<Button disabled variant="danger">{ LocalizeText('guide.help.common.report.link') }</Button>
|
||||
</Flex>
|
||||
<Column className="p-2 rounded bg-muted chat-messages" gap={1} overflow="hidden">
|
||||
<Column className="p-2 rounded bg-muted chat-messages" gap={ 1 } overflow="hidden">
|
||||
<Column overflow="auto">
|
||||
{messageGroups.map((group, index) =>
|
||||
{ messageGroups.map((group, index) =>
|
||||
{
|
||||
return (
|
||||
<Flex key={index} fullWidth gap={2} justifyContent={isOwnChat(group.userId) ? 'end' : 'start'}>
|
||||
<Flex key={ index } fullWidth gap={ 2 } justifyContent={ isOwnChat(group.userId) ? 'end' : 'start' }>
|
||||
<div className="flex-shrink-0 message-avatar">
|
||||
{(!isOwnChat(group.userId)) &&
|
||||
<LayoutAvatarImageView direction={2} figure={userFigure} />}
|
||||
{ (!isOwnChat(group.userId)) &&
|
||||
<LayoutAvatarImageView direction={ 2 } figure={ userFigure } /> }
|
||||
</div>
|
||||
<div className={'bg-light text-black border-radius mb-2 rounded py-1 px-2 messages-group-' + (isOwnChat(group.userId) ? 'right' : 'left')}>
|
||||
<div className={ 'bg-light text-black border-radius mb-2 rounded py-1 px-2 messages-group-' + (isOwnChat(group.userId) ? 'right' : 'left') }>
|
||||
<Text bold>
|
||||
{(isOwnChat(group.userId)) && GetSessionDataManager().userName}
|
||||
{(!isOwnChat(group.userId)) && userName}
|
||||
{ (isOwnChat(group.userId)) && GetSessionDataManager().userName }
|
||||
{ (!isOwnChat(group.userId)) && userName }
|
||||
</Text>
|
||||
{group.messages.map((chat, index) => <div key={index} className={classNames(chat.roomId ? 'text-break text-underline' : 'text-break', 'chat.roomId' && 'cursor-pointer')} onClick={() => chat.roomId ? TryVisitRoom(chat.roomId) : null}>{chat.message}</div>)}
|
||||
{ group.messages.map((chat, index) => <div key={ index } className={ classNames(chat.roomId ? 'text-break text-underline' : 'text-break', 'chat.roomId' && 'cursor-pointer') } onClick={ () => chat.roomId ? TryVisitRoom(chat.roomId) : null }>{ chat.message }</div>) }
|
||||
</div>
|
||||
{(isOwnChat(group.userId)) &&
|
||||
{ (isOwnChat(group.userId)) &&
|
||||
<div className="flex-shrink-0 message-avatar">
|
||||
<LayoutAvatarImageView direction={4} figure={GetSessionDataManager().figure} />
|
||||
</div>}
|
||||
<LayoutAvatarImageView direction={ 4 } figure={ GetSessionDataManager().figure } />
|
||||
</div> }
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
<div ref={scrollDiv} />
|
||||
}) }
|
||||
<div ref={ scrollDiv } />
|
||||
</Column>
|
||||
</Column>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex gap-1">
|
||||
<NitroInput placeholder={LocalizeText('guide.help.request.guide.ongoing.input.empty', ['name'], [userName])} type="text" value={messageText} onChange={event => setMessageText(event.target.value)} onKeyDown={onKeyDown} />
|
||||
<Button variant="success" onClick={sendMessage}>
|
||||
{LocalizeText('widgets.chatinput.say')}
|
||||
<NitroInput placeholder={ LocalizeText('guide.help.request.guide.ongoing.input.empty', [ 'name' ], [ userName ]) } type="text" value={ messageText } onChange={ event => setMessageText(event.target.value) } onKeyDown={ onKeyDown } />
|
||||
<Button variant="success" onClick={ sendMessage }>
|
||||
{ LocalizeText('widgets.chatinput.say') }
|
||||
</Button>
|
||||
</div>
|
||||
{isTyping &&
|
||||
<Text variant="muted">{LocalizeText('guide.help.common.typing')}</Text>}
|
||||
{ isTyping &&
|
||||
<Text variant="muted">{ LocalizeText('guide.help.common.typing') }</Text> }
|
||||
</div>
|
||||
<Button fullWidth variant="success" onClick={resolve}>
|
||||
{LocalizeText('guide.help.request.' + (isGuide ? 'guide' : 'user') + '.ongoing.close.link')}
|
||||
<Button fullWidth variant="success" onClick={ resolve }>
|
||||
{ LocalizeText('guide.help.request.' + (isGuide ? 'guide' : 'user') + '.ongoing.close.link') }
|
||||
</Button>
|
||||
</Column>
|
||||
);
|
||||
|
@ -7,10 +7,10 @@ import { useInventoryBadges, useMessageEvent, usePurse, useSessionInfo } from '.
|
||||
|
||||
export const HcCenterView: FC<{}> = props =>
|
||||
{
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [kickbackData, setKickbackData] = useState<ScrKickbackData>(null);
|
||||
const [unclaimedGifts, setUnclaimedGifts] = useState(0);
|
||||
const [badgeCode, setBadgeCode] = useState(null);
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const [ kickbackData, setKickbackData ] = useState<ScrKickbackData>(null);
|
||||
const [ unclaimedGifts, setUnclaimedGifts ] = useState(0);
|
||||
const [ badgeCode, setBadgeCode ] = useState(null);
|
||||
const { userFigure = null } = useSessionInfo();
|
||||
const { purse = null, clubStatus = null } = usePurse();
|
||||
const { badgeCodes = [], activate = null, deactivate = null } = useInventoryBadges();
|
||||
@ -32,16 +32,16 @@ export const HcCenterView: FC<{}> = props =>
|
||||
switch (clubStatus)
|
||||
{
|
||||
case ClubStatus.ACTIVE:
|
||||
return LocalizeText(`hccenter.status.${clubStatus}.info`, ['timeleft', 'joindate', 'streakduration'], [getClubText(), kickbackData?.firstSubscriptionDate, FriendlyTime.shortFormat(kickbackData?.currentHcStreak * 86400)]);
|
||||
return LocalizeText(`hccenter.status.${ clubStatus }.info`, [ 'timeleft', 'joindate', 'streakduration' ], [ getClubText(), kickbackData?.firstSubscriptionDate, FriendlyTime.shortFormat(kickbackData?.currentHcStreak * 86400) ]);
|
||||
case ClubStatus.EXPIRED:
|
||||
return LocalizeText(`hccenter.status.${clubStatus}.info`, ['joindate'], [kickbackData?.firstSubscriptionDate]);
|
||||
return LocalizeText(`hccenter.status.${ clubStatus }.info`, [ 'joindate' ], [ kickbackData?.firstSubscriptionDate ]);
|
||||
default:
|
||||
return LocalizeText(`hccenter.status.${clubStatus}.info`);
|
||||
return LocalizeText(`hccenter.status.${ clubStatus }.info`);
|
||||
}
|
||||
}
|
||||
|
||||
const getHcPaydayTime = () => (!kickbackData || kickbackData.timeUntilPayday < 60) ? LocalizeText('hccenter.special.time.soon') : FriendlyTime.shortFormat(kickbackData.timeUntilPayday * 60);
|
||||
const getHcPaydayAmount = () => LocalizeText('hccenter.special.sum', ['credits'], [(kickbackData?.creditRewardForStreakBonus + kickbackData?.creditRewardForMonthlySpent).toString()]);
|
||||
const getHcPaydayAmount = () => LocalizeText('hccenter.special.sum', [ 'credits' ], [ (kickbackData?.creditRewardForStreakBonus + kickbackData?.creditRewardForMonthlySpent).toString() ]);
|
||||
|
||||
useMessageEvent<ClubGiftInfoEvent>(ClubGiftInfoEvent, event =>
|
||||
{
|
||||
@ -92,7 +92,7 @@ export const HcCenterView: FC<{}> = props =>
|
||||
useEffect(() =>
|
||||
{
|
||||
setBadgeCode(GetClubBadge(badgeCodes));
|
||||
}, [badgeCodes]);
|
||||
}, [ badgeCodes ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -101,7 +101,7 @@ export const HcCenterView: FC<{}> = props =>
|
||||
const id = activate();
|
||||
|
||||
return () => deactivate(id);
|
||||
}, [isVisible, activate, deactivate]);
|
||||
}, [ isVisible, activate, deactivate ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -113,86 +113,86 @@ export const HcCenterView: FC<{}> = props =>
|
||||
|
||||
const popover = (
|
||||
<>
|
||||
<h5>{LocalizeText('hccenter.breakdown.title')}</h5>
|
||||
<div>{LocalizeText('hccenter.breakdown.creditsspent', ['credits'], [kickbackData?.totalCreditsSpent.toString()])}</div>
|
||||
<div>{LocalizeText('hccenter.breakdown.paydayfactor.percent', ['percent'], [(kickbackData?.kickbackPercentage * 100).toString()])}</div>
|
||||
<div>{LocalizeText('hccenter.breakdown.streakbonus', ['credits'], [kickbackData?.creditRewardForStreakBonus.toString()])}</div>
|
||||
<h5>{ LocalizeText('hccenter.breakdown.title') }</h5>
|
||||
<div>{ LocalizeText('hccenter.breakdown.creditsspent', [ 'credits' ], [ kickbackData?.totalCreditsSpent.toString() ]) }</div>
|
||||
<div>{ LocalizeText('hccenter.breakdown.paydayfactor.percent', [ 'percent' ], [ (kickbackData?.kickbackPercentage * 100).toString() ]) }</div>
|
||||
<div>{ LocalizeText('hccenter.breakdown.streakbonus', [ 'credits' ], [ kickbackData?.creditRewardForStreakBonus.toString() ]) }</div>
|
||||
<hr className="w-full text-black my-1" />
|
||||
<div>{LocalizeText('hccenter.breakdown.total', ['credits', 'actual'], [getHcPaydayAmount(), ((((kickbackData?.kickbackPercentage * kickbackData?.totalCreditsSpent) + kickbackData?.creditRewardForStreakBonus) * 100) / 100).toString()])}</div>
|
||||
<div className="btn btn-link text-primary p-0" onClick={() => CreateLinkEvent('habbopages/' + GetConfigurationValue('hc.center')['payday.habbopage'])}>
|
||||
{LocalizeText('hccenter.special.infolink')}
|
||||
<div>{ LocalizeText('hccenter.breakdown.total', [ 'credits', 'actual' ], [ getHcPaydayAmount(), ((((kickbackData?.kickbackPercentage * kickbackData?.totalCreditsSpent) + kickbackData?.creditRewardForStreakBonus) * 100) / 100).toString() ]) }</div>
|
||||
<div className="btn btn-link text-primary p-0" onClick={ () => CreateLinkEvent('habbopages/' + GetConfigurationValue('hc.center')['payday.habbopage']) }>
|
||||
{ LocalizeText('hccenter.special.infolink') }
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-hc-center" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={LocalizeText('generic.hccenter')} onCloseClick={() => setIsVisible(false)} />
|
||||
<NitroCardHeaderView headerText={ LocalizeText('generic.hccenter') } onCloseClick={ () => setIsVisible(false) } />
|
||||
<Flex className="bg-muted p-2" position="relative">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="hc-logo" />
|
||||
<Flex>
|
||||
<Button variant="success" onClick={event => CreateLinkEvent('catalog/open/' + GetConfigurationValue('catalog.links')['hc.buy_hc'])}>
|
||||
{LocalizeText((clubStatus === ClubStatus.ACTIVE) ? 'hccenter.btn.extend' : 'hccenter.btn.buy')}
|
||||
<Button variant="success" onClick={ event => CreateLinkEvent('catalog/open/' + GetConfigurationValue('catalog.links')['hc.buy_hc']) }>
|
||||
{ LocalizeText((clubStatus === ClubStatus.ACTIVE) ? 'hccenter.btn.extend' : 'hccenter.btn.buy') }
|
||||
</Button>
|
||||
</Flex>
|
||||
</div>
|
||||
<div className="end-0 p-4 top-0 habbo-avatar absolute">
|
||||
<LayoutAvatarImageView direction={4} figure={userFigure} scale={2} />
|
||||
<LayoutAvatarImageView direction={ 4 } figure={ userFigure } scale={ 2 } />
|
||||
</div>
|
||||
</Flex>
|
||||
<NitroCardContentView>
|
||||
<div className="flex gap-2">
|
||||
<LayoutBadgeImageView badgeCode={badgeCode} className="align-self-center flex-shrink-0 me-1" />
|
||||
<Column className="streak-info" gap={0} size={5}>
|
||||
<Text>{LocalizeText('hccenter.status.' + clubStatus)}</Text>
|
||||
<Text dangerouslySetInnerHTML={{ __html: getInfoText() }} />
|
||||
<LayoutBadgeImageView badgeCode={ badgeCode } className="align-self-center flex-shrink-0 me-1" />
|
||||
<Column className="streak-info" gap={ 0 } size={ 5 }>
|
||||
<Text>{ LocalizeText('hccenter.status.' + clubStatus) }</Text>
|
||||
<Text dangerouslySetInnerHTML={ { __html: getInfoText() } } />
|
||||
</Column>
|
||||
</div>
|
||||
{GetConfigurationValue('hc.center')['payday.info'] &&
|
||||
{ GetConfigurationValue('hc.center')['payday.info'] &&
|
||||
<Flex alignItems="center">
|
||||
|
||||
<Column className="rounded-start bg-primary p-2 payday-special mb-1">
|
||||
<h4 className="mb-1">{LocalizeText('hccenter.special.title')}</h4>
|
||||
<div>{LocalizeText('hccenter.special.info')}</div>
|
||||
<div className="btn btn-link text-white p-0 mt-auto align-self-baseline" onClick={() => CreateLinkEvent('habbopages/' + GetConfigurationValue('hc.center')['payday.habbopage'])}>{LocalizeText('hccenter.special.infolink')}</div>
|
||||
<h4 className="mb-1">{ LocalizeText('hccenter.special.title') }</h4>
|
||||
<div>{ LocalizeText('hccenter.special.info') }</div>
|
||||
<div className="btn btn-link text-white p-0 mt-auto align-self-baseline" onClick={ () => CreateLinkEvent('habbopages/' + GetConfigurationValue('hc.center')['payday.habbopage']) }>{ LocalizeText('hccenter.special.infolink') }</div>
|
||||
</Column>
|
||||
<div className="payday flex-shrink-0 p-2">
|
||||
<h5 className="mb-2 ms-2">{LocalizeText('hccenter.special.time.title')}</h5>
|
||||
<h5 className="mb-2 ms-2">{ LocalizeText('hccenter.special.time.title') }</h5>
|
||||
<div className="flex flex-row mb-2">
|
||||
<div className="clock me-2" />
|
||||
<h6 className="mb-0 align-self-center">{getHcPaydayTime()}</h6>
|
||||
<h6 className="mb-0 align-self-center">{ getHcPaydayTime() }</h6>
|
||||
</div>
|
||||
{clubStatus === ClubStatus.ACTIVE &&
|
||||
{ clubStatus === ClubStatus.ACTIVE &&
|
||||
<div className="pe-3">
|
||||
<h5 className="ms-2 mb-1 bolder">{LocalizeText('hccenter.special.amount.title')}</h5>
|
||||
<h5 className="ms-2 mb-1 bolder">{ LocalizeText('hccenter.special.amount.title') }</h5>
|
||||
<div className="flex flex-col">
|
||||
<div className="w-full text-center ms-4n">{getHcPaydayAmount()}</div>
|
||||
<div className="w-full text-center ms-4n">{ getHcPaydayAmount() }</div>
|
||||
<div className="btn btn-link align-self-end text-primary">
|
||||
{LocalizeText('hccenter.breakdown.infolink')}
|
||||
{ LocalizeText('hccenter.breakdown.infolink') }
|
||||
</div>
|
||||
</div>
|
||||
</div>}
|
||||
</div> }
|
||||
</div>
|
||||
</Flex>}
|
||||
{GetConfigurationValue('hc.center')['gift.info'] &&
|
||||
</Flex> }
|
||||
{ GetConfigurationValue('hc.center')['gift.info'] &&
|
||||
<div className="rounded bg-success p-2 flex flex-row mb-0">
|
||||
<div>
|
||||
<h4 className="mb-1">{LocalizeText('hccenter.gift.title')}</h4>
|
||||
<div dangerouslySetInnerHTML={{ __html: unclaimedGifts > 0 ? LocalizeText('hccenter.unclaimedgifts', ['unclaimedgifts'], [unclaimedGifts.toString()]) : LocalizeText('hccenter.gift.info') }}></div>
|
||||
<h4 className="mb-1">{ LocalizeText('hccenter.gift.title') }</h4>
|
||||
<div dangerouslySetInnerHTML={ { __html: unclaimedGifts > 0 ? LocalizeText('hccenter.unclaimedgifts', [ 'unclaimedgifts' ], [ unclaimedGifts.toString() ]) : LocalizeText('hccenter.gift.info') } }></div>
|
||||
</div>
|
||||
<button className="btn btn-primary btn-lg align-self-center ms-auto" onClick={() => CreateLinkEvent('catalog/open/' + GetConfigurationValue('catalog.links')['hc.hc_gifts'])}>
|
||||
{LocalizeText(clubStatus === ClubStatus.ACTIVE ? 'hccenter.btn.gifts.redeem' : 'hccenter.btn.gifts.view')}
|
||||
<button className="btn btn-primary btn-lg align-self-center ms-auto" onClick={ () => CreateLinkEvent('catalog/open/' + GetConfigurationValue('catalog.links')['hc.hc_gifts']) }>
|
||||
{ LocalizeText(clubStatus === ClubStatus.ACTIVE ? 'hccenter.btn.gifts.redeem' : 'hccenter.btn.gifts.view') }
|
||||
</button>
|
||||
</div>}
|
||||
{GetConfigurationValue('hc.center')['benefits.info'] &&
|
||||
</div> }
|
||||
{ GetConfigurationValue('hc.center')['benefits.info'] &&
|
||||
<div className="benefits text-black py-2">
|
||||
<h5 className="mb-1 text-primary">{LocalizeText('hccenter.general.title')}</h5>
|
||||
<div className="mb-2" dangerouslySetInnerHTML={{ __html: LocalizeText('hccenter.general.info') }} />
|
||||
<button className="btn btn-link p-0 text-primary" onClick={() => CreateLinkEvent('habbopages/' + GetConfigurationValue('hc.center')['benefits.habbopage'])}>
|
||||
{LocalizeText('hccenter.general.infolink')}
|
||||
<h5 className="mb-1 text-primary">{ LocalizeText('hccenter.general.title') }</h5>
|
||||
<div className="mb-2" dangerouslySetInnerHTML={ { __html: LocalizeText('hccenter.general.info') } } />
|
||||
<button className="btn btn-link p-0 text-primary" onClick={ () => CreateLinkEvent('habbopages/' + GetConfigurationValue('hc.center')['benefits.habbopage']) }>
|
||||
{ LocalizeText('hccenter.general.infolink') }
|
||||
</button>
|
||||
</div>}
|
||||
</div> }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
|
@ -5,7 +5,7 @@ import { useHelp } from '../../../hooks';
|
||||
|
||||
export const DescribeReportView: FC<{}> = props =>
|
||||
{
|
||||
const [message, setMessage] = useState('');
|
||||
const [ message, setMessage ] = useState('');
|
||||
const { activeReport = null, setActiveReport = null } = useHelp();
|
||||
|
||||
const submitMessage = () =>
|
||||
@ -31,16 +31,16 @@ export const DescribeReportView: FC<{}> = props =>
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text fontSize={4}>{LocalizeText('help.emergency.chat_report.subtitle')}</Text>
|
||||
<Text>{LocalizeText('help.cfh.input.text')}</Text>
|
||||
<Text fontSize={ 4 }>{ LocalizeText('help.emergency.chat_report.subtitle') }</Text>
|
||||
<Text>{ LocalizeText('help.cfh.input.text') }</Text>
|
||||
</div>
|
||||
<textarea className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] h-full" value={message} onChange={event => setMessage(event.target.value)} />
|
||||
<Flex gap={2} justifyContent="between">
|
||||
<Button disabled={!(activeReport.reportType === ReportType.BULLY || activeReport.reportType === ReportType.EMERGENCY)} variant="secondary" onClick={back}>
|
||||
{LocalizeText('generic.back')}
|
||||
<textarea className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] h-full" value={ message } onChange={ event => setMessage(event.target.value) } />
|
||||
<Flex gap={ 2 } justifyContent="between">
|
||||
<Button disabled={ !(activeReport.reportType === ReportType.BULLY || activeReport.reportType === ReportType.EMERGENCY) } variant="secondary" onClick={ back }>
|
||||
{ LocalizeText('generic.back') }
|
||||
</Button>
|
||||
<Button disabled={(message.length < 15)} onClick={submitMessage}>
|
||||
{LocalizeText('help.emergency.main.submit.button')}
|
||||
<Button disabled={ (message.length < 15) } onClick={ submitMessage }>
|
||||
{ LocalizeText('help.emergency.main.submit.button') }
|
||||
</Button>
|
||||
</Flex>
|
||||
</>
|
||||
|
@ -23,15 +23,15 @@ export const HelpIndexView: FC<{}> = props =>
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col justify-center alignp-items-enter !flex-grow gap-1">
|
||||
<Text fontSize={3}>{LocalizeText('help.main.frame.title')}</Text>
|
||||
<Text>{LocalizeText('help.main.self.description')}</Text>
|
||||
<Text fontSize={ 3 }>{ LocalizeText('help.main.frame.title') }</Text>
|
||||
<Text>{ LocalizeText('help.main.self.description') }</Text>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Button onClick={onReportClick}>{LocalizeText('help.main.bully.subtitle')}</Button>
|
||||
<Button disabled={!GetConfigurationValue('guides.enabled')} onClick={() => DispatchUiEvent(new GuideToolEvent(GuideToolEvent.CREATE_HELP_REQUEST))}>{LocalizeText('help.main.help.title')}</Button>
|
||||
<Button disabled={true}>{LocalizeText('help.main.self.tips.title')}</Button>
|
||||
<Button onClick={ onReportClick }>{ LocalizeText('help.main.bully.subtitle') }</Button>
|
||||
<Button disabled={ !GetConfigurationValue('guides.enabled') } onClick={ () => DispatchUiEvent(new GuideToolEvent(GuideToolEvent.CREATE_HELP_REQUEST)) }>{ LocalizeText('help.main.help.title') }</Button>
|
||||
<Button disabled={ true }>{ LocalizeText('help.main.self.tips.title') }</Button>
|
||||
</div>
|
||||
<Button textColor="black" variant="link" onClick={() => SendMessageComposer(new GetCfhStatusMessageComposer(false))}>{LocalizeText('help.main.my.sanction.status')}</Button>
|
||||
<Button textColor="black" variant="link" onClick={ () => SendMessageComposer(new GetCfhStatusMessageComposer(false)) }>{ LocalizeText('help.main.my.sanction.status') }</Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user