This commit is contained in:
Layne 2022-03-23 03:10:06 -04:00
commit 1c8c404d3e
97 changed files with 556 additions and 537 deletions

View File

@ -19,7 +19,7 @@ $grid-active-border-color: $white;
$toolbar-height: 55px;
$achievement-width: 375px;
$achievement-height: 425px;
$achievement-height: 415px;
$avatar-editor-width: 620px;
$avatar-editor-height: 374px;

View File

@ -0,0 +1,13 @@
import { AchievementData } from '@nitrots/nitro-renderer';
import { GetLocalization } from '..';
export const GetAchievementBadgeCode = (achievement: AchievementData) =>
{
if(!achievement) return null;
let badgeId = achievement.badgeId;
if(!achievement.finalLevel) badgeId = GetLocalization().getPreviousLevelBadgeId(badgeId);
return badgeId;
}

View File

@ -0,0 +1,10 @@
import { AchievementData } from '@nitrots/nitro-renderer';
export const GetAchievementHasStarted = (achievement: AchievementData) =>
{
if(!achievement) return false;
if(achievement.finalLevel || ((achievement.level - 1) > 0)) return true;
return false;
}

View File

@ -0,0 +1,15 @@
import { AchievementData } from '@nitrots/nitro-renderer';
import { GetConfiguration } from '..';
export const GetAchievementIsIgnored = (achievement: AchievementData) =>
{
if(!achievement) return false;
const ignored = GetConfiguration<string[]>('achievements.unseen.ignored');
const value = achievement.badgeId.replace(/[0-9]/g, '');
const index = ignored.indexOf(value);
if(index >= 0) return true;
return false;
}

View File

@ -0,0 +1,5 @@
export * from './AchievementCategory';
export * from './GetAchievementBadgeCode';
export * from './GetAchievementHasStarted';
export * from './GetAchievementIsIgnored';
export * from './GetAchievementLevel';

View File

@ -1,3 +1,4 @@
export * from './achievements';
export * from './common';
export * from './core';
export * from './friends';

View File

@ -0,0 +1,31 @@
import { FC, useMemo } from 'react';
import { Base, BaseProps, Flex } from '..';
interface LayoutProgressBarProps extends BaseProps<HTMLDivElement>
{
text: string;
progress: number;
maxProgress?: number;
}
export const LayoutProgressBar: FC<LayoutProgressBarProps> = props =>
{
const { text = '', progress = 0, maxProgress = 0, position = 'relative', classNames = [], children = null, ...rest } = props;
const getClassNames = useMemo(() =>
{
const newClassNames: string[] = [ 'progress', 'text-black' ];
if(classNames.length) newClassNames.push(...classNames);
return newClassNames;
}, [ classNames ]);
return (
<Base position={ position } classNames={ getClassNames } { ...rest }>
<Flex fit center position="absolute">{ text }</Flex>
<Base className="progress-bar bg-success" style={ { width: (~~((((progress - 0) * (100 - 0)) / (maxProgress - 0)) + 0) + '%') }} />
{ children }
</Base>
);
}

View File

@ -11,6 +11,7 @@ export * from './LayoutMiniCameraView';
export * from './LayoutNotificationAlertView';
export * from './LayoutNotificationBubbleView';
export * from './LayoutPetImageView';
export * from './LayoutProgressBar';
export * from './LayoutRarityLevelView';
export * from './LayoutRoomPreviewerView';
export * from './LayoutRoomThumbnailView';

View File

@ -1,13 +1,11 @@
import { AchievementData, AchievementEvent, AchievementsEvent, AchievementsScoreEvent, RequestAchievementsMessageComposer } from '@nitrots/nitro-renderer';
import { AchievementData, AchievementEvent, AchievementsEvent, AchievementsScoreEvent, ILinkEventTracker, RequestAchievementsMessageComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { GetConfiguration, LocalizeText, SendMessageComposer } from '../../api';
import { Base, Column, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardSubHeaderView, NitroCardView, Text } from '../../common';
import { AchievementsUIEvent, AchievementsUIUnseenCountEvent } from '../../events';
import { BatchUpdates, DispatchUiEvent, UseMessageEventHook, UseUiEvent } from '../../hooks';
import { AchievementCategory } from './common/AchievementCategory';
import { AchievementUtilities } from './common/AchievementUtilities';
import { AchievementCategory, AddEventLinkTracker, GetAchievementIsIgnored, LocalizeText, RemoveLinkEventTracker, SendMessageComposer } from '../../api';
import { Base, Column, LayoutProgressBar, NitroCardContentView, NitroCardHeaderView, NitroCardSubHeaderView, NitroCardView, Text } from '../../common';
import { AchievementsUIUnseenCountEvent } from '../../events';
import { BatchUpdates, DispatchUiEvent, UseMessageEventHook } from '../../hooks';
import { AchievementCategoryView } from './views/AchievementCategoryView';
import { AchievementsCategoryListView } from './views/category-list/AchievementsCategoryListView';
import { AchievementCategoryView } from './views/category/AchievementCategoryView';
export const AchievementsView: FC<{}> = props =>
{
@ -17,72 +15,56 @@ export const AchievementsView: FC<{}> = props =>
const [ selectedCategoryCode, setSelectedCategoryCode ] = useState<string>(null);
const [ achievementScore, setAchievementScore ] = useState(0);
const onAchievementsUIEvent = useCallback((event: AchievementsUIEvent) =>
{
switch(event.type)
{
case AchievementsUIEvent.SHOW_ACHIEVEMENTS:
setIsVisible(true);
return;
case AchievementsUIEvent.HIDE_ACHIEVEMENTS:
setIsVisible(false);
return;
case AchievementsUIEvent.TOGGLE_ACHIEVEMENTS:
setIsVisible(value => !value);
return;
}
}, []);
UseUiEvent(AchievementsUIEvent.SHOW_ACHIEVEMENTS, onAchievementsUIEvent);
UseUiEvent(AchievementsUIEvent.HIDE_ACHIEVEMENTS, onAchievementsUIEvent);
UseUiEvent(AchievementsUIEvent.TOGGLE_ACHIEVEMENTS, onAchievementsUIEvent);
const onAchievementEvent = useCallback((event: AchievementEvent) =>
{
const parser = event.getParser();
const achievement = parser.achievement;
const newCategories = [ ...achievementCategories ];
const categoryName = achievement.category;
const categoryIndex = newCategories.findIndex(existing => (existing.code === categoryName));
if(categoryIndex === -1)
{
const category = new AchievementCategory(categoryName);
category.achievements.push(achievement);
newCategories.push(category);
}
else
{
const category = newCategories[categoryIndex];
const newAchievements = [ ...category.achievements ];
const achievementIndex = newAchievements.findIndex(existing => (existing.achievementId === achievement.achievementId));
let previousAchievement: AchievementData = null;
if(achievementIndex === -1)
setAchievementCategories(prevValue =>
{
newAchievements.push(achievement);
}
else
{
previousAchievement = newAchievements[achievementIndex];
const newValue = [ ...prevValue ];
const categoryIndex = newValue.findIndex(existing => (existing.code === categoryName));
newAchievements[achievementIndex] = achievement;
}
if(categoryIndex === -1)
{
const category = new AchievementCategory(categoryName);
if(!AchievementUtilities.isIgnoredAchievement(achievement))
{
achievement.unseen++;
category.achievements.push(achievement);
if(previousAchievement) achievement.unseen += previousAchievement.unseen;
}
newValue.push(category);
}
else
{
const category = newValue[categoryIndex];
const newAchievements = [ ...category.achievements ];
const achievementIndex = newAchievements.findIndex(existing => (existing.achievementId === achievement.achievementId));
let previousAchievement: AchievementData = null;
category.achievements = newAchievements;
}
if(achievementIndex === -1)
{
newAchievements.push(achievement);
}
else
{
previousAchievement = newAchievements[achievementIndex];
setAchievementCategories(newCategories);
}, [ achievementCategories ]);
newAchievements[achievementIndex] = achievement;
}
if(!GetAchievementIsIgnored(achievement))
{
achievement.unseen++;
if(previousAchievement) achievement.unseen += previousAchievement.unseen;
}
category.achievements = newAchievements;
}
return newValue;
});
}, []);
UseMessageEventHook(AchievementEvent, onAchievementEvent);
@ -95,6 +77,7 @@ export const AchievementsView: FC<{}> = props =>
for(const achievement of parser.achievements)
{
const categoryName = achievement.category;
let existing = categories.find(category => (category.code === categoryName));
if(!existing)
@ -167,33 +150,59 @@ export const AchievementsView: FC<{}> = props =>
return achievementCategories.find(existing => (existing.code === selectedCategoryCode));
}, [ achievementCategories, selectedCategoryCode ]);
const getCategoryIcon = useMemo(() =>
{
if(!getSelectedCategory) return null;
const imageUrl = GetConfiguration<string>('achievements.images.url');
return imageUrl.replace('%image%', `achicon_${ getSelectedCategory.code }`);
}, [ getSelectedCategory ]);
const setAchievementSeen = useCallback((code: string, achievementId: number) =>
{
const newCategories = [ ...achievementCategories ];
for(const category of newCategories)
{
if(category.code !== code) continue;
for(const achievement of category.achievements)
setAchievementCategories(prevValue =>
{
if(achievement.achievementId !== achievementId) continue;
const newValue = [ ...prevValue ];
achievement.unseen = 0;
}
for(const category of newValue)
{
if(category.code !== code) continue;
for(const achievement of category.achievements)
{
if(achievement.achievementId !== achievementId) continue;
achievement.unseen = 0;
}
}
return newValue;
});
}, []);
const linkReceived = useCallback((url: string) =>
{
const parts = url.split('/');
if(parts.length < 2) return;
switch(parts[1])
{
case 'show':
setIsVisible(true);
return;
case 'hide':
setIsVisible(false);
return;
case 'toggle':
setIsVisible(prevValue => !prevValue);
return;
}
}, []);
setAchievementCategories(newCategories);
}, [ achievementCategories ]);
useEffect(() =>
{
const linkTracker: ILinkEventTracker = {
linkReceived,
eventUrlPrefix: 'achievements/'
};
AddEventLinkTracker(linkTracker);
return () => RemoveLinkEventTracker(linkTracker);
}, [ linkReceived ]);
useEffect(() =>
{
@ -220,15 +229,12 @@ export const AchievementsView: FC<{}> = props =>
<Text>{ LocalizeText('achievements.details.categoryprogress', [ 'progress', 'limit' ], [ getSelectedCategory.getProgress().toString(), getSelectedCategory.getMaxProgress().toString() ]) }</Text>
</Column>
</NitroCardSubHeaderView> }
<NitroCardContentView>
<NitroCardContentView gap={ 1 }>
{ !getSelectedCategory &&
<>
<AchievementsCategoryListView categories={ achievementCategories } selectedCategoryCode={ selectedCategoryCode } setSelectedCategoryCode={ setSelectedCategoryCode } />
<Column grow justifyContent="end">
<Base className="progress" position="relative">
<Flex fit center position="absolute" className="text-black">{ LocalizeText('achievements.categories.totalprogress', [ 'progress', 'limit' ], [ getProgress.toString(), getMaxProgress.toString() ]) }</Flex>
<Base className="progress-bar bg-success" style={ { width: (scaledProgressPercent + '%') }} />
</Base>
<LayoutProgressBar text={ LocalizeText('achievements.categories.totalprogress', [ 'progress', 'limit' ], [ getProgress.toString(), getMaxProgress.toString() ]) } progress={ getProgress } maxProgress={ getMaxProgress } />
<Text className="bg-muted rounded p-1" center>{ LocalizeText('achievements.categories.score', [ 'score' ], [ achievementScore.toString() ]) }</Text>
</Column>
</> }

View File

@ -1,38 +0,0 @@
import { AchievementData } from '@nitrots/nitro-renderer';
import { GetConfiguration, GetLocalization } from '../../../api';
export class AchievementUtilities
{
public static hasStarted(achievement: AchievementData): boolean
{
if(!achievement) return false;
if(achievement.finalLevel || ((achievement.level - 1) > 0)) return true;
return false;
}
public static getBadgeCode(achievement: AchievementData): string
{
if(!achievement) return null;
let badgeId = achievement.badgeId;
if(!achievement.finalLevel) badgeId = GetLocalization().getPreviousLevelBadgeId(badgeId);
return badgeId;
}
public static isIgnoredAchievement(achievement: AchievementData): boolean
{
if(!achievement) return false;
const ignored = GetConfiguration<string[]>('achievements.unseen.ignored');
const value = achievement.badgeId.replace(/[0-9]/g, '');
const index = ignored.indexOf(value);
if(index >= 0) return true;
return false;
}
}

View File

@ -1,8 +0,0 @@
import { AchievementData } from '@nitrots/nitro-renderer';
export const GetScaledProgressPercent = (achievement: AchievementData) =>
{
if(!achievement) return 0;
return ~~(((((achievement.currentPoints + achievement.scoreAtStartOfLevel) - 0) * (100 - 0)) / ((achievement.scoreLimit + achievement.scoreAtStartOfLevel) - 0)) + 0);
}

View File

@ -0,0 +1,19 @@
import { AchievementData } from '@nitrots/nitro-renderer';
import { FC } from 'react';
import { GetAchievementBadgeCode, GetAchievementHasStarted } from '../../../api';
import { BaseProps, LayoutBadgeImageView } from '../../../common';
interface AchievementBadgeViewProps extends BaseProps<HTMLDivElement>
{
achievement: AchievementData;
scale?: number;
}
export const AchievementBadgeView: FC<AchievementBadgeViewProps> = props =>
{
const { achievement = null, scale = 1, ...rest } = props;
if(!achievement) return null;
return <LayoutBadgeImageView badgeCode={ GetAchievementBadgeCode(achievement) } isGrayscale={ !GetAchievementHasStarted(achievement) } scale={ scale } { ...rest } />;
}

View File

@ -1,10 +1,10 @@
import { FC, useEffect, useMemo, useState } from 'react';
import { Column } from '../../../../common/Column';
import { AchievementCategory } from '../../common/AchievementCategory';
import { AchievementDetailsView } from '../achievement-details/AchievementDetailsView';
import { AchievementListView } from '../achievement-list/AchievementListView';
import { AchievementCategory } from '../../../api';
import { Column } from '../../../common';
import { AchievementListView } from './achievement-list/AchievementListView';
import { AchievementDetailsView } from './AchievementDetailsView';
export class AchievementCategoryViewProps
interface AchievementCategoryViewProps
{
category: AchievementCategory;
setAchievementSeen: (code: string, achievementId: number) => void;

View File

@ -1,13 +1,11 @@
import { AchievementData } from '@nitrots/nitro-renderer';
import { FC } from 'react';
import { LocalizeBadgeDescription, LocalizeBadgeName, LocalizeText } from '../../../../api';
import { Base, Column, Flex, LayoutCurrencyIcon, Text } from '../../../../common';
import { AchievementUtilities } from '../../common/AchievementUtilities';
import { GetAchievementLevel } from '../../common/GetAchievementLevel';
import { GetScaledProgressPercent } from '../../common/GetScaledProgressPercent';
import { AchievementBadgeView } from '../achievement-badge/AchievementBadgeView';
import { GetAchievementBadgeCode, LocalizeBadgeDescription, LocalizeBadgeName, LocalizeText } from '../../../api';
import { GetAchievementLevel } from '../../../api/achievements/GetAchievementLevel';
import { Column, Flex, LayoutCurrencyIcon, LayoutProgressBar, Text } from '../../../common';
import { AchievementBadgeView } from './AchievementBadgeView';
export interface AchievementDetailsViewProps
interface AchievementDetailsViewProps
{
achievement: AchievementData;
}
@ -19,7 +17,6 @@ export const AchievementDetailsView: FC<AchievementDetailsViewProps> = props =>
if(!achievement) return null;
const achievementLevel = GetAchievementLevel(achievement);
const scaledProgressPercent = GetScaledProgressPercent(achievement);
return (
<Flex shrink className="bg-muted rounded p-2 text-black" gap={ 2 } overflow="hidden">
@ -32,10 +29,10 @@ export const AchievementDetailsView: FC<AchievementDetailsViewProps> = props =>
<Column fullWidth justifyContent="center" overflow="hidden">
<Column gap={ 1 }>
<Text fontWeight="bold" truncate>
{ LocalizeBadgeName(AchievementUtilities.getBadgeCode(achievement)) }
{ LocalizeBadgeName(GetAchievementBadgeCode(achievement)) }
</Text>
<Text truncate>
{ LocalizeBadgeDescription(AchievementUtilities.getBadgeCode(achievement)) }
<Text textBreak>
{ LocalizeBadgeDescription(GetAchievementBadgeCode(achievement)) }
</Text>
</Column>
{ ((achievement.levelRewardPoints > 0) || (achievement.scoreLimit > 0)) &&
@ -51,10 +48,7 @@ export const AchievementDetailsView: FC<AchievementDetailsViewProps> = props =>
</Flex>
</Flex> }
{ (achievement.scoreLimit > 0) &&
<Base className="progress" position="relative">
<Flex fit center position="absolute" className="text-black"> { LocalizeText('achievements.details.progress', [ 'progress', 'limit' ], [ (achievement.currentPoints + achievement.scoreAtStartOfLevel).toString(), (achievement.scoreLimit + achievement.scoreAtStartOfLevel).toString() ]) }</Flex>
<Base className="progress-bar" style={ { width: (scaledProgressPercent + '%') }} />
</Base> }
<LayoutProgressBar text={ LocalizeText('achievements.details.progress', [ 'progress', 'limit' ], [ (achievement.currentPoints + achievement.scoreAtStartOfLevel).toString(), (achievement.scoreLimit + achievement.scoreAtStartOfLevel).toString() ]) } progress={ (achievement.currentPoints + achievement.scoreAtStartOfLevel) } maxProgress={ (achievement.scoreLimit + achievement.scoreAtStartOfLevel) } /> }
</Column> }
</Column>
</Flex>

View File

@ -1,21 +0,0 @@
import { AchievementData } from '@nitrots/nitro-renderer';
import { FC } from 'react';
import { BaseProps, LayoutBadgeImageView } from '../../../../common';
import { AchievementUtilities } from '../../common/AchievementUtilities';
export interface AchievementBadgeViewProps extends BaseProps<HTMLDivElement>
{
achievement: AchievementData;
scale?: number;
}
export const AchievementBadgeView: FC<AchievementBadgeViewProps> = props =>
{
const { achievement = null, scale = 1, ...rest } = props;
if(!achievement) return null;
return (
<LayoutBadgeImageView badgeCode={ AchievementUtilities.getBadgeCode(achievement) } isGrayscale={ !AchievementUtilities.hasStarted(achievement) } scale={ scale } { ...rest } />
);
}

View File

@ -1,9 +1,9 @@
import { AchievementData } from '@nitrots/nitro-renderer';
import { FC } from 'react';
import { LayoutGridItem, LayoutGridItemProps } from '../../../../common/layout/LayoutGridItem';
import { AchievementBadgeView } from '../achievement-badge/AchievementBadgeView';
import { LayoutGridItem, LayoutGridItemProps } from '../../../../common';
import { AchievementBadgeView } from '../AchievementBadgeView';
export interface AchievementListItemViewProps extends LayoutGridItemProps
interface AchievementListItemViewProps extends LayoutGridItemProps
{
achievement: AchievementData;
}

View File

@ -1,6 +1,6 @@
import { AchievementData } from '@nitrots/nitro-renderer';
import { Dispatch, FC, SetStateAction } from 'react';
import { AutoGrid } from '../../../../common/AutoGrid';
import { AutoGrid } from '../../../../common';
import { AchievementListItemView } from './AchievementListItemView';
export interface AchievementListViewProps

View File

@ -1,9 +1,6 @@
import { FC, useCallback, useMemo } from 'react';
import { GetConfiguration, LocalizeText } from '../../../../api';
import { LayoutGridItem, LayoutGridItemProps } from '../../../../common/layout/LayoutGridItem';
import { LayoutImage } from '../../../../common/layout/LayoutImage';
import { Text } from '../../../../common/Text';
import { AchievementCategory } from '../../common/AchievementCategory';
import { AchievementCategory, GetConfiguration, LocalizeText } from '../../../../api';
import { LayoutGridItem, LayoutGridItemProps, LayoutImage, Text } from '../../../../common';
export interface AchievementCategoryListItemViewProps extends LayoutGridItemProps
{

View File

@ -1,6 +1,6 @@
import { Dispatch, FC, SetStateAction } from 'react';
import { AutoGrid } from '../../../../common/AutoGrid';
import { AchievementCategory } from '../../common/AchievementCategory';
import { AchievementCategory } from '../../../../api';
import { AutoGrid } from '../../../../common';
import { AchievementsCategoryListItemView } from './AchievementsCategoryListItemView';
export interface AchievementsCategoryListViewProps

View File

@ -48,6 +48,8 @@ export const InventoryMessageHandler: FC<{}> = props =>
type: InventoryFurnitureActions.PROCESS_FRAGMENT,
payload: { fragment, unseenTracker }
});
furniMsgFragments = null;
}, [ unseenTracker, dispatchFurnitureState ]);
const onFurnitureListInvalidateEvent = useCallback((event: FurnitureListInvalidateEvent) =>

View File

@ -1,4 +1,4 @@
import { NavigatorCategoryDataParser, NavigatorSearchResultSet, NavigatorTopLevelContext } from '@nitrots/nitro-renderer';
import { NavigatorCategoryDataParser, NavigatorSearchResultSet, NavigatorTopLevelContext, RoomDataParser } from '@nitrots/nitro-renderer';
import { createContext, Dispatch, FC, ProviderProps, SetStateAction, useContext } from 'react';
import { NavigatorData } from './common/NavigatorData';
@ -12,6 +12,8 @@ interface INavigatorContext
setTopLevelContexts: Dispatch<SetStateAction<NavigatorTopLevelContext[]>>;
navigatorData: NavigatorData;
setNavigatorData: Dispatch<SetStateAction<NavigatorData>>;
doorData: { roomInfo: RoomDataParser, state: number };
setDoorData: Dispatch<SetStateAction<{ roomInfo: RoomDataParser, state: number }>>;
searchResult: NavigatorSearchResultSet;
setSearchResult: Dispatch<SetStateAction<NavigatorSearchResultSet>>;
}
@ -25,6 +27,8 @@ const NavigatorContext = createContext<INavigatorContext>({
setTopLevelContexts: null,
navigatorData: null,
setNavigatorData: null,
doorData: null,
setDoorData: null,
searchResult: null,
setSearchResult: null
});

View File

@ -1,13 +1,13 @@
import { CanCreateRoomEventEvent, CantConnectMessageParser, FollowFriendMessageComposer, GenericErrorEvent, GetGuestRoomResultEvent, HabboWebTools, LegacyExternalInterface, NavigatorCategoriesComposer, NavigatorCategoriesEvent, NavigatorHomeRoomEvent, NavigatorMetadataEvent, NavigatorOpenRoomCreatorEvent, NavigatorSearchEvent, NavigatorSettingsComposer, RoomCreatedEvent, RoomDataParser, RoomDoorbellAcceptedEvent, RoomDoorbellEvent, RoomDoorbellRejectedEvent, RoomEnterErrorEvent, RoomEntryInfoMessageEvent, RoomForwardEvent, RoomInfoComposer, RoomScoreEvent, RoomSettingsUpdatedEvent, SecurityLevel, UserInfoEvent, UserPermissionsEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react';
import { CreateLinkEvent, CreateRoomSession, GetConfiguration, GetSessionDataManager, LocalizeText, NotificationAlertType, NotificationUtilities, SendMessageComposer, TryVisitRoom, VisitDesktop } from '../../api';
import { UpdateDoorStateEvent } from '../../events';
import { BatchUpdates, DispatchUiEvent, UseMessageEventHook } from '../../hooks';
import { BatchUpdates, UseMessageEventHook } from '../../hooks';
import { DoorStateType } from './common/DoorStateType';
import { useNavigatorContext } from './NavigatorContext';
export const NavigatorMessageHandler: FC<{}> = props =>
{
const { setCategories = null, setTopLevelContext = null, topLevelContexts = null, setTopLevelContexts = null, setNavigatorData = null, setSearchResult = null } = useNavigatorContext();
const { setCategories = null, setTopLevelContext = null, topLevelContexts = null, setTopLevelContexts = null, setNavigatorData = null, setDoorData = null, setSearchResult = null } = useNavigatorContext();
const onRoomSettingsUpdatedEvent = useCallback((event: RoomSettingsUpdatedEvent) =>
{
@ -92,6 +92,8 @@ export const NavigatorMessageHandler: FC<{}> = props =>
if(parser.roomEnter)
{
setDoorData({ roomInfo: null, state: DoorStateType.NONE });
setNavigatorData(prevValue =>
{
const newValue = { ...prevValue };
@ -123,10 +125,26 @@ export const NavigatorMessageHandler: FC<{}> = props =>
switch(parser.data.doorMode)
{
case RoomDataParser.DOORBELL_STATE:
DispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.START_DOORBELL, parser.data));
setDoorData(prevValue =>
{
const newValue = { ...prevValue };
newValue.roomInfo = parser.data;
newValue.state = DoorStateType.START_DOORBELL;
return newValue;
});
return;
case RoomDataParser.PASSWORD_STATE:
DispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.START_PASSWORD, parser.data));
setDoorData(prevValue =>
{
const newValue = { ...prevValue };
newValue.roomInfo = parser.data;
newValue.state = DoorStateType.START_PASSWORD;
return newValue;
});
return;
}
}
@ -147,7 +165,7 @@ export const NavigatorMessageHandler: FC<{}> = props =>
return newValue;
});
}
}, [ setNavigatorData ]);
}, [ setNavigatorData, setDoorData ]);
const onRoomScoreEvent = useCallback((event: RoomScoreEvent) =>
{
@ -170,9 +188,16 @@ export const NavigatorMessageHandler: FC<{}> = props =>
if(!parser.userName || (parser.userName.length === 0))
{
DispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.STATE_WAITING));
setDoorData(prevValue =>
{
const newValue = { ...prevValue };
newValue.state = DoorStateType.STATE_WAITING;
return newValue;
});
}
}, []);
}, [ setDoorData ]);
const onRoomDoorbellAcceptedEvent = useCallback((event: RoomDoorbellAcceptedEvent) =>
{
@ -180,9 +205,16 @@ export const NavigatorMessageHandler: FC<{}> = props =>
if(!parser.userName || (parser.userName.length === 0))
{
DispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.STATE_ACCEPTED));
setDoorData(prevValue =>
{
const newValue = { ...prevValue };
newValue.state = DoorStateType.STATE_ACCEPTED;
return newValue;
});
}
}, []);
}, [ setDoorData ]);
const onRoomDoorbellRejectedEvent = useCallback((event: RoomDoorbellRejectedEvent) =>
{
@ -190,9 +222,16 @@ export const NavigatorMessageHandler: FC<{}> = props =>
if(!parser.userName || (parser.userName.length === 0))
{
DispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.STATE_NO_ANSWER));
setDoorData(prevValue =>
{
const newValue = { ...prevValue };
newValue.state = DoorStateType.STATE_NO_ANSWER;
return newValue;
});
}
}, []);
}, [ setDoorData ]);
const onGenericErrorEvent = useCallback((event: GenericErrorEvent) =>
{
@ -201,10 +240,33 @@ export const NavigatorMessageHandler: FC<{}> = props =>
switch(parser.errorCode)
{
case -100002:
DispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.STATE_WRONG_PASSWORD));
break;
setDoorData(prevValue =>
{
const newValue = { ...prevValue };
newValue.state = DoorStateType.STATE_WRONG_PASSWORD;
return newValue;
});
return;
case 4009:
NotificationUtilities.simpleAlert(LocalizeText('navigator.alert.need.to.be.vip'), NotificationAlertType.DEFAULT, null, null, LocalizeText('generic.alert.title'));
return;
case 4010:
NotificationUtilities.simpleAlert(LocalizeText('navigator.alert.invalid_room_name'), NotificationAlertType.DEFAULT, null, null, LocalizeText('generic.alert.title'));
return;
case 4011:
NotificationUtilities.simpleAlert(LocalizeText('navigator.alert.cannot_perm_ban'), NotificationAlertType.DEFAULT, null, null, LocalizeText('generic.alert.title'));
return;
case 4013:
NotificationUtilities.simpleAlert(LocalizeText('navigator.alert.room_in_maintenance'), NotificationAlertType.DEFAULT, null, null, LocalizeText('generic.alert.title'));
return;
}
}, []);
}, [ setDoorData ]);
const onNavigatorMetadataEvent = useCallback((event: NavigatorMetadataEvent) =>
{
@ -296,7 +358,7 @@ export const NavigatorMessageHandler: FC<{}> = props =>
let forwardType = -1;
let forwardId = -1;
if(GetConfiguration<string>('friend.id') !== undefined)
if((GetConfiguration<string>('friend.id') !== undefined) && (parseInt(GetConfiguration<string>('friend.id')) > 0))
{
forwardType = 0;
SendMessageComposer(new FollowFriendMessageComposer(parseInt(GetConfiguration<string>('friend.id'))));
@ -316,16 +378,13 @@ export const NavigatorMessageHandler: FC<{}> = props =>
else if((forwardType === -1) && (parser.roomIdToEnter > 0))
{
CreateLinkEvent('navigator/close');
CreateRoomSession(parser.roomIdToEnter);
if(parser.roomIdToEnter !== parser.homeRoomId)
{
CreateLinkEvent('navigator/close');
CreateRoomSession(parser.roomIdToEnter);
}
else
{
CreateLinkEvent('navigator/close');
CreateRoomSession(parser.homeRoomId);
}
}

View File

@ -1,18 +1,17 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ConvertGlobalRoomIdMessageComposer, HabboWebTools, ILinkEventTracker, LegacyExternalInterface, NavigatorCategoryDataParser, NavigatorInitComposer, NavigatorSearchComposer, NavigatorSearchResultSet, NavigatorTopLevelContext, RoomDataParser, RoomSessionEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { AddEventLinkTracker, GoToDesktop, LocalizeText, RemoveLinkEventTracker, SendMessageComposer, TryVisitRoom } from '../../api';
import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { AddEventLinkTracker, LocalizeText, RemoveLinkEventTracker, SendMessageComposer, TryVisitRoom } from '../../api';
import { Base, Column, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common';
import { UpdateDoorStateEvent } from '../../events';
import { BatchUpdates, UseRoomSessionManagerEvent, useSharedState, UseUiEvent } from '../../hooks';
import { BatchUpdates, UseRoomSessionManagerEvent, useSharedState } from '../../hooks';
import { DoorStateType } from './common/DoorStateType';
import { NavigatorData } from './common/NavigatorData';
import { NavigatorContextProvider } from './NavigatorContext';
import { NavigatorMessageHandler } from './NavigatorMessageHandler';
import { NavigatorDoorStateView } from './views/NavigatorDoorStateView';
import { NavigatorRoomCreatorView } from './views/NavigatorRoomCreatorView';
import { NavigatorRoomDoorbellView } from './views/NavigatorRoomDoorbellView';
import { NavigatorRoomInfoView } from './views/NavigatorRoomInfoView';
import { NavigatorRoomLinkView } from './views/NavigatorRoomLinkView';
import { NavigatorRoomPasswordView } from './views/NavigatorRoomPasswordView';
import { NavigatorRoomSettingsView } from './views/room-settings/NavigatorRoomSettingsView';
import { NavigatorSearchResultView } from './views/search/NavigatorSearchResultView';
import { NavigatorSearchView } from './views/search/NavigatorSearchView';
@ -44,53 +43,10 @@ export const NavigatorView: FC<{}> = props =>
currentRoomRating: 0,
canRate: true
});
const [ doorData, setDoorData ] = useState<{ roomInfo: RoomDataParser, state: number }>({ roomInfo: null, state: DoorStateType.NONE });
const [ searchResult, setSearchResult ] = useState<NavigatorSearchResultSet>(null);
const [ pendingDoorState, setPendingDoorState ] = useState<{ roomData: RoomDataParser, state: string }>(null);
const pendingSearch = useRef<{ value: string, code: string }>(null);
const onUpdateDoorStateEvent = useCallback((event: UpdateDoorStateEvent) =>
{
switch(event.type)
{
case UpdateDoorStateEvent.START_DOORBELL:
setPendingDoorState({ roomData: event.roomData, state: event.type });
return;
case UpdateDoorStateEvent.START_PASSWORD:
setPendingDoorState({ roomData: event.roomData, state: event.type });
return;
case UpdateDoorStateEvent.STATE_WAITING:
setPendingDoorState(prevValue =>
{
return { roomData: prevValue.roomData, state: event.type }
});
return;
case UpdateDoorStateEvent.STATE_NO_ANSWER:
setPendingDoorState(prevValue =>
{
if(prevValue.state === UpdateDoorStateEvent.STATE_WAITING) GoToDesktop();
return { roomData: prevValue.roomData, state: event.type }
});
return;
case UpdateDoorStateEvent.STATE_WRONG_PASSWORD:
setPendingDoorState(prevValue =>
{
return { roomData: prevValue.roomData, state: event.type }
});
return;
case UpdateDoorStateEvent.STATE_ACCEPTED:
setPendingDoorState(null);
return;
}
}, []);
UseUiEvent(UpdateDoorStateEvent.START_DOORBELL, onUpdateDoorStateEvent);
UseUiEvent(UpdateDoorStateEvent.START_PASSWORD, onUpdateDoorStateEvent);
UseUiEvent(UpdateDoorStateEvent.STATE_WAITING, onUpdateDoorStateEvent);
UseUiEvent(UpdateDoorStateEvent.STATE_NO_ANSWER, onUpdateDoorStateEvent);
UseUiEvent(UpdateDoorStateEvent.STATE_WRONG_PASSWORD, onUpdateDoorStateEvent);
UseUiEvent(UpdateDoorStateEvent.STATE_ACCEPTED, onUpdateDoorStateEvent);
const onRoomSessionEvent = useCallback((event: RoomSessionEvent) =>
{
switch(event.type)
@ -107,18 +63,6 @@ export const NavigatorView: FC<{}> = props =>
UseRoomSessionManagerEvent(RoomSessionEvent.CREATED, onRoomSessionEvent);
const closePendingDoorState = useCallback((state: string) =>
{
if(state !== null)
{
setPendingDoorState(prevValue =>
{
return { ...prevValue, state };
});
}
else setPendingDoorState(null);
}, []);
const sendSearch = useCallback((searchValue: string, contextCode: string) =>
{
setCreatorOpen(false);
@ -292,28 +236,9 @@ export const NavigatorView: FC<{}> = props =>
LegacyExternalInterface.addCallback(HabboWebTools.OPENROOM, (k: string, _arg_2: boolean = false, _arg_3: string = null) => SendMessageComposer(new ConvertGlobalRoomIdMessageComposer(k)));
}, []);
const getRoomDoorState = useMemo(() =>
{
if(!pendingDoorState) return null;
switch(pendingDoorState.state)
{
case UpdateDoorStateEvent.START_DOORBELL:
case UpdateDoorStateEvent.STATE_WAITING:
case UpdateDoorStateEvent.STATE_NO_ANSWER:
return <NavigatorRoomDoorbellView roomData={ pendingDoorState.roomData } state={ pendingDoorState.state } onClose={ closePendingDoorState } />;
case UpdateDoorStateEvent.START_PASSWORD:
case UpdateDoorStateEvent.STATE_WRONG_PASSWORD:
return <NavigatorRoomPasswordView roomData={ pendingDoorState.roomData } state={ pendingDoorState.state } onClose={ closePendingDoorState } />;
}
return null;
}, [ pendingDoorState, closePendingDoorState ]);
return (
<NavigatorContextProvider value={ { categories, setCategories, topLevelContext, setTopLevelContext, topLevelContexts, setTopLevelContexts, navigatorData, setNavigatorData, searchResult, setSearchResult } }>
<NavigatorContextProvider value={ { categories, setCategories, topLevelContext, setTopLevelContext, topLevelContexts, setTopLevelContexts, navigatorData, setNavigatorData, doorData, setDoorData, searchResult, setSearchResult } }>
<NavigatorMessageHandler />
{ getRoomDoorState }
{ isVisible &&
<NitroCardView uniqueKey="navigator" className="nitro-navigator">
<NitroCardHeaderView headerText={ LocalizeText(isCreatorOpen ? 'navigator.createroom.title' : 'navigator.title') } onCloseClick={ event => setIsVisible(false) } />
@ -343,6 +268,7 @@ export const NavigatorView: FC<{}> = props =>
{ isCreatorOpen && <NavigatorRoomCreatorView /> }
</NitroCardContentView>
</NitroCardView> }
<NavigatorDoorStateView />
{ isRoomInfoOpen && <NavigatorRoomInfoView onCloseClick={ () => setRoomInfoOpen(false) } /> }
{ isRoomLinkOpen && <NavigatorRoomLinkView onCloseClick={ () => setRoomLinkOpen(false) } /> }
<NavigatorRoomSettingsView />

View File

@ -0,0 +1,12 @@
export class DoorStateType
{
public static NONE: number = 0;
public static START_DOORBELL: number = 1;
public static START_PASSWORD: number = 2;
public static STATE_PENDING_SERVER: number = 3;
public static UPDATE_STATE: number = 4;
public static STATE_WAITING: number = 5;
public static STATE_NO_ANSWER: number = 6;
public static STATE_WRONG_PASSWORD: number = 7;
public static STATE_ACCEPTED: number = 8;
}

View File

@ -0,0 +1,111 @@
import { FC, useEffect, useState } from 'react';
import { CreateRoomSession, GoToDesktop, LocalizeText } from '../../../api';
import { Button, Column, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../common';
import { DoorStateType } from '../common/DoorStateType';
import { useNavigatorContext } from '../NavigatorContext';
const VISIBLE_STATES = [ DoorStateType.START_DOORBELL, DoorStateType.STATE_WAITING, DoorStateType.STATE_NO_ANSWER, DoorStateType.START_PASSWORD, DoorStateType.STATE_WRONG_PASSWORD ];
const DOORBELL_STATES = [ DoorStateType.START_DOORBELL, DoorStateType.STATE_WAITING, DoorStateType.STATE_NO_ANSWER ];
const PASSWORD_STATES = [ DoorStateType.START_PASSWORD, DoorStateType.STATE_WRONG_PASSWORD ];
export const NavigatorDoorStateView: FC<{}> = props =>
{
const [ password, setPassword ] = useState('');
const { doorData = null, setDoorData = null } = useNavigatorContext();
const close = () =>
{
if(doorData && (doorData.state === DoorStateType.STATE_WAITING)) GoToDesktop();
setDoorData(null);
}
const ring = () =>
{
if(!doorData || !doorData.roomInfo) return;
CreateRoomSession(doorData.roomInfo.roomId);
setDoorData(prevValue =>
{
const newValue = { ...prevValue };
newValue.state = DoorStateType.STATE_PENDING_SERVER;
return newValue;
});
}
const tryEntering = () =>
{
if(!doorData || !doorData.roomInfo) return;
CreateRoomSession(doorData.roomInfo.roomId, password);
setDoorData(prevValue =>
{
const newValue = { ...prevValue };
newValue.state = DoorStateType.STATE_PENDING_SERVER;
return newValue;
});
}
useEffect(() =>
{
if(!doorData || (doorData.state !== DoorStateType.STATE_NO_ANSWER)) return;
GoToDesktop();
}, [ doorData ]);
if(!doorData || (doorData.state === DoorStateType.NONE) || (VISIBLE_STATES.indexOf(doorData.state) === -1)) return null;
const isDoorbell = (DOORBELL_STATES.indexOf(doorData.state) >= 0);
return (
<NitroCardView className="nitro-navigator-doorbell" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText(isDoorbell ? 'navigator.doorbell.title' : 'navigator.password.title') } onCloseClick={ close } />
<NitroCardContentView>
<Column gap={ 1 }>
<Text bold>{ doorData && doorData.roomInfo && doorData.roomInfo.roomName }</Text>
{ (doorData.state === DoorStateType.START_DOORBELL) &&
<Text>{ LocalizeText('navigator.doorbell.info') }</Text> }
{ (doorData.state === DoorStateType.STATE_WAITING) &&
<Text>{ LocalizeText('navigator.doorbell.waiting') }</Text> }
{ (doorData.state === DoorStateType.STATE_NO_ANSWER) &&
<Text>{ LocalizeText('navigator.doorbell.no.answer') }</Text> }
{ (doorData.state === DoorStateType.START_PASSWORD) &&
<Text>{ LocalizeText('navigator.password.info') }</Text> }
{ (doorData.state === DoorStateType.STATE_WRONG_PASSWORD) &&
<Text>{ LocalizeText('navigator.password.retryinfo') }</Text> }
</Column>
{ isDoorbell &&
<Column gap={ 1 }>
{ (doorData.state === DoorStateType.START_DOORBELL) &&
<Button variant="success" onClick={ ring }>
{ LocalizeText('navigator.doorbell.button.ring') }
</Button> }
<Button variant="danger" onClick={ close }>
{ LocalizeText('generic.cancel') }
</Button>
</Column> }
{ !isDoorbell &&
<>
<Column gap={ 1 }>
<Text>{ LocalizeText('navigator.password.enter') }</Text>
<input type="password" className="form-control form-control-sm" onChange={ event => setPassword(event.target.value) } />
</Column>
<Column gap={ 1 }>
<Button variant="success" onClick={ tryEntering }>
{ LocalizeText('navigator.password.button.try') }
</Button>
<Button variant="danger" onClick={ close }>
{ LocalizeText('generic.cancel') }
</Button>
</Column>
</> }
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -1,63 +0,0 @@
import { RoomDataParser } from '@nitrots/nitro-renderer';
import { FC } from 'react';
import { CreateRoomSession, GoToDesktop, LocalizeText } from '../../../api';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../common';
import { Button } from '../../../common/Button';
import { Column } from '../../../common/Column';
import { Text } from '../../../common/Text';
import { UpdateDoorStateEvent } from '../../../events';
export interface NavigatorRoomDoorbellViewProps
{
roomData: RoomDataParser;
state: string;
onClose: (state: string) => void;
}
export const NavigatorRoomDoorbellView: FC<NavigatorRoomDoorbellViewProps> = props =>
{
const { roomData = null, state = null, onClose = null } = props;
const close = () =>
{
if(state === UpdateDoorStateEvent.STATE_WAITING) GoToDesktop();
onClose(null);
}
const ring = () =>
{
if(!roomData) return;
CreateRoomSession(roomData.roomId);
onClose(UpdateDoorStateEvent.STATE_PENDING_SERVER);
}
return (
<NitroCardView className="nitro-navigator-doorbell" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText('navigator.doorbell.title') } onCloseClick={ close } />
<NitroCardContentView>
<Column gap={ 1 }>
{ roomData &&
<Text bold>{ roomData.roomName }</Text> }
{ (state === UpdateDoorStateEvent.START_DOORBELL) &&
<Text>{ LocalizeText('navigator.doorbell.info') }</Text> }
{ (state === UpdateDoorStateEvent.STATE_WAITING) &&
<Text>{ LocalizeText('navigator.doorbell.waiting') }</Text> }
{ (state === UpdateDoorStateEvent.STATE_NO_ANSWER) &&
<Text>{ LocalizeText('navigator.doorbell.no.answer') }</Text> }
</Column>
<Column gap={ 1 }>
{ (state === UpdateDoorStateEvent.START_DOORBELL) &&
<Button variant="success" onClick={ ring }>
{ LocalizeText('navigator.doorbell.button.ring') }
</Button> }
<Button variant="danger" onClick={ close }>
{ LocalizeText('generic.cancel') }
</Button>
</Column>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -1,60 +0,0 @@
import { RoomDataParser } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { CreateRoomSession, LocalizeText } from '../../../api';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../common';
import { Button } from '../../../common/Button';
import { Column } from '../../../common/Column';
import { Text } from '../../../common/Text';
import { UpdateDoorStateEvent } from '../../../events';
export interface NavigatorRoomPasswordViewProps
{
roomData: RoomDataParser;
state: string;
onClose: (state: string) => void;
}
export const NavigatorRoomPasswordView: FC<NavigatorRoomPasswordViewProps> = props =>
{
const { roomData = null, state = null, onClose = null } = props;
const [ password, setPassword ] = useState('');
const close = () =>
{
onClose(null);
}
const tryEntering = useCallback(() =>
{
if(!roomData) return;
CreateRoomSession(roomData.roomId, password);
onClose(UpdateDoorStateEvent.STATE_PENDING_SERVER);
}, [ roomData, password, onClose ]);
return (
<NitroCardView className="nitro-navigator-password" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText('navigator.password.title') } onCloseClick={ close } />
<NitroCardContentView>
<Column gap={ 1 }>
{ roomData &&
<Text bold>{ roomData.roomName }</Text> }
{ (state === UpdateDoorStateEvent.START_PASSWORD) &&
<Text>{ LocalizeText('navigator.password.info') }</Text> }
{ (state === UpdateDoorStateEvent.STATE_WRONG_PASSWORD) &&
<Text>{ LocalizeText('navigator.password.retryinfo') }</Text> }
</Column>
<Column gap={ 1 }>
<Text>{ LocalizeText('navigator.password.enter') }</Text>
<input type="password" className="form-control form-control-sm" onChange={ event => setPassword(event.target.value) } />
</Column>
<Button variant="success" onClick={ tryEntering }>
{ LocalizeText('navigator.password.button.try') }</Button>
<Button variant="danger" onClick={ close }>
{ LocalizeText('generic.cancel') }
</Button>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -3,8 +3,8 @@ import { RoomDataParser } from '@nitrots/nitro-renderer';
import { FC, MouseEvent } from 'react';
import { CreateRoomSession, GetSessionDataManager, TryVisitRoom } from '../../../../api';
import { Column, Flex, LayoutBadgeImageView, LayoutGridItemProps, LayoutRoomThumbnailView, Text } from '../../../../common';
import { UpdateDoorStateEvent } from '../../../../events';
import { DispatchUiEvent } from '../../../../hooks';
import { DoorStateType } from '../../common/DoorStateType';
import { useNavigatorContext } from '../../NavigatorContext';
import { NavigatorSearchResultItemInfoView } from './NavigatorSearchResultItemInfoView';
export interface NavigatorSearchResultItemViewProps extends LayoutGridItemProps
@ -16,6 +16,7 @@ export interface NavigatorSearchResultItemViewProps extends LayoutGridItemProps
export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProps> = props =>
{
const { roomData = null, children = null, thumbnail = false, ...rest } = props;
const { setDoorData = null } = useNavigatorContext();
function getUserCounterColor(): string
{
@ -53,10 +54,26 @@ export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProp
switch(roomData.doorMode)
{
case RoomDataParser.DOORBELL_STATE:
DispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.START_DOORBELL, roomData));
setDoorData(prevValue =>
{
const newValue = { ...prevValue };
newValue.roomInfo = roomData;
newValue.state = DoorStateType.START_DOORBELL;
return newValue;
});
return;
case RoomDataParser.PASSWORD_STATE:
DispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.START_PASSWORD, roomData));
setDoorData(prevValue =>
{
const newValue = { ...prevValue };
newValue.roomInfo = roomData;
newValue.state = DoorStateType.START_PASSWORD;
return newValue;
});
return;
}
}

View File

@ -2,7 +2,7 @@ import { Dispose, DropBounce, EaseOut, FigureUpdateEvent, JumpBy, Motions, Nitro
import { FC, useCallback, useState } from 'react';
import { CreateLinkEvent, GetSessionDataManager, GetUserProfile, OpenMessengerChat, VisitDesktop } from '../../api';
import { Base, Flex, LayoutAvatarImageView, LayoutItemCountView, TransitionAnimation, TransitionAnimationTypes } from '../../common';
import { AchievementsUIEvent, AchievementsUIUnseenCountEvent, FriendsEvent, FriendsMessengerIconEvent, FriendsRequestCountEvent, GuideToolEvent, InventoryEvent, ModToolsEvent, UnseenItemTrackerUpdateEvent, UserSettingsUIEvent } from '../../events';
import { AchievementsUIUnseenCountEvent, FriendsEvent, FriendsMessengerIconEvent, FriendsRequestCountEvent, GuideToolEvent, InventoryEvent, ModToolsEvent, UnseenItemTrackerUpdateEvent, UserSettingsUIEvent } from '../../events';
import { BatchUpdates, DispatchUiEvent, UseMessageEventHook, UseRoomEngineEvent, UseUiEvent } from '../../hooks';
import { ToolbarViewItems } from './common/ToolbarViewItems';
import { ToolbarMeView } from './ToolbarMeView';
@ -157,7 +157,7 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
DispatchUiEvent(new ModToolsEvent(ModToolsEvent.TOGGLE_MOD_TOOLS));
return;
case ToolbarViewItems.ACHIEVEMENTS_ITEM:
DispatchUiEvent(new AchievementsUIEvent(AchievementsUIEvent.TOGGLE_ACHIEVEMENTS));
CreateLinkEvent('achievements/toggle');
setMeExpanded(false);
return;
case ToolbarViewItems.PROFILE_ITEM:

View File

@ -57,11 +57,22 @@ export const UserProfileView: FC<{}> = props =>
{
const parser = event.getParser();
let isSameProfile = false;
BatchUpdates(() =>
{
setUserProfile(parser);
setUserBadges([]);
setUserRelationships(null);
setUserProfile(prevValue =>
{
if(prevValue && prevValue.id) isSameProfile = (prevValue.id === parser.id);
return parser;
});
if(!isSameProfile)
{
setUserBadges([]);
setUserRelationships(null);
}
});
SendMessageComposer(new UserCurrentBadgesComposer(parser.id));

View File

@ -1,5 +1,6 @@
import { WiredFurniActionEvent, WiredFurniConditionEvent, WiredFurniTriggerEvent, WiredOpenEvent, WiredRewardResultMessageEvent, WiredSaveSuccessEvent, WiredValidationErrorEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react';
import { LocalizeText, NotificationAlertType, NotificationUtilities } from '../../api';
import { UseMessageEventHook } from '../../hooks/messages';
import { useWiredContext } from './context/WiredContext';
@ -51,6 +52,7 @@ export const WiredMessageHandler: FC<{}> = props =>
{
const parser = event.getParser();
NotificationUtilities.simpleAlert(parser.info, NotificationAlertType.DEFAULT, null, null, LocalizeText('error.title'));
console.log(parser);
}, []);

View File

@ -10,8 +10,8 @@ import { WiredMessageHandler } from './WiredMessageHandler';
export const WiredView: FC<{}> = props =>
{
const [ trigger, setTrigger ] = useState<Triggerable>(null);
const [ intParams, setIntParams ] = useState<number[]>(null);
const [ stringParam, setStringParam ] = useState<string>(null);
const [ intParams, setIntParams ] = useState<number[]>([]);
const [ stringParam, setStringParam ] = useState<string>('');
const [ furniIds, setFurniIds ] = useState<number[]>([]);
const [ actionDelay, setActionDelay ] = useState<number>(null);

View File

@ -12,13 +12,14 @@ export interface WiredBaseViewProps
{
wiredType: string;
requiresFurni: number;
hasSpecialInput: boolean;
save: () => void;
validate?: () => boolean;
}
export const WiredBaseView: FC<WiredBaseViewProps> = props =>
{
const { wiredType = '', requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_NONE, save = null, validate = null, children = null } = props;
const { wiredType = '', requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_NONE, save = null, validate = null, children = null, hasSpecialInput = false } = props;
const [ wiredName, setWiredName ] = useState<string>(null);
const [ wiredDescription, setWiredDescription ] = useState<string>(null);
const { trigger = null, setTrigger = null, setIntParams = null, setStringParam = null, setFurniIds = null } = useWiredContext();
@ -57,23 +58,30 @@ export const WiredBaseView: FC<WiredBaseViewProps> = props =>
setWiredDescription(furniData.description);
}
setIntParams(trigger.intData);
setStringParam(trigger.stringData);
setFurniIds(prevValue =>
{
if(prevValue && prevValue.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prevValue);
if(hasSpecialInput)
{
setIntParams(trigger.intData);
setStringParam(trigger.stringData);
}
if(trigger.selectedItems && trigger.selectedItems.length)
if(requiresFurni > WiredFurniType.STUFF_SELECTION_OPTION_NONE)
{
setFurniIds(prevValue =>
{
WiredSelectionVisualizer.applySelectionShaderToFurni(trigger.selectedItems);
if(prevValue && prevValue.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prevValue);
return trigger.selectedItems;
}
if(trigger.selectedItems && trigger.selectedItems.length)
{
WiredSelectionVisualizer.applySelectionShaderToFurni(trigger.selectedItems);
return [];
});
return trigger.selectedItems;
}
return [];
});
}
});
}, [ trigger, setIntParams, setStringParam, setFurniIds ]);
}, [trigger, setIntParams, setStringParam, setFurniIds, hasSpecialInput, requiresFurni]);
return (
<NitroCardView uniqueKey="nitro-wired" className="nitro-wired" theme="primary-slim">

View File

@ -11,13 +11,14 @@ import { WiredBaseView } from '../WiredBaseView';
export interface WiredActionBaseViewProps
{
hasSpecialInput: boolean;
requiresFurni: number;
save: () => void;
}
export const WiredActionBaseView: FC<WiredActionBaseViewProps> = props =>
{
const { requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_NONE, save = null, children = null } = props;
const { requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_NONE, save = null, hasSpecialInput = false, children = null } = props;
const [ delay, setDelay ] = useState(-1);
const { trigger = null, setActionDelay = null } = useWiredContext();
@ -34,7 +35,7 @@ export const WiredActionBaseView: FC<WiredActionBaseViewProps> = props =>
}, [ delay, save, setActionDelay ]);
return (
<WiredBaseView wiredType="action" requiresFurni={ requiresFurni } save={ onSave }>
<WiredBaseView wiredType="action" requiresFurni={ requiresFurni } save={ onSave } hasSpecialInput={ hasSpecialInput }>
{ children }
{ !!children && <hr className="m-0 bg-dark" /> }
<Column>

View File

@ -37,7 +37,7 @@ export const WiredActionBotChangeFigureView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<input type="text" className="form-control form-control-sm" maxLength={ 32 } value={ botName } onChange={ event => setBotName(event.target.value) } />

View File

@ -33,7 +33,7 @@ export const WiredActionBotFollowAvatarView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<label className="fw-bold">{ LocalizeText('wiredfurni.params.bot.name') }</label>
<input type="text" className="form-control form-control-sm" maxLength={ 32 } value={ botName } onChange={ event => setBotName(event.target.value) } />

View File

@ -34,7 +34,7 @@ export const WiredActionBotGiveHandItemView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<input type="text" className="form-control form-control-sm" maxLength={ 32 } value={ botName } onChange={ event => setBotName(event.target.value) } />

View File

@ -22,7 +22,7 @@ export const WiredActionBotMoveView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<input type="text" className="form-control form-control-sm" maxLength={ 32 } value={ botName } onChange={ event => setBotName(event.target.value) } />

View File

@ -39,7 +39,7 @@ export const WiredActionBotTalkToAvatarView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<input type="text" className="form-control form-control-sm" maxLength={ 32 } value={ botName } onChange={ event => setBotName(event.target.value) } />

View File

@ -39,7 +39,7 @@ export const WiredActionBotTalkView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<input type="text" className="form-control form-control-sm" maxLength={ 32 } value={ botName } onChange={ event => setBotName(event.target.value) } />

View File

@ -22,7 +22,7 @@ export const WiredActionBotTeleportView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<input type="text" className="form-control form-control-sm" maxLength={ 32 } value={ botName } onChange={ event => setBotName(event.target.value) } />

View File

@ -4,5 +4,5 @@ import { WiredActionBaseView } from './WiredActionBaseView';
export const WiredActionCallAnotherStackView: FC<{}> = props =>
{
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } save={ null } />;
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } hasSpecialInput={ false } save={ null } />;
}

View File

@ -4,5 +4,5 @@ import { WiredActionBaseView } from './WiredActionBaseView';
export const WiredActionChaseView: FC<{}> = props =>
{
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } save={ null } />;
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } hasSpecialInput={ false } save={ null } />;
}

View File

@ -22,7 +22,7 @@ export const WiredActionChatView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.message') }</Text>
<input type="text" className="form-control form-control-sm" value={ message } onChange={ event => setMessage(event.target.value) } maxLength={ 100 } />

View File

@ -4,5 +4,5 @@ import { WiredActionBaseView } from './WiredActionBaseView';
export const WiredActionFleeView: FC<{}> = props =>
{
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } save={ null } />;
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } hasSpecialInput={ false } save={ null } />;
}

View File

@ -106,7 +106,7 @@ export const WiredActionGiveRewardView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Flex alignItems="center" gap={ 1 }>
<input className="form-check-input" type="checkbox" id="limitEnabled" onChange={ event => setLimitEnabled(event.target.checked)} />
<Text>{ LocalizeText('wiredfurni.params.prizelimit', ['amount'], [limitEnabled ? rewardsLimit.toString() : '']) }</Text>

View File

@ -41,7 +41,7 @@ export const WiredActionGiveScoreToPredefinedTeamView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.setpoints', [ 'points' ], [ points.toString() ]) }</Text>
<ReactSlider

View File

@ -37,7 +37,7 @@ export const WiredActionGiveScoreView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.setpoints', [ 'points' ], [ points.toString() ]) }</Text>
<ReactSlider

View File

@ -23,7 +23,7 @@ export const WiredActionJoinTeamView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.team') }</Text>
{ [1, 2, 3, 4].map(team =>

View File

@ -22,7 +22,7 @@ export const WiredActionKickFromRoomView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.message') }</Text>
<input type="text" className="form-control form-control-sm" value={ message } onChange={ event => setMessage(event.target.value) } maxLength={ 100 } />

View File

@ -4,5 +4,5 @@ import { WiredActionBaseView } from './WiredActionBaseView';
export const WiredActionLeaveTeamView: FC<{}> = props =>
{
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ null } />;
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ false } save={ null } />;
}

View File

@ -58,7 +58,7 @@ export const WiredActionMoveAndRotateFurniView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.startdir') }</Text>
<Flex gap={ 1 }>

View File

@ -57,7 +57,7 @@ export const WiredActionMoveFurniToView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.emptytiles', [ 'tiles' ], [ spacing.toString() ]) }</Text>
<ReactSlider

View File

@ -70,7 +70,7 @@ export const WiredActionMoveFurniView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.movefurni') }</Text>
<Flex alignItems="center" gap={ 1 }>

View File

@ -33,7 +33,7 @@ export const WiredActionMuteUserView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.length.minutes', ['minutes'], [ time.toString() ]) }</Text>
<ReactSlider

View File

@ -4,5 +4,5 @@ import { WiredActionBaseView } from './WiredActionBaseView';
export const WiredActionResetView: FC<{}> = props =>
{
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ null } />;
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ false } save={ null } />;
}

View File

@ -31,7 +31,7 @@ export const WiredActionSetFurniStateToView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } save={ save }>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.conditions') }</Text>
<Flex alignItems="center" gap={ 1 }>

View File

@ -4,5 +4,5 @@ import { WiredActionBaseView } from './WiredActionBaseView';
export const WiredActionTeleportView: FC<{}> = props =>
{
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } save={ null } />;
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } hasSpecialInput={ false } save={ null } />;
}

View File

@ -4,5 +4,5 @@ import { WiredActionBaseView } from './WiredActionBaseView';
export const WiredActionToggleFurniStateView: FC<{}> = props =>
{
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } save={ null } />;
return <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT } hasSpecialInput={ false } save={ null } />;
}

View File

@ -24,7 +24,7 @@ export const WiredConditionActorHasHandItemView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.handitem') }</Text>
<select className="form-select form-select-sm" value={ handItemId } onChange={ event => setHandItemId(parseInt(event.target.value)) }>

View File

@ -4,5 +4,5 @@ import { WiredConditionBaseView } from './WiredConditionBaseView';
export const WiredConditionActorIsGroupMemberView: FC<{}> = props =>
{
return <WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ null } />;
return <WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ false } save={ null } />;
}

View File

@ -4,5 +4,5 @@ import { WiredConditionBaseView } from './WiredConditionBaseView';
export const WiredConditionActorIsOnFurniView: FC<{}> = props =>
{
return <WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } save={ null } />;
return <WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } hasSpecialInput={ false } save={ null } />;
}

View File

@ -25,7 +25,7 @@ export const WiredConditionActorIsTeamMemberView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.team') }</Text>
{ teamIds.map(value =>

View File

@ -22,7 +22,7 @@ export const WiredConditionActorIsWearingBadgeView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.badgecode') }</Text>
<input type="text" className="form-control form-control-sm" value={ badge } onChange={ event => setBadge(event.target.value) } />

View File

@ -22,7 +22,7 @@ export const WiredConditionActorIsWearingEffectView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.effectid') }</Text>
<input type="number" className="form-control form-control-sm" value={ effect } onChange={ event => setEffect(parseInt(event.target.value)) } />

View File

@ -4,13 +4,14 @@ import { WiredBaseView } from '../WiredBaseView';
export interface WiredConditionBaseViewProps
{
hasSpecialInput: boolean;
requiresFurni: number;
save: () => void;
}
export const WiredConditionBaseView: FC<WiredConditionBaseViewProps> = props =>
{
const { requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_NONE, save = null, children = null } = props;
const { requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_NONE, save = null, hasSpecialInput = false, children = null } = props;
const onSave = useCallback(() =>
{
@ -18,7 +19,7 @@ export const WiredConditionBaseView: FC<WiredConditionBaseViewProps> = props =>
}, [ save ]);
return (
<WiredBaseView wiredType="condition" requiresFurni={ requiresFurni } save={ onSave }>
<WiredBaseView wiredType="condition" requiresFurni={ requiresFurni } hasSpecialInput={ hasSpecialInput } save={ onSave }>
{ children }
</WiredBaseView>
);

View File

@ -55,7 +55,7 @@ export const WiredConditionDateRangeView: FC<{}> = props =>
}, [ trigger, dateToString ]);
return (
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.startdate') }</Text>
<input type="text" className="form-control form-control-sm" value={ startDate } onChange={ (e) => setStartDate(e.target.value) } />

View File

@ -4,5 +4,5 @@ import { WiredConditionBaseView } from './WiredConditionBaseView';
export const WiredConditionFurniHasAvatarOnView: FC<{}> = props =>
{
return <WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } save={ null } />;
return <WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } hasSpecialInput={ false } save={ null } />;
}

View File

@ -23,7 +23,7 @@ export const WiredConditionFurniHasFurniOnView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } save={ save }>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.requireall') }</Text>
{ [0, 1].map(value =>

View File

@ -23,7 +23,7 @@ export const WiredConditionFurniHasNotFurniOnView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } save={ save }>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.not_requireall') }</Text>
{ [0, 1].map(value =>

View File

@ -4,5 +4,5 @@ import { WiredConditionBaseView } from './WiredConditionBaseView';
export const WiredConditionFurniIsOfTypeView: FC<{}> = props =>
{
return <WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } save={ null } />;
return <WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } hasSpecialInput={ false } save={ null } />;
}

View File

@ -31,7 +31,7 @@ export const WiredConditionFurniMatchesSnapshotView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } save={ save }>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.conditions') }</Text>
<Flex alignItems="center" gap={ 1 }>

View File

@ -24,7 +24,7 @@ export const WiredConditionTimeElapsedLessView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.allowbefore', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</Text>
<ReactSlider

View File

@ -24,7 +24,7 @@ export const WiredConditionTimeElapsedMoreView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.allowafter', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</Text>
<ReactSlider

View File

@ -37,7 +37,7 @@ export const WiredConditionUserCountInRoomView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.usercountmin', [ 'value' ], [ min.toString() ]) }</Text>
<ReactSlider

View File

@ -30,7 +30,7 @@ export const WiredTriggerAvatarEnterRoomView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.picktriggerer') }</Text>
<Flex alignItems="center" gap={ 1 }>

View File

@ -33,7 +33,7 @@ export const WiredTriggerAvatarSaysSomethingView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.whatissaid') }</Text>
<input type="text" className="form-control form-control-sm" value={ message } onChange={ event => setMessage(event.target.value) } />

View File

@ -4,5 +4,5 @@ import { WiredTriggerBaseView } from './WiredTriggerBaseView';
export const WiredTriggerAvatarWalksOffFurniView: FC<{}> = props =>
{
return <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } save={ null } />;
return <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } hasSpecialInput={ false } save={ null } />;
}

View File

@ -4,5 +4,5 @@ import { WiredTriggerBaseView } from './WiredTriggerBaseView';
export const WiredTriggerAvatarWalksOnFurniView: FC<{}> = props =>
{
return <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } save={ null } />;
return <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } hasSpecialInput={ false } save={ null } />;
}

View File

@ -4,13 +4,14 @@ import { WiredBaseView } from '../WiredBaseView';
export interface WiredTriggerBaseViewProps
{
hasSpecialInput: boolean;
requiresFurni: number;
save: () => void;
}
export const WiredTriggerBaseView: FC<WiredTriggerBaseViewProps> = props =>
{
const { requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_NONE, save = null, children = null } = props;
const { requiresFurni = WiredFurniType.STUFF_SELECTION_OPTION_NONE, save = null, hasSpecialInput = false, children = null } = props;
const onSave = useCallback(() =>
{
@ -18,7 +19,7 @@ export const WiredTriggerBaseView: FC<WiredTriggerBaseViewProps> = props =>
}, [ save ]);
return (
<WiredBaseView wiredType="trigger" requiresFurni={ requiresFurni } save={ onSave }>
<WiredBaseView wiredType="trigger" requiresFurni={ requiresFurni } hasSpecialInput={ hasSpecialInput } save={ onSave }>
{ children }
</WiredBaseView>
);

View File

@ -22,7 +22,7 @@ export const WiredTriggerBotReachedAvatarView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<input type="text" className="form-control form-control-sm" maxLength={ 32 } value={ botName } onChange={ event => setBotName(event.target.value) } />

View File

@ -22,7 +22,7 @@ export const WiredTriggerBotReachedStuffView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.bot.name') }</Text>
<input type="text" className="form-control form-control-sm" maxLength={ 32 } value={ botName } onChange={ event => setBotName(event.target.value) } />

View File

@ -4,5 +4,5 @@ import { WiredTriggerBaseView } from './WiredTriggerBaseView';
export const WiredTriggerCollisionView: FC<{}> = props =>
{
return <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ null } />;
return <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ false } save={ null } />;
}

View File

@ -24,7 +24,7 @@ export const WiredTriggeExecuteOnceView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.settime', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</Text>
<ReactSlider

View File

@ -24,7 +24,7 @@ export const WiredTriggeExecutePeriodicallyLongView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.setlongtime', [ 'time' ], [ FriendlyTime.format(time * 5).toString() ]) }</Text>
<ReactSlider

View File

@ -24,7 +24,7 @@ export const WiredTriggeExecutePeriodicallyView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.settime', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</Text>
<ReactSlider

View File

@ -4,5 +4,5 @@ import { WiredTriggerBaseView } from './WiredTriggerBaseView';
export const WiredTriggerGameEndsView: FC<{}> = props =>
{
return <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ null } />;
return <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ false } save={ null } />;
}

View File

@ -4,5 +4,5 @@ import { WiredTriggerBaseView } from './WiredTriggerBaseView';
export const WiredTriggerGameStartsView: FC<{}> = props =>
{
return <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ null } />;
return <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ false } save={ null } />;
}

View File

@ -23,7 +23,7 @@ export const WiredTriggeScoreAchievedView: FC<{}> = props =>
}, [ trigger ]);
return (
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } hasSpecialInput={ true } save={ save }>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('wiredfurni.params.setscore', [ 'points' ], [ points.toString() ]) }</Text>
<ReactSlider

View File

@ -4,5 +4,5 @@ import { WiredTriggerBaseView } from './WiredTriggerBaseView';
export const WiredTriggerToggleFurniView: FC<{}> = props =>
{
return <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } save={ null } />;
return <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } hasSpecialInput={ false } save={ null } />;
}

View File

@ -1,8 +0,0 @@
import { NitroEvent } from '@nitrots/nitro-renderer';
export class AchievementsUIEvent extends NitroEvent
{
public static SHOW_ACHIEVEMENTS: string = 'AE_SHOW_ACHIEVEMENTS';
public static HIDE_ACHIEVEMENTS: string = 'AE_HIDE_ACHIEVEMENTS';
public static TOGGLE_ACHIEVEMENTS: string = 'AE_TOGGLE_ACHIEVEMENTS';
}

View File

@ -1,2 +1 @@
export * from './AchievementsUIEvent';
export * from './AchievementsUIUnseenCountEvent';

View File

@ -7,7 +7,6 @@ export * from './hc-center';
export * from './help';
export * from './inventory';
export * from './mod-tools';
export * from './navigator';
export * from './notification-center';
export * from './room-widgets';
export * from './room-widgets/thumbnail';

View File

@ -1,27 +0,0 @@
import { NitroEvent, RoomDataParser } from '@nitrots/nitro-renderer';
export class UpdateDoorStateEvent extends NitroEvent
{
public static START_DOORBELL: string = 'UDSE_START_DOORBELL';
public static START_PASSWORD: string = 'UDSE_START_PASSWORD';
public static STATE_PENDING_SERVER: string = 'UDSE_STATE_PENDING_SERVER';
public static UPDATE_STATE: string = 'UDSE_UPDATE_STATE';
public static STATE_WAITING: string = 'UDSE_STATE_WAITING';
public static STATE_NO_ANSWER: string = 'UDSE_STATE_NO_ANSWER';
public static STATE_WRONG_PASSWORD: string = 'UDSE_STATE_WRONG_PASSWORD';
public static STATE_ACCEPTED: string = 'UDSE_STATE_ACCEPTED';
private _roomData: RoomDataParser
constructor(type: string, roomData: RoomDataParser = null)
{
super(type);
this._roomData = roomData;
}
public get roomData(): RoomDataParser
{
return this._roomData;
}
}

View File

@ -1 +0,0 @@
export * from './UpdateDoorStateEvent';