mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-20 07:26:19 +00:00
🆙 Init V3
This commit is contained in:
@@ -0,0 +1 @@
|
||||
export * from './useAchievements';
|
||||
@@ -0,0 +1,185 @@
|
||||
import { AchievementData, AchievementEvent, AchievementsEvent, AchievementsScoreEvent, RequestAchievementsMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { AchievementCategory, AchievementUtilities, CloneObject, SendMessageComposer } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
|
||||
const useAchievementsState = () =>
|
||||
{
|
||||
const [ needsUpdate, setNeedsUpdate ] = useState<boolean>(true);
|
||||
const [ achievementCategories, setAchievementCategories ] = useState<AchievementCategory[]>([]);
|
||||
const [ selectedCategoryCode, setSelectedCategoryCode ] = useState<string>(null);
|
||||
const [ selectedAchievementId, setSelectedAchievementId ] = useState<number>(-1);
|
||||
const [ achievementScore, setAchievementScore ] = useState<number>(0);
|
||||
|
||||
const getTotalUnseen = useMemo(() =>
|
||||
{
|
||||
let unseen = 0;
|
||||
|
||||
achievementCategories.forEach(category => unseen += AchievementUtilities.getAchievementCategoryTotalUnseen(category));
|
||||
|
||||
return unseen;
|
||||
}, [ achievementCategories ]);
|
||||
|
||||
const getProgress = useMemo(() =>
|
||||
{
|
||||
let progress = 0;
|
||||
|
||||
achievementCategories.forEach(category => (progress += category.getProgress()));
|
||||
|
||||
return progress;
|
||||
}, [ achievementCategories ]);
|
||||
|
||||
const getMaxProgress = useMemo(() =>
|
||||
{
|
||||
let progress = 0;
|
||||
|
||||
achievementCategories.forEach(category => (progress += category.getMaxProgress()));
|
||||
|
||||
return progress;
|
||||
}, [ achievementCategories ]);
|
||||
|
||||
const scaledProgressPercent = useMemo(() =>
|
||||
{
|
||||
return ~~((((getProgress - 0) * (100 - 0)) / (getMaxProgress - 0)) + 0);
|
||||
}, [ getProgress, getMaxProgress ]);
|
||||
|
||||
const selectedCategory = useMemo(() =>
|
||||
{
|
||||
if(selectedCategoryCode === null) return null;
|
||||
|
||||
return achievementCategories.find(category => (category.code === selectedCategoryCode));
|
||||
}, [ achievementCategories, selectedCategoryCode ]);
|
||||
|
||||
const selectedAchievement = useMemo(() =>
|
||||
{
|
||||
if((selectedAchievementId === -1) || !selectedCategory) return null;
|
||||
|
||||
return selectedCategory.achievements.find(achievement => (achievement.achievementId === selectedAchievementId));
|
||||
}, [ selectedCategory, selectedAchievementId ]);
|
||||
|
||||
const setAchievementSeen = useCallback((categoryCode: string, achievementId: number) =>
|
||||
{
|
||||
setAchievementCategories(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
for(const category of newValue)
|
||||
{
|
||||
if(category.code !== categoryCode) continue;
|
||||
|
||||
for(const achievement of category.achievements)
|
||||
{
|
||||
if(achievement.achievementId !== achievementId) continue;
|
||||
|
||||
achievement.unseen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, []);
|
||||
|
||||
useMessageEvent<AchievementEvent>(AchievementEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const achievement = parser.achievement;
|
||||
|
||||
setAchievementCategories(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
const categoryIndex = newValue.findIndex(existing => (existing.code === achievement.category));
|
||||
|
||||
if(categoryIndex === -1)
|
||||
{
|
||||
const category = new AchievementCategory(achievement.category);
|
||||
|
||||
category.achievements.push(achievement);
|
||||
|
||||
newValue.push(category);
|
||||
}
|
||||
else
|
||||
{
|
||||
const category = CloneObject(newValue[categoryIndex]);
|
||||
const newAchievements = [ ...category.achievements ];
|
||||
const achievementIndex = newAchievements.findIndex(existing => (existing.achievementId === achievement.achievementId));
|
||||
let previousAchievement: AchievementData = null;
|
||||
|
||||
if(achievementIndex === -1)
|
||||
{
|
||||
newAchievements.push(achievement);
|
||||
}
|
||||
else
|
||||
{
|
||||
previousAchievement = newAchievements[achievementIndex];
|
||||
|
||||
newAchievements[achievementIndex] = achievement;
|
||||
}
|
||||
|
||||
if(!AchievementUtilities.getAchievementIsIgnored(achievement))
|
||||
{
|
||||
achievement.unseen++;
|
||||
|
||||
if(previousAchievement) achievement.unseen += previousAchievement.unseen;
|
||||
}
|
||||
|
||||
category.achievements = newAchievements;
|
||||
|
||||
newValue[categoryIndex] = category;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<AchievementsEvent>(AchievementsEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const categories: AchievementCategory[] = [];
|
||||
|
||||
for(const achievement of parser.achievements)
|
||||
{
|
||||
const categoryName = achievement.category;
|
||||
|
||||
let existing = categories.find(category => (category.code === categoryName));
|
||||
|
||||
if(!existing)
|
||||
{
|
||||
existing = new AchievementCategory(categoryName);
|
||||
|
||||
categories.push(existing);
|
||||
}
|
||||
|
||||
existing.achievements.push(achievement);
|
||||
}
|
||||
|
||||
setAchievementCategories(categories);
|
||||
});
|
||||
|
||||
useMessageEvent<AchievementsScoreEvent>(AchievementsScoreEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setAchievementScore(parser.score);
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!needsUpdate) return;
|
||||
|
||||
SendMessageComposer(new RequestAchievementsMessageComposer());
|
||||
|
||||
setNeedsUpdate(false);
|
||||
}, [ needsUpdate ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!selectedCategoryCode || (selectedAchievementId === -1)) return;
|
||||
|
||||
setAchievementSeen(selectedCategoryCode, selectedAchievementId);
|
||||
}, [ selectedCategoryCode, selectedAchievementId, setAchievementSeen ]);
|
||||
|
||||
return { achievementCategories, selectedCategoryCode, setSelectedCategoryCode, selectedAchievementId, setSelectedAchievementId, achievementScore, getTotalUnseen, getProgress, getMaxProgress, scaledProgressPercent, selectedCategory, selectedAchievement, setAchievementSeen };
|
||||
};
|
||||
|
||||
export const useAchievements = () => useBetween(useAchievementsState);
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './useAvatarEditor';
|
||||
export * from './useFigureData';
|
||||
@@ -0,0 +1,318 @@
|
||||
import { AvatarEditorFigureCategory, AvatarFigureContainer, AvatarFigurePartType, FigureSetIdsMessageEvent, GetAvatarRenderManager, GetSessionDataManager, GetWardrobeMessageComposer, IAvatarFigureContainer, IFigurePartSet, IPalette, IPartColor, SetType, UserWardrobePageEvent } from '@nitrots/nitro-renderer';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { AvatarEditorColorSorter, AvatarEditorPartSorter, AvatarEditorThumbnailsHelper, GetClubMemberLevel, GetConfigurationValue, IAvatarEditorCategory, IAvatarEditorCategoryPartItem, Randomizer, SendMessageComposer } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
import { useFigureData } from './useFigureData';
|
||||
|
||||
const MAX_PALETTES: number = 2;
|
||||
|
||||
const useAvatarEditorState = () =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
||||
const [ avatarModels, setAvatarModels ] = useState<{ [index: string]: IAvatarEditorCategory[] }>({});
|
||||
const [ activeModelKey, setActiveModelKey ] = useState<string>('');
|
||||
const [ maxPaletteCount, setMaxPaletteCount ] = useState<number>(1);
|
||||
const [ figureSetIds, setFigureSetIds ] = useState<number[]>([]);
|
||||
const [ boundFurnitureNames, setBoundFurnitureNames ] = useState<string[]>([]);
|
||||
const [ savedFigures, setSavedFigures ] = useState<[ IAvatarFigureContainer, string ][]>(null);
|
||||
const { selectedColors, gender, setGender, loadAvatarData, selectPart, selectColor, getFigureString, getFigureStringWithFace, selectedParts } = useFigureData();
|
||||
|
||||
const activeModel = useMemo(() => (avatarModels[activeModelKey] ?? null), [ activeModelKey, avatarModels ]);
|
||||
|
||||
const selectedColorParts = useMemo(() =>
|
||||
{
|
||||
const colorSets: { [index: string]: IPartColor[] } = {};
|
||||
|
||||
for(const setType of Object.keys(selectedColors))
|
||||
{
|
||||
if(!selectedColors[setType]) continue;
|
||||
|
||||
const parts: IPartColor[] = [];
|
||||
|
||||
for(const paletteId of Object.keys(selectedColors[setType]))
|
||||
{
|
||||
const partColor = activeModel.find(category => (category.setType === setType))?.colorItems[paletteId]?.find(partColor => (partColor.id === selectedColors[setType][paletteId]));
|
||||
|
||||
if(partColor) parts.push(partColor);
|
||||
}
|
||||
|
||||
colorSets[setType] = parts;
|
||||
}
|
||||
|
||||
return colorSets;
|
||||
|
||||
}, [ activeModel, selectedColors ]);
|
||||
|
||||
const selectEditorPart = useCallback((setType: string, partId: number) =>
|
||||
{
|
||||
if(!setType || !setType.length) return;
|
||||
|
||||
const category = activeModel.find(category => (category.setType === setType));
|
||||
|
||||
if(!category || !category.partItems || !category.partItems.length) return;
|
||||
|
||||
const partItem = category.partItems.find(partItem => partItem.id === partId);
|
||||
|
||||
if(!partItem) return;
|
||||
|
||||
if(partItem.isClear)
|
||||
{
|
||||
selectPart(setType, -1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(GetClubMemberLevel() < partItem.partSet.clubLevel) return;
|
||||
|
||||
setMaxPaletteCount(partItem.maxPaletteCount || 1);
|
||||
|
||||
selectPart(setType, partId);
|
||||
}, [ activeModel, selectPart ]);
|
||||
|
||||
const selectEditorColor = useCallback((setType: string, paletteId: number, colorId: number) =>
|
||||
{
|
||||
if(!setType || !setType.length) return;
|
||||
|
||||
const category = activeModel.find(category => (category.setType === setType));
|
||||
|
||||
if(!category || !category.colorItems || !category.colorItems.length) return;
|
||||
|
||||
const palette = category.colorItems[paletteId];
|
||||
|
||||
if(!palette || !palette.length) return;
|
||||
|
||||
const partColor = palette.find(partColor => (partColor.id === colorId));
|
||||
|
||||
if(!partColor) return;
|
||||
|
||||
if(GetClubMemberLevel() < partColor.clubLevel) return;
|
||||
|
||||
selectColor(setType, paletteId, colorId);
|
||||
}, [ activeModel, selectColor ]);
|
||||
|
||||
const getFirstSelectableColor = useCallback((setType: string) =>
|
||||
{
|
||||
const set = GetAvatarRenderManager().structureData.getSetType(setType);
|
||||
|
||||
if(!setType) return -1;
|
||||
|
||||
const palette = GetAvatarRenderManager().structureData.getPalette(set.paletteID);
|
||||
|
||||
if(!palette) return -1;
|
||||
|
||||
for(const color of palette.colors.getValues())
|
||||
{
|
||||
if(!color.isSelectable || (GetClubMemberLevel() < color.clubLevel)) continue;
|
||||
|
||||
return color.id;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}, []);
|
||||
|
||||
const randomizeCurrentFigure = useCallback((ignoredSets: string[] = []) =>
|
||||
{
|
||||
const structure = GetAvatarRenderManager().structure;
|
||||
const figureContainer = new AvatarFigureContainer('');
|
||||
|
||||
const getRandomSetTypes = (requiredSets: string[], options: string[]) =>
|
||||
{
|
||||
options = options.filter(option => (requiredSets.indexOf(option) === -1));
|
||||
|
||||
return [ ...requiredSets, ...Randomizer.getRandomElements(options, (Randomizer.getRandomNumber(options.length) + 1)) ];
|
||||
};
|
||||
|
||||
const requiredSets = getRandomSetTypes(structure.getMandatorySetTypeIds(gender, GetClubMemberLevel()), AvatarFigurePartType.FIGURE_SETS);
|
||||
|
||||
const getRandomPartSet = (setType: SetType, gender: string, clubLevel: number, figureSetIds: number[]) =>
|
||||
{
|
||||
const options = setType.partSets.getValues().filter(option =>
|
||||
{
|
||||
if(!option.isSelectable || ((option.gender !== 'U') && (option.gender !== gender)) || (option.clubLevel > clubLevel) || (option.isSellable && (figureSetIds.indexOf(option.id) === -1))) return null;
|
||||
|
||||
return option;
|
||||
});
|
||||
|
||||
if(!options || !options.length) return null;
|
||||
|
||||
return Randomizer.getRandomElement(options);
|
||||
};
|
||||
|
||||
const getRandomColors = (palette: IPalette, partSet: IFigurePartSet, clubLevel: number) =>
|
||||
{
|
||||
const options = palette.colors.getValues().filter(option =>
|
||||
{
|
||||
if(!option.isSelectable || (option.clubLevel > clubLevel)) return null;
|
||||
|
||||
return option;
|
||||
});
|
||||
|
||||
if(!options || !options.length) return null;
|
||||
|
||||
const getTotalColors = (partSet: IFigurePartSet) =>
|
||||
{
|
||||
const parts = partSet.parts;
|
||||
|
||||
let totalColors = 0;
|
||||
|
||||
for(const part of parts) totalColors = Math.max(totalColors, part.colorLayerIndex);
|
||||
|
||||
return totalColors;
|
||||
};
|
||||
|
||||
return Randomizer.getRandomElements(options, getTotalColors(partSet));
|
||||
};
|
||||
|
||||
for(const setType of ignoredSets)
|
||||
{
|
||||
const partSetId = selectedParts[setType];
|
||||
const colors = selectedColors[setType];
|
||||
|
||||
figureContainer.updatePart(setType, partSetId, colors);
|
||||
}
|
||||
|
||||
for(const type of requiredSets)
|
||||
{
|
||||
if(figureContainer.hasPartType(type)) continue;
|
||||
|
||||
const setType = (structure.figureData.getSetType(type) as SetType);
|
||||
const selectedSet = getRandomPartSet(setType, gender, GetClubMemberLevel(), figureSetIds);
|
||||
|
||||
if(!selectedSet) continue;
|
||||
|
||||
let selectedColors: number[] = [];
|
||||
|
||||
if(selectedSet.isColorable)
|
||||
{
|
||||
selectedColors = getRandomColors(structure.figureData.getPalette(setType.paletteID), selectedSet, GetClubMemberLevel()).map(color => color.id);
|
||||
}
|
||||
|
||||
figureContainer.updatePart(setType.type, selectedSet.id, selectedColors);
|
||||
}
|
||||
|
||||
loadAvatarData(figureContainer.getFigureString(), gender);
|
||||
}, [ figureSetIds, gender, loadAvatarData, selectedColors, selectedParts ]);
|
||||
|
||||
useMessageEvent<FigureSetIdsMessageEvent>(FigureSetIdsMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setFigureSetIds(parser.figureSetIds);
|
||||
setBoundFurnitureNames(parser.boundsFurnitureNames);
|
||||
});
|
||||
|
||||
useMessageEvent<UserWardrobePageEvent>(UserWardrobePageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const savedFigures: [ IAvatarFigureContainer, string ][] = [];
|
||||
|
||||
let i = 0;
|
||||
|
||||
while(i < GetConfigurationValue<number>('avatar.wardrobe.max.slots', 10))
|
||||
{
|
||||
savedFigures.push([ null, null ]);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
for(let [ index, [ look, gender ] ] of parser.looks.entries())
|
||||
{
|
||||
const container = GetAvatarRenderManager().createFigureContainer(look);
|
||||
|
||||
savedFigures[(index - 1)] = [ container, gender ];
|
||||
}
|
||||
|
||||
setSavedFigures(savedFigures);
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
AvatarEditorThumbnailsHelper.clearCache();
|
||||
}, [ selectedColorParts ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible) return;
|
||||
|
||||
const newAvatarModels: { [index: string]: IAvatarEditorCategory[] } = {};
|
||||
|
||||
const buildCategory = (setType: string) =>
|
||||
{
|
||||
const partItems: IAvatarEditorCategoryPartItem[] = [];
|
||||
const colorItems: IPartColor[][] = [];
|
||||
|
||||
for(let i = 0; i < MAX_PALETTES; i++) colorItems.push([]);
|
||||
|
||||
const set = GetAvatarRenderManager().structureData.getSetType(setType);
|
||||
const palette = GetAvatarRenderManager().structureData.getPalette(set.paletteID);
|
||||
|
||||
if(!set || !palette) return null;
|
||||
|
||||
for(const partColor of palette.colors.getValues())
|
||||
{
|
||||
if(!partColor || !partColor.isSelectable) continue;
|
||||
|
||||
for(let i = 0; i < MAX_PALETTES; i++) colorItems[i].push(partColor);
|
||||
}
|
||||
|
||||
let mandatorySetIds: string[] = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(gender, GetClubMemberLevel());
|
||||
|
||||
const isntMandatorySet = (mandatorySetIds.indexOf(setType) === -1);
|
||||
|
||||
if(isntMandatorySet) partItems.push({ id: -1, isClear: true });
|
||||
|
||||
const usesColor = (setType !== AvatarFigurePartType.HEAD);
|
||||
const partSets = set.partSets;
|
||||
|
||||
for(let i = (partSets.length); i >= 0; i--)
|
||||
{
|
||||
const partSet = partSets.getWithIndex(i);
|
||||
|
||||
if(!partSet || !partSet.isSelectable || ((partSet.gender !== gender) && (partSet.gender !== AvatarFigurePartType.UNISEX))) continue;
|
||||
|
||||
if(partSet.isSellable && figureSetIds.indexOf(partSet.id) === -1) continue;
|
||||
|
||||
let maxPaletteCount = 0;
|
||||
|
||||
for(const part of partSet.parts) maxPaletteCount = Math.max(maxPaletteCount, part.colorLayerIndex);
|
||||
|
||||
partItems.push({ id: partSet.id, partSet, usesColor, maxPaletteCount });
|
||||
}
|
||||
|
||||
partItems.sort(AvatarEditorPartSorter(false));
|
||||
|
||||
for(let i = 0; i < MAX_PALETTES; i++) colorItems[i].sort(AvatarEditorColorSorter);
|
||||
|
||||
return { setType, partItems, colorItems };
|
||||
};
|
||||
|
||||
newAvatarModels[AvatarEditorFigureCategory.GENERIC] = [ AvatarFigurePartType.HEAD ].map(setType => buildCategory(setType));
|
||||
newAvatarModels[AvatarEditorFigureCategory.HEAD] = [ AvatarFigurePartType.HAIR, AvatarFigurePartType.HEAD_ACCESSORY, AvatarFigurePartType.HEAD_ACCESSORY_EXTRA, AvatarFigurePartType.EYE_ACCESSORY, AvatarFigurePartType.FACE_ACCESSORY ].map(setType => buildCategory(setType));
|
||||
newAvatarModels[AvatarEditorFigureCategory.TORSO] = [ AvatarFigurePartType.CHEST, AvatarFigurePartType.CHEST_PRINT, AvatarFigurePartType.COAT_CHEST, AvatarFigurePartType.CHEST_ACCESSORY ].map(setType => buildCategory(setType));
|
||||
newAvatarModels[AvatarEditorFigureCategory.LEGS] = [ AvatarFigurePartType.LEGS, AvatarFigurePartType.SHOES, AvatarFigurePartType.WAIST_ACCESSORY ].map(setType => buildCategory(setType));
|
||||
newAvatarModels[AvatarEditorFigureCategory.WARDROBE] = [];
|
||||
|
||||
setAvatarModels(newAvatarModels);
|
||||
setActiveModelKey(AvatarEditorFigureCategory.GENERIC);
|
||||
}, [ isVisible, gender, figureSetIds ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible) return;
|
||||
|
||||
loadAvatarData(GetSessionDataManager().figure, GetSessionDataManager().gender);
|
||||
}, [ isVisible, loadAvatarData ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible || savedFigures) return;
|
||||
|
||||
setSavedFigures(new Array(GetConfigurationValue<number>('avatar.wardrobe.max.slots', 10)));
|
||||
SendMessageComposer(new GetWardrobeMessageComposer());
|
||||
}, [ isVisible, savedFigures ]);
|
||||
|
||||
return { isVisible, setIsVisible, avatarModels, activeModelKey, setActiveModelKey, maxPaletteCount, selectedColorParts, selectEditorColor, selectEditorPart, loadAvatarData, getFigureString, getFigureStringWithFace, selectedParts, gender, setGender, figureSetIds, randomizeCurrentFigure, savedFigures, setSavedFigures, getFirstSelectableColor };
|
||||
};
|
||||
|
||||
export const useAvatarEditor = () => useBetween(useAvatarEditorState);
|
||||
@@ -0,0 +1,150 @@
|
||||
import { AvatarFigurePartType } from '@nitrots/nitro-renderer';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
const useFigureDataState = () =>
|
||||
{
|
||||
const [ selectedParts, setSelectedParts ] = useState<{ [index: string]: number }>({});
|
||||
const [ selectedColors, setSelectedColors ] = useState<{ [index: string]: number[] }>({});
|
||||
const [ gender, setGender ] = useState<string>(AvatarFigurePartType.MALE);
|
||||
|
||||
const loadAvatarData = useCallback((figureString: string, gender: string) =>
|
||||
{
|
||||
const parse = (figure: string) =>
|
||||
{
|
||||
const sets = figure.split('.');
|
||||
|
||||
if(!sets || !sets.length) return;
|
||||
|
||||
const partSets: { [index: string]: number } = {};
|
||||
const colorSets: { [index: string]: number[] } = {};
|
||||
|
||||
for(const set of sets)
|
||||
{
|
||||
const parts = set.split('-');
|
||||
|
||||
if(!parts.length) continue;
|
||||
|
||||
const setType = parts[0];
|
||||
const partId = parseInt(parts[1]);
|
||||
const colorIds: number[] = [];
|
||||
|
||||
let offset = 2;
|
||||
|
||||
while(offset < parts.length)
|
||||
{
|
||||
colorIds.push(parseInt(parts[offset]));
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
if(!colorIds.length) colorIds.push(0);
|
||||
|
||||
if(partId >= 0) partSets[setType] = partId;
|
||||
|
||||
if(colorIds.length) colorSets[setType] = colorIds;
|
||||
}
|
||||
|
||||
return { partSets, colorSets };
|
||||
};
|
||||
|
||||
const { partSets, colorSets } = parse(figureString);
|
||||
|
||||
setSelectedParts(partSets);
|
||||
setSelectedColors(colorSets);
|
||||
setGender(gender);
|
||||
}, []);
|
||||
|
||||
const selectPart = useCallback((setType: string, partId: number) =>
|
||||
{
|
||||
if(!setType || !setType.length) return;
|
||||
|
||||
setSelectedParts(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
if(partId === -1) delete newValue[setType];
|
||||
else newValue[setType] = partId;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const selectColor = useCallback((setType: string, paletteId: number, colorId: number) =>
|
||||
{
|
||||
if(!setType || !setType.length) return;
|
||||
|
||||
setSelectedColors(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
if(!newValue[setType]) newValue[setType] = [];
|
||||
|
||||
if(!newValue[setType][paletteId]) newValue[setType][paletteId] = 0;
|
||||
|
||||
newValue[setType][paletteId] = colorId;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const getFigureString = useMemo(() =>
|
||||
{
|
||||
let figureString = '';
|
||||
|
||||
const partSets: string[] = [];
|
||||
const setTypes = Object.keys(selectedParts);
|
||||
|
||||
for(const setType of setTypes)
|
||||
{
|
||||
const partId = selectedParts[setType];
|
||||
|
||||
if(!partId) continue;
|
||||
|
||||
let setPart = `${ setType }-${ partId }`;
|
||||
|
||||
if(selectedColors[setType] && selectedColors[setType].length)
|
||||
{
|
||||
let i = 0;
|
||||
|
||||
while(i < selectedColors[setType].length)
|
||||
{
|
||||
setPart += `-${ selectedColors[setType][i] }`;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
partSets.push(setPart);
|
||||
}
|
||||
|
||||
for(const partSet of partSets)
|
||||
{
|
||||
figureString += partSet;
|
||||
|
||||
if(partSets.indexOf(partSet) < (partSets.length - 1)) figureString += '.';
|
||||
}
|
||||
|
||||
return figureString;
|
||||
}, [ selectedParts, selectedColors ]);
|
||||
|
||||
const getFigureStringWithFace = useCallback((overridePartId: number, override: boolean = true) =>
|
||||
{
|
||||
const figureSets = [ AvatarFigurePartType.HEAD ].map(setType =>
|
||||
{
|
||||
let partId = (setType === AvatarFigurePartType.HEAD && override) ? overridePartId : selectedParts[setType];
|
||||
const colors = selectedColors[setType] || [];
|
||||
|
||||
let figureSet = `${ setType }-${ partId }`;
|
||||
|
||||
if(partId >= 0) figureSet += colors.map(color => `-${ color }`).join('');
|
||||
|
||||
return figureSet;
|
||||
});
|
||||
|
||||
return figureSets.join('.');
|
||||
}, [ selectedParts, selectedColors ]);
|
||||
|
||||
return { selectedParts, selectedColors, gender, setGender, loadAvatarData, selectPart, selectColor, getFigureString, getFigureStringWithFace };
|
||||
};
|
||||
|
||||
export const useFigureData = useFigureDataState;
|
||||
@@ -0,0 +1 @@
|
||||
export * from './useCamera';
|
||||
@@ -0,0 +1,39 @@
|
||||
import { GetRoomCameraWidgetManager, InitCameraMessageEvent, IRoomCameraWidgetEffect, RequestCameraConfigurationComposer, RoomCameraWidgetManagerEvent } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { CameraPicture, SendMessageComposer } from '../../api';
|
||||
import { useMessageEvent, useNitroEvent } from '../events';
|
||||
|
||||
const useCameraState = () =>
|
||||
{
|
||||
const [ availableEffects, setAvailableEffects ] = useState<IRoomCameraWidgetEffect[]>([]);
|
||||
const [ cameraRoll, setCameraRoll ] = useState<CameraPicture[]>([]);
|
||||
const [ selectedPictureIndex, setSelectedPictureIndex ] = useState(-1);
|
||||
const [ myLevel, setMyLevel ] = useState(10);
|
||||
const [ price, setPrice ] = useState<{ credits: number, duckets: number, publishDucketPrice: number }>(null);
|
||||
|
||||
useNitroEvent<RoomCameraWidgetManagerEvent>(RoomCameraWidgetManagerEvent.INITIALIZED, event =>
|
||||
{
|
||||
setAvailableEffects(Array.from(GetRoomCameraWidgetManager().effects.values()));
|
||||
});
|
||||
|
||||
useMessageEvent<InitCameraMessageEvent>(InitCameraMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setPrice({ credits: parser.creditPrice, duckets: parser.ducketPrice, publishDucketPrice: parser.publishDucketPrice });
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(GetRoomCameraWidgetManager().isLoaded) return;
|
||||
|
||||
GetRoomCameraWidgetManager().init();
|
||||
|
||||
SendMessageComposer(new RequestCameraConfigurationComposer());
|
||||
}, []);
|
||||
|
||||
return { availableEffects, cameraRoll, setCameraRoll, selectedPictureIndex, setSelectedPictureIndex, myLevel, price };
|
||||
};
|
||||
|
||||
export const useCamera = () => useBetween(useCameraState);
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './useCatalog';
|
||||
export * from './useCatalogPlaceMultipleItems';
|
||||
export * from './useCatalogSkipPurchaseConfirmation';
|
||||
@@ -0,0 +1,913 @@
|
||||
import { BuildersClubFurniCountMessageEvent, BuildersClubPlaceRoomItemMessageComposer, BuildersClubPlaceWallItemMessageComposer, BuildersClubQueryFurniCountMessageComposer, BuildersClubSubscriptionStatusMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent, CatalogPublishedMessageEvent, ClubGiftInfoEvent, CreateLinkEvent, FrontPageItem, FurniturePlaceComposer, FurniturePlacePaintComposer, GetCatalogIndexComposer, GetCatalogPageComposer, GetClubGiftInfo, GetGiftWrappingConfigurationComposer, GetRoomEngine, GetTickerTime, GiftWrappingConfigurationEvent, GuildMembershipsMessageEvent, HabboClubOffersMessageEvent, LegacyDataType, LimitedEditionSoldOutEvent, MarketplaceMakeOfferResult, NodeData, ProductOfferEvent, PurchaseErrorMessageEvent, PurchaseFromCatalogComposer, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, RoomControllerLevel, RoomEngineObjectPlacedEvent, RoomObjectCategory, RoomObjectPlacementSource, RoomObjectType, RoomObjectVariable, RoomPreviewer, SellablePetPalettesMessageEvent, Vector3d } from '@nitrots/nitro-renderer';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { BuilderFurniPlaceableStatus, CatalogNode, CatalogPage, CatalogPetPalette, CatalogType, DispatchUiEvent, FurniCategory, GetFurnitureData, GetProductDataForLocalization, GetRoomSession, GiftWrappingConfiguration, ICatalogNode, ICatalogOptions, ICatalogPage, IPageLocalization, IProduct, IPurchasableOffer, IPurchaseOptions, LocalizeText, NotificationAlertType, Offer, PageLocalization, PlacedObjectPurchaseData, PlaySound, Product, ProductTypeEnum, RequestedPage, SearchResult, SendMessageComposer, SoundNames } from '../../api';
|
||||
import { CatalogPurchaseFailureEvent, CatalogPurchaseNotAllowedEvent, CatalogPurchaseSoldOutEvent, CatalogPurchasedEvent, InventoryFurniAddedEvent } from '../../events';
|
||||
import { useMessageEvent, useNitroEvent, useUiEvent } from '../events';
|
||||
import { useNotification } from '../notification';
|
||||
import { useCatalogPlaceMultipleItems } from './useCatalogPlaceMultipleItems';
|
||||
import { useCatalogSkipPurchaseConfirmation } from './useCatalogSkipPurchaseConfirmation';
|
||||
|
||||
const DUMMY_PAGE_ID_FOR_OFFER_SEARCH = -12345678;
|
||||
const DRAG_AND_DROP_ENABLED = true;
|
||||
|
||||
const useCatalogState = () =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const [ isBusy, setIsBusy ] = useState(false);
|
||||
const [ pageId, setPageId ] = useState(-1);
|
||||
const [ previousPageId, setPreviousPageId ] = useState(-1);
|
||||
const [ currentType, setCurrentType ] = useState(CatalogType.NORMAL);
|
||||
const [ rootNode, setRootNode ] = useState<ICatalogNode>(null);
|
||||
const [ offersToNodes, setOffersToNodes ] = useState<Map<number, ICatalogNode[]>>(null);
|
||||
const [ currentPage, setCurrentPage ] = useState<ICatalogPage>(null);
|
||||
const [ currentOffer, setCurrentOffer ] = useState<IPurchasableOffer>(null);
|
||||
const [ activeNodes, setActiveNodes ] = useState<ICatalogNode[]>([]);
|
||||
const [ searchResult, setSearchResult ] = useState<SearchResult>(null);
|
||||
const [ frontPageItems, setFrontPageItems ] = useState<FrontPageItem[]>([]);
|
||||
const [ roomPreviewer, setRoomPreviewer ] = useState<RoomPreviewer>(null);
|
||||
const [ navigationHidden, setNavigationHidden ] = useState(false);
|
||||
const [ purchaseOptions, setPurchaseOptions ] = useState<IPurchaseOptions>({ quantity: 1, extraData: null, extraParamRequired: false, previewStuffData: null });
|
||||
const [ catalogOptions, setCatalogOptions ] = useState<ICatalogOptions>({});
|
||||
const [ objectMoverRequested, setObjectMoverRequested ] = useState(false);
|
||||
const [ catalogPlaceMultipleObjects, setCatalogPlaceMultipleObjects ] = useCatalogPlaceMultipleItems();
|
||||
const [ catalogSkipPurchaseConfirmation, setCatalogSkipPurchaseConfirmation ] = useCatalogSkipPurchaseConfirmation();
|
||||
const [ purchasableOffer, setPurchaseableOffer ] = useState<IPurchasableOffer>(null);
|
||||
const [ placedObjectPurchaseData, setPlacedObjectPurchaseData ] = useState<PlacedObjectPurchaseData>(null);
|
||||
const [ furniCount, setFurniCount ] = useState(0);
|
||||
const [ furniLimit, setFurniLimit ] = useState(0);
|
||||
const [ maxFurniLimit, setMaxFurniLimit ] = useState(0);
|
||||
const [ secondsLeft, setSecondsLeft ] = useState(0);
|
||||
const [ updateTime, setUpdateTime ] = useState(0);
|
||||
const [ secondsLeftWithGrace, setSecondsLeftWithGrace ] = useState(0);
|
||||
const { simpleAlert = null } = useNotification();
|
||||
const requestedPage = useRef(new RequestedPage());
|
||||
|
||||
const resetState = useCallback(() =>
|
||||
{
|
||||
setPageId(-1);
|
||||
setPreviousPageId(-1);
|
||||
setRootNode(null);
|
||||
setOffersToNodes(null);
|
||||
setCurrentPage(null);
|
||||
setCurrentOffer(null);
|
||||
setActiveNodes([]);
|
||||
setSearchResult(null);
|
||||
setFrontPageItems([]);
|
||||
setIsVisible(false);
|
||||
}, []);
|
||||
|
||||
const getBuilderFurniPlaceableStatus = useCallback((offer: IPurchasableOffer) =>
|
||||
{
|
||||
if(!offer) return BuilderFurniPlaceableStatus.MISSING_OFFER;
|
||||
|
||||
if((furniCount < 0) || (furniCount >= furniLimit)) return BuilderFurniPlaceableStatus.FURNI_LIMIT_REACHED;
|
||||
|
||||
const roomSession = GetRoomSession();
|
||||
|
||||
if(!roomSession) return BuilderFurniPlaceableStatus.NOT_IN_ROOM;
|
||||
|
||||
if(!roomSession.isRoomOwner) return BuilderFurniPlaceableStatus.NOT_ROOM_OWNER;
|
||||
|
||||
if(secondsLeft <= 0)
|
||||
{
|
||||
const roomEngine = GetRoomEngine();
|
||||
|
||||
let objectCount = roomEngine.getRoomObjectCount(roomSession.roomId, RoomObjectCategory.UNIT);
|
||||
|
||||
while(objectCount > 0)
|
||||
{
|
||||
const roomObject = roomEngine.getRoomObjectByIndex(roomSession.roomId, objectCount, RoomObjectCategory.UNIT);
|
||||
const userData = roomSession.userDataManager.getUserDataByIndex(roomObject.id);
|
||||
|
||||
if(userData && (userData.type === RoomObjectType.USER) && (userData.roomIndex !== roomSession.ownRoomIndex) && !userData.isModerator) return BuilderFurniPlaceableStatus.VISITORS_IN_ROOM;
|
||||
|
||||
objectCount--;
|
||||
}
|
||||
}
|
||||
|
||||
return BuilderFurniPlaceableStatus.OKAY;
|
||||
}, [ furniCount, furniLimit, secondsLeft ]);
|
||||
|
||||
const isDraggable = useCallback((offer: IPurchasableOffer) =>
|
||||
{
|
||||
const roomSession = GetRoomSession();
|
||||
|
||||
if(((DRAG_AND_DROP_ENABLED && roomSession && offer.page && (offer.page.layoutCode !== 'sold_ltd_items') && (currentType === CatalogType.NORMAL) && (roomSession.isRoomOwner || (roomSession.isGuildRoom && (roomSession.controllerLevel >= RoomControllerLevel.GUILD_MEMBER)))) || ((currentType === CatalogType.BUILDER) && (getBuilderFurniPlaceableStatus(offer) === BuilderFurniPlaceableStatus.OKAY))) && (offer.pricingModel !== Offer.PRICING_MODEL_BUNDLE) && (offer.product.productType !== ProductTypeEnum.EFFECT) && (offer.product.productType !== ProductTypeEnum.HABBO_CLUB)) return true;
|
||||
|
||||
return false;
|
||||
}, [ currentType, getBuilderFurniPlaceableStatus ]);
|
||||
|
||||
const requestOfferToMover = useCallback((offer: IPurchasableOffer) =>
|
||||
{
|
||||
if(!isDraggable(offer)) return;
|
||||
|
||||
const product = offer.product;
|
||||
|
||||
if(!product) return;
|
||||
|
||||
let category = 0;
|
||||
|
||||
switch(product.productType)
|
||||
{
|
||||
case ProductTypeEnum.FLOOR:
|
||||
category = RoomObjectCategory.FLOOR;
|
||||
break;
|
||||
case ProductTypeEnum.WALL:
|
||||
category = RoomObjectCategory.WALL;
|
||||
break;
|
||||
}
|
||||
|
||||
if(GetRoomEngine().processRoomObjectPlacement(RoomObjectPlacementSource.CATALOG, -(offer.offerId), category, product.productClassId, product.extraParam))
|
||||
{
|
||||
setPurchaseableOffer(offer);
|
||||
setObjectMoverRequested(true);
|
||||
|
||||
setIsVisible(false);
|
||||
}
|
||||
}, [ isDraggable ]);
|
||||
|
||||
const resetRoomPaint = useCallback((planeType: string, type: string) =>
|
||||
{
|
||||
const roomEngine = GetRoomEngine();
|
||||
|
||||
let wallType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_WALL_TYPE);
|
||||
let floorType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_FLOOR_TYPE);
|
||||
let landscapeType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_LANDSCAPE_TYPE);
|
||||
|
||||
wallType = (wallType && wallType.length) ? wallType : '101';
|
||||
floorType = (floorType && floorType.length) ? floorType : '101';
|
||||
landscapeType = (landscapeType && landscapeType.length) ? landscapeType : '1.1';
|
||||
|
||||
switch(planeType)
|
||||
{
|
||||
case 'floor':
|
||||
roomEngine.updateRoomInstancePlaneType(roomEngine.activeRoomId, type, wallType, landscapeType, true);
|
||||
return;
|
||||
case 'wallpaper':
|
||||
roomEngine.updateRoomInstancePlaneType(roomEngine.activeRoomId, floorType, type, landscapeType, true);
|
||||
return;
|
||||
case 'landscape':
|
||||
roomEngine.updateRoomInstancePlaneType(roomEngine.activeRoomId, floorType, wallType, type, true);
|
||||
return;
|
||||
default:
|
||||
roomEngine.updateRoomInstancePlaneType(roomEngine.activeRoomId, floorType, wallType, landscapeType, true);
|
||||
return;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const cancelObjectMover = useCallback(() =>
|
||||
{
|
||||
if(!purchasableOffer) return;
|
||||
|
||||
GetRoomEngine().cancelRoomObjectInsert();
|
||||
|
||||
setObjectMoverRequested(false);
|
||||
setPurchaseableOffer(null);
|
||||
}, [ purchasableOffer ]);
|
||||
|
||||
const resetObjectMover = useCallback((flag: boolean = true) =>
|
||||
{
|
||||
setObjectMoverRequested(prevValue =>
|
||||
{
|
||||
if(prevValue && flag)
|
||||
{
|
||||
CreateLinkEvent('catalog/open');
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const resetPlacedOfferData = useCallback((flag: boolean = false) =>
|
||||
{
|
||||
if(!flag) resetObjectMover();
|
||||
|
||||
setPlacedObjectPurchaseData(prevValue =>
|
||||
{
|
||||
if(prevValue)
|
||||
{
|
||||
switch(prevValue.category)
|
||||
{
|
||||
case RoomObjectCategory.FLOOR:
|
||||
GetRoomEngine().removeRoomObjectFloor(prevValue.roomId, prevValue.objectId);
|
||||
break;
|
||||
case RoomObjectCategory.WALL: {
|
||||
|
||||
switch(prevValue.furniData.className)
|
||||
{
|
||||
case 'floor':
|
||||
case 'wallpaper':
|
||||
case 'landscape':
|
||||
resetRoomPaint('reset', '');
|
||||
break;
|
||||
default:
|
||||
GetRoomEngine().removeRoomObjectWall(prevValue.roomId, prevValue.objectId);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
GetRoomEngine().deleteRoomObject(prevValue.objectId, prevValue.category);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}, [ resetObjectMover, resetRoomPaint ]);
|
||||
|
||||
const getNodeById = useCallback((id: number, node: ICatalogNode) =>
|
||||
{
|
||||
if((node.pageId === id) && (node !== rootNode)) return node;
|
||||
|
||||
for(const child of node.children)
|
||||
{
|
||||
const found = (getNodeById(id, child));
|
||||
|
||||
if(found) return found;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [ rootNode ]);
|
||||
|
||||
const getNodeByName = useCallback((name: string, node: ICatalogNode) =>
|
||||
{
|
||||
if((node.pageName === name) && (node !== rootNode)) return node;
|
||||
|
||||
for(const child of node.children)
|
||||
{
|
||||
const found = (getNodeByName(name, child));
|
||||
|
||||
if(found) return found;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [ rootNode ]);
|
||||
|
||||
const getNodesByOfferId = useCallback((offerId: number, flag: boolean = false) =>
|
||||
{
|
||||
if(!offersToNodes || !offersToNodes.size) return null;
|
||||
|
||||
if(flag)
|
||||
{
|
||||
const nodes: ICatalogNode[] = [];
|
||||
const offers = offersToNodes.get(offerId);
|
||||
|
||||
if(offers && offers.length) for(const offer of offers) (offer.isVisible && nodes.push(offer));
|
||||
|
||||
if(nodes.length) return nodes;
|
||||
}
|
||||
|
||||
return offersToNodes.get(offerId);
|
||||
}, [ offersToNodes ]);
|
||||
|
||||
const loadCatalogPage = useCallback((pageId: number, offerId: number) =>
|
||||
{
|
||||
if(pageId < 0) return;
|
||||
|
||||
setIsBusy(true);
|
||||
setPageId(pageId);
|
||||
|
||||
if(pageId > -1) SendMessageComposer(new GetCatalogPageComposer(pageId, offerId, currentType));
|
||||
}, [ currentType ]);
|
||||
|
||||
const showCatalogPage = useCallback((pageId: number, layoutCode: string, localization: IPageLocalization, offers: IPurchasableOffer[], offerId: number, acceptSeasonCurrencyAsCredits: boolean) =>
|
||||
{
|
||||
const catalogPage = (new CatalogPage(pageId, layoutCode, localization, offers, acceptSeasonCurrencyAsCredits) as ICatalogPage);
|
||||
|
||||
setCurrentPage(catalogPage);
|
||||
setPreviousPageId(prevValue => ((pageId !== -1) ? pageId : prevValue));
|
||||
setNavigationHidden(false);
|
||||
|
||||
if((offerId > -1) && catalogPage.offers.length)
|
||||
{
|
||||
for(const offer of catalogPage.offers)
|
||||
{
|
||||
if(offer.offerId !== offerId) continue;
|
||||
|
||||
setCurrentOffer(offer);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
const activateNode = useCallback((targetNode: ICatalogNode, offerId: number = -1) =>
|
||||
{
|
||||
cancelObjectMover();
|
||||
|
||||
if(targetNode.parent.pageName === 'root')
|
||||
{
|
||||
if(targetNode.children.length)
|
||||
{
|
||||
for(const child of targetNode.children)
|
||||
{
|
||||
if(!child.isVisible) continue;
|
||||
|
||||
targetNode = child;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const nodes: ICatalogNode[] = [];
|
||||
|
||||
let node = targetNode;
|
||||
|
||||
while(node && (node.pageName !== 'root'))
|
||||
{
|
||||
nodes.push(node);
|
||||
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
nodes.reverse();
|
||||
|
||||
setActiveNodes(prevValue =>
|
||||
{
|
||||
const isActive = (prevValue.indexOf(targetNode) >= 0);
|
||||
const isOpen = targetNode.isOpen;
|
||||
|
||||
for(const existing of prevValue)
|
||||
{
|
||||
existing.deactivate();
|
||||
|
||||
if(nodes.indexOf(existing) === -1) existing.close();
|
||||
}
|
||||
|
||||
for(const n of nodes)
|
||||
{
|
||||
n.activate();
|
||||
|
||||
if(n.parent) n.open();
|
||||
|
||||
if((n === targetNode.parent) && n.children.length) n.open();
|
||||
}
|
||||
|
||||
if(isActive && isOpen) targetNode.close();
|
||||
else targetNode.open();
|
||||
|
||||
return nodes;
|
||||
});
|
||||
|
||||
if(targetNode.pageId > -1) loadCatalogPage(targetNode.pageId, offerId);
|
||||
}, [ setActiveNodes, loadCatalogPage, cancelObjectMover ]);
|
||||
|
||||
const openPageById = useCallback((id: number) =>
|
||||
{
|
||||
if(id !== -1) setSearchResult(null);
|
||||
|
||||
if(!isVisible)
|
||||
{
|
||||
requestedPage.current.requestById = id;
|
||||
|
||||
setIsVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
const node = getNodeById(id, rootNode);
|
||||
|
||||
if(node) activateNode(node);
|
||||
}
|
||||
}, [ isVisible, rootNode, getNodeById, activateNode ]);
|
||||
|
||||
const openPageByName = useCallback((name: string) =>
|
||||
{
|
||||
setSearchResult(null);
|
||||
|
||||
if(!isVisible)
|
||||
{
|
||||
requestedPage.current.requestByName = name;
|
||||
|
||||
setIsVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
const node = getNodeByName(name, rootNode);
|
||||
|
||||
if(node) activateNode(node);
|
||||
}
|
||||
}, [ isVisible, rootNode, getNodeByName, activateNode ]);
|
||||
|
||||
const openPageByOfferId = useCallback((offerId: number) =>
|
||||
{
|
||||
setSearchResult(null);
|
||||
|
||||
if(!isVisible)
|
||||
{
|
||||
requestedPage.current.requestedByOfferId = offerId;
|
||||
|
||||
setIsVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
const nodes = getNodesByOfferId(offerId);
|
||||
|
||||
if(!nodes || !nodes.length) return;
|
||||
|
||||
activateNode(nodes[0], offerId);
|
||||
}
|
||||
}, [ isVisible, getNodesByOfferId, activateNode ]);
|
||||
|
||||
const refreshBuilderStatus = useCallback(() =>
|
||||
{
|
||||
|
||||
}, []);
|
||||
|
||||
useMessageEvent<CatalogPagesListEvent>(CatalogPagesListEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const offers: Map<number, ICatalogNode[]> = new Map();
|
||||
|
||||
const getCatalogNode = (node: NodeData, depth: number, parent: ICatalogNode) =>
|
||||
{
|
||||
const catalogNode = (new CatalogNode(node, depth, parent) as ICatalogNode);
|
||||
|
||||
for(const offerId of catalogNode.offerIds)
|
||||
{
|
||||
if(offers.has(offerId)) offers.get(offerId).push(catalogNode);
|
||||
else offers.set(offerId, [ catalogNode ]);
|
||||
}
|
||||
|
||||
depth++;
|
||||
|
||||
for(const child of node.children) catalogNode.addChild(getCatalogNode(child, depth, catalogNode));
|
||||
|
||||
return catalogNode;
|
||||
};
|
||||
|
||||
setRootNode(getCatalogNode(parser.root, 0, null));
|
||||
setOffersToNodes(offers);
|
||||
});
|
||||
|
||||
useMessageEvent<CatalogPageMessageEvent>(CatalogPageMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(parser.catalogType !== currentType) return;
|
||||
|
||||
const purchasableOffers: IPurchasableOffer[] = [];
|
||||
|
||||
for(const offer of parser.offers)
|
||||
{
|
||||
const products: IProduct[] = [];
|
||||
const productData = GetProductDataForLocalization(offer.localizationId);
|
||||
|
||||
for(const product of offer.products)
|
||||
{
|
||||
const furnitureData = GetFurnitureData(product.furniClassId, product.productType);
|
||||
|
||||
products.push(new Product(product.productType, product.furniClassId, product.extraParam, product.productCount, productData, furnitureData, product.uniqueLimitedItem, product.uniqueLimitedSeriesSize, product.uniqueLimitedItemsLeft));
|
||||
}
|
||||
|
||||
if(!products.length) continue;
|
||||
|
||||
const purchasableOffer = new Offer(offer.offerId, offer.localizationId, offer.rent, offer.priceCredits, offer.priceActivityPoints, offer.priceActivityPointsType, offer.giftable, offer.clubLevel, products, offer.bundlePurchaseAllowed);
|
||||
|
||||
if((currentType === CatalogType.NORMAL) || ((purchasableOffer.pricingModel !== Offer.PRICING_MODEL_BUNDLE) && (purchasableOffer.pricingModel !== Offer.PRICING_MODEL_MULTI))) purchasableOffers.push(purchasableOffer);
|
||||
}
|
||||
|
||||
if(parser.frontPageItems && parser.frontPageItems.length) setFrontPageItems(parser.frontPageItems);
|
||||
|
||||
setIsBusy(false);
|
||||
|
||||
if(pageId === parser.pageId)
|
||||
{
|
||||
showCatalogPage(parser.pageId, parser.layoutCode, new PageLocalization(parser.localization.images.concat(), parser.localization.texts.concat()), purchasableOffers, parser.offerId, parser.acceptSeasonCurrencyAsCredits);
|
||||
}
|
||||
});
|
||||
|
||||
useMessageEvent<PurchaseOKMessageEvent>(PurchaseOKMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
DispatchUiEvent(new CatalogPurchasedEvent(parser.offer));
|
||||
});
|
||||
|
||||
useMessageEvent<PurchaseErrorMessageEvent>(PurchaseErrorMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
DispatchUiEvent(new CatalogPurchaseFailureEvent(parser.code));
|
||||
});
|
||||
|
||||
useMessageEvent<PurchaseNotAllowedMessageEvent>(PurchaseNotAllowedMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
DispatchUiEvent(new CatalogPurchaseNotAllowedEvent(parser.code));
|
||||
});
|
||||
|
||||
useMessageEvent<LimitedEditionSoldOutEvent>(LimitedEditionSoldOutEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
DispatchUiEvent(new CatalogPurchaseSoldOutEvent());
|
||||
});
|
||||
|
||||
useMessageEvent<ProductOfferEvent>(ProductOfferEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const offerData = parser.offer;
|
||||
|
||||
if(!offerData || !offerData.products.length) return;
|
||||
|
||||
const offerProductData = offerData.products[0];
|
||||
|
||||
if(offerProductData.uniqueLimitedItem)
|
||||
{
|
||||
// update unique
|
||||
}
|
||||
|
||||
const products: IProduct[] = [];
|
||||
const productData = GetProductDataForLocalization(offerData.localizationId);
|
||||
|
||||
for(const product of offerData.products)
|
||||
{
|
||||
const furnitureData = GetFurnitureData(product.furniClassId, product.productType);
|
||||
|
||||
products.push(new Product(product.productType, product.furniClassId, product.extraParam, product.productCount, productData, furnitureData, product.uniqueLimitedItem, product.uniqueLimitedSeriesSize, product.uniqueLimitedItemsLeft));
|
||||
}
|
||||
|
||||
const offer = new Offer(offerData.offerId, offerData.localizationId, offerData.rent, offerData.priceCredits, offerData.priceActivityPoints, offerData.priceActivityPointsType, offerData.giftable, offerData.clubLevel, products, offerData.bundlePurchaseAllowed);
|
||||
|
||||
if(!((currentType === CatalogType.NORMAL) || ((offer.pricingModel !== Offer.PRICING_MODEL_BUNDLE) && (offer.pricingModel !== Offer.PRICING_MODEL_MULTI)))) return;
|
||||
|
||||
offer.page = currentPage;
|
||||
|
||||
setCurrentOffer(offer);
|
||||
|
||||
if(offer.product && (offer.product.productType === ProductTypeEnum.WALL))
|
||||
{
|
||||
setPurchaseOptions(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.extraData =( offer.product.extraParam || null);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
|
||||
// (this._isObjectMoverRequested) && (this._purchasableOffer)
|
||||
});
|
||||
|
||||
useMessageEvent<SellablePetPalettesMessageEvent>(SellablePetPalettesMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const petPalette = new CatalogPetPalette(parser.productCode, parser.palettes.slice());
|
||||
|
||||
setCatalogOptions(prevValue =>
|
||||
{
|
||||
const petPalettes = [];
|
||||
|
||||
if(prevValue.petPalettes) petPalettes.push(...prevValue.petPalettes);
|
||||
|
||||
for(let i = 0; i < petPalettes.length; i++)
|
||||
{
|
||||
const palette = petPalettes[i];
|
||||
|
||||
if(palette.breed === petPalette.breed)
|
||||
{
|
||||
petPalettes.splice(i, 1);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
petPalettes.push(petPalette);
|
||||
|
||||
return { ...prevValue, petPalettes };
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<HabboClubOffersMessageEvent>(HabboClubOffersMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setCatalogOptions(prevValue =>
|
||||
{
|
||||
const clubOffers = parser.offers;
|
||||
|
||||
return { ...prevValue, clubOffers };
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<GuildMembershipsMessageEvent>(GuildMembershipsMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setCatalogOptions(prevValue =>
|
||||
{
|
||||
const groups = parser.groups;
|
||||
|
||||
return { ...prevValue, groups };
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<GiftWrappingConfigurationEvent>(GiftWrappingConfigurationEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setCatalogOptions(prevValue =>
|
||||
{
|
||||
const giftConfiguration = new GiftWrappingConfiguration(parser);
|
||||
|
||||
return { ...prevValue, giftConfiguration };
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<MarketplaceMakeOfferResult>(MarketplaceMakeOfferResult, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser) return;
|
||||
|
||||
let title = '';
|
||||
if(parser.result === 1)
|
||||
{
|
||||
title = LocalizeText('inventory.marketplace.result.title.success');
|
||||
}
|
||||
else
|
||||
{
|
||||
title = LocalizeText('inventory.marketplace.result.title.failure');
|
||||
}
|
||||
|
||||
const message = LocalizeText(`inventory.marketplace.result.${ parser.result }`);
|
||||
|
||||
simpleAlert(message, NotificationAlertType.DEFAULT, null, null, title);
|
||||
});
|
||||
|
||||
useMessageEvent<ClubGiftInfoEvent>(ClubGiftInfoEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setCatalogOptions(prevValue =>
|
||||
{
|
||||
const clubGifts = parser;
|
||||
|
||||
return { ...prevValue, clubGifts };
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<CatalogPublishedMessageEvent>(CatalogPublishedMessageEvent, event =>
|
||||
{
|
||||
const wasVisible = isVisible;
|
||||
|
||||
resetState();
|
||||
|
||||
if(wasVisible) simpleAlert(LocalizeText('catalog.alert.published.description'), NotificationAlertType.ALERT, null, null, LocalizeText('catalog.alert.published.title'));
|
||||
});
|
||||
|
||||
useMessageEvent<BuildersClubFurniCountMessageEvent>(BuildersClubFurniCountMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setFurniCount(parser.furniCount);
|
||||
|
||||
refreshBuilderStatus();
|
||||
});
|
||||
|
||||
useMessageEvent<BuildersClubSubscriptionStatusMessageEvent>(BuildersClubSubscriptionStatusMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setFurniLimit(parser.furniLimit);
|
||||
setMaxFurniLimit(parser.maxFurniLimit);
|
||||
setSecondsLeft(parser.secondsLeft);
|
||||
setUpdateTime(GetTickerTime());
|
||||
setSecondsLeftWithGrace(parser.secondsLeftWithGrace);
|
||||
|
||||
refreshBuilderStatus();
|
||||
});
|
||||
|
||||
useUiEvent<CatalogPurchasedEvent>(CatalogPurchasedEvent.PURCHASE_SUCCESS, event => PlaySound(SoundNames.CREDITS));
|
||||
|
||||
useNitroEvent<RoomEngineObjectPlacedEvent>(RoomEngineObjectPlacedEvent.PLACED, event =>
|
||||
{
|
||||
if(!objectMoverRequested || (event.type !== RoomEngineObjectPlacedEvent.PLACED)) return;
|
||||
|
||||
resetPlacedOfferData(true);
|
||||
|
||||
if(!purchasableOffer)
|
||||
{
|
||||
resetObjectMover();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let placed = false;
|
||||
|
||||
const product = purchasableOffer.product;
|
||||
|
||||
if(event.category === RoomObjectCategory.WALL)
|
||||
{
|
||||
switch(product.furnitureData.className)
|
||||
{
|
||||
case 'floor':
|
||||
case 'wallpaper':
|
||||
case 'landscape':
|
||||
placed = (event.placedOnFloor || event.placedOnWall);
|
||||
break;
|
||||
default:
|
||||
placed = event.placedInRoom;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
placed = event.placedInRoom;
|
||||
}
|
||||
|
||||
if(!placed)
|
||||
{
|
||||
resetObjectMover();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setPlacedObjectPurchaseData(new PlacedObjectPurchaseData(event.roomId, event.objectId, event.category, event.wallLocation, event.x, event.y, event.direction, purchasableOffer));
|
||||
|
||||
switch(currentType)
|
||||
{
|
||||
case CatalogType.NORMAL: {
|
||||
switch(event.category)
|
||||
{
|
||||
case RoomObjectCategory.FLOOR:
|
||||
GetRoomEngine().addFurnitureFloor(event.roomId, event.objectId, product.productClassId, new Vector3d(event.x, event.y, event.z), new Vector3d(event.direction), 0, new LegacyDataType());
|
||||
break;
|
||||
case RoomObjectCategory.WALL: {
|
||||
switch(product.furnitureData.className)
|
||||
{
|
||||
case 'floor':
|
||||
case 'wallpaper':
|
||||
case 'landscape':
|
||||
resetRoomPaint(product.furnitureData.className, product.extraParam);
|
||||
break;
|
||||
default:
|
||||
GetRoomEngine().addFurnitureWall(event.roomId, event.objectId, product.productClassId, new Vector3d(event.x, event.y, event.z), new Vector3d(event.direction * 45), 0, event.instanceData, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(roomObject) roomObject.model.setValue(RoomObjectVariable.FURNITURE_ALPHA_MULTIPLIER, 0.5);
|
||||
|
||||
if(catalogSkipPurchaseConfirmation)
|
||||
{
|
||||
SendMessageComposer(new PurchaseFromCatalogComposer(pageId, purchasableOffer.offerId, product.extraParam, 1));
|
||||
|
||||
if(catalogPlaceMultipleObjects) requestOfferToMover(purchasableOffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// confirm
|
||||
|
||||
if(catalogPlaceMultipleObjects) requestOfferToMover(purchasableOffer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CatalogType.BUILDER: {
|
||||
let pageId = purchasableOffer.page.pageId;
|
||||
|
||||
if(pageId === DUMMY_PAGE_ID_FOR_OFFER_SEARCH)
|
||||
{
|
||||
pageId = -1;
|
||||
}
|
||||
|
||||
switch(event.category)
|
||||
{
|
||||
case RoomObjectCategory.FLOOR:
|
||||
SendMessageComposer(new BuildersClubPlaceRoomItemMessageComposer(pageId, purchasableOffer.offerId, product.extraParam, event.x, event.y, event.direction));
|
||||
break;
|
||||
case RoomObjectCategory.WALL:
|
||||
SendMessageComposer(new BuildersClubPlaceWallItemMessageComposer(pageId, purchasableOffer.offerId, product.extraParam, event.wallLocation));
|
||||
break;
|
||||
}
|
||||
|
||||
if(catalogPlaceMultipleObjects) requestOfferToMover(purchasableOffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useUiEvent<InventoryFurniAddedEvent>(InventoryFurniAddedEvent.FURNI_ADDED, event =>
|
||||
{
|
||||
const roomEngine = GetRoomEngine();
|
||||
|
||||
if(!placedObjectPurchaseData || (placedObjectPurchaseData.productClassId !== event.spriteId) || (placedObjectPurchaseData.roomId !== roomEngine.activeRoomId)) return;
|
||||
|
||||
switch(event.category)
|
||||
{
|
||||
case FurniCategory.FLOOR: {
|
||||
const floorType = roomEngine.getRoomInstanceVariable(roomEngine.activeRoomId, RoomObjectVariable.ROOM_FLOOR_TYPE);
|
||||
|
||||
if(placedObjectPurchaseData.extraParam !== floorType) SendMessageComposer(new FurniturePlacePaintComposer(event.id));
|
||||
break;
|
||||
}
|
||||
case FurniCategory.WALL_PAPER: {
|
||||
const wallType = roomEngine.getRoomInstanceVariable(roomEngine.activeRoomId, RoomObjectVariable.ROOM_WALL_TYPE);
|
||||
|
||||
if(placedObjectPurchaseData.extraParam !== wallType) SendMessageComposer(new FurniturePlacePaintComposer(event.id));
|
||||
break;
|
||||
}
|
||||
case FurniCategory.LANDSCAPE: {
|
||||
const landscapeType = roomEngine.getRoomInstanceVariable(roomEngine.activeRoomId, RoomObjectVariable.ROOM_LANDSCAPE_TYPE);
|
||||
|
||||
if(placedObjectPurchaseData.extraParam !== landscapeType) SendMessageComposer(new FurniturePlacePaintComposer(event.id));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
SendMessageComposer(new FurniturePlaceComposer(event.id, placedObjectPurchaseData.category, placedObjectPurchaseData.wallLocation, placedObjectPurchaseData.x, placedObjectPurchaseData.y, placedObjectPurchaseData.direction));
|
||||
}
|
||||
|
||||
if(!catalogPlaceMultipleObjects) resetPlacedOfferData();
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
return () => setCurrentOffer(null);
|
||||
}, [ currentPage ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible || !rootNode || !offersToNodes || !requestedPage.current) return;
|
||||
|
||||
switch(requestedPage.current.requestType)
|
||||
{
|
||||
case RequestedPage.REQUEST_TYPE_NONE:
|
||||
if(currentPage) return;
|
||||
|
||||
if(rootNode.isBranch)
|
||||
{
|
||||
for(const child of rootNode.children)
|
||||
{
|
||||
if(child && child.isVisible)
|
||||
{
|
||||
activateNode(child);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
case RequestedPage.REQUEST_TYPE_ID:
|
||||
openPageById(requestedPage.current.requestById);
|
||||
requestedPage.current.resetRequest();
|
||||
return;
|
||||
case RequestedPage.REQUEST_TYPE_OFFER:
|
||||
openPageByOfferId(requestedPage.current.requestedByOfferId);
|
||||
requestedPage.current.resetRequest();
|
||||
return;
|
||||
case RequestedPage.REQUEST_TYPE_NAME:
|
||||
openPageByName(requestedPage.current.requestByName);
|
||||
requestedPage.current.resetRequest();
|
||||
return;
|
||||
}
|
||||
}, [ isVisible, rootNode, offersToNodes, currentPage, activateNode, openPageById, openPageByOfferId, openPageByName ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!searchResult && currentPage && (currentPage.pageId === -1)) openPageById(previousPageId);
|
||||
}, [ searchResult, currentPage, previousPageId, openPageById ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!currentOffer) return;
|
||||
|
||||
setPurchaseOptions({ quantity: 1, extraData: null, extraParamRequired: false, previewStuffData: null });
|
||||
}, [ currentOffer ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible || rootNode) return;
|
||||
|
||||
SendMessageComposer(new GetGiftWrappingConfigurationComposer());
|
||||
SendMessageComposer(new GetClubGiftInfo());
|
||||
SendMessageComposer(new GetCatalogIndexComposer(currentType));
|
||||
SendMessageComposer(new BuildersClubQueryFurniCountMessageComposer());
|
||||
}, [ isVisible, rootNode, currentType ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setRoomPreviewer(new RoomPreviewer(GetRoomEngine(), ++RoomPreviewer.PREVIEW_COUNTER));
|
||||
|
||||
return () =>
|
||||
{
|
||||
setRoomPreviewer(prevValue =>
|
||||
{
|
||||
prevValue.dispose();
|
||||
|
||||
return null;
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { isVisible, setIsVisible, isBusy, pageId, previousPageId, currentType, rootNode, offersToNodes, currentPage, setCurrentPage, currentOffer, setCurrentOffer, activeNodes, searchResult, setSearchResult, frontPageItems, roomPreviewer, navigationHidden, setNavigationHidden, purchaseOptions, setPurchaseOptions, catalogOptions, setCatalogOptions, getNodeById, getNodeByName, activateNode, openPageById, openPageByName, openPageByOfferId, requestOfferToMover };
|
||||
};
|
||||
|
||||
export const useCatalog = () => useBetween(useCatalogState);
|
||||
@@ -0,0 +1,7 @@
|
||||
import { useBetween } from 'use-between';
|
||||
import { LocalStorageKeys } from '../../api';
|
||||
import { useLocalStorage } from '../useLocalStorage';
|
||||
|
||||
const useCatalogPlaceMultipleItemsState = () => useLocalStorage(LocalStorageKeys.CATALOG_PLACE_MULTIPLE_OBJECTS, false);
|
||||
|
||||
export const useCatalogPlaceMultipleItems = () => useBetween(useCatalogPlaceMultipleItemsState);
|
||||
@@ -0,0 +1,7 @@
|
||||
import { useBetween } from 'use-between';
|
||||
import { LocalStorageKeys } from '../../api';
|
||||
import { useLocalStorage } from '../useLocalStorage';
|
||||
|
||||
const useCatalogSkipPurchaseConfirmationState = () => useLocalStorage(LocalStorageKeys.CATALOG_SKIP_PURCHASE_CONFIRMATION, false);
|
||||
|
||||
export const useCatalogSkipPurchaseConfirmation = () => useBetween(useCatalogSkipPurchaseConfirmationState);
|
||||
@@ -0,0 +1 @@
|
||||
export * from './useChatHistory';
|
||||
@@ -0,0 +1,105 @@
|
||||
import { GetGuestRoomResultEvent, NewConsoleMessageEvent, RoomInviteEvent, RoomSessionEvent } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { ChatEntryType, ChatHistoryCurrentDate, IChatEntry, IRoomHistoryEntry, MessengerHistoryCurrentDate } from '../../api';
|
||||
import { useMessageEvent, useNitroEvent } from '../events';
|
||||
import { useLocalStorage } from '../useLocalStorage';
|
||||
|
||||
const CHAT_HISTORY_MAX = 1000;
|
||||
const ROOM_HISTORY_MAX = 10;
|
||||
const MESSENGER_HISTORY_MAX = 1000;
|
||||
|
||||
let CHAT_HISTORY_COUNTER: number = 0;
|
||||
let MESSENGER_HISTORY_COUNTER: number = 0;
|
||||
|
||||
const useChatHistoryState = () =>
|
||||
{
|
||||
const [ chatHistory, setChatHistory ] = useLocalStorage<IChatEntry[]>('chatHistory', []);
|
||||
const [ roomHistory, setRoomHistory ] = useLocalStorage<IRoomHistoryEntry[]>('roomHistory', []);
|
||||
const [ messengerHistory, setMessengerHistory ] = useLocalStorage<IChatEntry[]>('messengerHistory', []);
|
||||
const [ needsRoomInsert, setNeedsRoomInsert ] = useLocalStorage('needsRoomInsert', false);
|
||||
|
||||
const addChatEntry = (entry: IChatEntry) =>
|
||||
{
|
||||
entry.id = CHAT_HISTORY_COUNTER++;
|
||||
|
||||
setChatHistory(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
newValue.push(entry);
|
||||
|
||||
if(newValue.length > CHAT_HISTORY_MAX) newValue.shift();
|
||||
|
||||
return newValue;
|
||||
});
|
||||
};
|
||||
|
||||
const addRoomHistoryEntry = (entry: IRoomHistoryEntry) =>
|
||||
{
|
||||
setRoomHistory(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
newValue.push(entry);
|
||||
|
||||
if(newValue.length > ROOM_HISTORY_MAX) newValue.shift();
|
||||
|
||||
return newValue;
|
||||
});
|
||||
};
|
||||
|
||||
const addMessengerEntry = (entry: IChatEntry) =>
|
||||
{
|
||||
entry.id = MESSENGER_HISTORY_COUNTER++;
|
||||
|
||||
setMessengerHistory(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
newValue.push(entry);
|
||||
|
||||
if(newValue.length > MESSENGER_HISTORY_MAX) newValue.shift();
|
||||
|
||||
return newValue;
|
||||
});
|
||||
};
|
||||
|
||||
useNitroEvent<RoomSessionEvent>(RoomSessionEvent.STARTED, event => setNeedsRoomInsert(true));
|
||||
|
||||
useMessageEvent<GetGuestRoomResultEvent>(GetGuestRoomResultEvent, event =>
|
||||
{
|
||||
if(!needsRoomInsert) return;
|
||||
|
||||
const parser = event.getParser();
|
||||
|
||||
if(roomHistory.length)
|
||||
{
|
||||
if(roomHistory[(roomHistory.length - 1)].id === parser.data.roomId) return;
|
||||
}
|
||||
|
||||
addChatEntry({ id: -1, webId: -1, entityId: -1, name: parser.data.roomName, timestamp: ChatHistoryCurrentDate(), type: ChatEntryType.TYPE_ROOM_INFO, roomId: parser.data.roomId });
|
||||
|
||||
addRoomHistoryEntry({ id: parser.data.roomId, name: parser.data.roomName });
|
||||
|
||||
setNeedsRoomInsert(false);
|
||||
});
|
||||
|
||||
useMessageEvent<NewConsoleMessageEvent>(NewConsoleMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
addMessengerEntry({ id: -1, webId: parser.senderId, entityId: -1, name: '', message: parser.messageText, roomId: -1, timestamp: MessengerHistoryCurrentDate(parser.secondsSinceSent), type: ChatEntryType.TYPE_IM });
|
||||
});
|
||||
|
||||
useMessageEvent<RoomInviteEvent>(RoomInviteEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
addMessengerEntry({ id: -1, webId: parser.senderId, entityId: -1, name: '', message: parser.messageText, roomId: -1, timestamp: MessengerHistoryCurrentDate(), type: ChatEntryType.TYPE_IM });
|
||||
});
|
||||
|
||||
return { addChatEntry, chatHistory, roomHistory, messengerHistory };
|
||||
};
|
||||
|
||||
export const useChatHistory = () => useBetween(useChatHistoryState);
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './useEventDispatcher';
|
||||
export * from './useMessageEvent';
|
||||
export * from './useNitroEvent';
|
||||
export * from './useUiEvent';
|
||||
@@ -0,0 +1,31 @@
|
||||
import { IEventDispatcher, NitroEvent } from '@nitrots/nitro-renderer';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useEventDispatcher = <T extends NitroEvent>(type: string | string[], eventDispatcher: IEventDispatcher, handler: (event: T) => void, enabled: boolean = true) =>
|
||||
{
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!enabled) return;
|
||||
|
||||
if(Array.isArray(type))
|
||||
{
|
||||
type.map(name => eventDispatcher.addEventListener(name, handler));
|
||||
}
|
||||
else
|
||||
{
|
||||
eventDispatcher.addEventListener(type, handler);
|
||||
}
|
||||
|
||||
return () =>
|
||||
{
|
||||
if(Array.isArray(type))
|
||||
{
|
||||
type.map(name => eventDispatcher.removeEventListener(name, handler));
|
||||
}
|
||||
else
|
||||
{
|
||||
eventDispatcher.removeEventListener(type, handler);
|
||||
}
|
||||
};
|
||||
}, [ type, eventDispatcher, enabled, handler ]);
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
GetCommunication,
|
||||
IMessageEvent,
|
||||
MessageEvent,
|
||||
} from '@nitrots/nitro-renderer';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useMessageEvent = <T extends IMessageEvent>(
|
||||
eventType: typeof MessageEvent,
|
||||
handler: (event: T) => void
|
||||
) =>
|
||||
{
|
||||
useEffect(() =>
|
||||
{
|
||||
//@ts-ignore
|
||||
const event = new eventType(handler);
|
||||
|
||||
GetCommunication().registerMessageEvent(event);
|
||||
|
||||
return () => GetCommunication().removeMessageEvent(event);
|
||||
}, [eventType, handler]);
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
import { GetEventDispatcher, NitroEvent } from '@nitrots/nitro-renderer';
|
||||
import { useEventDispatcher } from './useEventDispatcher';
|
||||
|
||||
export const useNitroEvent = <T extends NitroEvent>(
|
||||
type: string | string[],
|
||||
handler: (event: T) => void,
|
||||
enabled = true
|
||||
) => useEventDispatcher(type, GetEventDispatcher(), handler, enabled);
|
||||
@@ -0,0 +1,5 @@
|
||||
import { NitroEvent } from '@nitrots/nitro-renderer';
|
||||
import { UI_EVENT_DISPATCHER } from '../../api';
|
||||
import { useEventDispatcher } from './useEventDispatcher';
|
||||
|
||||
export const useUiEvent = <T extends NitroEvent>(type: string | string[], handler: (event: T) => void, enabled: boolean = true) => useEventDispatcher<T>(type, UI_EVENT_DISPATCHER, handler, enabled);
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './useFriends';
|
||||
export * from './useMessenger';
|
||||
@@ -0,0 +1,252 @@
|
||||
import { AcceptFriendMessageComposer, DeclineFriendMessageComposer, FollowFriendMessageComposer, FriendListFragmentEvent, FriendListUpdateComposer, FriendListUpdateEvent, FriendParser, FriendRequestsEvent, GetFriendRequestsComposer, GetSessionDataManager, MessengerInitComposer, MessengerInitEvent, NewFriendRequestEvent, RequestFriendComposer, SetRelationshipStatusComposer } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { CloneObject, MessengerFriend, MessengerRequest, MessengerSettings, SendMessageComposer } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
|
||||
const useFriendsState = () =>
|
||||
{
|
||||
const [ friends, setFriends ] = useState<MessengerFriend[]>([]);
|
||||
const [ requests, setRequests ] = useState<MessengerRequest[]>([]);
|
||||
const [ sentRequests, setSentRequests ] = useState<number[]>([]);
|
||||
const [ dismissedRequestIds, setDismissedRequestIds ] = useState<number[]>([]);
|
||||
const [ settings, setSettings ] = useState<MessengerSettings>(null);
|
||||
|
||||
const onlineFriends = useMemo(() =>
|
||||
{
|
||||
const onlineFriends = friends.filter(friend => friend.online);
|
||||
|
||||
onlineFriends.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
||||
|
||||
return onlineFriends;
|
||||
}, [ friends ]);
|
||||
|
||||
const offlineFriends = useMemo(() =>
|
||||
{
|
||||
const offlineFriends = friends.filter(friend => !friend.online);
|
||||
|
||||
offlineFriends.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
||||
|
||||
return offlineFriends;
|
||||
}, [ friends ]);
|
||||
|
||||
const followFriend = (friend: MessengerFriend) => SendMessageComposer(new FollowFriendMessageComposer(friend.id));
|
||||
|
||||
const updateRelationship = (friend: MessengerFriend, type: number) => ((type !== friend.relationshipStatus) && SendMessageComposer(new SetRelationshipStatusComposer(friend.id, type)));
|
||||
|
||||
const getFriend = (userId: number) =>
|
||||
{
|
||||
for(const friend of friends)
|
||||
{
|
||||
if(friend.id === userId) return friend;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const canRequestFriend = (userId: number) =>
|
||||
{
|
||||
if(userId === GetSessionDataManager().userId) return false;
|
||||
|
||||
if(getFriend(userId)) return false;
|
||||
|
||||
if(requests.find(request => (request.requesterUserId === userId))) return false;
|
||||
|
||||
if(sentRequests.indexOf(userId) >= 0) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const requestFriend = (userId: number, userName: string) =>
|
||||
{
|
||||
if(!canRequestFriend(userId)) return false;
|
||||
|
||||
setSentRequests(prevValue =>
|
||||
{
|
||||
const newSentRequests = [ ...prevValue ];
|
||||
|
||||
newSentRequests.push(userId);
|
||||
|
||||
return newSentRequests;
|
||||
});
|
||||
|
||||
SendMessageComposer(new RequestFriendComposer(userName));
|
||||
};
|
||||
|
||||
const requestResponse = (requestId: number, flag: boolean) =>
|
||||
{
|
||||
if(requestId === -1 && !flag)
|
||||
{
|
||||
SendMessageComposer(new DeclineFriendMessageComposer(true));
|
||||
|
||||
setRequests([]);
|
||||
}
|
||||
else
|
||||
{
|
||||
setRequests(prevValue =>
|
||||
{
|
||||
const newRequests = [ ...prevValue ];
|
||||
const index = newRequests.findIndex(request => (request.id === requestId));
|
||||
|
||||
if(index === -1) return prevValue;
|
||||
|
||||
if(flag)
|
||||
{
|
||||
SendMessageComposer(new AcceptFriendMessageComposer(newRequests[index].id));
|
||||
}
|
||||
else
|
||||
{
|
||||
SendMessageComposer(new DeclineFriendMessageComposer(false, newRequests[index].id));
|
||||
}
|
||||
|
||||
newRequests.splice(index, 1);
|
||||
|
||||
return newRequests;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useMessageEvent<MessengerInitEvent>(MessengerInitEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setSettings(new MessengerSettings(
|
||||
parser.userFriendLimit,
|
||||
parser.normalFriendLimit,
|
||||
parser.extendedFriendLimit,
|
||||
parser.categories));
|
||||
|
||||
SendMessageComposer(new GetFriendRequestsComposer());
|
||||
});
|
||||
|
||||
useMessageEvent<FriendListFragmentEvent>(FriendListFragmentEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setFriends(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
for(const friend of parser.fragment)
|
||||
{
|
||||
const index = newValue.findIndex(existingFriend => (existingFriend.id === friend.id));
|
||||
const newFriend = new MessengerFriend();
|
||||
newFriend.populate(friend);
|
||||
|
||||
if(index > -1) newValue[index] = newFriend;
|
||||
else newValue.push(newFriend);
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<FriendListUpdateEvent>(FriendListUpdateEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setFriends(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
const processUpdate = (friend: FriendParser) =>
|
||||
{
|
||||
const index = newValue.findIndex(existingFriend => (existingFriend.id === friend.id));
|
||||
|
||||
if(index === -1)
|
||||
{
|
||||
const newFriend = new MessengerFriend();
|
||||
newFriend.populate(friend);
|
||||
|
||||
newValue.unshift(newFriend);
|
||||
}
|
||||
else
|
||||
{
|
||||
newValue[index].populate(friend);
|
||||
}
|
||||
};
|
||||
|
||||
for(const friend of parser.addedFriends) processUpdate(friend);
|
||||
|
||||
for(const friend of parser.updatedFriends) processUpdate(friend);
|
||||
|
||||
for(const removedFriendId of parser.removedFriendIds)
|
||||
{
|
||||
const index = newValue.findIndex(existingFriend => (existingFriend.id === removedFriendId));
|
||||
|
||||
if(index > -1) newValue.splice(index, 1);
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<FriendRequestsEvent>(FriendRequestsEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setRequests(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
for(const request of parser.requests)
|
||||
{
|
||||
const index = newValue.findIndex(existing => (existing.requesterUserId === request.requesterUserId));
|
||||
|
||||
if(index > 0)
|
||||
{
|
||||
newValue[index] = CloneObject(newValue[index]);
|
||||
newValue[index].populate(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
const newRequest = new MessengerRequest();
|
||||
newRequest.populate(request);
|
||||
|
||||
newValue.push(newRequest);
|
||||
}
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<NewFriendRequestEvent>(NewFriendRequestEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const request = parser.request;
|
||||
|
||||
setRequests(prevValue =>
|
||||
{
|
||||
const newRequests = [ ...prevValue ];
|
||||
|
||||
const index = newRequests.findIndex(existing => (existing.requesterUserId === request.requesterUserId));
|
||||
|
||||
if(index === -1)
|
||||
{
|
||||
const newRequest = new MessengerRequest();
|
||||
newRequest.populate(request);
|
||||
|
||||
newRequests.push(newRequest);
|
||||
}
|
||||
|
||||
return newRequests;
|
||||
});
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageComposer(new MessengerInitComposer());
|
||||
|
||||
const interval = setInterval(() => SendMessageComposer(new FriendListUpdateComposer()), 120000);
|
||||
|
||||
return () =>
|
||||
{
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { friends, requests, sentRequests, dismissedRequestIds, setDismissedRequestIds, settings, onlineFriends, offlineFriends, getFriend, canRequestFriend, requestFriend, requestResponse, followFriend, updateRelationship };
|
||||
};
|
||||
|
||||
export const useFriends = () => useBetween(useFriendsState);
|
||||
@@ -0,0 +1,187 @@
|
||||
import { GetSessionDataManager, NewConsoleMessageEvent, RoomInviteErrorEvent, RoomInviteEvent, SendMessageComposer as SendMessageComposerPacket } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { CloneObject, LocalizeText, MessengerIconState, MessengerThread, MessengerThreadChat, NotificationAlertType, PlaySound, SendMessageComposer, SoundNames } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
import { useNotification } from '../notification';
|
||||
import { useFriends } from './useFriends';
|
||||
|
||||
const useMessengerState = () =>
|
||||
{
|
||||
const [messageThreads, setMessageThreads] = useState<MessengerThread[]>([]);
|
||||
const [activeThreadId, setActiveThreadId] = useState<number>(-1);
|
||||
const [hiddenThreadIds, setHiddenThreadIds] = useState<number[]>([]);
|
||||
const [iconState, setIconState] = useState<number>(MessengerIconState.HIDDEN);
|
||||
const { getFriend = null } = useFriends();
|
||||
const { simpleAlert = null } = useNotification();
|
||||
|
||||
const visibleThreads = useMemo(() => messageThreads.filter(thread => (hiddenThreadIds.indexOf(thread.threadId) === -1)), [messageThreads, hiddenThreadIds]);
|
||||
const activeThread = useMemo(() => ((activeThreadId > 0) && visibleThreads.find(thread => (thread.threadId === activeThreadId) || null)), [activeThreadId, visibleThreads]);
|
||||
|
||||
const getMessageThread = (userId: number) =>
|
||||
{
|
||||
let thread = messageThreads.find(thread => (thread.participant && (thread.participant.id === userId)));
|
||||
|
||||
if (!thread)
|
||||
{
|
||||
const friend = getFriend(userId);
|
||||
|
||||
if (!friend) return null;
|
||||
|
||||
thread = new MessengerThread(friend);
|
||||
|
||||
thread.addMessage(null, LocalizeText('messenger.moderationinfo'), 0, null, MessengerThreadChat.SECURITY_NOTIFICATION);
|
||||
|
||||
thread.setRead();
|
||||
|
||||
setMessageThreads(prevValue =>
|
||||
{
|
||||
const newValue = [...prevValue];
|
||||
|
||||
newValue.push(thread);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
const hiddenIndex = hiddenThreadIds.indexOf(thread.threadId);
|
||||
|
||||
if (hiddenIndex >= 0)
|
||||
{
|
||||
setHiddenThreadIds(prevValue =>
|
||||
{
|
||||
const newValue = [...prevValue];
|
||||
|
||||
newValue.splice(hiddenIndex, 1);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return thread;
|
||||
};
|
||||
|
||||
const closeThread = (threadId: number) =>
|
||||
{
|
||||
setHiddenThreadIds(prevValue =>
|
||||
{
|
||||
const newValue = [...prevValue];
|
||||
|
||||
if (newValue.indexOf(threadId) >= 0) return prevValue;
|
||||
|
||||
newValue.push(threadId);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
if (activeThreadId === threadId) setActiveThreadId(-1);
|
||||
};
|
||||
|
||||
const sendMessage = (thread: MessengerThread, senderId: number, messageText: string, secondsSinceSent: number = 0, extraData: string = null, messageType: number = MessengerThreadChat.CHAT) =>
|
||||
{
|
||||
if (!thread || !messageText || !messageText.length) return;
|
||||
|
||||
const ownMessage = (senderId === GetSessionDataManager().userId);
|
||||
|
||||
if (ownMessage && (messageText.length <= 255)) SendMessageComposer(new SendMessageComposerPacket(thread.participant.id, messageText));
|
||||
|
||||
setMessageThreads(prevValue =>
|
||||
{
|
||||
const newValue = [...prevValue];
|
||||
const index = newValue.findIndex(newThread => (newThread.threadId === thread.threadId));
|
||||
|
||||
if (index === -1) return prevValue;
|
||||
|
||||
thread = CloneObject(newValue[index]);
|
||||
|
||||
if (ownMessage && (thread.groups.length === 1)) PlaySound(SoundNames.MESSENGER_NEW_THREAD);
|
||||
|
||||
thread.addMessage(((messageType === MessengerThreadChat.ROOM_INVITE) ? null : senderId), messageText, secondsSinceSent, extraData, messageType);
|
||||
|
||||
if (activeThreadId === thread.threadId) thread.setRead();
|
||||
|
||||
newValue[index] = thread;
|
||||
|
||||
if (!ownMessage && thread.unread) PlaySound(SoundNames.MESSENGER_MESSAGE_RECEIVED);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
};
|
||||
|
||||
useMessageEvent<NewConsoleMessageEvent>(NewConsoleMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const thread = getMessageThread(parser.senderId);
|
||||
|
||||
if (!thread) return;
|
||||
|
||||
sendMessage(thread, parser.senderId, parser.messageText, parser.secondsSinceSent, parser.extraData);
|
||||
});
|
||||
|
||||
useMessageEvent<RoomInviteEvent>(RoomInviteEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const thread = getMessageThread(parser.senderId);
|
||||
|
||||
if (!thread) return;
|
||||
|
||||
sendMessage(thread, parser.senderId, parser.messageText, 0, null, MessengerThreadChat.ROOM_INVITE);
|
||||
});
|
||||
|
||||
useMessageEvent<RoomInviteErrorEvent>(RoomInviteErrorEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
simpleAlert(`Received room invite error: ${ parser.errorCode },recipients: ${ parser.failedRecipients.join(',') }`, NotificationAlertType.DEFAULT, null, null, LocalizeText('friendlist.alert.title'));
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (activeThreadId <= 0) return;
|
||||
|
||||
setMessageThreads(prevValue =>
|
||||
{
|
||||
const newValue = [...prevValue];
|
||||
const index = newValue.findIndex(newThread => (newThread.threadId === activeThreadId));
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
newValue[index] = CloneObject(newValue[index]);
|
||||
|
||||
newValue[index].setRead();
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, [activeThreadId]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setIconState(prevValue =>
|
||||
{
|
||||
if (!visibleThreads.length) return MessengerIconState.HIDDEN;
|
||||
|
||||
let isUnread = false;
|
||||
|
||||
for (const thread of visibleThreads)
|
||||
{
|
||||
if (thread.unreadCount > 0)
|
||||
{
|
||||
isUnread = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isUnread) return MessengerIconState.UNREAD;
|
||||
|
||||
return MessengerIconState.SHOW;
|
||||
});
|
||||
}, [visibleThreads]);
|
||||
|
||||
return { messageThreads, activeThread, iconState, visibleThreads, getMessageThread, setActiveThreadId, closeThread, sendMessage };
|
||||
};
|
||||
|
||||
export const useMessenger = () => useBetween(useMessengerState);
|
||||
@@ -0,0 +1 @@
|
||||
export * from './useGameCenter';
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Game2AccountGameStatusMessageEvent, Game2AccountGameStatusMessageParser, GameConfigurationData, GameListMessageEvent, GameStatusMessageEvent, GetGameListMessageComposer, LoadGameUrlEvent } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { SendMessageComposer, VisitDesktop } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
|
||||
const useGameCenterState = () =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
||||
const [ games, setGames ] = useState<GameConfigurationData[]>(null);
|
||||
const [ selectedGame, setSelectedGame ] = useState<GameConfigurationData>(null);
|
||||
const [ accountStatus, setAccountStatus ] = useState<Game2AccountGameStatusMessageParser>(null);
|
||||
const [ gameOffline, setGameOffline ] = useState<boolean>(false);
|
||||
const [ gameURL, setGameURL ] = useState<string>(null);
|
||||
|
||||
useMessageEvent<GameListMessageEvent>(GameListMessageEvent, event =>
|
||||
{
|
||||
let parser = event.getParser();
|
||||
|
||||
if(!parser || parser && !parser.games.length) return;
|
||||
|
||||
setSelectedGame(parser.games[0]);
|
||||
|
||||
setGames(parser.games);
|
||||
});
|
||||
|
||||
useMessageEvent<Game2AccountGameStatusMessageEvent>(Game2AccountGameStatusMessageEvent, event =>
|
||||
{
|
||||
let parser = event.getParser();
|
||||
|
||||
if(!parser) return;
|
||||
|
||||
setAccountStatus(parser);
|
||||
});
|
||||
|
||||
useMessageEvent<GameStatusMessageEvent>(GameStatusMessageEvent, event =>
|
||||
{
|
||||
let parser = event.getParser();
|
||||
|
||||
if(!parser) return;
|
||||
|
||||
setGameOffline(parser.isInMaintenance);
|
||||
});
|
||||
|
||||
useMessageEvent<LoadGameUrlEvent>(LoadGameUrlEvent, event =>
|
||||
{
|
||||
let parser = event.getParser();
|
||||
|
||||
if(!parser) return;
|
||||
|
||||
switch(parser.gameTypeId)
|
||||
{
|
||||
case 2:
|
||||
return console.log('snowwar');
|
||||
default:
|
||||
return setGameURL(parser.url);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(()=>
|
||||
{
|
||||
if(isVisible)
|
||||
{
|
||||
SendMessageComposer(new GetGameListMessageComposer());
|
||||
VisitDesktop();
|
||||
}
|
||||
else
|
||||
{
|
||||
// dispose or wtv
|
||||
}
|
||||
},[ isVisible ]);
|
||||
|
||||
return {
|
||||
isVisible, setIsVisible,
|
||||
games,
|
||||
accountStatus,
|
||||
selectedGame, setSelectedGame,
|
||||
gameOffline,
|
||||
gameURL, setGameURL
|
||||
};
|
||||
};
|
||||
|
||||
export const useGameCenter = () => useBetween(useGameCenterState);
|
||||
@@ -0,0 +1 @@
|
||||
export * from './useGroup';
|
||||
@@ -0,0 +1,55 @@
|
||||
import { GroupBadgePartsComposer, GroupBadgePartsEvent } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { IGroupCustomize, SendMessageComposer } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
|
||||
const useGroupState = () =>
|
||||
{
|
||||
const [ groupCustomize, setGroupCustomize ] = useState<IGroupCustomize>(null);
|
||||
|
||||
useMessageEvent<GroupBadgePartsEvent>(GroupBadgePartsEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
const customize: IGroupCustomize = {
|
||||
badgeBases: [],
|
||||
badgeSymbols: [],
|
||||
badgePartColors: [],
|
||||
groupColorsA: [],
|
||||
groupColorsB: []
|
||||
};
|
||||
|
||||
parser.bases.forEach((images, id) => customize.badgeBases.push({ id, images }));
|
||||
parser.symbols.forEach((images, id) => customize.badgeSymbols.push({ id, images }));
|
||||
parser.partColors.forEach((color, id) => customize.badgePartColors.push({ id, color }));
|
||||
parser.colorsA.forEach((color, id) => customize.groupColorsA.push({ id, color }));
|
||||
parser.colorsB.forEach((color, id) => customize.groupColorsB.push({ id, color }));
|
||||
|
||||
const CompareId = (a: { id: number }, b: { id: number }) =>
|
||||
{
|
||||
if(a.id < b.id) return -1;
|
||||
|
||||
if(a.id > b.id) return 1;
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
customize.badgeBases.sort(CompareId);
|
||||
customize.badgeSymbols.sort(CompareId);
|
||||
customize.badgePartColors.sort(CompareId);
|
||||
customize.groupColorsA.sort(CompareId);
|
||||
customize.groupColorsB.sort(CompareId);
|
||||
|
||||
setGroupCustomize(customize);
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageComposer(new GroupBadgePartsComposer());
|
||||
}, []);
|
||||
|
||||
return { groupCustomize };
|
||||
};
|
||||
|
||||
export const useGroup = () => useBetween(useGroupState);
|
||||
@@ -0,0 +1 @@
|
||||
export * from './useHelp';
|
||||
@@ -0,0 +1,149 @@
|
||||
import { CallForHelpDisabledNotifyMessageEvent, CallForHelpPendingCallsDeletedMessageEvent, CallForHelpPendingCallsMessageEvent, CallForHelpReplyMessageEvent, CallForHelpResultMessageEvent, DeletePendingCallsForHelpMessageComposer, GetPendingCallsForHelpMessageComposer, IssueCloseNotificationMessageEvent, SanctionStatusEvent, SanctionStatusMessageParser } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { CallForHelpResult, GetCloseReasonKey, IHelpReport, LocalizeText, NotificationAlertType, ReportState, ReportType, SendMessageComposer } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
import { useNotification } from '../notification';
|
||||
|
||||
const useHelpState = () =>
|
||||
{
|
||||
const [ activeReport, setActiveReport ] = useState<IHelpReport>(null);
|
||||
const [ sanctionInfo, setSanctionInfo ] = useState<SanctionStatusMessageParser>(null);
|
||||
const { simpleAlert = null, showConfirm = null } = useNotification();
|
||||
|
||||
const report = (type: number, options: Partial<IHelpReport>) =>
|
||||
{
|
||||
const newReport: IHelpReport = {
|
||||
reportType: type,
|
||||
reportedUserId: -1,
|
||||
reportedChats: [],
|
||||
cfhCategory: -1,
|
||||
cfhTopic: -1,
|
||||
roomId: -1,
|
||||
roomName: '',
|
||||
messageId: -1,
|
||||
threadId: -1,
|
||||
groupId: -1,
|
||||
extraData: '',
|
||||
roomObjectId: -1,
|
||||
message: '',
|
||||
currentStep: 0
|
||||
};
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case ReportType.BULLY:
|
||||
case ReportType.EMERGENCY:
|
||||
case ReportType.IM:
|
||||
newReport.reportedUserId = options.reportedUserId;
|
||||
newReport.currentStep = ReportState.SELECT_CHATS;
|
||||
break;
|
||||
case ReportType.ROOM:
|
||||
newReport.roomId = options.roomId;
|
||||
newReport.roomName = options.roomName;
|
||||
newReport.currentStep = ReportState.SELECT_TOPICS;
|
||||
break;
|
||||
case ReportType.THREAD:
|
||||
newReport.groupId = options.groupId;
|
||||
newReport.threadId = options.threadId;
|
||||
newReport.currentStep = ReportState.SELECT_TOPICS;
|
||||
break;
|
||||
case ReportType.MESSAGE:
|
||||
newReport.groupId = options.groupId;
|
||||
newReport.threadId = options.threadId;
|
||||
newReport.messageId = options.messageId;
|
||||
newReport.currentStep = ReportState.SELECT_TOPICS;
|
||||
break;
|
||||
case ReportType.PHOTO:
|
||||
newReport.extraData = options.extraData;
|
||||
newReport.roomId = options.roomId;
|
||||
newReport.reportedUserId = options.reportedUserId;
|
||||
newReport.roomObjectId = options.roomObjectId;
|
||||
newReport.currentStep = ReportState.SELECT_TOPICS;
|
||||
break;
|
||||
case ReportType.GUIDE:
|
||||
break;
|
||||
}
|
||||
|
||||
setActiveReport(newReport);
|
||||
};
|
||||
|
||||
useMessageEvent<CallForHelpResultMessageEvent>(CallForHelpResultMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
let message = parser.messageText;
|
||||
|
||||
switch(parser.resultType)
|
||||
{
|
||||
case CallForHelpResult.TOO_MANY_PENDING_CALLS_CODE:
|
||||
SendMessageComposer(new GetPendingCallsForHelpMessageComposer());
|
||||
simpleAlert(LocalizeText('help.cfh.error.pending'), NotificationAlertType.MODERATION, null, null, LocalizeText('help.cfh.error.title'));
|
||||
break;
|
||||
case CallForHelpResult.HAS_ABUSIVE_CALL_CODE:
|
||||
simpleAlert(LocalizeText('help.cfh.error.abusive'), NotificationAlertType.MODERATION, null, null, LocalizeText('help.cfh.error.title'));
|
||||
break;
|
||||
default:
|
||||
if(message.trim().length === 0)
|
||||
{
|
||||
message = LocalizeText('help.cfh.sent.text');
|
||||
}
|
||||
|
||||
simpleAlert(message, NotificationAlertType.MODERATION, null, null, LocalizeText('help.cfh.sent.title'));
|
||||
}
|
||||
});
|
||||
|
||||
useMessageEvent<IssueCloseNotificationMessageEvent>(IssueCloseNotificationMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
const message = parser.messageText.length === 0 ? LocalizeText('help.cfh.closed.' + GetCloseReasonKey(parser.closeReason)) : parser.messageText;
|
||||
|
||||
simpleAlert(message, NotificationAlertType.MODERATION, null, null, LocalizeText('mod.alert.title'));
|
||||
});
|
||||
|
||||
useMessageEvent<CallForHelpPendingCallsMessageEvent>(CallForHelpPendingCallsMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(parser.count > 0)
|
||||
{
|
||||
showConfirm(LocalizeText('help.emergency.pending.title') + '\n' + parser.pendingCalls[0].message, () =>
|
||||
{
|
||||
SendMessageComposer(new DeletePendingCallsForHelpMessageComposer());
|
||||
}, null, LocalizeText('help.emergency.pending.button.discard'), LocalizeText('help.emergency.pending.button.keep'), LocalizeText('help.emergency.pending.message.subtitle'));
|
||||
}
|
||||
});
|
||||
|
||||
useMessageEvent<CallForHelpPendingCallsDeletedMessageEvent>(CallForHelpPendingCallsDeletedMessageEvent, event =>
|
||||
{
|
||||
const message = 'Your pending calls were deleted'; // todo: add localization
|
||||
|
||||
simpleAlert(message, NotificationAlertType.MODERATION, null, null, LocalizeText('mod.alert.title'));
|
||||
});
|
||||
|
||||
useMessageEvent<CallForHelpReplyMessageEvent>(CallForHelpReplyMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
simpleAlert(parser.message, NotificationAlertType.MODERATION, null, null, LocalizeText('help.cfh.reply.title'));
|
||||
});
|
||||
|
||||
useMessageEvent<CallForHelpDisabledNotifyMessageEvent>(CallForHelpDisabledNotifyMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
simpleAlert(LocalizeText('help.emergency.global_mute.message'), NotificationAlertType.MODERATION, parser.infoUrl, LocalizeText('help.emergency.global_mute.link'), LocalizeText('help.emergency.global_mute.subtitle'));
|
||||
});
|
||||
|
||||
useMessageEvent<SanctionStatusEvent>(SanctionStatusEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setSanctionInfo(parser);
|
||||
});
|
||||
|
||||
return { activeReport, setActiveReport, sanctionInfo, setSanctionInfo, report };
|
||||
};
|
||||
|
||||
export const useHelp = () => useBetween(useHelpState);
|
||||
@@ -0,0 +1,24 @@
|
||||
export * from './achievements';
|
||||
export * from './avatar-editor';
|
||||
export * from './camera';
|
||||
export * from './catalog';
|
||||
export * from './chat-history';
|
||||
export * from './events';
|
||||
export * from './friends';
|
||||
export * from './game-center';
|
||||
export * from './groups';
|
||||
export * from './help';
|
||||
export * from './inventory';
|
||||
export * from './mod-tools';
|
||||
export * from './navigator';
|
||||
export * from './notification';
|
||||
export * from './purse';
|
||||
export * from './rooms';
|
||||
export * from './rooms/engine';
|
||||
export * from './rooms/promotes';
|
||||
export * from './rooms/widgets';
|
||||
export * from './rooms/widgets/furniture';
|
||||
export * from './session';
|
||||
export * from './useLocalStorage';
|
||||
export * from './useSharedVisibility';
|
||||
export * from './wired';
|
||||
@@ -0,0 +1,6 @@
|
||||
export * from './useInventoryBadges';
|
||||
export * from './useInventoryBots';
|
||||
export * from './useInventoryFurni';
|
||||
export * from './useInventoryPets';
|
||||
export * from './useInventoryTrade';
|
||||
export * from './useInventoryUnseenTracker';
|
||||
@@ -0,0 +1,152 @@
|
||||
import { BadgeReceivedEvent, BadgesEvent, RequestBadgesComposer, SetActivatedBadgesComposer } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { GetConfigurationValue, SendMessageComposer, UnseenItemCategory } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
import { useSharedVisibility } from '../useSharedVisibility';
|
||||
import { useInventoryUnseenTracker } from './useInventoryUnseenTracker';
|
||||
|
||||
const useInventoryBadgesState = () =>
|
||||
{
|
||||
const [ needsUpdate, setNeedsUpdate ] = useState(true);
|
||||
const [ badgeCodes, setBadgeCodes ] = useState<string[]>([]);
|
||||
const [ badgeIds, setBadgeIds ] = useState<Map<string, number>>(new Map<string, number>());
|
||||
const [ activeBadgeCodes, setActiveBadgeCodes ] = useState<string[]>([]);
|
||||
const [ selectedBadgeCode, setSelectedBadgeCode ] = useState<string>(null);
|
||||
const { isVisible = false, activate = null, deactivate = null } = useSharedVisibility();
|
||||
const { isUnseen = null, resetCategory = null } = useInventoryUnseenTracker();
|
||||
|
||||
const maxBadgeCount = GetConfigurationValue<number>('user.badges.max.slots', 5);
|
||||
const isWearingBadge = (badgeCode: string) => (activeBadgeCodes.indexOf(badgeCode) >= 0);
|
||||
const canWearBadges = () => (activeBadgeCodes.length < maxBadgeCount);
|
||||
|
||||
const toggleBadge = (badgeCode: string) =>
|
||||
{
|
||||
setActiveBadgeCodes(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
const index = newValue.indexOf(badgeCode);
|
||||
|
||||
if(index === -1)
|
||||
{
|
||||
if(!canWearBadges()) return prevValue;
|
||||
|
||||
newValue.push(badgeCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
newValue.splice(index, 1);
|
||||
}
|
||||
|
||||
const composer = new SetActivatedBadgesComposer();
|
||||
|
||||
for(let i = 0; i < maxBadgeCount; i++) composer.addActivatedBadge(newValue[i] ?? '');
|
||||
|
||||
SendMessageComposer(composer);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
};
|
||||
|
||||
const getBadgeId = (badgeCode: string) =>
|
||||
{
|
||||
const index = badgeCodes.indexOf(badgeCode);
|
||||
|
||||
if(index === -1) return 0;
|
||||
|
||||
return (badgeIds.get(badgeCode) ?? 0);
|
||||
};
|
||||
|
||||
useMessageEvent<BadgesEvent>(BadgesEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const badgesToAdd: string[] = [];
|
||||
|
||||
setBadgeIds(prevValue =>
|
||||
{
|
||||
const newValue = new Map(prevValue);
|
||||
|
||||
parser.getAllBadgeCodes().forEach(code =>
|
||||
{
|
||||
const exists = badgeCodes.indexOf(code) >= 0;
|
||||
const badgeId = parser.getBadgeId(code);
|
||||
|
||||
newValue.set(code, badgeId);
|
||||
|
||||
if(exists) return;
|
||||
|
||||
badgesToAdd.push(code);
|
||||
});
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
setActiveBadgeCodes(parser.getActiveBadgeCodes());
|
||||
setBadgeCodes(prev => [ ...prev, ...badgesToAdd ]);
|
||||
});
|
||||
|
||||
useMessageEvent<BadgeReceivedEvent>(BadgeReceivedEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const unseen = isUnseen(UnseenItemCategory.BADGE, parser.badgeId);
|
||||
|
||||
setBadgeCodes(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
if(unseen) newValue.unshift(parser.badgeCode);
|
||||
else newValue.push(parser.badgeCode);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
setBadgeIds(prevValue =>
|
||||
{
|
||||
const newValue = new Map(prevValue);
|
||||
|
||||
newValue.set(parser.badgeCode, parser.badgeId);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!badgeCodes || !badgeCodes.length) return;
|
||||
|
||||
setSelectedBadgeCode(prevValue =>
|
||||
{
|
||||
let newValue = prevValue;
|
||||
|
||||
if(newValue && (badgeCodes.indexOf(newValue) === -1)) newValue = null;
|
||||
|
||||
if(!newValue) newValue = badgeCodes[0];
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, [ badgeCodes ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible) return;
|
||||
|
||||
return () =>
|
||||
{
|
||||
resetCategory(UnseenItemCategory.BADGE);
|
||||
};
|
||||
}, [ isVisible, resetCategory ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible || !needsUpdate) return;
|
||||
|
||||
SendMessageComposer(new RequestBadgesComposer());
|
||||
|
||||
setNeedsUpdate(false);
|
||||
}, [ isVisible, needsUpdate ]);
|
||||
|
||||
return { badgeCodes, activeBadgeCodes, selectedBadgeCode, setSelectedBadgeCode, isWearingBadge, canWearBadges, toggleBadge, getBadgeId, activate, deactivate };
|
||||
};
|
||||
|
||||
export const useInventoryBadges = () => useBetween(useInventoryBadgesState);
|
||||
@@ -0,0 +1,158 @@
|
||||
import { BotAddedToInventoryEvent, BotData, BotInventoryMessageEvent, BotRemovedFromInventoryEvent, CreateLinkEvent, GetBotInventoryComposer } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { IBotItem, SendMessageComposer, UnseenItemCategory, cancelRoomObjectPlacement, getPlacingItemId } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
import { useSharedVisibility } from '../useSharedVisibility';
|
||||
import { useInventoryUnseenTracker } from './useInventoryUnseenTracker';
|
||||
|
||||
const useInventoryBotsState = () =>
|
||||
{
|
||||
const [ needsUpdate, setNeedsUpdate ] = useState(true);
|
||||
const [ botItems, setBotItems ] = useState<IBotItem[]>([]);
|
||||
const [ selectedBot, setSelectedBot ] = useState<IBotItem>(null);
|
||||
const { isVisible = false, activate = null, deactivate = null } = useSharedVisibility();
|
||||
const { isUnseen = null, resetCategory = null } = useInventoryUnseenTracker();
|
||||
|
||||
useMessageEvent<BotInventoryMessageEvent>(BotInventoryMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setBotItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
const existingIds = newValue.map(item => item.botData.id);
|
||||
const addedDatas: BotData[] = [];
|
||||
|
||||
for(const botData of parser.items.values()) ((existingIds.indexOf(botData.id) === -1) && addedDatas.push(botData));
|
||||
|
||||
for(const existingId of existingIds)
|
||||
{
|
||||
let remove = true;
|
||||
|
||||
for(const botData of parser.items.values())
|
||||
{
|
||||
if(botData.id === existingId)
|
||||
{
|
||||
remove = false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!remove) continue;
|
||||
|
||||
const index = newValue.findIndex(item => (item.botData.id === existingId));
|
||||
const botItem = newValue[index];
|
||||
|
||||
if((index === -1) || !botItem) continue;
|
||||
|
||||
if(getPlacingItemId() === botItem.botData.id)
|
||||
{
|
||||
cancelRoomObjectPlacement();
|
||||
|
||||
CreateLinkEvent('inventory/open');
|
||||
}
|
||||
|
||||
newValue.splice(index, 1);
|
||||
}
|
||||
|
||||
for(const botData of addedDatas)
|
||||
{
|
||||
const botItem = { botData } as IBotItem;
|
||||
const unseen = isUnseen(UnseenItemCategory.BOT, botData.id);
|
||||
|
||||
if(unseen) newValue.unshift(botItem);
|
||||
else newValue.push(botItem);
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<BotAddedToInventoryEvent>(BotAddedToInventoryEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setBotItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
const index = newValue.findIndex(item => (item.botData.id === parser.item.id));
|
||||
|
||||
if(index >= 0) return prevValue;
|
||||
|
||||
const botItem = { botData: parser.item } as IBotItem;
|
||||
const unseen = isUnseen(UnseenItemCategory.BOT, botItem.botData.id);
|
||||
|
||||
if(unseen) newValue.unshift(botItem);
|
||||
else newValue.push(botItem);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<BotRemovedFromInventoryEvent>(BotRemovedFromInventoryEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setBotItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
const index = newValue.findIndex(item => (item.botData.id === parser.itemId));
|
||||
|
||||
if(index === -1) return prevValue;
|
||||
|
||||
newValue.splice(index, 1);
|
||||
|
||||
if(getPlacingItemId() === parser.itemId)
|
||||
{
|
||||
cancelRoomObjectPlacement();
|
||||
|
||||
CreateLinkEvent('inventory/show');
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!botItems || !botItems.length) return;
|
||||
|
||||
setSelectedBot(prevValue =>
|
||||
{
|
||||
let newValue = prevValue;
|
||||
|
||||
if(newValue && (botItems.indexOf(newValue) === -1)) newValue = null;
|
||||
|
||||
if(!newValue) newValue = botItems[0];
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, [ botItems ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible) return;
|
||||
|
||||
return () =>
|
||||
{
|
||||
resetCategory(UnseenItemCategory.BOT);
|
||||
};
|
||||
}, [ isVisible, resetCategory ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible || !needsUpdate) return;
|
||||
|
||||
SendMessageComposer(new GetBotInventoryComposer());
|
||||
|
||||
setNeedsUpdate(false);
|
||||
}, [ isVisible, needsUpdate ]);
|
||||
|
||||
return { botItems, selectedBot, setSelectedBot, activate, deactivate };
|
||||
};
|
||||
|
||||
export const useInventoryBots = () => useBetween(useInventoryBotsState);
|
||||
@@ -0,0 +1,298 @@
|
||||
import { CreateLinkEvent, FurnitureListAddOrUpdateEvent, FurnitureListComposer, FurnitureListEvent, FurnitureListInvalidateEvent, FurnitureListItemParser, FurnitureListRemovedEvent, FurniturePostItPlacedEvent } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { CloneObject, DispatchUiEvent, FurnitureItem, GroupItem, SendMessageComposer, UnseenItemCategory, addFurnitureItem, attemptItemPlacement, cancelRoomObjectPlacement, getAllItemIds, getPlacingItemId, mergeFurniFragments } from '../../api';
|
||||
import { InventoryFurniAddedEvent } from '../../events';
|
||||
import { useMessageEvent } from '../events';
|
||||
import { useSharedVisibility } from '../useSharedVisibility';
|
||||
import { useInventoryUnseenTracker } from './useInventoryUnseenTracker';
|
||||
|
||||
let furniMsgFragments: Map<number, FurnitureListItemParser>[] = null;
|
||||
|
||||
const useInventoryFurniState = () =>
|
||||
{
|
||||
const [ needsUpdate, setNeedsUpdate ] = useState(true);
|
||||
const [ groupItems, setGroupItems ] = useState<GroupItem[]>([]);
|
||||
const [ selectedItem, setSelectedItem ] = useState<GroupItem>(null);
|
||||
const { isVisible = false, activate = null, deactivate = null } = useSharedVisibility();
|
||||
const { isUnseen = null, resetCategory = null } = useInventoryUnseenTracker();
|
||||
|
||||
const getItemsByType = (type: number) =>
|
||||
{
|
||||
if(!groupItems || !groupItems.length) return;
|
||||
|
||||
return groupItems.filter((i) => i.type === type);
|
||||
};
|
||||
|
||||
const getWallItemById = (id: number) =>
|
||||
{
|
||||
if(!groupItems || !groupItems.length) return;
|
||||
|
||||
for(const groupItem of groupItems)
|
||||
{
|
||||
const item = groupItem.getItemById(id);
|
||||
|
||||
if(item && item.isWallItem) return groupItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const getFloorItemById = (id: number) =>
|
||||
{
|
||||
if(!groupItems || !groupItems.length) return;
|
||||
|
||||
for(const groupItem of groupItems)
|
||||
{
|
||||
const item = groupItem.getItemById(id);
|
||||
|
||||
if(item && !item.isWallItem) return groupItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
useMessageEvent<FurnitureListAddOrUpdateEvent>(FurnitureListAddOrUpdateEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setGroupItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
for(const item of parser.items)
|
||||
{
|
||||
let i = 0;
|
||||
let groupItem: GroupItem = null;
|
||||
|
||||
while(i < newValue.length)
|
||||
{
|
||||
const group = newValue[i];
|
||||
|
||||
let j = 0;
|
||||
|
||||
while(j < group.items.length)
|
||||
{
|
||||
const furniture = group.items[j];
|
||||
|
||||
if(furniture.id === item.itemId)
|
||||
{
|
||||
furniture.update(item);
|
||||
|
||||
const newFurniture = [ ...group.items ];
|
||||
|
||||
newFurniture[j] = furniture;
|
||||
|
||||
group.items = newFurniture;
|
||||
|
||||
groupItem = group;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
|
||||
if(groupItem) break;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if(groupItem)
|
||||
{
|
||||
groupItem.hasUnseenItems = true;
|
||||
|
||||
newValue[i] = CloneObject(groupItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
const furniture = new FurnitureItem(item);
|
||||
|
||||
addFurnitureItem(newValue, furniture, isUnseen(UnseenItemCategory.FURNI, item.itemId));
|
||||
|
||||
DispatchUiEvent(new InventoryFurniAddedEvent(furniture.id, furniture.type, furniture.category));
|
||||
}
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<FurnitureListEvent>(FurnitureListEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!furniMsgFragments) furniMsgFragments = new Array(parser.totalFragments);
|
||||
|
||||
const fragment = mergeFurniFragments(parser.fragment, parser.totalFragments, parser.fragmentNumber, furniMsgFragments);
|
||||
|
||||
if(!fragment) return;
|
||||
|
||||
setGroupItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
const existingIds = getAllItemIds(newValue);
|
||||
|
||||
for(const existingId of existingIds)
|
||||
{
|
||||
if(fragment.get(existingId)) continue;
|
||||
|
||||
let index = 0;
|
||||
|
||||
while(index < newValue.length)
|
||||
{
|
||||
const group = newValue[index];
|
||||
const item = group.remove(existingId);
|
||||
|
||||
if(!item)
|
||||
{
|
||||
index++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(getPlacingItemId() === item.ref)
|
||||
{
|
||||
cancelRoomObjectPlacement();
|
||||
|
||||
if(!attemptItemPlacement(group))
|
||||
{
|
||||
CreateLinkEvent('inventory/show');
|
||||
}
|
||||
}
|
||||
|
||||
if(group.getTotalCount() <= 0)
|
||||
{
|
||||
newValue.splice(index, 1);
|
||||
|
||||
group.dispose();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(const itemId of fragment.keys())
|
||||
{
|
||||
if(existingIds.indexOf(itemId) >= 0) continue;
|
||||
|
||||
const parser = fragment.get(itemId);
|
||||
|
||||
if(!parser) continue;
|
||||
|
||||
const item = new FurnitureItem(parser);
|
||||
|
||||
addFurnitureItem(newValue, item, isUnseen(UnseenItemCategory.FURNI, itemId));
|
||||
|
||||
DispatchUiEvent(new InventoryFurniAddedEvent(item.id, item.type, item.category));
|
||||
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
furniMsgFragments = null;
|
||||
});
|
||||
|
||||
useMessageEvent<FurnitureListInvalidateEvent>(FurnitureListInvalidateEvent, event =>
|
||||
{
|
||||
setNeedsUpdate(true);
|
||||
});
|
||||
|
||||
useMessageEvent<FurnitureListRemovedEvent>(FurnitureListRemovedEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setGroupItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
let index = 0;
|
||||
|
||||
while(index < newValue.length)
|
||||
{
|
||||
const group = newValue[index];
|
||||
const item = group.remove(parser.itemId);
|
||||
|
||||
if(!item)
|
||||
{
|
||||
index++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(getPlacingItemId() === item.ref)
|
||||
{
|
||||
cancelRoomObjectPlacement();
|
||||
|
||||
if(!attemptItemPlacement(group)) CreateLinkEvent('inventory/show');
|
||||
}
|
||||
|
||||
if(group.getTotalCount() <= 0)
|
||||
{
|
||||
newValue.splice(index, 1);
|
||||
|
||||
group.dispose();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<FurniturePostItPlacedEvent>(FurniturePostItPlacedEvent, event =>
|
||||
{
|
||||
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!groupItems || !groupItems.length) return;
|
||||
|
||||
setSelectedItem(prevValue =>
|
||||
{
|
||||
let newValue = prevValue;
|
||||
|
||||
if(newValue && (groupItems.indexOf(newValue) === -1)) newValue = null;
|
||||
|
||||
if(!newValue) newValue = groupItems[0];
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, [ groupItems ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible) return;
|
||||
|
||||
return () =>
|
||||
{
|
||||
if(resetCategory(UnseenItemCategory.FURNI))
|
||||
{
|
||||
setGroupItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
for(const newGroup of newValue) newGroup.hasUnseenItems = false;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
};
|
||||
}, [ isVisible, resetCategory ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible || !needsUpdate) return;
|
||||
|
||||
SendMessageComposer(new FurnitureListComposer());
|
||||
|
||||
setNeedsUpdate(false);
|
||||
}, [ isVisible, needsUpdate ]);
|
||||
|
||||
return { isVisible, groupItems, setGroupItems, selectedItem, setSelectedItem, activate, deactivate, getWallItemById, getFloorItemById, getItemsByType };
|
||||
};
|
||||
|
||||
export const useInventoryFurni = () => useBetween(useInventoryFurniState);
|
||||
@@ -0,0 +1,107 @@
|
||||
import { PetAddedToInventoryEvent, PetData, PetInventoryEvent, PetRemovedFromInventory, RequestPetsComposer } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { addSinglePetItem, IPetItem, mergePetFragments, processPetFragment, removePetItemById, SendMessageComposer, UnseenItemCategory } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
import { useSharedVisibility } from '../useSharedVisibility';
|
||||
import { useInventoryUnseenTracker } from './useInventoryUnseenTracker';
|
||||
|
||||
let petMsgFragments: Map<number, PetData>[] = null;
|
||||
|
||||
const useInventoryPetsState = () =>
|
||||
{
|
||||
const [ needsUpdate, setNeedsUpdate ] = useState(true);
|
||||
const [ petItems, setPetItems ] = useState<IPetItem[]>([]);
|
||||
const [ selectedPet, setSelectedPet ] = useState<IPetItem>(null);
|
||||
const { isVisible = false, activate = null, deactivate = null } = useSharedVisibility();
|
||||
const { isUnseen = null, resetCategory = null } = useInventoryUnseenTracker();
|
||||
|
||||
useMessageEvent<PetInventoryEvent>(PetInventoryEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!petMsgFragments) petMsgFragments = new Array(parser.totalFragments);
|
||||
|
||||
const fragment = mergePetFragments(parser.fragment, parser.totalFragments, parser.fragmentNumber, petMsgFragments);
|
||||
|
||||
if(!fragment) return;
|
||||
|
||||
setPetItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
processPetFragment(newValue, fragment, isUnseen);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
petMsgFragments = null;
|
||||
});
|
||||
|
||||
useMessageEvent<PetAddedToInventoryEvent>(PetAddedToInventoryEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setPetItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
addSinglePetItem(parser.pet, newValue, isUnseen(UnseenItemCategory.PET, parser.pet.id));
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<PetRemovedFromInventory>(PetRemovedFromInventory, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setPetItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
removePetItemById(parser.petId, newValue);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!petItems || !petItems.length) return;
|
||||
|
||||
setSelectedPet(prevValue =>
|
||||
{
|
||||
let newValue = prevValue;
|
||||
|
||||
if(newValue && (petItems.indexOf(newValue) === -1)) newValue = null;
|
||||
|
||||
if(!newValue) newValue = petItems[0];
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, [ petItems ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible) return;
|
||||
|
||||
return () =>
|
||||
{
|
||||
resetCategory(UnseenItemCategory.PET);
|
||||
};
|
||||
}, [ isVisible, resetCategory ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isVisible || !needsUpdate) return;
|
||||
|
||||
SendMessageComposer(new RequestPetsComposer());
|
||||
|
||||
setNeedsUpdate(false);
|
||||
}, [ isVisible, needsUpdate ]);
|
||||
|
||||
return { petItems, selectedPet, setSelectedPet, activate, deactivate };
|
||||
};
|
||||
|
||||
export const useInventoryPets = () => useBetween(useInventoryPetsState);
|
||||
@@ -0,0 +1,288 @@
|
||||
import { AdvancedMap, GetSessionDataManager, TradingAcceptComposer, TradingAcceptEvent, TradingCancelComposer, TradingCloseComposer, TradingCloseEvent, TradingCloseParser, TradingCompletedEvent, TradingConfirmationComposer, TradingConfirmationEvent, TradingListItemEvent, TradingListItemRemoveComposer, TradingNotOpenEvent, TradingOpenEvent, TradingOpenFailedEvent, TradingOtherNotAllowedEvent, TradingUnacceptComposer, TradingYouAreNotAllowedEvent } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { CloneObject, GetRoomSession, GroupItem, LocalizeText, SendMessageComposer, TradeState, TradeUserData, TradingNotificationType, parseTradeItems } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
import { useNotification } from '../notification';
|
||||
import { useInventoryFurni } from './useInventoryFurni';
|
||||
|
||||
const useInventoryTradeState = () =>
|
||||
{
|
||||
const [ ownUser, setOwnUser ] = useState<TradeUserData>(null);
|
||||
const [ otherUser, setOtherUser ] = useState<TradeUserData>(null);
|
||||
const [ tradeState, setTradeState ] = useState(TradeState.TRADING_STATE_READY);
|
||||
const { groupItems = [], setGroupItems = null, activate = null, deactivate = null } = useInventoryFurni();
|
||||
const { simpleAlert = null, showTradeAlert = null } = useNotification();
|
||||
const isTrading = (tradeState >= TradeState.TRADING_STATE_RUNNING);
|
||||
|
||||
const progressTrade = () =>
|
||||
{
|
||||
switch(tradeState)
|
||||
{
|
||||
case TradeState.TRADING_STATE_RUNNING:
|
||||
if(!otherUser.itemCount && !ownUser.accepts)
|
||||
{
|
||||
simpleAlert(LocalizeText('inventory.trading.warning.other_not_offering'), null, null, null);
|
||||
}
|
||||
|
||||
if(ownUser.accepts)
|
||||
{
|
||||
SendMessageComposer(new TradingUnacceptComposer());
|
||||
}
|
||||
else
|
||||
{
|
||||
SendMessageComposer(new TradingAcceptComposer());
|
||||
}
|
||||
return;
|
||||
case TradeState.TRADING_STATE_CONFIRMING:
|
||||
SendMessageComposer(new TradingConfirmationComposer());
|
||||
|
||||
setTradeState(TradeState.TRADING_STATE_CONFIRMED);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const removeItem = (group: GroupItem) =>
|
||||
{
|
||||
const item = group.getLastItem();
|
||||
|
||||
if(!item) return;
|
||||
|
||||
SendMessageComposer(new TradingListItemRemoveComposer(item.id));
|
||||
};
|
||||
|
||||
const stopTrading = () =>
|
||||
{
|
||||
if(!isTrading) return;
|
||||
|
||||
switch(tradeState)
|
||||
{
|
||||
case TradeState.TRADING_STATE_RUNNING:
|
||||
SendMessageComposer(new TradingCloseComposer());
|
||||
return;
|
||||
default:
|
||||
SendMessageComposer(new TradingCancelComposer());
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
useMessageEvent<TradingAcceptEvent>(TradingAcceptEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!ownUser || !otherUser) return;
|
||||
|
||||
if(ownUser.userId === parser.userID)
|
||||
{
|
||||
setOwnUser(prevValue =>
|
||||
{
|
||||
const newValue = CloneObject(prevValue);
|
||||
|
||||
newValue.accepts = parser.userAccepts;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
|
||||
else if(otherUser.userId === parser.userID)
|
||||
{
|
||||
setOtherUser(prevValue =>
|
||||
{
|
||||
const newValue = CloneObject(prevValue);
|
||||
|
||||
newValue.accepts = parser.userAccepts;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
useMessageEvent<TradingCloseEvent>(TradingCloseEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(parser.reason === TradingCloseParser.ERROR_WHILE_COMMIT)
|
||||
{
|
||||
showTradeAlert(TradingNotificationType.ERROR_WHILE_COMMIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ownUser && (parser.userID !== ownUser.userId))
|
||||
{
|
||||
showTradeAlert(TradingNotificationType.THEY_CANCELLED);
|
||||
}
|
||||
}
|
||||
|
||||
setOwnUser(null);
|
||||
setOtherUser(null);
|
||||
setTradeState(TradeState.TRADING_STATE_READY);
|
||||
});
|
||||
|
||||
useMessageEvent<TradingCompletedEvent>(TradingCompletedEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setOwnUser(null);
|
||||
setOtherUser(null);
|
||||
setTradeState(TradeState.TRADING_STATE_READY);
|
||||
});
|
||||
|
||||
useMessageEvent<TradingConfirmationEvent>(TradingConfirmationEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setTradeState(TradeState.TRADING_STATE_COUNTDOWN);
|
||||
});
|
||||
|
||||
useMessageEvent<TradingListItemEvent>(TradingListItemEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const firstUserItems = parseTradeItems(parser.firstUserItemArray);
|
||||
const secondUserItems = parseTradeItems(parser.secondUserItemArray);
|
||||
|
||||
setOwnUser(prevValue =>
|
||||
{
|
||||
const newValue = CloneObject(prevValue);
|
||||
|
||||
if(newValue.userId === parser.firstUserID)
|
||||
{
|
||||
newValue.creditsCount = parser.firstUserNumCredits;
|
||||
newValue.itemCount = parser.firstUserNumItems;
|
||||
newValue.userItems = firstUserItems;
|
||||
}
|
||||
else
|
||||
{
|
||||
newValue.creditsCount = parser.secondUserNumCredits;
|
||||
newValue.itemCount = parser.secondUserNumItems;
|
||||
newValue.userItems = secondUserItems;
|
||||
}
|
||||
|
||||
const tradeIds: number[] = [];
|
||||
|
||||
for(const groupItem of newValue.userItems.getValues())
|
||||
{
|
||||
let i = 0;
|
||||
|
||||
while(i < groupItem.getTotalCount())
|
||||
{
|
||||
const item = groupItem.getItemByIndex(i);
|
||||
|
||||
if(item) tradeIds.push(item.ref);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
setGroupItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
for(const groupItem of newValue) groupItem.lockItemIds(tradeIds);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
setOtherUser(prevValue =>
|
||||
{
|
||||
const newValue = CloneObject(prevValue);
|
||||
|
||||
if(newValue.userId === parser.firstUserID)
|
||||
{
|
||||
newValue.creditsCount = parser.firstUserNumCredits;
|
||||
newValue.itemCount = parser.firstUserNumItems;
|
||||
newValue.userItems = firstUserItems;
|
||||
}
|
||||
else
|
||||
{
|
||||
newValue.creditsCount = parser.secondUserNumCredits;
|
||||
newValue.itemCount = parser.secondUserNumItems;
|
||||
newValue.userItems = secondUserItems;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<TradingNotOpenEvent>(TradingNotOpenEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
});
|
||||
|
||||
useMessageEvent<TradingOpenEvent>(TradingOpenEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
const firstUser = new TradeUserData();
|
||||
const firstUserData = GetRoomSession().userDataManager.getUserData(parser.userID);
|
||||
|
||||
firstUser.userItems = new AdvancedMap();
|
||||
|
||||
const secondUser = new TradeUserData();
|
||||
const secondUserData = GetRoomSession().userDataManager.getUserData(parser.otherUserID);
|
||||
|
||||
secondUser.userItems = new AdvancedMap();
|
||||
|
||||
if(firstUserData.webID === GetSessionDataManager().userId)
|
||||
{
|
||||
firstUser.userId = firstUserData.webID;
|
||||
firstUser.userName = firstUserData.name;
|
||||
firstUser.canTrade = parser.userCanTrade;
|
||||
|
||||
secondUser.userId = secondUserData.webID;
|
||||
secondUser.userName = secondUserData.name;
|
||||
secondUser.canTrade = parser.otherUserCanTrade;
|
||||
}
|
||||
|
||||
else if(secondUserData.webID === GetSessionDataManager().userId)
|
||||
{
|
||||
firstUser.userId = secondUserData.webID;
|
||||
firstUser.userName = secondUserData.name;
|
||||
firstUser.canTrade = parser.otherUserCanTrade;
|
||||
|
||||
secondUser.userId = firstUserData.webID;
|
||||
secondUser.userName = firstUserData.name;
|
||||
secondUser.canTrade = parser.userCanTrade;
|
||||
}
|
||||
|
||||
setOwnUser(firstUser);
|
||||
setOtherUser(secondUser);
|
||||
setTradeState(TradeState.TRADING_STATE_RUNNING);
|
||||
});
|
||||
|
||||
useMessageEvent<TradingOpenFailedEvent>(TradingOpenFailedEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
showTradeAlert(parser.reason, parser.otherUserName);
|
||||
});
|
||||
|
||||
useMessageEvent<TradingOtherNotAllowedEvent>(TradingOtherNotAllowedEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
showTradeAlert(TradingNotificationType.THEY_NOT_ALLOWED);
|
||||
});
|
||||
|
||||
useMessageEvent<TradingYouAreNotAllowedEvent>(TradingYouAreNotAllowedEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
showTradeAlert(TradingNotificationType.YOU_NOT_ALLOWED);
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(tradeState === TradeState.TRADING_STATE_READY) return;
|
||||
|
||||
const id = activate();
|
||||
|
||||
return () => deactivate(id);
|
||||
}, [ tradeState, activate, deactivate ]);
|
||||
|
||||
return { ownUser, otherUser, tradeState, setTradeState, isTrading, groupItems, progressTrade, removeItem, stopTrading };
|
||||
};
|
||||
|
||||
export const useInventoryTrade = () => useBetween(useInventoryTradeState);
|
||||
@@ -0,0 +1,132 @@
|
||||
import { UnseenItemsEvent, UnseenResetCategoryComposer, UnseenResetItemsComposer } from '@nitrots/nitro-renderer';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { SendMessageComposer } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
|
||||
const sendResetCategoryMessage = (category: number) => SendMessageComposer(new UnseenResetCategoryComposer(category));
|
||||
const sendResetItemsMessage = (category: number, itemIds: number[]) => SendMessageComposer(new UnseenResetItemsComposer(category, ...itemIds));
|
||||
|
||||
const useInventoryUnseenTrackerState = () =>
|
||||
{
|
||||
const [ unseenItems, setUnseenItems ] = useState<Map<number, number[]>>(new Map());
|
||||
|
||||
const getCount = useCallback((category: number) => (unseenItems.get(category)?.length || 0), [ unseenItems ]);
|
||||
|
||||
const getFullCount = useMemo(() =>
|
||||
{
|
||||
let count = 0;
|
||||
|
||||
for(const key of unseenItems.keys()) count += getCount(key);
|
||||
|
||||
return count;
|
||||
}, [ unseenItems, getCount ]);
|
||||
|
||||
const resetCategory = useCallback((category: number) =>
|
||||
{
|
||||
let didReset = true;
|
||||
|
||||
setUnseenItems(prevValue =>
|
||||
{
|
||||
if(!prevValue.has(category))
|
||||
{
|
||||
didReset = false;
|
||||
|
||||
return prevValue;
|
||||
}
|
||||
|
||||
const newValue = new Map(prevValue);
|
||||
|
||||
newValue.delete(category);
|
||||
|
||||
sendResetCategoryMessage(category);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
return didReset;
|
||||
}, []);
|
||||
|
||||
const resetItems = useCallback((category: number, itemIds: number[]) =>
|
||||
{
|
||||
let didReset = true;
|
||||
|
||||
setUnseenItems(prevValue =>
|
||||
{
|
||||
if(!prevValue.has(category))
|
||||
{
|
||||
didReset = false;
|
||||
|
||||
return prevValue;
|
||||
}
|
||||
|
||||
const newValue = new Map(prevValue);
|
||||
const existing = newValue.get(category);
|
||||
|
||||
if(existing) for(const itemId of itemIds) existing.splice(existing.indexOf(itemId), 1);
|
||||
|
||||
sendResetItemsMessage(category, itemIds);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
return didReset;
|
||||
}, []);
|
||||
|
||||
const isUnseen = useCallback((category: number, itemId: number) =>
|
||||
{
|
||||
if(!unseenItems.has(category)) return false;
|
||||
|
||||
const items = unseenItems.get(category);
|
||||
|
||||
return (items.indexOf(itemId) >= 0);
|
||||
}, [ unseenItems ]);
|
||||
|
||||
const removeUnseen = useCallback((category: number, itemId: number) =>
|
||||
{
|
||||
setUnseenItems(prevValue =>
|
||||
{
|
||||
if(!prevValue.has(category)) return prevValue;
|
||||
|
||||
const newValue = new Map(prevValue);
|
||||
const items = newValue.get(category);
|
||||
const index = items.indexOf(itemId);
|
||||
|
||||
if(index >= 0) items.splice(index, 1);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, []);
|
||||
|
||||
useMessageEvent<UnseenItemsEvent>(UnseenItemsEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setUnseenItems(prevValue =>
|
||||
{
|
||||
const newValue = new Map(prevValue);
|
||||
|
||||
for(const category of parser.categories)
|
||||
{
|
||||
let existing = newValue.get(category);
|
||||
|
||||
if(!existing)
|
||||
{
|
||||
existing = [];
|
||||
|
||||
newValue.set(category, existing);
|
||||
}
|
||||
|
||||
const itemIds = parser.getItemsByCategory(category);
|
||||
|
||||
for(const itemId of itemIds) ((existing.indexOf(itemId) === -1) && existing.push(itemId));
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
return { getCount, getFullCount, resetCategory, resetItems, isUnseen, removeUnseen };
|
||||
};
|
||||
|
||||
export const useInventoryUnseenTracker = () => useBetween(useInventoryUnseenTrackerState);
|
||||
@@ -0,0 +1 @@
|
||||
export * from './useModTools';
|
||||
@@ -0,0 +1,207 @@
|
||||
import { CallForHelpCategoryData, CfhSanctionMessageEvent, CfhTopicsInitEvent, IssueDeletedMessageEvent, IssueInfoMessageEvent, IssueMessageData, IssuePickFailedMessageEvent, ModeratorActionResultMessageEvent, ModeratorInitData, ModeratorInitMessageEvent, ModeratorToolPreferencesEvent } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { NotificationAlertType, PlaySound, SoundNames } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
import { useNotification } from '../notification';
|
||||
|
||||
const useModToolsState = () =>
|
||||
{
|
||||
const [ settings, setSettings ] = useState<ModeratorInitData>(null);
|
||||
const [ openRooms, setOpenRooms ] = useState<number[]>([]);
|
||||
const [ openRoomChatlogs, setOpenRoomChatlogs ] = useState<number[]>([]);
|
||||
const [ openUserInfos, setOpenUserInfos ] = useState<number[]>([]);
|
||||
const [ openUserChatlogs, setOpenUserChatlogs ] = useState<number[]>([]);
|
||||
const [ tickets, setTickets ] = useState<IssueMessageData[]>([]);
|
||||
const [ cfhCategories, setCfhCategories ] = useState<CallForHelpCategoryData[]>([]);
|
||||
const { simpleAlert = null } = useNotification();
|
||||
|
||||
const openRoomInfo = (roomId: number) =>
|
||||
{
|
||||
if(openRooms.indexOf(roomId) >= 0) return;
|
||||
|
||||
setOpenRooms(prevValue => [ ...prevValue, roomId ]);
|
||||
};
|
||||
|
||||
const closeRoomInfo = (roomId: number) =>
|
||||
{
|
||||
setOpenRooms(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
const existingIndex = newValue.indexOf(roomId);
|
||||
|
||||
if(existingIndex >= 0) newValue.splice(existingIndex);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
};
|
||||
|
||||
const toggleRoomInfo = (roomId: number) =>
|
||||
{
|
||||
if(openRooms.indexOf(roomId) >= 0) closeRoomInfo(roomId);
|
||||
else openRoomInfo(roomId);
|
||||
};
|
||||
|
||||
const openRoomChatlog = (roomId: number) =>
|
||||
{
|
||||
if(openRoomChatlogs.indexOf(roomId) >= 0) return;
|
||||
|
||||
setOpenRoomChatlogs(prevValue => [ ...prevValue, roomId ]);
|
||||
};
|
||||
|
||||
const closeRoomChatlog = (roomId: number) =>
|
||||
{
|
||||
setOpenRoomChatlogs(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
const existingIndex = newValue.indexOf(roomId);
|
||||
|
||||
if(existingIndex >= 0) newValue.splice(existingIndex);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
};
|
||||
|
||||
const toggleRoomChatlog = (roomId: number) =>
|
||||
{
|
||||
if(openRoomChatlogs.indexOf(roomId) >= 0) closeRoomChatlog(roomId);
|
||||
else openRoomChatlog(roomId);
|
||||
};
|
||||
|
||||
const openUserInfo = (userId: number) =>
|
||||
{
|
||||
if(openUserInfos.indexOf(userId) >= 0) return;
|
||||
|
||||
setOpenUserInfos(prevValue => [ ...prevValue, userId ]);
|
||||
};
|
||||
|
||||
const closeUserInfo = (userId: number) =>
|
||||
{
|
||||
setOpenUserInfos(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
const existingIndex = newValue.indexOf(userId);
|
||||
|
||||
if(existingIndex >= 0) newValue.splice(existingIndex);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
};
|
||||
|
||||
const toggleUserInfo = (userId: number) =>
|
||||
{
|
||||
if(openUserInfos.indexOf(userId) >= 0) closeUserInfo(userId);
|
||||
else openUserInfo(userId);
|
||||
};
|
||||
|
||||
const openUserChatlog = (userId: number) =>
|
||||
{
|
||||
if(openUserChatlogs.indexOf(userId) >= 0) return;
|
||||
|
||||
setOpenUserChatlogs(prevValue => [ ...prevValue, userId ]);
|
||||
};
|
||||
|
||||
const closeUserChatlog = (userId: number) =>
|
||||
{
|
||||
setOpenUserChatlogs(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
const existingIndex = newValue.indexOf(userId);
|
||||
|
||||
if(existingIndex >= 0) newValue.splice(existingIndex);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
};
|
||||
|
||||
const toggleUserChatlog = (userId: number) =>
|
||||
{
|
||||
if(openRoomChatlogs.indexOf(userId) >= 0) closeUserChatlog(userId);
|
||||
else openUserChatlog(userId);
|
||||
};
|
||||
|
||||
useMessageEvent<ModeratorInitMessageEvent>(ModeratorInitMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const data = parser.data;
|
||||
|
||||
setSettings(data);
|
||||
setTickets(data.issues);
|
||||
});
|
||||
|
||||
useMessageEvent<IssueInfoMessageEvent>(IssueInfoMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setTickets(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
const existingIndex = newValue.findIndex(ticket => (ticket.issueId === parser.issueData.issueId));
|
||||
|
||||
if(existingIndex >= 0) newValue[existingIndex] = parser.issueData;
|
||||
else
|
||||
{
|
||||
newValue.push(parser.issueData);
|
||||
|
||||
PlaySound(SoundNames.MODTOOLS_NEW_TICKET);
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<ModeratorToolPreferencesEvent>(ModeratorToolPreferencesEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
});
|
||||
|
||||
useMessageEvent<IssuePickFailedMessageEvent>(IssuePickFailedMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser) return;
|
||||
|
||||
simpleAlert('Failed to pick issue', NotificationAlertType.DEFAULT, null, null, 'Error');
|
||||
});
|
||||
|
||||
useMessageEvent<IssueDeletedMessageEvent>(IssueDeletedMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setTickets(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
const existingIndex = newValue.findIndex(ticket => (ticket.issueId === parser.issueId));
|
||||
|
||||
if(existingIndex >= 0) newValue.splice(existingIndex, 1);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<ModeratorActionResultMessageEvent>(ModeratorActionResultMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(parser.success) simpleAlert('Moderation action was successfull', NotificationAlertType.MODERATION, null, null, 'Success');
|
||||
else simpleAlert('There was a problem applying tht moderation action', NotificationAlertType.MODERATION, null, null, 'Error');
|
||||
});
|
||||
|
||||
useMessageEvent<CfhTopicsInitEvent>(CfhTopicsInitEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setCfhCategories(parser.callForHelpCategories);
|
||||
});
|
||||
|
||||
useMessageEvent<CfhSanctionMessageEvent>(CfhSanctionMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
// todo: update sanction data
|
||||
});
|
||||
|
||||
return { settings, openRooms, openRoomChatlogs, openUserChatlogs, openUserInfos, cfhCategories, tickets, openRoomInfo, closeRoomInfo, toggleRoomInfo, openRoomChatlog, closeRoomChatlog, toggleRoomChatlog, openUserInfo, closeUserInfo, toggleUserInfo, openUserChatlog, closeUserChatlog, toggleUserChatlog };
|
||||
};
|
||||
|
||||
export const useModTools = () => useBetween(useModToolsState);
|
||||
@@ -0,0 +1 @@
|
||||
export * from './useNavigator';
|
||||
@@ -0,0 +1,442 @@
|
||||
import { CanCreateRoomEventEvent, CantConnectMessageParser, CreateLinkEvent, DoorbellMessageEvent, FlatAccessDeniedMessageEvent, FlatCreatedEvent, FollowFriendMessageComposer, GenericErrorEvent, GetGuestRoomMessageComposer, GetGuestRoomResultEvent, GetSessionDataManager, GetUserEventCatsMessageComposer, GetUserFlatCatsMessageComposer, HabboWebTools, LegacyExternalInterface, NavigatorCategoryDataParser, NavigatorEventCategoryDataParser, NavigatorHomeRoomEvent, NavigatorMetadataEvent, NavigatorOpenRoomCreatorEvent, NavigatorSearchEvent, NavigatorSearchResultSet, NavigatorTopLevelContext, RoomDataParser, RoomDoorbellAcceptedEvent, RoomEnterErrorEvent, RoomEntryInfoMessageEvent, RoomForwardEvent, RoomScoreEvent, RoomSettingsUpdatedEvent, SecurityLevel, UserEventCatsEvent, UserFlatCatsEvent, UserInfoEvent, UserPermissionsEvent } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { CreateRoomSession, DoorStateType, GetConfigurationValue, INavigatorData, LocalizeText, NotificationAlertType, SendMessageComposer, TryVisitRoom, VisitDesktop } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
import { useNotification } from '../notification';
|
||||
|
||||
const useNavigatorState = () =>
|
||||
{
|
||||
const [ categories, setCategories ] = useState<NavigatorCategoryDataParser[]>(null);
|
||||
const [ eventCategories, setEventCategories ] = useState<NavigatorEventCategoryDataParser[]>(null);
|
||||
const [ topLevelContext, setTopLevelContext ] = useState<NavigatorTopLevelContext>(null);
|
||||
const [ topLevelContexts, setTopLevelContexts ] = useState<NavigatorTopLevelContext[]>(null);
|
||||
const [ doorData, setDoorData ] = useState<{ roomInfo: RoomDataParser, state: number }>({ roomInfo: null, state: DoorStateType.NONE });
|
||||
const [ searchResult, setSearchResult ] = useState<NavigatorSearchResultSet>(null);
|
||||
const [ navigatorData, setNavigatorData ] = useState<INavigatorData>({
|
||||
settingsReceived: false,
|
||||
homeRoomId: 0,
|
||||
enteredGuestRoom: null,
|
||||
currentRoomOwner: false,
|
||||
currentRoomId: 0,
|
||||
currentRoomIsStaffPick: false,
|
||||
createdFlatId: 0,
|
||||
avatarId: 0,
|
||||
roomPicker: false,
|
||||
eventMod: false,
|
||||
currentRoomRating: 0,
|
||||
canRate: true
|
||||
});
|
||||
const { simpleAlert = null } = useNotification();
|
||||
|
||||
useMessageEvent<RoomSettingsUpdatedEvent>(RoomSettingsUpdatedEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
SendMessageComposer(new GetGuestRoomMessageComposer(parser.roomId, false, false));
|
||||
});
|
||||
|
||||
useMessageEvent<CanCreateRoomEventEvent>(CanCreateRoomEventEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(parser.canCreate)
|
||||
{
|
||||
// show room event cvreate
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
simpleAlert(LocalizeText(`navigator.cannotcreateevent.error.${ parser.errorCode }`), null, null, null, LocalizeText('navigator.cannotcreateevent.title'));
|
||||
});
|
||||
|
||||
useMessageEvent<UserInfoEvent>(UserInfoEvent, event =>
|
||||
{
|
||||
SendMessageComposer(new GetUserFlatCatsMessageComposer());
|
||||
SendMessageComposer(new GetUserEventCatsMessageComposer());
|
||||
});
|
||||
|
||||
useMessageEvent<UserPermissionsEvent>(UserPermissionsEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setNavigatorData(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.eventMod = (parser.securityLevel >= SecurityLevel.MODERATOR);
|
||||
newValue.roomPicker = (parser.securityLevel >= SecurityLevel.COMMUNITY);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<RoomForwardEvent>(RoomForwardEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
TryVisitRoom(parser.roomId);
|
||||
});
|
||||
|
||||
useMessageEvent<RoomEntryInfoMessageEvent>(RoomEntryInfoMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setNavigatorData(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.enteredGuestRoom = null;
|
||||
newValue.currentRoomOwner = parser.isOwner;
|
||||
newValue.currentRoomId = parser.roomId;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
// close room info
|
||||
// close room settings
|
||||
// close room filter
|
||||
|
||||
SendMessageComposer(new GetGuestRoomMessageComposer(parser.roomId, true, false));
|
||||
|
||||
if(LegacyExternalInterface.available) LegacyExternalInterface.call('legacyTrack', 'navigator', 'private', [ parser.roomId ]);
|
||||
});
|
||||
|
||||
useMessageEvent<GetGuestRoomResultEvent>(GetGuestRoomResultEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(parser.roomEnter)
|
||||
{
|
||||
setDoorData({ roomInfo: null, state: DoorStateType.NONE });
|
||||
|
||||
setNavigatorData(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.enteredGuestRoom = parser.data;
|
||||
newValue.currentRoomIsStaffPick = parser.staffPick;
|
||||
|
||||
const isCreated = (newValue.createdFlatId === parser.data.roomId);
|
||||
|
||||
if(!isCreated && parser.data.displayRoomEntryAd)
|
||||
{
|
||||
if(GetConfigurationValue<boolean>('roomenterad.habblet.enabled', false)) HabboWebTools.openRoomEnterAd();
|
||||
}
|
||||
|
||||
newValue.createdFlatId = 0;
|
||||
|
||||
if(newValue.enteredGuestRoom && (newValue.enteredGuestRoom.habboGroupId > 0))
|
||||
{
|
||||
// close event info
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
else if(parser.roomForward)
|
||||
{
|
||||
if((parser.data.ownerName !== GetSessionDataManager().userName) && !parser.isGroupMember)
|
||||
{
|
||||
switch(parser.data.doorMode)
|
||||
{
|
||||
case RoomDataParser.DOORBELL_STATE:
|
||||
setDoorData(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.roomInfo = parser.data;
|
||||
newValue.state = DoorStateType.START_DOORBELL;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
return;
|
||||
case RoomDataParser.PASSWORD_STATE:
|
||||
setDoorData(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.roomInfo = parser.data;
|
||||
newValue.state = DoorStateType.START_PASSWORD;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if((parser.data.doorMode === RoomDataParser.NOOB_STATE) && !GetSessionDataManager().isAmbassador && !GetSessionDataManager().isRealNoob && !GetSessionDataManager().isModerator) return;
|
||||
|
||||
CreateRoomSession(parser.data.roomId);
|
||||
}
|
||||
else
|
||||
{
|
||||
setNavigatorData(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.enteredGuestRoom = parser.data;
|
||||
newValue.currentRoomIsStaffPick = parser.staffPick;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
useMessageEvent<RoomScoreEvent>(RoomScoreEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setNavigatorData(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.currentRoomRating = parser.totalLikes;
|
||||
newValue.canRate = parser.canLike;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<DoorbellMessageEvent>(DoorbellMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser.userName || (parser.userName.length === 0))
|
||||
{
|
||||
setDoorData(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.state = DoorStateType.STATE_WAITING;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
useMessageEvent<RoomDoorbellAcceptedEvent>(RoomDoorbellAcceptedEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser.userName || (parser.userName.length === 0))
|
||||
{
|
||||
setDoorData(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.state = DoorStateType.STATE_ACCEPTED;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
useMessageEvent<FlatAccessDeniedMessageEvent>(FlatAccessDeniedMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser.userName || (parser.userName.length === 0))
|
||||
{
|
||||
setDoorData(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.state = DoorStateType.STATE_NO_ANSWER;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
useMessageEvent<GenericErrorEvent>(GenericErrorEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
switch(parser.errorCode)
|
||||
{
|
||||
case -100002:
|
||||
setDoorData(prevValue =>
|
||||
{
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.state = DoorStateType.STATE_WRONG_PASSWORD;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
return;
|
||||
case 4009:
|
||||
simpleAlert(LocalizeText('navigator.alert.need.to.be.vip'), NotificationAlertType.DEFAULT, null, null, LocalizeText('generic.alert.title'));
|
||||
|
||||
return;
|
||||
case 4010:
|
||||
simpleAlert(LocalizeText('navigator.alert.invalid_room_name'), NotificationAlertType.DEFAULT, null, null, LocalizeText('generic.alert.title'));
|
||||
|
||||
return;
|
||||
case 4011:
|
||||
simpleAlert(LocalizeText('navigator.alert.cannot_perm_ban'), NotificationAlertType.DEFAULT, null, null, LocalizeText('generic.alert.title'));
|
||||
|
||||
return;
|
||||
case 4013:
|
||||
simpleAlert(LocalizeText('navigator.alert.room_in_maintenance'), NotificationAlertType.DEFAULT, null, null, LocalizeText('generic.alert.title'));
|
||||
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
useMessageEvent<NavigatorMetadataEvent>(NavigatorMetadataEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setTopLevelContexts(parser.topLevelContexts);
|
||||
setTopLevelContext(parser.topLevelContexts.length ? parser.topLevelContexts[0] : null);
|
||||
});
|
||||
|
||||
useMessageEvent<NavigatorSearchEvent>(NavigatorSearchEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setTopLevelContext(prevValue =>
|
||||
{
|
||||
let newValue = prevValue;
|
||||
|
||||
if(!newValue) newValue = ((topLevelContexts && topLevelContexts.length && topLevelContexts[0]) || null);
|
||||
|
||||
if(!newValue) return null;
|
||||
|
||||
if((parser.result.code !== newValue.code) && topLevelContexts && topLevelContexts.length)
|
||||
{
|
||||
for(const context of topLevelContexts)
|
||||
{
|
||||
if(context.code !== parser.result.code) continue;
|
||||
|
||||
newValue = context;
|
||||
}
|
||||
}
|
||||
|
||||
for(const context of topLevelContexts)
|
||||
{
|
||||
if(context.code !== parser.result.code) continue;
|
||||
|
||||
newValue = context;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
setSearchResult(parser.result);
|
||||
});
|
||||
|
||||
useMessageEvent<UserFlatCatsEvent>(UserFlatCatsEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setCategories(parser.categories);
|
||||
});
|
||||
|
||||
useMessageEvent<UserEventCatsEvent>(UserEventCatsEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setEventCategories(parser.categories);
|
||||
});
|
||||
|
||||
useMessageEvent<FlatCreatedEvent>(FlatCreatedEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
CreateRoomSession(parser.roomId);
|
||||
});
|
||||
|
||||
useMessageEvent<NavigatorHomeRoomEvent>(NavigatorHomeRoomEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
let prevSettingsReceived = false;
|
||||
|
||||
setNavigatorData(prevValue =>
|
||||
{
|
||||
prevSettingsReceived = prevValue.settingsReceived;
|
||||
|
||||
const newValue = { ...prevValue };
|
||||
|
||||
newValue.homeRoomId = parser.homeRoomId;
|
||||
newValue.settingsReceived = true;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
if(prevSettingsReceived)
|
||||
{
|
||||
// refresh room info window
|
||||
return;
|
||||
}
|
||||
|
||||
let forwardType = -1;
|
||||
let forwardId = -1;
|
||||
|
||||
if((GetConfigurationValue<string>('friend.id') !== undefined) && (parseInt(GetConfigurationValue<string>('friend.id')) > 0))
|
||||
{
|
||||
forwardType = 0;
|
||||
SendMessageComposer(new FollowFriendMessageComposer(parseInt(GetConfigurationValue<string>('friend.id'))));
|
||||
}
|
||||
|
||||
if((GetConfigurationValue<number>('forward.type') !== undefined) && (GetConfigurationValue<number>('forward.id') !== undefined))
|
||||
{
|
||||
forwardType = parseInt(GetConfigurationValue<string>('forward.type'));
|
||||
forwardId = parseInt(GetConfigurationValue<string>('forward.id'));
|
||||
}
|
||||
|
||||
if(forwardType === 2)
|
||||
{
|
||||
TryVisitRoom(forwardId);
|
||||
}
|
||||
|
||||
else if((forwardType === -1) && (parser.roomIdToEnter > 0))
|
||||
{
|
||||
CreateLinkEvent('navigator/close');
|
||||
|
||||
if(parser.roomIdToEnter !== parser.homeRoomId)
|
||||
{
|
||||
CreateRoomSession(parser.roomIdToEnter);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateRoomSession(parser.homeRoomId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useMessageEvent<RoomEnterErrorEvent>(RoomEnterErrorEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
switch(parser.reason)
|
||||
{
|
||||
case CantConnectMessageParser.REASON_FULL:
|
||||
simpleAlert(LocalizeText('navigator.guestroomfull.text'), NotificationAlertType.DEFAULT, null, null, LocalizeText('navigator.guestroomfull.title'));
|
||||
|
||||
break;
|
||||
case CantConnectMessageParser.REASON_QUEUE_ERROR:
|
||||
simpleAlert(LocalizeText(`room.queue.error.${ parser.parameter }`), NotificationAlertType.DEFAULT, null, null, LocalizeText('room.queue.error.title'));
|
||||
|
||||
break;
|
||||
case CantConnectMessageParser.REASON_BANNED:
|
||||
simpleAlert(LocalizeText('navigator.banned.text'), NotificationAlertType.DEFAULT, null, null, LocalizeText('navigator.banned.title'));
|
||||
|
||||
break;
|
||||
default:
|
||||
simpleAlert(LocalizeText('room.queue.error.title'), NotificationAlertType.DEFAULT, null, null, LocalizeText('room.queue.error.title'));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
VisitDesktop();
|
||||
});
|
||||
|
||||
useMessageEvent<NavigatorOpenRoomCreatorEvent>(NavigatorOpenRoomCreatorEvent, event => CreateLinkEvent('navigator/show'));
|
||||
|
||||
return { categories, doorData, setDoorData, topLevelContext, topLevelContexts, searchResult, navigatorData };
|
||||
};
|
||||
|
||||
export const useNavigator = () => useBetween(useNavigatorState);
|
||||
@@ -0,0 +1 @@
|
||||
export * from './useNotification';
|
||||
@@ -0,0 +1,433 @@
|
||||
import { AchievementNotificationMessageEvent, ActivityPointNotificationMessageEvent, ClubGiftNotificationEvent, ClubGiftSelectedEvent, ConnectionErrorEvent, GetLocalizationManager, GetRoomEngine, GetSessionDataManager, HabboBroadcastMessageEvent, HotelClosedAndOpensEvent, HotelClosesAndWillOpenAtEvent, HotelWillCloseInMinutesEvent, InfoFeedEnableMessageEvent, MaintenanceStatusMessageEvent, ModeratorCautionEvent, ModeratorMessageEvent, MOTDNotificationEvent, NotificationDialogMessageEvent, PetLevelNotificationEvent, PetReceivedMessageEvent, RespectReceivedEvent, RoomEnterEffect, RoomEnterEvent, SimpleAlertMessageEvent, UserBannedMessageEvent, Vector3d } from '@nitrots/nitro-renderer';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { GetConfigurationValue, LocalizeBadgeName, LocalizeText, NotificationAlertItem, NotificationAlertType, NotificationBubbleItem, NotificationBubbleType, NotificationConfirmItem, PlaySound, ProductImageUtility, TradingNotificationType } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
|
||||
const cleanText = (text: string) => (text && text.length) ? text.replace(/\\r/g, '\r') : '';
|
||||
|
||||
const getTimeZeroPadded = (time: number) =>
|
||||
{
|
||||
const text = ('0' + time);
|
||||
|
||||
return text.substr((text.length - 2), text.length);
|
||||
};
|
||||
|
||||
let modDisclaimerTimeout: ReturnType<typeof setTimeout> = null;
|
||||
|
||||
const useNotificationState = () =>
|
||||
{
|
||||
const [ alerts, setAlerts ] = useState<NotificationAlertItem[]>([]);
|
||||
const [ bubbleAlerts, setBubbleAlerts ] = useState<NotificationBubbleItem[]>([]);
|
||||
const [ confirms, setConfirms ] = useState<NotificationConfirmItem[]>([]);
|
||||
const [ bubblesDisabled, setBubblesDisabled ] = useState(false);
|
||||
const [ modDisclaimerShown, setModDisclaimerShown ] = useState(false);
|
||||
|
||||
const getMainNotificationConfig = () => GetConfigurationValue<{ [key: string]: { delivery?: string, display?: string; title?: string; image?: string }}>('notification', {});
|
||||
|
||||
const getNotificationConfig = (key: string) =>
|
||||
{
|
||||
const mainConfig = getMainNotificationConfig();
|
||||
|
||||
if(!mainConfig) return null;
|
||||
|
||||
return mainConfig[key];
|
||||
};
|
||||
|
||||
const getNotificationPart = (options: Map<string, string>, type: string, key: string, localize: boolean) =>
|
||||
{
|
||||
if(options.has(key)) return options.get(key);
|
||||
|
||||
const localizeKey = [ 'notification', type, key ].join('.');
|
||||
|
||||
if(GetLocalizationManager().hasValue(localizeKey) || localize) return LocalizeText(localizeKey, Array.from(options.keys()), Array.from(options.values()));
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const getNotificationImageUrl = (options: Map<string, string>, type: string) =>
|
||||
{
|
||||
let imageUrl = options.get('image');
|
||||
|
||||
if(!imageUrl) imageUrl = GetConfigurationValue<string>('image.library.notifications.url', '').replace('%image%', type.replace(/\./g, '_'));
|
||||
|
||||
return LocalizeText(imageUrl);
|
||||
};
|
||||
|
||||
const simpleAlert = useCallback((message: string, type: string = null, clickUrl: string = null, clickUrlText: string = null, title: string = null, imageUrl: string = null) =>
|
||||
{
|
||||
if(!title || !title.length) title = LocalizeText('notifications.broadcast.title');
|
||||
|
||||
if(!type || !type.length) type = NotificationAlertType.DEFAULT;
|
||||
|
||||
const alertItem = new NotificationAlertItem([ cleanText(message) ], type, clickUrl, clickUrlText, title, imageUrl);
|
||||
|
||||
setAlerts(prevValue => [ alertItem, ...prevValue ]);
|
||||
}, []);
|
||||
|
||||
const showNitroAlert = useCallback(() => simpleAlert(null, NotificationAlertType.NITRO), [ simpleAlert ]);
|
||||
|
||||
const showSingleBubble = useCallback((message: string, type: string, imageUrl: string = null, internalLink: string = null) =>
|
||||
{
|
||||
if(bubblesDisabled) return;
|
||||
|
||||
const notificationItem = new NotificationBubbleItem(message, type, imageUrl, internalLink);
|
||||
|
||||
setBubbleAlerts(prevValue => [ notificationItem, ...prevValue ]);
|
||||
}, [ bubblesDisabled ]);
|
||||
|
||||
const showNotification = (type: string, options: Map<string, string> = null) =>
|
||||
{
|
||||
if(!options) options = new Map();
|
||||
|
||||
const configuration = getNotificationConfig(('notification.' + type));
|
||||
|
||||
if(configuration) for(const key in configuration) options.set(key, configuration[key]);
|
||||
|
||||
if(type === 'floorplan_editor.error') options.set('message', options.get('message').replace(/[^a-zA-Z._ ]/g, ''));
|
||||
|
||||
const title = getNotificationPart(options, type, 'title', true);
|
||||
const message = getNotificationPart(options, type, 'message', true).replace(/\\r/g, '\r');
|
||||
const linkTitle = getNotificationPart(options, type, 'linkTitle', false);
|
||||
const linkUrl = getNotificationPart(options, type, 'linkUrl', false);
|
||||
const image = getNotificationImageUrl(options, type);
|
||||
|
||||
if(options.get('display') === 'BUBBLE')
|
||||
{
|
||||
showSingleBubble(LocalizeText(message), NotificationBubbleType.INFO, image, linkUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
simpleAlert(LocalizeText(message), type, linkUrl, linkTitle, title, image);
|
||||
}
|
||||
|
||||
if(options.get('sound')) PlaySound(options.get('sound'));
|
||||
};
|
||||
|
||||
const showConfirm = useCallback((message: string, onConfirm: () => void, onCancel: () => void, confirmText: string = null, cancelText: string = null, title: string = null, type: string = null) =>
|
||||
{
|
||||
if(!confirmText || !confirmText.length) confirmText = LocalizeText('generic.confirm');
|
||||
|
||||
if(!cancelText || !cancelText.length) cancelText = LocalizeText('generic.cancel');
|
||||
|
||||
if(!title || !title.length) title = LocalizeText('notifications.broadcast.title');
|
||||
|
||||
const confirmItem = new NotificationConfirmItem(type, message, onConfirm, onCancel, confirmText, cancelText, title);
|
||||
|
||||
setConfirms(prevValue => [ confirmItem, ...prevValue ]);
|
||||
}, []);
|
||||
|
||||
const showModeratorMessage = (message: string, url: string = null, showHabboWay: boolean = true) =>
|
||||
{
|
||||
simpleAlert(message, NotificationAlertType.DEFAULT, url, LocalizeText('mod.alert.link'), LocalizeText('mod.alert.title'));
|
||||
};
|
||||
|
||||
const showTradeAlert = useCallback((type: number, otherUsername: string = '') =>
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case TradingNotificationType.ALERT_SCAM:
|
||||
simpleAlert(LocalizeText('inventory.trading.warning.other_not_offering'), null, null, null, LocalizeText('inventory.trading.notification.title'));
|
||||
return;
|
||||
case TradingNotificationType.HOTEL_TRADING_DISABLED:
|
||||
case TradingNotificationType.YOU_NOT_ALLOWED:
|
||||
case TradingNotificationType.THEY_NOT_ALLOWED:
|
||||
case TradingNotificationType.ROOM_DISABLED:
|
||||
case TradingNotificationType.YOU_OPEN:
|
||||
case TradingNotificationType.THEY_OPEN:
|
||||
simpleAlert(LocalizeText(`inventory.trading.openfail.${ type }`, [ 'otherusername' ], [ otherUsername ]), null, null, null, LocalizeText('inventory.trading.openfail.title'));
|
||||
return;
|
||||
case TradingNotificationType.ERROR_WHILE_COMMIT:
|
||||
simpleAlert(`${ LocalizeText('inventory.trading.notification.caption') }, ${ LocalizeText('inventory.trading.notification.commiterror.info') }`, null, null, null, LocalizeText('inventory.trading.notification.title'));
|
||||
return;
|
||||
case TradingNotificationType.THEY_CANCELLED:
|
||||
simpleAlert(LocalizeText('inventory.trading.info.closed'), null, null, null, LocalizeText('inventory.trading.notification.title'));
|
||||
return;
|
||||
}
|
||||
}, [ simpleAlert ]);
|
||||
|
||||
const closeAlert = useCallback((alert: NotificationAlertItem) =>
|
||||
{
|
||||
setAlerts(prevValue =>
|
||||
{
|
||||
const newAlerts = [ ...prevValue ];
|
||||
const index = newAlerts.findIndex(value => (alert === value));
|
||||
|
||||
if(index >= 0) newAlerts.splice(index, 1);
|
||||
|
||||
return newAlerts;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const closeBubbleAlert = useCallback((item: NotificationBubbleItem) =>
|
||||
{
|
||||
setBubbleAlerts(prevValue =>
|
||||
{
|
||||
const newAlerts = [ ...prevValue ];
|
||||
const index = newAlerts.findIndex(value => (item === value));
|
||||
|
||||
if(index >= 0) newAlerts.splice(index, 1);
|
||||
|
||||
return newAlerts;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const closeConfirm = useCallback((item: NotificationConfirmItem) =>
|
||||
{
|
||||
setConfirms(prevValue =>
|
||||
{
|
||||
const newConfirms = [ ...prevValue ];
|
||||
const index = newConfirms.findIndex(value => (item === value));
|
||||
|
||||
if(index >= 0) newConfirms.splice(index, 1);
|
||||
|
||||
return newConfirms;
|
||||
});
|
||||
}, []);
|
||||
|
||||
useMessageEvent<RespectReceivedEvent>(RespectReceivedEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(parser.userId !== GetSessionDataManager().userId) return;
|
||||
|
||||
const text1 = LocalizeText('notifications.text.respect.1');
|
||||
const text2 = LocalizeText('notifications.text.respect.2', [ 'count' ], [ parser.respectsReceived.toString() ]);
|
||||
|
||||
showSingleBubble(text1, NotificationBubbleType.RESPECT);
|
||||
showSingleBubble(text2, NotificationBubbleType.RESPECT);
|
||||
});
|
||||
|
||||
useMessageEvent<HabboBroadcastMessageEvent>(HabboBroadcastMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
simpleAlert(parser.message.replace(/\\r/g, '\r'), null, null, LocalizeText('notifications.broadcast.title'));
|
||||
});
|
||||
|
||||
useMessageEvent<AchievementNotificationMessageEvent>(AchievementNotificationMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
const text1 = LocalizeText('achievements.levelup.desc');
|
||||
const badgeName = LocalizeBadgeName(parser.data.badgeCode);
|
||||
const badgeImage = GetSessionDataManager().getBadgeUrl(parser.data.badgeCode);
|
||||
const internalLink = 'questengine/achievements/' + parser.data.category;
|
||||
|
||||
showSingleBubble((text1 + ' ' + badgeName), NotificationBubbleType.ACHIEVEMENT, badgeImage, internalLink);
|
||||
});
|
||||
|
||||
useMessageEvent<ClubGiftNotificationEvent>(ClubGiftNotificationEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(parser.numGifts <= 0) return;
|
||||
|
||||
showSingleBubble(parser.numGifts.toString(), NotificationBubbleType.CLUBGIFT, null, ('catalog/open/' + GetConfigurationValue('catalog.links')['hc.hc_gifts']));
|
||||
});
|
||||
|
||||
useMessageEvent<ModeratorMessageEvent>(ModeratorMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
showModeratorMessage(parser.message, parser.url, false);
|
||||
});
|
||||
|
||||
useMessageEvent<ActivityPointNotificationMessageEvent>(ActivityPointNotificationMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if((parser.amountChanged <= 0) || (parser.type !== 5)) return;
|
||||
|
||||
const imageUrl = GetConfigurationValue<string>('currency.asset.icon.url', '').replace('%type%', parser.type.toString());
|
||||
|
||||
showSingleBubble(LocalizeText('notifications.text.loyalty.received', [ 'AMOUNT' ], [ parser.amountChanged.toString() ]), NotificationBubbleType.INFO, imageUrl);
|
||||
});
|
||||
|
||||
useMessageEvent<UserBannedMessageEvent>(UserBannedMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
showModeratorMessage(parser.message);
|
||||
});
|
||||
|
||||
useMessageEvent<HotelClosesAndWillOpenAtEvent>(HotelClosesAndWillOpenAtEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
simpleAlert( LocalizeText(('opening.hours.' + (parser.userThrowOutAtClose ? 'disconnected' : 'closed')), [ 'h', 'm' ], [ getTimeZeroPadded(parser.openHour), getTimeZeroPadded(parser.openMinute) ]), NotificationAlertType.DEFAULT, null, null, LocalizeText('opening.hours.title'));
|
||||
});
|
||||
|
||||
useMessageEvent<PetReceivedMessageEvent>(PetReceivedMessageEvent, async event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
const text = LocalizeText('notifications.text.' + (parser.boughtAsGift ? 'petbought' : 'petreceived'));
|
||||
|
||||
let imageUrl: string = null;
|
||||
|
||||
const imageResult = GetRoomEngine().getRoomObjectPetImage(parser.pet.typeId, parser.pet.paletteId, parseInt(parser.pet.color, 16), new Vector3d(45 * 3), 64, null, true);
|
||||
|
||||
if(imageResult) imageUrl = (await imageResult.getImage())?.src;
|
||||
|
||||
showSingleBubble(text, NotificationBubbleType.PETLEVEL, imageUrl);
|
||||
});
|
||||
|
||||
useMessageEvent<MOTDNotificationEvent>(MOTDNotificationEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
const messages = parser.messages.map(message => cleanText(message));
|
||||
|
||||
const alertItem = new NotificationAlertItem(messages, NotificationAlertType.MOTD, null, null, LocalizeText('notifications.motd.title'));
|
||||
|
||||
setAlerts(prevValue => [ alertItem, ...prevValue ]);
|
||||
});
|
||||
|
||||
useMessageEvent<PetLevelNotificationEvent>(PetLevelNotificationEvent, async event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
let imageUrl: string = null;
|
||||
|
||||
const imageResult = GetRoomEngine().getRoomObjectPetImage(parser.figureData.typeId, parser.figureData.paletteId, parseInt(parser.figureData.color, 16), new Vector3d(45 * 3), 64, null, true);
|
||||
|
||||
if(imageResult) imageUrl = (await imageResult.getImage())?.src;
|
||||
|
||||
showSingleBubble(LocalizeText('notifications.text.petlevel', [ 'pet_name', 'level' ], [ parser.petName, parser.level.toString() ]), NotificationBubbleType.PETLEVEL, imageUrl);
|
||||
});
|
||||
|
||||
useMessageEvent<InfoFeedEnableMessageEvent>(InfoFeedEnableMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setBubblesDisabled(!parser.enabled);
|
||||
});
|
||||
|
||||
useMessageEvent<ClubGiftSelectedEvent>(ClubGiftSelectedEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser.products || !parser.products.length) return;
|
||||
|
||||
const productData = parser.products[0];
|
||||
|
||||
if(!productData) return;
|
||||
|
||||
showSingleBubble(LocalizeText('notifications.text.club_gift.selected'), NotificationBubbleType.INFO, ProductImageUtility.getProductImageUrl(productData.productType, productData.furniClassId, productData.extraParam));
|
||||
});
|
||||
|
||||
useMessageEvent<MaintenanceStatusMessageEvent>(MaintenanceStatusMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
simpleAlert(LocalizeText('maintenance.shutdown', [ 'm', 'd' ], [ parser.minutesUntilMaintenance.toString(), parser.duration.toString() ]), NotificationAlertType.DEFAULT, null, null, LocalizeText('opening.hours.title'));
|
||||
});
|
||||
|
||||
useMessageEvent<ModeratorCautionEvent>(ModeratorCautionEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
showModeratorMessage(parser.message, parser.url);
|
||||
});
|
||||
|
||||
useMessageEvent<NotificationDialogMessageEvent>(NotificationDialogMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
showNotification(parser.type, parser.parameters);
|
||||
});
|
||||
|
||||
useMessageEvent<HotelWillCloseInMinutesEvent>(HotelWillCloseInMinutesEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
simpleAlert(LocalizeText('opening.hours.shutdown', [ 'm' ], [ parser.openMinute.toString() ]), NotificationAlertType.DEFAULT, null, null, LocalizeText('opening.hours.title'));
|
||||
});
|
||||
|
||||
useMessageEvent<HotelClosedAndOpensEvent>(HotelClosedAndOpensEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
simpleAlert(LocalizeText('opening.hours.disconnected', [ 'h', 'm' ], [ parser.openHour.toString(), parser.openMinute.toString() ]), NotificationAlertType.DEFAULT, null, null, LocalizeText('opening.hours.title'));
|
||||
});
|
||||
|
||||
useMessageEvent<ConnectionErrorEvent>(ConnectionErrorEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
switch(parser.errorCode)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
simpleAlert(LocalizeText('connection.server.error.desc', [ 'errorCode' ], [ parser.errorCode.toString() ]), NotificationAlertType.ALERT, null, null, LocalizeText('connection.server.error.title'));
|
||||
break;
|
||||
case 1001:
|
||||
case 1002:
|
||||
case 1003:
|
||||
case 1004:
|
||||
case 1005:
|
||||
case 1006:
|
||||
case 1007:
|
||||
case 1008:
|
||||
case 1009:
|
||||
case 1010:
|
||||
case 1011:
|
||||
case 1012:
|
||||
case 1013:
|
||||
case 1014:
|
||||
case 1015:
|
||||
case 1016:
|
||||
case 1017:
|
||||
case 1018:
|
||||
case 1019:
|
||||
// TODO: fix dispose
|
||||
//event.connection.dispose();
|
||||
break;
|
||||
case 4013:
|
||||
simpleAlert(LocalizeText('connection.room.maintenance.desc'), NotificationAlertType.ALERT, null, null, LocalizeText('connection.room.maintenance.title'));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
useMessageEvent<SimpleAlertMessageEvent>(SimpleAlertMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
simpleAlert(LocalizeText(parser.alertMessage), NotificationAlertType.DEFAULT, null, null, LocalizeText(parser.titleMessage ? parser.titleMessage : 'notifications.broadcast.title'));
|
||||
});
|
||||
|
||||
const onRoomEnterEvent = useCallback(() =>
|
||||
{
|
||||
if(modDisclaimerShown) return;
|
||||
|
||||
if(RoomEnterEffect.isRunning())
|
||||
{
|
||||
if(modDisclaimerTimeout) return;
|
||||
|
||||
modDisclaimerTimeout = setTimeout(() =>
|
||||
{
|
||||
onRoomEnterEvent();
|
||||
}, (RoomEnterEffect.totalRunningTime + 5000));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(modDisclaimerTimeout)
|
||||
{
|
||||
clearTimeout(modDisclaimerTimeout);
|
||||
|
||||
modDisclaimerTimeout = null;
|
||||
}
|
||||
|
||||
showSingleBubble(LocalizeText('mod.chatdisclaimer'), NotificationBubbleType.INFO);
|
||||
|
||||
setModDisclaimerShown(true);
|
||||
}
|
||||
}, [ modDisclaimerShown, showSingleBubble ]);
|
||||
|
||||
useMessageEvent<RoomEnterEvent>(RoomEnterEvent, onRoomEnterEvent);
|
||||
|
||||
return { alerts, bubbleAlerts, confirms, simpleAlert, showNitroAlert, showTradeAlert, showConfirm, showSingleBubble, closeAlert, closeBubbleAlert, closeConfirm };
|
||||
};
|
||||
|
||||
export const useNotification = () => useBetween(useNotificationState);
|
||||
@@ -0,0 +1 @@
|
||||
export * from './usePurse';
|
||||
@@ -0,0 +1,126 @@
|
||||
import { ActivityPointNotificationMessageEvent, UserCreditsEvent, UserCurrencyComposer, UserCurrencyEvent, UserSubscriptionComposer, UserSubscriptionEvent, UserSubscriptionParser } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { CloneObject, ClubStatus, GetConfigurationValue, IPurse, PlaySound, Purse, SendMessageComposer, SoundNames } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
|
||||
const usePurseState = () =>
|
||||
{
|
||||
const [ purse, setPurse ] = useState<IPurse>(new Purse());
|
||||
const hcDisabled = useMemo(() => GetConfigurationValue<boolean>('hc.disabled', false), []);
|
||||
|
||||
const clubStatus = useMemo(() =>
|
||||
{
|
||||
if(hcDisabled || (purse.clubDays > 0)) return ClubStatus.ACTIVE;
|
||||
|
||||
if((purse.pastVipDays > 0) || (purse.pastVipDays > 0)) return ClubStatus.EXPIRED;
|
||||
|
||||
return ClubStatus.NONE;
|
||||
}, [ purse, hcDisabled ]);
|
||||
|
||||
const getCurrencyAmount = (type: number) =>
|
||||
{
|
||||
if(type === -1) return purse.credits;
|
||||
|
||||
for(const [ key, value ] of purse.activityPoints.entries())
|
||||
{
|
||||
if(key !== type) continue;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
useMessageEvent<UserCreditsEvent>(UserCreditsEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setPurse(prevValue =>
|
||||
{
|
||||
const newValue = CloneObject(prevValue);
|
||||
|
||||
newValue.credits = parseFloat(parser.credits);
|
||||
|
||||
if(prevValue.credits !== newValue.credits) PlaySound(SoundNames.CREDITS);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<UserCurrencyEvent>(UserCurrencyEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setPurse(prevValue =>
|
||||
{
|
||||
const newValue = CloneObject(prevValue);
|
||||
|
||||
newValue.activityPoints = parser.currencies;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<ActivityPointNotificationMessageEvent>(ActivityPointNotificationMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setPurse(prevValue =>
|
||||
{
|
||||
const newValue = CloneObject(prevValue);
|
||||
|
||||
newValue.activityPoints = new Map(newValue.activityPoints);
|
||||
|
||||
newValue.activityPoints.set(parser.type, parser.amount);
|
||||
|
||||
if(parser.type === 0) PlaySound(SoundNames.DUCKETS);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<UserSubscriptionEvent>(UserSubscriptionEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const productName = parser.productName;
|
||||
|
||||
if((productName !== 'club_habbo') && (productName !== 'habbo_club')) return;
|
||||
|
||||
setPurse(prevValue =>
|
||||
{
|
||||
const newValue = CloneObject(prevValue);
|
||||
|
||||
newValue.clubDays = Math.max(0, parser.daysToPeriodEnd);
|
||||
newValue.clubPeriods = Math.max(0, parser.periodsSubscribedAhead);
|
||||
newValue.isVip = parser.isVip;
|
||||
newValue.pastClubDays = parser.pastClubDays;
|
||||
newValue.pastVipDays = parser.pastVipDays;
|
||||
newValue.isExpiring = ((parser.responseType === UserSubscriptionParser.RESPONSE_TYPE_DISCOUNT_AVAILABLE) ? true : false);
|
||||
newValue.minutesUntilExpiration = parser.minutesUntilExpiration;
|
||||
newValue.minutesSinceLastModified = parser.minutesSinceLastModified;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(hcDisabled) return;
|
||||
|
||||
SendMessageComposer(new UserSubscriptionComposer('habbo_club'));
|
||||
|
||||
const interval = setInterval(() => SendMessageComposer(new UserSubscriptionComposer('habbo_club')), 50000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [ hcDisabled ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageComposer(new UserCurrencyComposer());
|
||||
}, []);
|
||||
|
||||
return { purse, hcDisabled, clubStatus, getCurrencyAmount };
|
||||
};
|
||||
|
||||
export const usePurse = () => useBetween(usePurseState);
|
||||
@@ -0,0 +1,9 @@
|
||||
export * from './useFurniAddedEvent';
|
||||
export * from './useFurniRemovedEvent';
|
||||
export * from './useObjectDeselectedEvent';
|
||||
export * from './useObjectDoubleClickedEvent';
|
||||
export * from './useObjectRollOutEvent';
|
||||
export * from './useObjectRollOverEvent';
|
||||
export * from './useObjectSelectedEvent';
|
||||
export * from './useUserAddedEvent';
|
||||
export * from './useUserRemovedEvent';
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useEffect } from 'react';
|
||||
import { RoomWidgetUpdateRoomObjectEvent, UI_EVENT_DISPATCHER } from '../../../api';
|
||||
|
||||
export const useFurniAddedEvent = (isActive: boolean, handler: (event: RoomWidgetUpdateRoomObjectEvent) => void) =>
|
||||
{
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isActive) return;
|
||||
|
||||
const onRoomWidgetUpdateRoomObjectEvent = (event: RoomWidgetUpdateRoomObjectEvent) => handler(event);
|
||||
|
||||
UI_EVENT_DISPATCHER.addEventListener(RoomWidgetUpdateRoomObjectEvent.FURNI_ADDED, onRoomWidgetUpdateRoomObjectEvent);
|
||||
|
||||
return () =>
|
||||
{
|
||||
UI_EVENT_DISPATCHER.removeEventListener(RoomWidgetUpdateRoomObjectEvent.FURNI_ADDED, onRoomWidgetUpdateRoomObjectEvent);
|
||||
};
|
||||
}, [ isActive, handler ]);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useEffect } from 'react';
|
||||
import { RoomWidgetUpdateRoomObjectEvent, UI_EVENT_DISPATCHER } from '../../../api';
|
||||
|
||||
export const useFurniRemovedEvent = (isActive: boolean, handler: (event: RoomWidgetUpdateRoomObjectEvent) => void) =>
|
||||
{
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isActive) return;
|
||||
|
||||
const onRoomWidgetUpdateRoomObjectEvent = (event: RoomWidgetUpdateRoomObjectEvent) => handler(event);
|
||||
|
||||
UI_EVENT_DISPATCHER.addEventListener(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, onRoomWidgetUpdateRoomObjectEvent);
|
||||
|
||||
return () =>
|
||||
{
|
||||
UI_EVENT_DISPATCHER.removeEventListener(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, onRoomWidgetUpdateRoomObjectEvent);
|
||||
};
|
||||
}, [ isActive, handler ]);
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { RoomWidgetUpdateRoomObjectEvent } from '../../../api';
|
||||
import { useUiEvent } from '../../events';
|
||||
|
||||
export const useObjectDeselectedEvent = (handler: (event: RoomWidgetUpdateRoomObjectEvent) => void) =>
|
||||
{
|
||||
useUiEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_DESELECTED, handler);
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { RoomWidgetUpdateRoomObjectEvent } from '../../../api';
|
||||
import { useUiEvent } from '../../events';
|
||||
|
||||
export const useObjectDoubleClickedEvent = (handler: (event: RoomWidgetUpdateRoomObjectEvent) => void) =>
|
||||
{
|
||||
useUiEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_DOUBLE_CLICKED, handler);
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { RoomWidgetUpdateRoomObjectEvent } from '../../../api';
|
||||
import { useUiEvent } from '../../events';
|
||||
|
||||
export const useObjectRollOutEvent = (handler: (event: RoomWidgetUpdateRoomObjectEvent) => void) =>
|
||||
{
|
||||
useUiEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_ROLL_OUT, handler);
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { RoomWidgetUpdateRoomObjectEvent } from '../../../api';
|
||||
import { useUiEvent } from '../../events';
|
||||
|
||||
export const useObjectRollOverEvent = (handler: (event: RoomWidgetUpdateRoomObjectEvent) => void) =>
|
||||
{
|
||||
useUiEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_ROLL_OVER, handler);
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { RoomWidgetUpdateRoomObjectEvent } from '../../../api';
|
||||
import { useUiEvent } from '../../events';
|
||||
|
||||
export const useObjectSelectedEvent = (handler: (event: RoomWidgetUpdateRoomObjectEvent) => void) =>
|
||||
{
|
||||
useUiEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_SELECTED, handler);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useEffect } from 'react';
|
||||
import { RoomWidgetUpdateRoomObjectEvent, UI_EVENT_DISPATCHER } from '../../../api';
|
||||
|
||||
export const useUserAddedEvent = (isActive: boolean, handler: (event: RoomWidgetUpdateRoomObjectEvent) => void) =>
|
||||
{
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isActive) return;
|
||||
|
||||
const onRoomWidgetUpdateRoomObjectEvent = (event: RoomWidgetUpdateRoomObjectEvent) => handler(event);
|
||||
|
||||
UI_EVENT_DISPATCHER.addEventListener(RoomWidgetUpdateRoomObjectEvent.USER_ADDED, onRoomWidgetUpdateRoomObjectEvent);
|
||||
|
||||
return () =>
|
||||
{
|
||||
UI_EVENT_DISPATCHER.removeEventListener(RoomWidgetUpdateRoomObjectEvent.USER_ADDED, onRoomWidgetUpdateRoomObjectEvent);
|
||||
};
|
||||
}, [ isActive, handler ]);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useEffect } from 'react';
|
||||
import { RoomWidgetUpdateRoomObjectEvent, UI_EVENT_DISPATCHER } from '../../../api';
|
||||
|
||||
export const useUserRemovedEvent = (isActive: boolean, handler: (event: RoomWidgetUpdateRoomObjectEvent) => void) =>
|
||||
{
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isActive) return;
|
||||
|
||||
const onRoomWidgetUpdateRoomObjectEvent = (event: RoomWidgetUpdateRoomObjectEvent) => handler(event);
|
||||
|
||||
UI_EVENT_DISPATCHER.addEventListener(RoomWidgetUpdateRoomObjectEvent.USER_REMOVED, onRoomWidgetUpdateRoomObjectEvent);
|
||||
|
||||
return () =>
|
||||
{
|
||||
UI_EVENT_DISPATCHER.removeEventListener(RoomWidgetUpdateRoomObjectEvent.USER_REMOVED, onRoomWidgetUpdateRoomObjectEvent);
|
||||
};
|
||||
}, [ isActive, handler ]);
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './engine';
|
||||
export * from './promotes';
|
||||
export * from './useRoom';
|
||||
export * from './widgets';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './useRoomPromote';
|
||||
@@ -0,0 +1,23 @@
|
||||
import { RoomEventEvent, RoomEventMessageParser } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { useMessageEvent } from '../../events';
|
||||
|
||||
const useRoomPromoteState = () =>
|
||||
{
|
||||
const [ promoteInformation, setPromoteInformation ] = useState<RoomEventMessageParser>(null);
|
||||
const [ isExtended, setIsExtended ] = useState<boolean>(false);
|
||||
|
||||
useMessageEvent<RoomEventEvent>(RoomEventEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser) return;
|
||||
|
||||
setPromoteInformation(parser);
|
||||
});
|
||||
|
||||
return { promoteInformation, isExtended, setPromoteInformation, setIsExtended };
|
||||
};
|
||||
|
||||
export const useRoomPromote = () => useBetween(useRoomPromoteState);
|
||||
@@ -0,0 +1,282 @@
|
||||
import { ColorConverter, GetRenderer, GetRoomEngine, GetStage, IRoomSession, NitroAdjustmentFilter, NitroSprite, NitroTexture, RoomBackgroundColorEvent, RoomEngineEvent, RoomEngineObjectEvent, RoomGeometry, RoomId, RoomObjectCategory, RoomObjectHSLColorEnabledEvent, RoomObjectOperationType, RoomSessionEvent, RoomVariableEnum, Vector3d } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { CanManipulateFurniture, DispatchUiEvent, GetRoomSession, InitializeRoomInstanceRenderingCanvas, IsFurnitureSelectionDisabled, ProcessRoomObjectOperation, RoomWidgetUpdateBackgroundColorPreviewEvent, RoomWidgetUpdateRoomObjectEvent, SetActiveRoomId, StartRoomSession } from '../../api';
|
||||
import { useNitroEvent, useUiEvent } from '../events';
|
||||
|
||||
const useRoomState = () =>
|
||||
{
|
||||
const [roomSession, setRoomSession] = useState<IRoomSession>(null);
|
||||
const [roomBackground, setRoomBackground] = useState<NitroSprite>(null);
|
||||
const [roomFilter, setRoomFilter] = useState<NitroAdjustmentFilter>(null);
|
||||
const [originalRoomBackgroundColor, setOriginalRoomBackgroundColor] = useState(0);
|
||||
|
||||
const updateRoomBackgroundColor = (hue: number, saturation: number, lightness: number, original: boolean = false) =>
|
||||
{
|
||||
if (!roomBackground) return;
|
||||
|
||||
const newColor = ColorConverter.hslToRGB(((((hue & 0xFF) << 16) + ((saturation & 0xFF) << 8)) + (lightness & 0xFF)));
|
||||
|
||||
if (original) setOriginalRoomBackgroundColor(newColor);
|
||||
|
||||
if (!hue && !saturation && !lightness)
|
||||
{
|
||||
roomBackground.tint = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
roomBackground.tint = newColor;
|
||||
}
|
||||
};
|
||||
|
||||
const updateRoomFilter = (color: number) =>
|
||||
{
|
||||
if (!roomFilter) return;
|
||||
|
||||
const r = ((color >> 16) & 0xFF);
|
||||
const g = ((color >> 8) & 0xFF);
|
||||
const b = (color & 0xFF);
|
||||
|
||||
roomFilter.red = (r / 255);
|
||||
roomFilter.green = (g / 255);
|
||||
roomFilter.blue = (b / 255);
|
||||
};
|
||||
|
||||
useUiEvent<RoomWidgetUpdateBackgroundColorPreviewEvent>(RoomWidgetUpdateBackgroundColorPreviewEvent.PREVIEW, event => updateRoomBackgroundColor(event.hue, event.saturation, event.lightness));
|
||||
|
||||
useUiEvent<RoomWidgetUpdateBackgroundColorPreviewEvent>(RoomWidgetUpdateBackgroundColorPreviewEvent.CLEAR_PREVIEW, event =>
|
||||
{
|
||||
if (!roomBackground) return;
|
||||
|
||||
roomBackground.tint = originalRoomBackgroundColor;
|
||||
});
|
||||
|
||||
useNitroEvent<RoomObjectHSLColorEnabledEvent>(RoomObjectHSLColorEnabledEvent.ROOM_BACKGROUND_COLOR, event =>
|
||||
{
|
||||
if (RoomId.isRoomPreviewerId(event.roomId)) return;
|
||||
|
||||
if (event.enable) updateRoomBackgroundColor(event.hue, event.saturation, event.lightness, true);
|
||||
else updateRoomBackgroundColor(0, 0, 0, true);
|
||||
});
|
||||
|
||||
useNitroEvent<RoomBackgroundColorEvent>(RoomBackgroundColorEvent.ROOM_COLOR, event =>
|
||||
{
|
||||
if (RoomId.isRoomPreviewerId(event.roomId)) return;
|
||||
|
||||
let color = 0x000000;
|
||||
let brightness = 0xFF;
|
||||
|
||||
if (!event.bgOnly)
|
||||
{
|
||||
color = event.color;
|
||||
brightness = event.brightness;
|
||||
}
|
||||
|
||||
updateRoomFilter(ColorConverter.hslToRGB(((ColorConverter.rgbToHSL(color) & 0xFFFF00) + brightness)));
|
||||
});
|
||||
|
||||
useNitroEvent<RoomEngineEvent>([
|
||||
RoomEngineEvent.INITIALIZED,
|
||||
RoomEngineEvent.DISPOSED
|
||||
], event =>
|
||||
{
|
||||
if (RoomId.isRoomPreviewerId(event.roomId)) return;
|
||||
|
||||
const session = GetRoomSession();
|
||||
|
||||
if (!session) return;
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case RoomEngineEvent.INITIALIZED:
|
||||
SetActiveRoomId(event.roomId);
|
||||
setRoomSession(session);
|
||||
return;
|
||||
case RoomEngineEvent.DISPOSED:
|
||||
setRoomSession(null);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
useNitroEvent<RoomSessionEvent>([
|
||||
RoomSessionEvent.CREATED,
|
||||
RoomSessionEvent.ENDED
|
||||
], event =>
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case RoomSessionEvent.CREATED:
|
||||
StartRoomSession(event.session);
|
||||
return;
|
||||
case RoomSessionEvent.ENDED:
|
||||
setRoomSession(null);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
useNitroEvent<RoomEngineObjectEvent>([
|
||||
RoomEngineObjectEvent.SELECTED,
|
||||
RoomEngineObjectEvent.DESELECTED,
|
||||
RoomEngineObjectEvent.ADDED,
|
||||
RoomEngineObjectEvent.REMOVED,
|
||||
RoomEngineObjectEvent.PLACED,
|
||||
RoomEngineObjectEvent.REQUEST_MOVE,
|
||||
RoomEngineObjectEvent.REQUEST_ROTATE,
|
||||
RoomEngineObjectEvent.MOUSE_ENTER,
|
||||
RoomEngineObjectEvent.MOUSE_LEAVE,
|
||||
RoomEngineObjectEvent.DOUBLE_CLICK
|
||||
], event =>
|
||||
{
|
||||
if (RoomId.isRoomPreviewerId(event.roomId)) return;
|
||||
|
||||
let updateEvent: RoomWidgetUpdateRoomObjectEvent = null;
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case RoomEngineObjectEvent.SELECTED:
|
||||
if (!IsFurnitureSelectionDisabled(event)) updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_SELECTED, event.objectId, event.category, event.roomId);
|
||||
break;
|
||||
case RoomEngineObjectEvent.DESELECTED:
|
||||
updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_DESELECTED, event.objectId, event.category, event.roomId);
|
||||
break;
|
||||
case RoomEngineObjectEvent.ADDED: {
|
||||
let addedEventType: string = null;
|
||||
|
||||
switch (event.category)
|
||||
{
|
||||
case RoomObjectCategory.FLOOR:
|
||||
case RoomObjectCategory.WALL:
|
||||
addedEventType = RoomWidgetUpdateRoomObjectEvent.FURNI_ADDED;
|
||||
break;
|
||||
case RoomObjectCategory.UNIT:
|
||||
addedEventType = RoomWidgetUpdateRoomObjectEvent.USER_ADDED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (addedEventType) updateEvent = new RoomWidgetUpdateRoomObjectEvent(addedEventType, event.objectId, event.category, event.roomId);
|
||||
break;
|
||||
}
|
||||
case RoomEngineObjectEvent.REMOVED: {
|
||||
let removedEventType: string = null;
|
||||
|
||||
switch (event.category)
|
||||
{
|
||||
case RoomObjectCategory.FLOOR:
|
||||
case RoomObjectCategory.WALL:
|
||||
removedEventType = RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED;
|
||||
break;
|
||||
case RoomObjectCategory.UNIT:
|
||||
removedEventType = RoomWidgetUpdateRoomObjectEvent.USER_REMOVED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (removedEventType) updateEvent = new RoomWidgetUpdateRoomObjectEvent(removedEventType, event.objectId, event.category, event.roomId);
|
||||
break;
|
||||
}
|
||||
case RoomEngineObjectEvent.REQUEST_MOVE:
|
||||
if (CanManipulateFurniture(roomSession, event.objectId, event.category)) ProcessRoomObjectOperation(event.objectId, event.category, RoomObjectOperationType.OBJECT_MOVE);
|
||||
break;
|
||||
case RoomEngineObjectEvent.REQUEST_ROTATE:
|
||||
if (CanManipulateFurniture(roomSession, event.objectId, event.category)) ProcessRoomObjectOperation(event.objectId, event.category, RoomObjectOperationType.OBJECT_ROTATE_POSITIVE);
|
||||
break;
|
||||
case RoomEngineObjectEvent.MOUSE_ENTER:
|
||||
updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_ROLL_OVER, event.objectId, event.category, event.roomId);
|
||||
break;
|
||||
case RoomEngineObjectEvent.MOUSE_LEAVE:
|
||||
updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_ROLL_OUT, event.objectId, event.category, event.roomId);
|
||||
break;
|
||||
case RoomEngineObjectEvent.DOUBLE_CLICK:
|
||||
updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_DOUBLE_CLICKED, event.objectId, event.category, event.roomId);
|
||||
break;
|
||||
}
|
||||
|
||||
if (updateEvent) DispatchUiEvent(updateEvent);
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (!roomSession) return;
|
||||
|
||||
const roomEngine = GetRoomEngine();
|
||||
const roomId = roomSession.roomId;
|
||||
const canvasId = 1;
|
||||
const width = Math.floor(window.innerWidth);
|
||||
const height = Math.floor(window.innerHeight);
|
||||
const renderer = GetRenderer();
|
||||
|
||||
if (renderer) renderer.resize(width, height);
|
||||
|
||||
const displayObject = roomEngine.getRoomInstanceDisplay(roomId, canvasId, width, height, RoomGeometry.SCALE_ZOOMED_IN);
|
||||
const canvas = GetRoomEngine().getRoomInstanceRenderingCanvas(roomId, canvasId);
|
||||
|
||||
if (!displayObject || !canvas) return;
|
||||
|
||||
const background = new NitroSprite(NitroTexture.WHITE);
|
||||
const filter = new NitroAdjustmentFilter();
|
||||
const master = canvas.master;
|
||||
|
||||
background.tint = 0;
|
||||
background.width = width;
|
||||
background.height = height;
|
||||
|
||||
master.addChildAt(background, 0);
|
||||
master.filters = [filter];
|
||||
|
||||
setRoomBackground(background);
|
||||
setRoomFilter(filter);
|
||||
|
||||
const geometry = (roomEngine.getRoomInstanceGeometry(roomId, canvasId) as RoomGeometry);
|
||||
|
||||
if (geometry)
|
||||
{
|
||||
const minX = (roomEngine.getRoomInstanceVariable<number>(roomId, RoomVariableEnum.ROOM_MIN_X) || 0);
|
||||
const maxX = (roomEngine.getRoomInstanceVariable<number>(roomId, RoomVariableEnum.ROOM_MAX_X) || 0);
|
||||
const minY = (roomEngine.getRoomInstanceVariable<number>(roomId, RoomVariableEnum.ROOM_MIN_Y) || 0);
|
||||
const maxY = (roomEngine.getRoomInstanceVariable<number>(roomId, RoomVariableEnum.ROOM_MAX_Y) || 0);
|
||||
|
||||
let x = ((minX + maxX) / 2);
|
||||
let y = ((minY + maxY) / 2);
|
||||
|
||||
const offset = 20;
|
||||
|
||||
x = (x + (offset - 1));
|
||||
y = (y + (offset - 1));
|
||||
|
||||
const z = (Math.sqrt(((offset * offset) + (offset * offset))) * Math.tan(((30 / 180) * Math.PI)));
|
||||
|
||||
geometry.location = new Vector3d(x, y, z);
|
||||
}
|
||||
|
||||
GetStage().addChild(displayObject);
|
||||
|
||||
SetActiveRoomId(roomSession.roomId);
|
||||
|
||||
const resize = (event: UIEvent) =>
|
||||
{
|
||||
const width = Math.floor(window.innerWidth);
|
||||
const height = Math.floor(window.innerHeight);
|
||||
|
||||
renderer.resize(width, height, window.devicePixelRatio);
|
||||
|
||||
background.width = width;
|
||||
background.height = height;
|
||||
|
||||
InitializeRoomInstanceRenderingCanvas(width, height, 1);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
|
||||
return () =>
|
||||
{
|
||||
setRoomBackground(null);
|
||||
setRoomFilter(null);
|
||||
setOriginalRoomBackgroundColor(0);
|
||||
|
||||
window.removeEventListener('resize', resize);
|
||||
};
|
||||
}, [roomSession]);
|
||||
|
||||
return { roomSession };
|
||||
};
|
||||
|
||||
export const useRoom = () => useBetween(useRoomState);
|
||||
@@ -0,0 +1,20 @@
|
||||
export * from './useFurnitureAreaHideWidget';
|
||||
export * from './useFurnitureBackgroundColorWidget';
|
||||
export * from './useFurnitureBadgeDisplayWidget';
|
||||
export * from './useFurnitureContextMenuWidget';
|
||||
export * from './useFurnitureCraftingWidget';
|
||||
export * from './useFurnitureDimmerWidget';
|
||||
export * from './useFurnitureExchangeWidget';
|
||||
export * from './useFurnitureExternalImageWidget';
|
||||
export * from './useFurnitureFriendFurniWidget';
|
||||
export * from './useFurnitureHighScoreWidget';
|
||||
export * from './useFurnitureInternalLinkWidget';
|
||||
export * from './useFurnitureMannequinWidget';
|
||||
export * from './useFurniturePlaylistEditorWidget';
|
||||
export * from './useFurniturePresentWidget';
|
||||
export * from './useFurnitureRoomLinkWidget';
|
||||
export * from './useFurnitureSpamWallPostItWidget';
|
||||
export * from './useFurnitureStackHeightWidget';
|
||||
export * from './useFurnitureStickieWidget';
|
||||
export * from './useFurnitureTrophyWidget';
|
||||
export * from './useFurnitureYoutubeWidget';
|
||||
@@ -0,0 +1,93 @@
|
||||
import { GetRoomEngine, RoomAreaSelectionManager, RoomEngineAreaHideStateEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { CanManipulateFurniture } from '../../../../api';
|
||||
import { useNitroEvent } from '../../../events';
|
||||
import { useRoom } from '../../useRoom';
|
||||
|
||||
const useFurnitureAreaHideWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState<number>(-1);
|
||||
const [ category, setCategory ] = useState<number>(-1);
|
||||
const [ isOn, setIsOn ] = useState<boolean>(false);
|
||||
const [ rootX, setRootX ] = useState<number>(0);
|
||||
const [ rootY, setRootY ] = useState<number>(0);
|
||||
const [ width, setWidth ] = useState<number>(0);
|
||||
const [ length, setLength ] = useState<number>(0);
|
||||
const [ invisibility, setInvisibility ] = useState<boolean>(false);
|
||||
const [ wallItems, setWallItems ] = useState<boolean>(false);
|
||||
const [ inverted, setInverted ] = useState<boolean>(false);
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setCategory(-1);
|
||||
setIsOn(false);
|
||||
setRootX(0);
|
||||
setRootY(0);
|
||||
setWidth(0);
|
||||
setLength(0);
|
||||
setInvisibility(false);
|
||||
setWallItems(false);
|
||||
setInverted(false);
|
||||
|
||||
GetRoomEngine().areaSelectionManager.deactivate();
|
||||
};
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_AREA_HIDE, event =>
|
||||
{
|
||||
if(!CanManipulateFurniture(roomSession, event.objectId, event.category)) return;
|
||||
|
||||
setObjectId(event.objectId);
|
||||
setCategory(event.category);
|
||||
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
const model = roomObject.model;
|
||||
|
||||
setIsOn(roomObject.getState(0) === 1);
|
||||
setRootX(model.getValue<number>(RoomObjectVariable.FURNITURE_AREA_HIDE_ROOT_X) ?? 0);
|
||||
setRootY(model.getValue<number>(RoomObjectVariable.FURNITURE_AREA_HIDE_ROOT_Y) ?? 0);
|
||||
setWidth(model.getValue<number>(RoomObjectVariable.FURNITURE_AREA_HIDE_WIDTH) ?? 0);
|
||||
setLength(model.getValue<number>(RoomObjectVariable.FURNITURE_AREA_HIDE_LENGTH) ?? 0);
|
||||
setInvisibility(model.getValue<number>(RoomObjectVariable.FURNITURE_AREA_HIDE_INVISIBILITY) === 1);
|
||||
setWallItems(model.getValue<number>(RoomObjectVariable.FURNITURE_AREA_HIDE_WALL_ITEMS) === 1);
|
||||
setInverted(model.getValue<number>(RoomObjectVariable.FURNITURE_AREA_HIDE_INVERT) === 1);
|
||||
});
|
||||
|
||||
useNitroEvent<RoomEngineAreaHideStateEvent>(RoomEngineAreaHideStateEvent.UPDATE_STATE_AREA_HIDE, event =>
|
||||
{
|
||||
setObjectId(event.objectId);
|
||||
setCategory(event.category);
|
||||
setIsOn(event.isOn);
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(objectId === -1) return;
|
||||
|
||||
if(!isOn)
|
||||
{
|
||||
const callback = (rootX: number, rootY: number, width: number, height: number) =>
|
||||
{
|
||||
setRootX(rootX);
|
||||
setRootY(rootY);
|
||||
setWidth(width);
|
||||
setLength(height);
|
||||
};
|
||||
|
||||
if(GetRoomEngine().areaSelectionManager.activate(callback, RoomAreaSelectionManager.HIGHLIGHT_DARKEN))
|
||||
{
|
||||
GetRoomEngine().areaSelectionManager.setHighlight(rootX, rootY, width, length);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetRoomEngine().areaSelectionManager.deactivate();
|
||||
}
|
||||
}, [ objectId, isOn, rootX, rootY, width, length ]);
|
||||
|
||||
return { objectId, category, isOn, setIsOn, rootX, setRootX, rootY, setRootY, width, setWidth, length, setLength, invisibility, setInvisibility, wallItems, setWallItems, inverted, setInverted, onClose };
|
||||
};
|
||||
|
||||
export const useFurnitureAreaHideWidget = useFurnitureAreaHideWidgetState;
|
||||
@@ -0,0 +1,71 @@
|
||||
import { ApplyTonerComposer, ColorConverter, GetRoomEngine, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { CanManipulateFurniture, ColorUtils, DispatchUiEvent, RoomWidgetUpdateBackgroundColorPreviewEvent, SendMessageComposer } from '../../../../api';
|
||||
import { useNitroEvent } from '../../../events';
|
||||
import { useFurniRemovedEvent } from '../../engine';
|
||||
import { useRoom } from '../../useRoom';
|
||||
|
||||
const useFurnitureBackgroundColorWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ category, setCategory ] = useState(-1);
|
||||
const [ color, setColor ] = useState(0);
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const applyToner = () =>
|
||||
{
|
||||
const hsl = ColorConverter.rgbToHSL(color);
|
||||
const [ _, hue, saturation, lightness ] = ColorUtils.int_to_8BitVals(hsl);
|
||||
SendMessageComposer(new ApplyTonerComposer(objectId, hue, saturation, lightness));
|
||||
};
|
||||
|
||||
const toggleToner = () => roomSession.useMultistateItem(objectId);
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
DispatchUiEvent(new RoomWidgetUpdateBackgroundColorPreviewEvent(RoomWidgetUpdateBackgroundColorPreviewEvent.CLEAR_PREVIEW));
|
||||
|
||||
setObjectId(-1);
|
||||
setCategory(-1);
|
||||
setColor(0);
|
||||
};
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_BACKGROUND_COLOR, event =>
|
||||
{
|
||||
if(!CanManipulateFurniture(roomSession, event.objectId, event.category)) return;
|
||||
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
const model = roomObject.model;
|
||||
|
||||
setObjectId(event.objectId);
|
||||
setCategory(event.category);
|
||||
const hue = parseInt(model.getValue<string>(RoomObjectVariable.FURNITURE_ROOM_BACKGROUND_COLOR_HUE));
|
||||
const saturation = parseInt(model.getValue<string>(RoomObjectVariable.FURNITURE_ROOM_BACKGROUND_COLOR_SATURATION));
|
||||
const light = parseInt(model.getValue<string>(RoomObjectVariable.FURNITURE_ROOM_BACKGROUND_COLOR_LIGHTNESS));
|
||||
|
||||
const hsl = ColorUtils.eight_bitVals_to_int(0, hue,saturation,light);
|
||||
|
||||
const rgbColor = ColorConverter.hslToRGB(hsl);
|
||||
setColor(rgbColor);
|
||||
});
|
||||
|
||||
useFurniRemovedEvent(((objectId !== -1) && (category !== -1)), event =>
|
||||
{
|
||||
if((event.id !== objectId) || (event.category !== category)) return;
|
||||
|
||||
onClose();
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if((objectId === -1) || (category === -1)) return;
|
||||
|
||||
const hls = ColorConverter.rgbToHSL(color);
|
||||
const [ _, hue, saturation, lightness ] = ColorUtils.int_to_8BitVals(hls);
|
||||
DispatchUiEvent(new RoomWidgetUpdateBackgroundColorPreviewEvent(RoomWidgetUpdateBackgroundColorPreviewEvent.PREVIEW, hue, saturation, lightness));
|
||||
}, [ objectId, category, color ]);
|
||||
|
||||
return { objectId, color, setColor, applyToner, toggleToner, onClose };
|
||||
};
|
||||
|
||||
export const useFurnitureBackgroundColorWidget = useFurnitureBackgroundColorWidgetState;
|
||||
@@ -0,0 +1,75 @@
|
||||
import { GetRoomEngine, GetSessionDataManager, RoomEngineTriggerWidgetEvent, RoomObjectVariable, StringDataType } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { LocalizeBadgeDescription, LocalizeBadgeName, LocalizeText } from '../../../../api';
|
||||
import { useNitroEvent } from '../../../events';
|
||||
import { useNotification } from '../../../notification';
|
||||
import { useFurniRemovedEvent } from '../../engine';
|
||||
|
||||
const useFurnitureBadgeDisplayWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ category, setCategory ] = useState(-1);
|
||||
const [ color, setColor ] = useState('1');
|
||||
const [ badgeName, setBadgeName ] = useState('');
|
||||
const [ badgeDesc, setBadgeDesc ] = useState('');
|
||||
const [ date, setDate ] = useState('');
|
||||
const [ senderName, setSenderName ] = useState('');
|
||||
const { simpleAlert = null } = useNotification();
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setCategory(-1);
|
||||
setColor('1');
|
||||
setBadgeName('');
|
||||
setBadgeDesc('');
|
||||
setDate('');
|
||||
setSenderName('');
|
||||
};
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>([
|
||||
RoomEngineTriggerWidgetEvent.REQUEST_BADGE_DISPLAY_ENGRAVING,
|
||||
RoomEngineTriggerWidgetEvent.REQUEST_ACHIEVEMENT_RESOLUTION_ENGRAVING
|
||||
], event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
const stringStuff = new StringDataType();
|
||||
|
||||
stringStuff.initializeFromRoomObjectModel(roomObject.model);
|
||||
|
||||
setObjectId(event.objectId);
|
||||
setCategory(event.category);
|
||||
setColor('1');
|
||||
setBadgeName(LocalizeBadgeName(stringStuff.getValue(1)));
|
||||
setBadgeDesc(LocalizeBadgeDescription(stringStuff.getValue(1)));
|
||||
setDate(stringStuff.getValue(2));
|
||||
setSenderName(stringStuff.getValue(3));
|
||||
});
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_ACHIEVEMENT_RESOLUTION_FAILED, event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
const ownerId = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_OWNER_ID);
|
||||
|
||||
if(ownerId !== GetSessionDataManager().userId) return;
|
||||
|
||||
simpleAlert(`${ LocalizeText('resolution.failed.subtitle') } ${ LocalizeText('resolution.failed.text') }`, null, null, null, LocalizeText('resolution.failed.title'));
|
||||
});
|
||||
|
||||
useFurniRemovedEvent(((objectId !== -1) && (category !== -1)), event =>
|
||||
{
|
||||
if((event.id !== objectId) || (event.category !== category)) return;
|
||||
|
||||
onClose();
|
||||
});
|
||||
|
||||
return { objectId, category, color, badgeName, badgeDesc, date, senderName, onClose };
|
||||
};
|
||||
|
||||
export const useFurnitureBadgeDisplayWidget = useFurnitureBadgeDisplayWidgetState;
|
||||
@@ -0,0 +1,179 @@
|
||||
import { ContextMenuEnum, GetRoomEngine, GroupFurniContextMenuInfoMessageEvent, GroupFurniContextMenuInfoMessageParser, RoomEngineTriggerWidgetEvent, RoomObjectCategory, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { IsOwnerOfFurniture, TryJoinGroup, TryVisitRoom } from '../../../../api';
|
||||
import { useMessageEvent, useNitroEvent } from '../../../events';
|
||||
import { useRoom } from '../../useRoom';
|
||||
|
||||
export const MONSTERPLANT_SEED_CONFIRMATION: string = 'MONSTERPLANT_SEED_CONFIRMATION';
|
||||
export const PURCHASABLE_CLOTHING_CONFIRMATION: string = 'PURCHASABLE_CLOTHING_CONFIRMATION';
|
||||
export const GROUP_FURNITURE: string = 'GROUP_FURNITURE';
|
||||
export const EFFECTBOX_OPEN: string = 'EFFECTBOX_OPEN';
|
||||
export const MYSTERYTROPHY_OPEN_DIALOG: string = 'MYSTERYTROPHY_OPEN_DIALOG';
|
||||
|
||||
const useFurnitureContextMenuWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ mode, setMode ] = useState<string>(null);
|
||||
const [ confirmMode, setConfirmMode ] = useState<string>(null);
|
||||
const [ confirmingObjectId, setConfirmingObjectId ] = useState(-1);
|
||||
const [ groupData, setGroupData ] = useState<GroupFurniContextMenuInfoMessageParser>(null);
|
||||
const [ isGroupMember, setIsGroupMember ] = useState(false);
|
||||
const [ objectOwnerId, setObjectOwnerId ] = useState(-1);
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setGroupData(null);
|
||||
setIsGroupMember(false);
|
||||
setMode(null);
|
||||
};
|
||||
|
||||
const closeConfirm = () =>
|
||||
{
|
||||
setConfirmMode(null);
|
||||
setConfirmingObjectId(-1);
|
||||
};
|
||||
|
||||
const processAction = (name: string) =>
|
||||
{
|
||||
if(name)
|
||||
{
|
||||
switch(name)
|
||||
{
|
||||
case 'use_friend_furni':
|
||||
roomSession.useMultistateItem(objectId);
|
||||
break;
|
||||
case 'use_monsterplant_seed':
|
||||
setConfirmMode(MONSTERPLANT_SEED_CONFIRMATION);
|
||||
setConfirmingObjectId(objectId);
|
||||
break;
|
||||
case 'use_random_teleport':
|
||||
GetRoomEngine().useRoomObject(objectId, RoomObjectCategory.FLOOR);
|
||||
break;
|
||||
case 'use_purchaseable_clothing':
|
||||
setConfirmMode(PURCHASABLE_CLOTHING_CONFIRMATION);
|
||||
setConfirmingObjectId(objectId);
|
||||
break;
|
||||
case 'use_mystery_box':
|
||||
roomSession.useMultistateItem(objectId);
|
||||
break;
|
||||
case 'use_mystery_trophy':
|
||||
setConfirmMode(MYSTERYTROPHY_OPEN_DIALOG);
|
||||
setConfirmingObjectId(objectId);
|
||||
break;
|
||||
case 'join_group':
|
||||
TryJoinGroup(groupData.guildId);
|
||||
setIsGroupMember(true);
|
||||
return;
|
||||
case 'go_to_group_homeroom':
|
||||
if(groupData) TryVisitRoom(groupData.guildHomeRoomId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onClose();
|
||||
};
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>([
|
||||
RoomEngineTriggerWidgetEvent.OPEN_FURNI_CONTEXT_MENU,
|
||||
RoomEngineTriggerWidgetEvent.CLOSE_FURNI_CONTEXT_MENU,
|
||||
RoomEngineTriggerWidgetEvent.REQUEST_MONSTERPLANT_SEED_PLANT_CONFIRMATION_DIALOG,
|
||||
RoomEngineTriggerWidgetEvent.REQUEST_PURCHASABLE_CLOTHING_CONFIRMATION_DIALOG,
|
||||
RoomEngineTriggerWidgetEvent.REQUEST_EFFECTBOX_OPEN_DIALOG,
|
||||
RoomEngineTriggerWidgetEvent.REQUEST_MYSTERYBOX_OPEN_DIALOG,
|
||||
RoomEngineTriggerWidgetEvent.REQUEST_MYSTERYTROPHY_OPEN_DIALOG
|
||||
], event =>
|
||||
{
|
||||
const object = GetRoomEngine().getRoomObject(roomSession.roomId, event.objectId, event.category);
|
||||
|
||||
if(!object) return;
|
||||
|
||||
setObjectOwnerId(object.model.getValue<number>(RoomObjectVariable.FURNITURE_OWNER_ID));
|
||||
|
||||
switch(event.type)
|
||||
{
|
||||
case RoomEngineTriggerWidgetEvent.REQUEST_MONSTERPLANT_SEED_PLANT_CONFIRMATION_DIALOG:
|
||||
if(!IsOwnerOfFurniture(object)) return;
|
||||
|
||||
setConfirmingObjectId(object.id);
|
||||
setConfirmMode(MONSTERPLANT_SEED_CONFIRMATION);
|
||||
|
||||
onClose();
|
||||
return;
|
||||
case RoomEngineTriggerWidgetEvent.REQUEST_EFFECTBOX_OPEN_DIALOG:
|
||||
if(!IsOwnerOfFurniture(object)) return;
|
||||
|
||||
setConfirmingObjectId(object.id);
|
||||
setConfirmMode(EFFECTBOX_OPEN);
|
||||
|
||||
onClose();
|
||||
return;
|
||||
case RoomEngineTriggerWidgetEvent.REQUEST_PURCHASABLE_CLOTHING_CONFIRMATION_DIALOG:
|
||||
if(!IsOwnerOfFurniture(object)) return;
|
||||
|
||||
setConfirmingObjectId(object.id);
|
||||
setConfirmMode(PURCHASABLE_CLOTHING_CONFIRMATION);
|
||||
|
||||
onClose();
|
||||
return;
|
||||
case RoomEngineTriggerWidgetEvent.REQUEST_MYSTERYBOX_OPEN_DIALOG:
|
||||
roomSession.useMultistateItem(object.id);
|
||||
|
||||
onClose();
|
||||
return;
|
||||
case RoomEngineTriggerWidgetEvent.REQUEST_MYSTERYTROPHY_OPEN_DIALOG:
|
||||
if(!IsOwnerOfFurniture(object)) return;
|
||||
|
||||
setConfirmingObjectId(object.id);
|
||||
setConfirmMode(MYSTERYTROPHY_OPEN_DIALOG);
|
||||
|
||||
onClose();
|
||||
return;
|
||||
case RoomEngineTriggerWidgetEvent.OPEN_FURNI_CONTEXT_MENU:
|
||||
|
||||
setObjectId(object.id);
|
||||
|
||||
switch(event.contextMenu)
|
||||
{
|
||||
case ContextMenuEnum.FRIEND_FURNITURE:
|
||||
setMode(ContextMenuEnum.FRIEND_FURNITURE);
|
||||
return;
|
||||
case ContextMenuEnum.MONSTERPLANT_SEED:
|
||||
if(IsOwnerOfFurniture(object)) setMode(ContextMenuEnum.MONSTERPLANT_SEED);
|
||||
return;
|
||||
case ContextMenuEnum.MYSTERY_BOX:
|
||||
setMode(ContextMenuEnum.MYSTERY_BOX);
|
||||
return;
|
||||
case ContextMenuEnum.MYSTERY_TROPHY:
|
||||
if(IsOwnerOfFurniture(object)) setMode(ContextMenuEnum.MYSTERY_TROPHY);
|
||||
return;
|
||||
case ContextMenuEnum.RANDOM_TELEPORT:
|
||||
setMode(ContextMenuEnum.RANDOM_TELEPORT);
|
||||
return;
|
||||
case ContextMenuEnum.PURCHASABLE_CLOTHING:
|
||||
if(IsOwnerOfFurniture(object)) setMode(ContextMenuEnum.PURCHASABLE_CLOTHING);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
case RoomEngineTriggerWidgetEvent.CLOSE_FURNI_CONTEXT_MENU:
|
||||
if(object.id === objectId) onClose();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
useMessageEvent<GroupFurniContextMenuInfoMessageEvent>(GroupFurniContextMenuInfoMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setObjectId(parser.objectId);
|
||||
setGroupData(parser);
|
||||
setIsGroupMember(parser.userIsMember);
|
||||
setMode(GROUP_FURNITURE);
|
||||
});
|
||||
|
||||
return { objectId, mode, confirmMode, confirmingObjectId, groupData, isGroupMember, objectOwnerId, closeConfirm, processAction, onClose };
|
||||
};
|
||||
|
||||
export const useFurnitureContextMenuWidget = useFurnitureContextMenuWidgetState;
|
||||
@@ -0,0 +1,166 @@
|
||||
import { CraftableProductsEvent, CraftComposer, CraftingRecipeEvent, CraftingRecipeIngredientParser, CraftingRecipesAvailableEvent, CraftingResultEvent, GetCraftableProductsComposer, GetCraftingRecipeComposer, GetRoomContentLoader, GetRoomEngine, RoomEngineTriggerWidgetEvent, RoomWidgetEnum } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ICraftingIngredient, ICraftingRecipe, LocalizeText, SendMessageComposer } from '../../../../api';
|
||||
import { useMessageEvent, useNitroEvent } from '../../../events';
|
||||
import { useInventoryFurni } from '../../../inventory';
|
||||
import { useNotification } from './../../../notification';
|
||||
|
||||
const useFurnitureCraftingWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ recipes, setRecipes ] = useState<ICraftingRecipe[]>([]);
|
||||
const [ selectedRecipe, setSelectedRecipe ] = useState<ICraftingRecipe>(null);
|
||||
const [ ingredients, setIngredients ] = useState<ICraftingIngredient[]>([]);
|
||||
const [ ingredientNames, setIngredientNames ] = useState<string[]>(null);
|
||||
const [ cachedIngredients, setCachedIngredients ] = useState<Map<string, CraftingRecipeIngredientParser[]>>(new Map());
|
||||
const [ isCrafting, setIsCrafting ] = useState(false);
|
||||
const { groupItems = [], getItemsByType = null, activate = null, deactivate = null } = useInventoryFurni();
|
||||
const { simpleAlert = null } = useNotification();
|
||||
|
||||
const requiredIngredients = ((selectedRecipe && cachedIngredients.get(selectedRecipe.name) || null));
|
||||
|
||||
const resetData = () =>
|
||||
{
|
||||
setRecipes([]);
|
||||
setSelectedRecipe(null);
|
||||
setIngredients([]);
|
||||
setCachedIngredients(new Map());
|
||||
};
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
resetData();
|
||||
};
|
||||
|
||||
const craft = () =>
|
||||
{
|
||||
setIsCrafting(true);
|
||||
|
||||
SendMessageComposer(new CraftComposer(objectId, selectedRecipe.name));
|
||||
};
|
||||
|
||||
const selectRecipe = (recipe: ICraftingRecipe) =>
|
||||
{
|
||||
setSelectedRecipe(recipe);
|
||||
|
||||
const cache = cachedIngredients.get(recipe.name);
|
||||
|
||||
if(!cache) SendMessageComposer(new GetCraftingRecipeComposer(recipe.name));
|
||||
};
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.OPEN_WIDGET, event =>
|
||||
{
|
||||
if(event.widget !== RoomWidgetEnum.CRAFTING) return;
|
||||
|
||||
setObjectId(event.objectId);
|
||||
resetData();
|
||||
SendMessageComposer(new GetCraftableProductsComposer(event.objectId));
|
||||
});
|
||||
|
||||
useMessageEvent<CraftableProductsEvent>(CraftableProductsEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser.isActive())
|
||||
{
|
||||
setObjectId(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setRecipes(prevValue =>
|
||||
{
|
||||
const newValue: ICraftingRecipe[] = [];
|
||||
|
||||
for(const recipe of parser.recipes)
|
||||
{
|
||||
//@ts-ignore
|
||||
const itemId = GetRoomContentLoader()._activeObjectTypeIds.get(recipe.itemName);
|
||||
const iconUrl = GetRoomEngine().getFurnitureFloorIconUrl(itemId);
|
||||
|
||||
newValue.push({
|
||||
name: recipe.recipeName,
|
||||
localizedName: LocalizeText('roomItem.name.' + itemId),
|
||||
iconUrl
|
||||
});
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
setIngredientNames(parser.ingredients);
|
||||
});
|
||||
|
||||
useMessageEvent<CraftingRecipeEvent>(CraftingRecipeEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setCachedIngredients(prevValue =>
|
||||
{
|
||||
const newValue = new Map(prevValue);
|
||||
|
||||
newValue.set(selectedRecipe.name, parser.ingredients);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useMessageEvent<CraftingResultEvent>(CraftingResultEvent, event =>
|
||||
{
|
||||
setSelectedRecipe(null);
|
||||
setIsCrafting(false);
|
||||
|
||||
const parser = event.getParser();
|
||||
|
||||
if(parser.result) simpleAlert(LocalizeText('crafting.info.result.ok'));
|
||||
});
|
||||
|
||||
useMessageEvent<CraftingRecipesAvailableEvent>(CraftingRecipesAvailableEvent, event =>
|
||||
{
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!ingredientNames || !ingredientNames.length) return;
|
||||
|
||||
setIngredients(prevValue =>
|
||||
{
|
||||
const newValue: ICraftingIngredient[] = [];
|
||||
|
||||
for(const name of ingredientNames)
|
||||
{
|
||||
//@ts-ignore
|
||||
const itemId = GetRoomContentLoader()._activeObjectTypeIds.get(name);
|
||||
const iconUrl = GetRoomEngine().getFurnitureFloorIconUrl(itemId);
|
||||
|
||||
const inventoryItems = getItemsByType(itemId);
|
||||
|
||||
let amountAvailable = 0;
|
||||
|
||||
if(inventoryItems) for(const inventoryItem of inventoryItems) amountAvailable += inventoryItem.items.length;
|
||||
|
||||
newValue.push({
|
||||
name: name,
|
||||
iconUrl,
|
||||
count: amountAvailable
|
||||
});
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, [ groupItems, ingredientNames, getItemsByType ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if((objectId === -1)) return;
|
||||
|
||||
const id = activate();
|
||||
|
||||
return () => deactivate(id);
|
||||
}, [ objectId, activate, deactivate ]);
|
||||
|
||||
return { objectId, recipes, ingredients, selectedRecipe, requiredIngredients, isCrafting, selectRecipe, craft, onClose };
|
||||
};
|
||||
|
||||
export const useFurnitureCraftingWidget = useFurnitureCraftingWidgetState;
|
||||
@@ -0,0 +1,110 @@
|
||||
import { GetSessionDataManager, RoomControllerLevel, RoomEngineDimmerStateEvent, RoomEngineTriggerWidgetEvent, RoomId, RoomSessionDimmerPresetsEvent } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { DimmerFurnitureWidgetPresetItem, FurnitureDimmerUtilities } from '../../../../api';
|
||||
import { useNitroEvent } from '../../../events';
|
||||
import { useRoom } from '../../useRoom';
|
||||
|
||||
const useFurnitureDimmerWidgetState = () =>
|
||||
{
|
||||
const [ presets, setPresets ] = useState<DimmerFurnitureWidgetPresetItem[]>([]);
|
||||
const [ selectedPresetId, setSelectedPresetId ] = useState(0);
|
||||
const [ dimmerState, setDimmerState ] = useState(0);
|
||||
const [ lastDimmerState, setLastDimmerState ] = useState(0);
|
||||
const [ effectId, setEffectId ] = useState(0);
|
||||
const [ color, setColor ] = useState(0xFFFFFF);
|
||||
const [ brightness, setBrightness ] = useState(0xFF);
|
||||
const [ selectedEffectId, setSelectedEffectId ] = useState(0);
|
||||
const [ selectedColor, setSelectedColor ] = useState(0);
|
||||
const [ selectedBrightness, setSelectedBrightness ] = useState(0);
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const canOpenWidget = () => (roomSession.isRoomOwner || (roomSession.controllerLevel >= RoomControllerLevel.GUEST) || GetSessionDataManager().isModerator);
|
||||
|
||||
const selectPresetId = (id: number) =>
|
||||
{
|
||||
const preset = presets[(id - 1)];
|
||||
|
||||
if(!preset) return;
|
||||
|
||||
setSelectedPresetId(preset.id);
|
||||
setSelectedEffectId(preset.type);
|
||||
setSelectedColor(preset.color);
|
||||
setSelectedBrightness(preset.light);
|
||||
};
|
||||
|
||||
const applyChanges = () =>
|
||||
{
|
||||
if(dimmerState === 0) return;
|
||||
|
||||
const selectedPresetIndex = (selectedPresetId - 1);
|
||||
|
||||
if((selectedPresetId < 1) || (selectedPresetId > presets.length)) return;
|
||||
|
||||
const preset = presets[selectedPresetIndex];
|
||||
|
||||
if(!preset || ((selectedEffectId === preset.type) && (selectedColor === preset.color) && (selectedBrightness === preset.light))) return;
|
||||
|
||||
setPresets(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
newValue[selectedPresetIndex] = new DimmerFurnitureWidgetPresetItem(preset.id, selectedEffectId, selectedColor, selectedBrightness);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
FurnitureDimmerUtilities.savePreset(preset.id, selectedEffectId, selectedColor, selectedBrightness, true);
|
||||
};
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_DIMMER, event =>
|
||||
{
|
||||
if(!canOpenWidget()) return;
|
||||
|
||||
roomSession.requestMoodlightSettings();
|
||||
});
|
||||
|
||||
useNitroEvent<RoomSessionDimmerPresetsEvent>(RoomSessionDimmerPresetsEvent.ROOM_DIMMER_PRESETS, event =>
|
||||
{
|
||||
const presets: DimmerFurnitureWidgetPresetItem[] = [];
|
||||
|
||||
let i = 0;
|
||||
|
||||
while(i < event.presetCount)
|
||||
{
|
||||
const preset = event.getPreset(i);
|
||||
|
||||
if(preset) presets.push(new DimmerFurnitureWidgetPresetItem(preset.id, preset.type, preset.color, preset.brightness));
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
setPresets(presets);
|
||||
setSelectedPresetId(event.selectedPresetId);
|
||||
});
|
||||
|
||||
useNitroEvent<RoomEngineDimmerStateEvent>(RoomEngineDimmerStateEvent.ROOM_COLOR, event =>
|
||||
{
|
||||
if(RoomId.isRoomPreviewerId(event.roomId)) return;
|
||||
|
||||
setLastDimmerState(dimmerState);
|
||||
setDimmerState(event.state);
|
||||
setSelectedPresetId(event.presetId);
|
||||
setEffectId(event.effectId);
|
||||
setSelectedEffectId(event.effectId);
|
||||
setColor(event.color);
|
||||
setSelectedColor(event.color);
|
||||
setBrightness(event.brightness);
|
||||
setSelectedBrightness(event.brightness);
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if((dimmerState === 0) && (lastDimmerState === 0)) return;
|
||||
|
||||
FurnitureDimmerUtilities.previewDimmer(selectedColor, selectedBrightness, (selectedEffectId === 2));
|
||||
}, [ dimmerState, lastDimmerState, selectedColor, selectedBrightness, selectedEffectId ]);
|
||||
|
||||
return { presets, selectedPresetId, dimmerState, lastDimmerState, effectId, color, brightness, selectedEffectId, setSelectedEffectId, selectedColor, setSelectedColor, selectedBrightness, setSelectedBrightness, selectPresetId, applyChanges };
|
||||
};
|
||||
|
||||
export const useFurnitureDimmerWidget = useFurnitureDimmerWidgetState;
|
||||
@@ -0,0 +1,48 @@
|
||||
import { FurnitureExchangeComposer, GetRoomEngine, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { IsOwnerOfFurniture, SendMessageComposer } from '../../../../api';
|
||||
import { useNitroEvent } from '../../../events';
|
||||
import { useFurniRemovedEvent } from '../../engine';
|
||||
|
||||
const useFurnitureExchangeWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ category, setCategory ] = useState(-1);
|
||||
const [ value, setValue ] = useState(0);
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setCategory(-1);
|
||||
setValue(0);
|
||||
};
|
||||
|
||||
const redeem = () =>
|
||||
{
|
||||
SendMessageComposer(new FurnitureExchangeComposer(objectId));
|
||||
|
||||
onClose();
|
||||
};
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_CREDITFURNI, event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject || !IsOwnerOfFurniture(roomObject)) return;
|
||||
|
||||
setObjectId(event.objectId);
|
||||
setCategory(event.category);
|
||||
setValue(roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_CREDIT_VALUE) || 0);
|
||||
});
|
||||
|
||||
useFurniRemovedEvent(((objectId !== -1) && (category !== -1)), event =>
|
||||
{
|
||||
if((event.id !== objectId) || (event.category !== category)) return;
|
||||
|
||||
onClose();
|
||||
});
|
||||
|
||||
return { objectId, value, redeem, onClose };
|
||||
};
|
||||
|
||||
export const useFurnitureExchangeWidget = useFurnitureExchangeWidgetState;
|
||||
@@ -0,0 +1,74 @@
|
||||
import { GetRoomEngine, RoomEngineTriggerWidgetEvent, RoomObjectCategory, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { IPhotoData } from '../../../../api';
|
||||
import { useNitroEvent } from '../../../events';
|
||||
import { useFurniRemovedEvent } from '../../engine';
|
||||
import { useRoom } from '../../useRoom';
|
||||
|
||||
const useFurnitureExternalImageWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ category, setCategory ] = useState(-1);
|
||||
const [ currentPhotoIndex, setCurrentPhotoIndex ] = useState(-1);
|
||||
const [ currentPhotos, setCurrentPhotos ] = useState<IPhotoData[]>([]);
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setCategory(-1);
|
||||
setCurrentPhotoIndex(-1);
|
||||
setCurrentPhotos([]);
|
||||
};
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_EXTERNAL_IMAGE, event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
const roomTotalImages = GetRoomEngine().getRoomObjects(roomSession?.roomId, RoomObjectCategory.WALL);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
const datas: IPhotoData[] = [];
|
||||
|
||||
roomTotalImages.forEach(object =>
|
||||
{
|
||||
if(object.type !== 'external_image_wallitem_poster_small') return null;
|
||||
|
||||
const data = object.model.getValue<string>(RoomObjectVariable.FURNITURE_DATA);
|
||||
const jsonData: IPhotoData = JSON.parse(data);
|
||||
|
||||
datas.push(jsonData);
|
||||
});
|
||||
|
||||
setObjectId(event.objectId);
|
||||
setCategory(event.category);
|
||||
setCurrentPhotos(datas);
|
||||
|
||||
const roomObjectPhotoData = (JSON.parse(roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_DATA)) as IPhotoData);
|
||||
|
||||
setCurrentPhotoIndex(prevValue =>
|
||||
{
|
||||
let index = 0;
|
||||
|
||||
if(roomObjectPhotoData)
|
||||
{
|
||||
index = datas.findIndex(data => (data.u === roomObjectPhotoData.u));
|
||||
}
|
||||
|
||||
if(index < 0) index = 0;
|
||||
|
||||
return index;
|
||||
});
|
||||
});
|
||||
|
||||
useFurniRemovedEvent(((objectId !== -1) && (category !== -1)), event =>
|
||||
{
|
||||
if((event.id !== objectId) || (event.category !== category)) return;
|
||||
|
||||
onClose();
|
||||
});
|
||||
|
||||
return { objectId, currentPhotoIndex, currentPhotos, onClose };
|
||||
};
|
||||
|
||||
export const useFurnitureExternalImageWidget = useFurnitureExternalImageWidgetState;
|
||||
@@ -0,0 +1,75 @@
|
||||
import { FriendFurniConfirmLockMessageComposer, GetRoomEngine, LoveLockFurniFinishedEvent, LoveLockFurniFriendConfirmedEvent, LoveLockFurniStartEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { SendMessageComposer } from '../../../../api';
|
||||
import { useMessageEvent, useNitroEvent } from '../../../events';
|
||||
import { useFurniRemovedEvent } from '../../engine';
|
||||
|
||||
const useFurnitureFriendFurniWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ category, setCategory ] = useState(-1);
|
||||
const [ type, setType ] = useState(0);
|
||||
const [ usernames, setUsernames ] = useState<string[]>([]);
|
||||
const [ figures, setFigures ] = useState<string[]>([]);
|
||||
const [ date, setDate ] = useState<string>(null);
|
||||
const [ stage, setStage ] = useState(0);
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setCategory(-1);
|
||||
setType(0);
|
||||
setUsernames([]);
|
||||
setFigures([]);
|
||||
setDate(null);
|
||||
};
|
||||
|
||||
const respond = (flag: boolean) =>
|
||||
{
|
||||
SendMessageComposer(new FriendFurniConfirmLockMessageComposer(objectId, flag));
|
||||
|
||||
onClose();
|
||||
};
|
||||
|
||||
useMessageEvent<LoveLockFurniStartEvent>(LoveLockFurniStartEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setObjectId(parser.furniId);
|
||||
setStage(parser.start ? 1 : 2);
|
||||
});
|
||||
|
||||
useMessageEvent<LoveLockFurniFinishedEvent>(LoveLockFurniFinishedEvent, event => onClose());
|
||||
useMessageEvent<LoveLockFurniFriendConfirmedEvent>(LoveLockFurniFriendConfirmedEvent, event => onClose());
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_FRIEND_FURNITURE_ENGRAVING, event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
const data = roomObject.model.getValue<string[]>(RoomObjectVariable.FURNITURE_DATA);
|
||||
const type = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_FRIENDFURNI_ENGRAVING);
|
||||
|
||||
if((data[0] !== '1') || (data.length !== 6)) return;
|
||||
|
||||
setObjectId(event.objectId);
|
||||
setCategory(event.category);
|
||||
setType(type);
|
||||
setUsernames([ data[1], data[2] ]);
|
||||
setFigures([ data[3], data[4] ]);
|
||||
setDate(data[5]);
|
||||
setStage(0);
|
||||
});
|
||||
|
||||
useFurniRemovedEvent(((objectId !== -1) && (category !== -1)), event =>
|
||||
{
|
||||
if((event.id !== objectId) || (event.category !== category)) return;
|
||||
|
||||
onClose();
|
||||
});
|
||||
|
||||
return { objectId, type, usernames, figures, date, stage, onClose, respond };
|
||||
};
|
||||
|
||||
export const useFurnitureFriendFurniWidget = useFurnitureFriendFurniWidgetState;
|
||||
@@ -0,0 +1,55 @@
|
||||
import { GetRoomEngine, HighScoreDataType, ObjectDataFactory, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { useNitroEvent } from '../../../events';
|
||||
import { useRoom } from '../../useRoom';
|
||||
|
||||
const SCORE_TYPES = [ 'perteam', 'mostwins', 'classic' ];
|
||||
const CLEAR_TYPES = [ 'alltime', 'daily', 'weekly', 'monthly' ];
|
||||
|
||||
const useFurnitureHighScoreWidgetState = () =>
|
||||
{
|
||||
const [ stuffDatas, setStuffDatas ] = useState<Map<number, HighScoreDataType>>(new Map());
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const getScoreType = (type: number) => SCORE_TYPES[type];
|
||||
const getClearType = (type: number) => CLEAR_TYPES[type];
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_HIGH_SCORE_DISPLAY, event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
const formatKey = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_DATA_FORMAT);
|
||||
const stuffData = (ObjectDataFactory.getData(formatKey) as HighScoreDataType);
|
||||
|
||||
stuffData.initializeFromRoomObjectModel(roomObject.model);
|
||||
|
||||
setStuffDatas(prevValue =>
|
||||
{
|
||||
const newValue = new Map(prevValue);
|
||||
|
||||
newValue.set(roomObject.id, stuffData);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_HIDE_HIGH_SCORE_DISPLAY, event =>
|
||||
{
|
||||
if(event.roomId !== roomSession.roomId) return;
|
||||
|
||||
setStuffDatas(prevValue =>
|
||||
{
|
||||
const newValue = new Map(prevValue);
|
||||
|
||||
newValue.delete(event.objectId);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
return { stuffDatas, getScoreType, getClearType };
|
||||
};
|
||||
|
||||
export const useFurnitureHighScoreWidget = useFurnitureHighScoreWidgetState;
|
||||
@@ -0,0 +1,26 @@
|
||||
import { CreateLinkEvent, GetRoomEngine, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||
import { useNitroEvent } from '../../../events';
|
||||
|
||||
const INTERNALLINK = 'internalLink';
|
||||
|
||||
const useFurnitureInternalLinkWidgetState = () =>
|
||||
{
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_INTERNAL_LINK, event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
const data = roomObject.model.getValue<any>(RoomObjectVariable.FURNITURE_DATA);
|
||||
|
||||
let link = data[INTERNALLINK];
|
||||
|
||||
if(!link || !link.length) link = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_INTERNAL_LINK);
|
||||
|
||||
if(link && link.length) CreateLinkEvent(link);
|
||||
});
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
export const useFurnitureInternalLinkWidget = useFurnitureInternalLinkWidgetState;
|
||||
@@ -0,0 +1,80 @@
|
||||
import { FurnitureMannequinSaveLookComposer, FurnitureMannequinSaveNameComposer, FurnitureMultiStateComposer, GetAvatarRenderManager, GetRoomEngine, HabboClubLevelEnum, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { MannequinUtilities, SendMessageComposer } from '../../../../api';
|
||||
import { useNitroEvent } from '../../../events';
|
||||
import { useFurniRemovedEvent } from '../../engine';
|
||||
|
||||
const useFurnitureMannequinWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ category, setCategory ] = useState(-1);
|
||||
const [ figure, setFigure ] = useState(null);
|
||||
const [ gender, setGender ] = useState(null);
|
||||
const [ clubLevel, setClubLevel ] = useState(HabboClubLevelEnum.NO_CLUB);
|
||||
const [ name, setName ] = useState(null);
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setCategory(-1);
|
||||
setFigure(null);
|
||||
setGender(null);
|
||||
setName(null);
|
||||
};
|
||||
|
||||
const saveFigure = () =>
|
||||
{
|
||||
if(objectId === -1) return;
|
||||
|
||||
SendMessageComposer(new FurnitureMannequinSaveLookComposer(objectId));
|
||||
|
||||
onClose();
|
||||
};
|
||||
|
||||
const wearFigure = () =>
|
||||
{
|
||||
if(objectId === -1) return;
|
||||
|
||||
SendMessageComposer(new FurnitureMultiStateComposer(objectId));
|
||||
|
||||
onClose();
|
||||
};
|
||||
|
||||
const saveName = () =>
|
||||
{
|
||||
if(objectId === -1) return;
|
||||
|
||||
SendMessageComposer(new FurnitureMannequinSaveNameComposer(objectId, name));
|
||||
};
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_MANNEQUIN, event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
const model = roomObject.model;
|
||||
const figure = (model.getValue<string>(RoomObjectVariable.FURNITURE_MANNEQUIN_FIGURE) || null);
|
||||
const gender = (model.getValue<string>(RoomObjectVariable.FURNITURE_MANNEQUIN_GENDER) || null);
|
||||
const figureContainer = GetAvatarRenderManager().createFigureContainer(figure);
|
||||
const figureClubLevel = GetAvatarRenderManager().getFigureClubLevel(figureContainer, gender, MannequinUtilities.MANNEQUIN_CLOTHING_PART_TYPES);
|
||||
|
||||
setObjectId(event.objectId);
|
||||
setCategory(event.category);
|
||||
setFigure(figure);
|
||||
setGender(gender);
|
||||
setClubLevel(figureClubLevel);
|
||||
setName(model.getValue<string>(RoomObjectVariable.FURNITURE_MANNEQUIN_NAME) || null);
|
||||
});
|
||||
|
||||
useFurniRemovedEvent(((objectId !== -1) && (category !== -1)), event =>
|
||||
{
|
||||
if((event.id !== objectId) || (event.category !== category)) return;
|
||||
|
||||
onClose();
|
||||
});
|
||||
|
||||
return { objectId, figure, gender, clubLevel, name, setName, saveFigure, wearFigure, saveName, onClose };
|
||||
};
|
||||
|
||||
export const useFurnitureMannequinWidget = useFurnitureMannequinWidgetState;
|
||||
@@ -0,0 +1,108 @@
|
||||
import { AddJukeboxDiskComposer, AdvancedMap, FurnitureListAddOrUpdateEvent, FurnitureListEvent, FurnitureListRemovedEvent, FurnitureMultiStateComposer, GetRoomEngine, GetSessionDataManager, GetSoundManager, IAdvancedMap, IMessageEvent, ISongInfo, NotifyPlayedSongEvent, NowPlayingEvent, PlayListStatusEvent, RemoveJukeboxDiskComposer, RoomControllerLevel, RoomEngineTriggerWidgetEvent, SongDiskInventoryReceivedEvent } from '@nitrots/nitro-renderer';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { IsOwnerOfFurniture, LocalizeText, NotificationAlertType, NotificationBubbleType, SendMessageComposer } from '../../../../api';
|
||||
import { useMessageEvent, useNitroEvent } from '../../../events';
|
||||
import { useNotification } from '../../../notification';
|
||||
import { useFurniRemovedEvent } from '../../engine';
|
||||
import { useRoom } from '../../useRoom';
|
||||
|
||||
const useFurniturePlaylistEditorWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ category, setCategory ] = useState(-1);
|
||||
const [ currentPlayingIndex, setCurrentPlayingIndex ] = useState(-1);
|
||||
const [ diskInventory, setDiskInventory ] = useState<IAdvancedMap<number, number>>(new AdvancedMap());
|
||||
const [ playlist, setPlaylist ] = useState<ISongInfo[]>([]);
|
||||
const { roomSession = null } = useRoom();
|
||||
const { showSingleBubble = null, simpleAlert = null } = useNotification();
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setCategory(-1);
|
||||
};
|
||||
|
||||
const addToPlaylist = useCallback((diskId: number, slotNumber: number) => SendMessageComposer(new AddJukeboxDiskComposer(diskId, slotNumber)), []);
|
||||
|
||||
const removeFromPlaylist = useCallback((slotNumber: number) => SendMessageComposer(new RemoveJukeboxDiskComposer(slotNumber)), []);
|
||||
|
||||
const togglePlayPause = useCallback((furniId: number, position: number) => SendMessageComposer(new FurnitureMultiStateComposer(furniId, position)), []);
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_PLAYLIST_EDITOR, event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
if(IsOwnerOfFurniture(roomObject))
|
||||
{
|
||||
// show the editor
|
||||
setObjectId(event.objectId);
|
||||
setCategory(event.category);
|
||||
|
||||
GetSoundManager().musicController?.requestUserSongDisks();
|
||||
GetSoundManager().musicController?.getRoomItemPlaylist()?.requestPlayList();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(roomSession.isRoomOwner || (roomSession.controllerLevel >= RoomControllerLevel.GUEST) || GetSessionDataManager().isModerator) SendMessageComposer(new FurnitureMultiStateComposer(event.objectId, -2));
|
||||
});
|
||||
|
||||
useFurniRemovedEvent(((objectId !== -1) && (category !== -1)), event =>
|
||||
{
|
||||
if((event.id !== objectId) || (event.category !== category)) return;
|
||||
|
||||
onClose();
|
||||
});
|
||||
|
||||
useNitroEvent<NowPlayingEvent>(NowPlayingEvent.NPE_SONG_CHANGED, event =>
|
||||
{
|
||||
setCurrentPlayingIndex(event.position);
|
||||
});
|
||||
|
||||
useNitroEvent<NotifyPlayedSongEvent>(NotifyPlayedSongEvent.NOTIFY_PLAYED_SONG, event =>
|
||||
{
|
||||
showSingleBubble(LocalizeText('soundmachine.notification.playing', [ 'songname', 'songauthor' ], [ event.name, event.creator ]), NotificationBubbleType.SOUNDMACHINE);
|
||||
});
|
||||
|
||||
useNitroEvent<SongDiskInventoryReceivedEvent>(SongDiskInventoryReceivedEvent.SDIR_SONG_DISK_INVENTORY_RECEIVENT_EVENT, event =>
|
||||
{
|
||||
setDiskInventory(GetSoundManager().musicController?.songDiskInventory.clone());
|
||||
});
|
||||
|
||||
useNitroEvent<PlayListStatusEvent>(PlayListStatusEvent.PLUE_PLAY_LIST_UPDATED, event =>
|
||||
{
|
||||
setPlaylist(GetSoundManager().musicController?.getRoomItemPlaylist()?.entries.concat());
|
||||
});
|
||||
|
||||
useNitroEvent<PlayListStatusEvent>(PlayListStatusEvent.PLUE_PLAY_LIST_FULL, event =>
|
||||
{
|
||||
simpleAlert(LocalizeText('playlist.editor.alert.playlist.full'), NotificationAlertType.ALERT, '', '', LocalizeText('playlist.editor.alert.playlist.full.title'));
|
||||
});
|
||||
|
||||
const onFurniListUpdated = (event : IMessageEvent) =>
|
||||
{
|
||||
if(objectId === -1) return;
|
||||
|
||||
if(event instanceof FurnitureListEvent)
|
||||
{
|
||||
if(event.getParser().fragmentNumber === 0)
|
||||
{
|
||||
GetSoundManager().musicController?.requestUserSongDisks();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetSoundManager().musicController?.requestUserSongDisks();
|
||||
}
|
||||
};
|
||||
|
||||
useMessageEvent(FurnitureListEvent, onFurniListUpdated);
|
||||
useMessageEvent(FurnitureListRemovedEvent, onFurniListUpdated);
|
||||
useMessageEvent(FurnitureListAddOrUpdateEvent, onFurniListUpdated);
|
||||
|
||||
return { objectId, diskInventory, playlist, currentPlayingIndex, onClose, addToPlaylist, removeFromPlaylist, togglePlayPause };
|
||||
};
|
||||
|
||||
export const useFurniturePlaylistEditorWidget = useFurniturePlaylistEditorWidgetState;
|
||||
@@ -0,0 +1,235 @@
|
||||
import { GetRoomEngine, GetSessionDataManager, IFurnitureData, IGetImageListener, PetFigureData, RoomEngineTriggerWidgetEvent, RoomObjectCategory, RoomObjectVariable, RoomSessionPresentEvent, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { IsOwnerOfFurniture, LocalizeText, ProductTypeEnum } from '../../../../api';
|
||||
import { useNitroEvent } from '../../../events';
|
||||
import { useFurniRemovedEvent } from '../../engine';
|
||||
import { useRoom } from '../../useRoom';
|
||||
|
||||
const FLOOR: string = 'floor';
|
||||
const WALLPAPER: string = 'wallpaper';
|
||||
const LANDSCAPE: string = 'landscape';
|
||||
const POSTER: string = 'poster';
|
||||
|
||||
const useFurniturePresentWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ classId, setClassId ] = useState(-1);
|
||||
const [ itemType, setItemType ] = useState<string>(null);
|
||||
const [ text, setText ] = useState<string>(null);
|
||||
const [ isOwnerOfFurniture, setIsOwnerOfFurniture ] = useState(false);
|
||||
const [ senderName, setSenderName ] = useState<string>(null);
|
||||
const [ senderFigure, setSenderFigure ] = useState<string>(null);
|
||||
const [ placedItemId, setPlacedItemId ] = useState(-1);
|
||||
const [ placedItemType, setPlacedItemType ] = useState<string>(null);
|
||||
const [ placedInRoom, setPlacedInRoom ] = useState(false);
|
||||
const [ imageUrl, setImageUrl ] = useState<string>(null);
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setClassId(-1);
|
||||
setItemType(null);
|
||||
setText(null);
|
||||
setIsOwnerOfFurniture(false);
|
||||
setSenderName(null);
|
||||
setSenderFigure(null);
|
||||
setPlacedItemId(-1);
|
||||
setPlacedItemType(null);
|
||||
setPlacedInRoom(false);
|
||||
setImageUrl(null);
|
||||
};
|
||||
|
||||
const openPresent = () =>
|
||||
{
|
||||
if(objectId === -1) return;
|
||||
|
||||
roomSession.openGift(objectId);
|
||||
|
||||
GetRoomEngine().changeObjectModelData(GetRoomEngine().activeRoomId, objectId, RoomObjectCategory.FLOOR, RoomObjectVariable.FURNITURE_DISABLE_PICKING_ANIMATION, 1);
|
||||
};
|
||||
|
||||
const imageListener: IGetImageListener = useMemo(() =>
|
||||
{
|
||||
// async fix image
|
||||
return {
|
||||
imageReady: (id, texture, image) =>
|
||||
{
|
||||
(async () =>
|
||||
{
|
||||
if(!image && texture)
|
||||
{
|
||||
image = await TextureUtils.generateImage(texture);
|
||||
}
|
||||
|
||||
setImageUrl(image.src);
|
||||
})();
|
||||
},
|
||||
imageFailed: null
|
||||
};
|
||||
}, []);
|
||||
|
||||
useNitroEvent<RoomSessionPresentEvent>(RoomSessionPresentEvent.RSPE_PRESENT_OPENED, event =>
|
||||
{
|
||||
let furniData: IFurnitureData = null;
|
||||
|
||||
if(event.itemType === ProductTypeEnum.FLOOR)
|
||||
{
|
||||
furniData = GetSessionDataManager().getFloorItemData(event.classId);
|
||||
}
|
||||
else if(event.itemType === ProductTypeEnum.WALL)
|
||||
{
|
||||
furniData = GetSessionDataManager().getWallItemData(event.classId);
|
||||
}
|
||||
|
||||
let isOwnerOfFurni = false;
|
||||
|
||||
if(event.placedInRoom)
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, event.placedItemId, RoomObjectCategory.FLOOR);
|
||||
|
||||
if(roomObject) isOwnerOfFurni = IsOwnerOfFurniture(roomObject);
|
||||
}
|
||||
|
||||
let giftImage: string = null;
|
||||
|
||||
switch(event.itemType)
|
||||
{
|
||||
case ProductTypeEnum.WALL: {
|
||||
if(furniData)
|
||||
{
|
||||
switch(furniData.className)
|
||||
{
|
||||
case FLOOR:
|
||||
case LANDSCAPE:
|
||||
case WALLPAPER:
|
||||
let imageType = null;
|
||||
let message = null;
|
||||
|
||||
if(furniData.className === FLOOR)
|
||||
{
|
||||
imageType = 'packagecard_icon_floor';
|
||||
message = LocalizeText('inventory.furni.item.floor.name');
|
||||
}
|
||||
|
||||
else if(furniData.className === LANDSCAPE)
|
||||
{
|
||||
imageType = 'packagecard_icon_landscape';
|
||||
message = LocalizeText('inventory.furni.item.landscape.name');
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
imageType = 'packagecard_icon_wallpaper';
|
||||
message = LocalizeText('inventory.furni.item.wallpaper.name');
|
||||
}
|
||||
|
||||
setText(message);
|
||||
//setImageUrl(getGiftImageUrl(imageType));
|
||||
break;
|
||||
case POSTER: {
|
||||
const productCode = event.productCode;
|
||||
|
||||
let extras: string = null;
|
||||
|
||||
if(productCode.indexOf('poster') === 0) extras = productCode.replace('poster', '');
|
||||
|
||||
const productData = GetSessionDataManager().getProductData(productCode);
|
||||
|
||||
let name: string = null;
|
||||
|
||||
if(productData) name = productData.name;
|
||||
else if(furniData) name = furniData.name;
|
||||
|
||||
setText(name);
|
||||
setImageUrl(GetRoomEngine().getFurnitureWallIconUrl(event.classId, extras));
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
setText(furniData.name || null);
|
||||
setImageUrl(GetRoomEngine().getFurnitureWallIconUrl(event.classId));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ProductTypeEnum.HABBO_CLUB:
|
||||
setText(LocalizeText('widget.furni.present.hc'));
|
||||
//setImageUrl(getGiftImageUrl('packagecard_icon_hc'));
|
||||
break;
|
||||
default: {
|
||||
if(event.placedItemType === ProductTypeEnum.PET)
|
||||
{
|
||||
const petfigureString = event.petFigureString;
|
||||
|
||||
if(petfigureString && petfigureString.length)
|
||||
{
|
||||
const petFigureData = new PetFigureData(petfigureString);
|
||||
|
||||
(async () =>
|
||||
{
|
||||
const petImage = GetRoomEngine().getRoomObjectPetImage(petFigureData.typeId, petFigureData.paletteId, petFigureData.color, new Vector3d(90), 64, imageListener, true, 0, petFigureData.customParts);
|
||||
|
||||
if(petImage) setImageUrl((await petImage.getImage()).src);
|
||||
})();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
(async () =>
|
||||
{
|
||||
const furniImage = GetRoomEngine().getFurnitureFloorImage(event.classId, new Vector3d(90), 64, imageListener);
|
||||
|
||||
if(furniImage) setImageUrl((await furniImage.getImage()).src);
|
||||
})();
|
||||
}
|
||||
|
||||
const productData = GetSessionDataManager().getProductData(event.productCode);
|
||||
|
||||
setText((productData && productData.name) || furniData.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setObjectId(0);
|
||||
setClassId(event.classId);
|
||||
setItemType(event.itemType);
|
||||
setIsOwnerOfFurniture(isOwnerOfFurni);
|
||||
setPlacedItemId(event.placedItemId);
|
||||
setPlacedItemType(event.placedItemType);
|
||||
setPlacedInRoom(event.placedInRoom);
|
||||
});
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_PRESENT, event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject) return null;
|
||||
|
||||
onClose();
|
||||
|
||||
setObjectId(event.objectId);
|
||||
setClassId(-1);
|
||||
setText((roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_DATA) || ''));
|
||||
setIsOwnerOfFurniture(IsOwnerOfFurniture(roomObject));
|
||||
setSenderName((roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_PURCHASER_NAME) || null));
|
||||
setSenderFigure((roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_PURCHASER_FIGURE) || null));
|
||||
});
|
||||
|
||||
useFurniRemovedEvent((objectId !== -1), event =>
|
||||
{
|
||||
if(event.id === objectId) onClose();
|
||||
|
||||
if(event.id === placedItemId)
|
||||
{
|
||||
if(placedInRoom) setPlacedInRoom(false);
|
||||
}
|
||||
});
|
||||
|
||||
return { objectId, classId, itemType, text, isOwnerOfFurniture, senderName, senderFigure, placedItemId, placedItemType, placedInRoom, imageUrl, openPresent, onClose };
|
||||
};
|
||||
|
||||
export const useFurniturePresentWidget = useFurniturePresentWidgetState;
|
||||
@@ -0,0 +1,49 @@
|
||||
import { GetGuestRoomMessageComposer, GetGuestRoomResultEvent, GetRoomEngine, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { SendMessageComposer } from '../../../../api';
|
||||
import { useMessageEvent, useNitroEvent } from '../../../events';
|
||||
|
||||
const INTERNALLINK = 'internalLink';
|
||||
|
||||
const useFurnitureRoomLinkWidgetState = () =>
|
||||
{
|
||||
const [ roomIdToEnter, setRoomIdToEnter ] = useState(0);
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_ROOM_LINK, event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
const data = roomObject.model.getValue<any>(RoomObjectVariable.FURNITURE_DATA);
|
||||
|
||||
let roomId = data[INTERNALLINK];
|
||||
|
||||
if(!roomId || !roomId.length) roomId = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_INTERNAL_LINK);
|
||||
|
||||
if(!roomId || !roomId.length) return;
|
||||
|
||||
roomId = parseInt(roomId, 10);
|
||||
|
||||
if(isNaN(roomId)) return;
|
||||
|
||||
setRoomIdToEnter(roomId);
|
||||
|
||||
SendMessageComposer(new GetGuestRoomMessageComposer(roomId, false, false));
|
||||
});
|
||||
|
||||
useMessageEvent<GetGuestRoomResultEvent>(GetGuestRoomResultEvent, event =>
|
||||
{
|
||||
if(!roomIdToEnter) return;
|
||||
|
||||
const parser = event.getParser();
|
||||
|
||||
if(parser.data.roomId !== roomIdToEnter) return;
|
||||
|
||||
setRoomIdToEnter(0);
|
||||
});
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
export const useFurnitureRoomLinkWidget = useFurnitureRoomLinkWidgetState;
|
||||
@@ -0,0 +1,59 @@
|
||||
import { AddSpamWallPostItMessageComposer, GetRoomEngine, RequestSpamWallPostItMessageEvent, RoomObjectCategory } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { SendMessageComposer } from '../../../../api';
|
||||
import { useMessageEvent } from '../../../events';
|
||||
import { useInventoryFurni } from '../../../inventory';
|
||||
|
||||
const useFurnitureSpamWallPostItWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ category, setCategory ] = useState(-1);
|
||||
const [ itemType, setItemType ] = useState('');
|
||||
const [ location, setLocation ] = useState('');
|
||||
const [ color, setColor ] = useState('0');
|
||||
const [ text, setText ] = useState('');
|
||||
const [ canModify, setCanModify ] = useState(false);
|
||||
const { getWallItemById = null } = useInventoryFurni();
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
SendMessageComposer(new AddSpamWallPostItMessageComposer(objectId, location, color, text));
|
||||
|
||||
setObjectId(-1);
|
||||
setCategory(-1);
|
||||
setItemType('');
|
||||
setLocation('');
|
||||
setColor('0');
|
||||
setText('');
|
||||
setCanModify(false);
|
||||
};
|
||||
|
||||
useMessageEvent<RequestSpamWallPostItMessageEvent>(RequestSpamWallPostItMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setObjectId(parser.itemId);
|
||||
setCategory(RoomObjectCategory.WALL);
|
||||
|
||||
const inventoryItem = getWallItemById(parser.itemId);
|
||||
|
||||
let itemType = 'post_it';
|
||||
|
||||
if(inventoryItem)
|
||||
{
|
||||
const wallItemType = GetRoomEngine().getFurnitureWallName(inventoryItem.type);
|
||||
|
||||
if(wallItemType.match('post_it_')) itemType = wallItemType;
|
||||
}
|
||||
|
||||
setItemType(itemType);
|
||||
setLocation(parser.location);
|
||||
setColor('FFFF33');
|
||||
setText('');
|
||||
setCanModify(true);
|
||||
});
|
||||
|
||||
return { objectId, color, setColor, text, setText, canModify, onClose };
|
||||
};
|
||||
|
||||
export const useFurnitureSpamWallPostItWidget = useFurnitureSpamWallPostItWidgetState;
|
||||
@@ -0,0 +1,79 @@
|
||||
import { FurnitureStackHeightComposer, FurnitureStackHeightEvent, GetRoomEngine, RoomEngineTriggerWidgetEvent } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { CanManipulateFurniture, GetRoomSession, SendMessageComposer } from '../../../../api';
|
||||
import { useMessageEvent, useNitroEvent } from '../../../events';
|
||||
import { useFurniRemovedEvent } from '../../engine';
|
||||
|
||||
const MAX_HEIGHT: number = 40;
|
||||
|
||||
const useFurnitureStackHeightWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ category, setCategory ] = useState(-1);
|
||||
const [ height, setHeight ] = useState(0);
|
||||
const [ pendingHeight, setPendingHeight ] = useState(-1);
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setCategory(-1);
|
||||
setHeight(0);
|
||||
setPendingHeight(-1);
|
||||
};
|
||||
|
||||
const updateHeight = (height: number, server: boolean = false) =>
|
||||
{
|
||||
if(!height) height = 0;
|
||||
|
||||
height = Math.abs(height);
|
||||
|
||||
if(!server) ((height > MAX_HEIGHT) && (height = MAX_HEIGHT));
|
||||
|
||||
setHeight(parseFloat(height.toFixed(2)));
|
||||
|
||||
if(!server) setPendingHeight(height * 100);
|
||||
};
|
||||
|
||||
useMessageEvent<FurnitureStackHeightEvent>(FurnitureStackHeightEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(objectId !== parser.furniId) return;
|
||||
|
||||
updateHeight(parser.height, true);
|
||||
});
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_STACK_HEIGHT, event =>
|
||||
{
|
||||
if(!CanManipulateFurniture(GetRoomSession(), event.objectId, event.category)) return;
|
||||
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
setObjectId(event.objectId);
|
||||
setCategory(event.category);
|
||||
setHeight(roomObject.getLocation().z);
|
||||
setPendingHeight(-1);
|
||||
});
|
||||
|
||||
useFurniRemovedEvent(((objectId !== -1) && (category !== -1)), event =>
|
||||
{
|
||||
if((event.id !== objectId) || (event.category !== category)) return;
|
||||
|
||||
onClose();
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if((objectId === -1) || (pendingHeight === -1)) return;
|
||||
|
||||
const timeout = setTimeout(() => SendMessageComposer(new FurnitureStackHeightComposer(objectId, ~~(pendingHeight))), 10);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [ objectId, pendingHeight ]);
|
||||
|
||||
return { objectId, height, maxHeight: MAX_HEIGHT, onClose, updateHeight };
|
||||
};
|
||||
|
||||
export const useFurnitureStackHeightWidget = useFurnitureStackHeightWidgetState;
|
||||
@@ -0,0 +1,85 @@
|
||||
import { GetRoomEngine, GetSessionDataManager, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { GetRoomSession, IsOwnerOfFurniture } from '../../../../api';
|
||||
import { useNitroEvent } from '../../../events';
|
||||
import { useFurniRemovedEvent } from '../../engine';
|
||||
|
||||
const useFurnitureStickieWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ category, setCategory ] = useState(-1);
|
||||
const [ color, setColor ] = useState('0');
|
||||
const [ text, setText ] = useState('');
|
||||
const [ type, setType ] = useState('');
|
||||
const [ canModify, setCanModify ] = useState(false);
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setCategory(-1);
|
||||
setColor('0');
|
||||
setText('');
|
||||
setType('');
|
||||
setCanModify(false);
|
||||
};
|
||||
|
||||
const updateColor = (newColor: string) =>
|
||||
{
|
||||
if(newColor === color) return;
|
||||
|
||||
setColor(newColor);
|
||||
|
||||
GetRoomEngine().modifyRoomObjectData(objectId, category, newColor, text);
|
||||
};
|
||||
|
||||
const updateText = (newText: string) =>
|
||||
{
|
||||
setText(newText);
|
||||
|
||||
GetRoomEngine().modifyRoomObjectData(objectId, category, color, newText);
|
||||
};
|
||||
|
||||
const trash = () => GetRoomEngine().deleteRoomObject(objectId, category);
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_STICKIE, event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
const data = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_ITEMDATA);
|
||||
|
||||
if(data.length < 6) return;
|
||||
|
||||
let color: string = null;
|
||||
let text: string = null;
|
||||
|
||||
if(data.indexOf(' ') > 0)
|
||||
{
|
||||
color = data.slice(0, data.indexOf(' '));
|
||||
text = data.slice((data.indexOf(' ') + 1), data.length);
|
||||
}
|
||||
else
|
||||
{
|
||||
color = data;
|
||||
}
|
||||
|
||||
setObjectId(event.objectId);
|
||||
setCategory(event.category);
|
||||
setColor(color || '0');
|
||||
setText(text || '');
|
||||
setType(roomObject.type || 'post_it');
|
||||
setCanModify(GetRoomSession().isRoomOwner || GetSessionDataManager().isModerator || IsOwnerOfFurniture(roomObject));
|
||||
});
|
||||
|
||||
useFurniRemovedEvent(((objectId !== -1) && (category !== -1)), event =>
|
||||
{
|
||||
if((event.id !== objectId) || (event.category !== category)) return;
|
||||
|
||||
onClose();
|
||||
});
|
||||
|
||||
return { objectId, color, text, type, canModify, updateColor, updateText, trash, onClose };
|
||||
};
|
||||
|
||||
export const useFurnitureStickieWidget = useFurnitureStickieWidgetState;
|
||||
@@ -0,0 +1,62 @@
|
||||
import { GetRoomEngine, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { useNitroEvent } from '../../../events';
|
||||
import { useFurniRemovedEvent } from '../../engine';
|
||||
|
||||
const useFurnitureTrophyWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ category, setCategory ] = useState(-1);
|
||||
const [ color, setColor ] = useState('1');
|
||||
const [ senderName, setSenderName ] = useState('');
|
||||
const [ date, setDate ] = useState('');
|
||||
const [ message, setMessage ] = useState('');
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setCategory(-1);
|
||||
setColor('1');
|
||||
setSenderName('');
|
||||
setDate('');
|
||||
setMessage('');
|
||||
};
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_TROPHY, event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
let data = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_DATA);
|
||||
let extra = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_EXTRAS);
|
||||
|
||||
if(!extra) extra = '0';
|
||||
|
||||
setObjectId(event.objectId);
|
||||
setCategory(event.category);
|
||||
setColor(roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_COLOR) || '1');
|
||||
|
||||
const senderName = data.substring(0, data.indexOf('\t'));
|
||||
|
||||
data = data.substring((senderName.length + 1), data.length);
|
||||
|
||||
const trophyDate = data.substring(0, data.indexOf('\t'));
|
||||
const trophyText = data.substr((trophyDate.length + 1), data.length);
|
||||
|
||||
setSenderName(senderName);
|
||||
setDate(trophyDate);
|
||||
setMessage(trophyText);
|
||||
});
|
||||
|
||||
useFurniRemovedEvent(((objectId !== -1) && (category !== -1)), event =>
|
||||
{
|
||||
if((event.id !== objectId) || (event.category !== category)) return;
|
||||
|
||||
onClose();
|
||||
});
|
||||
|
||||
return { objectId, color, senderName, date, message, onClose };
|
||||
};
|
||||
|
||||
export const useFurnitureTrophyWidget = useFurnitureTrophyWidgetState;
|
||||
@@ -0,0 +1,127 @@
|
||||
import { ControlYoutubeDisplayPlaybackMessageComposer, GetRoomEngine, GetSessionDataManager, GetYoutubeDisplayStatusMessageComposer, RoomEngineTriggerWidgetEvent, RoomId, SecurityLevel, SetYoutubeDisplayPlaylistMessageComposer, YoutubeControlVideoMessageEvent, YoutubeDisplayPlaylist, YoutubeDisplayPlaylistsEvent, YoutubeDisplayVideoMessageEvent } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { IsOwnerOfFurniture, SendMessageComposer, YoutubeVideoPlaybackStateEnum } from '../../../../api';
|
||||
import { useMessageEvent, useNitroEvent } from '../../../events';
|
||||
import { useFurniRemovedEvent } from '../../engine';
|
||||
|
||||
const CONTROL_COMMAND_PREVIOUS_VIDEO = 0;
|
||||
const CONTROL_COMMAND_NEXT_VIDEO = 1;
|
||||
const CONTROL_COMMAND_PAUSE_VIDEO = 2;
|
||||
const CONTROL_COMMAND_CONTINUE_VIDEO = 3;
|
||||
|
||||
const useFurnitureYoutubeWidgetState = () =>
|
||||
{
|
||||
const [ objectId, setObjectId ] = useState(-1);
|
||||
const [ category, setCategory ] = useState(-1);
|
||||
const [ videoId, setVideoId ] = useState<string>(null);
|
||||
const [ videoStart, setVideoStart ] = useState<number>(null);
|
||||
const [ videoEnd, setVideoEnd ] = useState<number>(null);
|
||||
const [ currentVideoState, setCurrentVideoState ] = useState(-1);
|
||||
const [ selectedVideo, setSelectedVideo ] = useState<string>(null);
|
||||
const [ playlists, setPlaylists ] = useState<YoutubeDisplayPlaylist[]>(null);
|
||||
const [ hasControl, setHasControl ] = useState(false);
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setCategory(-1);
|
||||
setVideoId(null);
|
||||
setVideoStart(null);
|
||||
setVideoEnd(null);
|
||||
setCurrentVideoState(-1);
|
||||
setSelectedVideo(null);
|
||||
setPlaylists(null);
|
||||
setHasControl(false);
|
||||
};
|
||||
|
||||
const previous = () => SendMessageComposer(new ControlYoutubeDisplayPlaybackMessageComposer(objectId, CONTROL_COMMAND_PREVIOUS_VIDEO));
|
||||
|
||||
const next = () => SendMessageComposer(new ControlYoutubeDisplayPlaybackMessageComposer(objectId, CONTROL_COMMAND_NEXT_VIDEO));
|
||||
|
||||
const pause = () => (hasControl && videoId && videoId.length) && SendMessageComposer(new ControlYoutubeDisplayPlaybackMessageComposer(objectId, CONTROL_COMMAND_PAUSE_VIDEO));
|
||||
|
||||
const play = () => (hasControl && videoId && videoId.length) && SendMessageComposer(new ControlYoutubeDisplayPlaybackMessageComposer(objectId, CONTROL_COMMAND_CONTINUE_VIDEO));
|
||||
|
||||
const selectVideo = (video: string) =>
|
||||
{
|
||||
if(selectedVideo === video)
|
||||
{
|
||||
setSelectedVideo(null);
|
||||
SendMessageComposer(new SetYoutubeDisplayPlaylistMessageComposer(objectId, ''));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedVideo(video);
|
||||
SendMessageComposer(new SetYoutubeDisplayPlaylistMessageComposer(objectId, video));
|
||||
};
|
||||
|
||||
useNitroEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.REQUEST_YOUTUBE, event =>
|
||||
{
|
||||
if(RoomId.isRoomPreviewerId(event.roomId)) return;
|
||||
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
setObjectId(event.objectId);
|
||||
setCategory(event.category);
|
||||
setHasControl(GetSessionDataManager().hasSecurity(SecurityLevel.EMPLOYEE) || IsOwnerOfFurniture(roomObject));
|
||||
|
||||
SendMessageComposer(new GetYoutubeDisplayStatusMessageComposer(event.objectId));
|
||||
});
|
||||
|
||||
useMessageEvent<YoutubeDisplayVideoMessageEvent>(YoutubeDisplayVideoMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if((objectId === -1) || (objectId !== parser.furniId)) return;
|
||||
|
||||
setVideoId(parser.videoId);
|
||||
setVideoStart(parser.startAtSeconds);
|
||||
setVideoEnd(parser.endAtSeconds);
|
||||
setCurrentVideoState(parser.state);
|
||||
});
|
||||
|
||||
useMessageEvent<YoutubeDisplayPlaylistsEvent>(YoutubeDisplayPlaylistsEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if((objectId === -1) || (objectId !== parser.furniId)) return;
|
||||
|
||||
setPlaylists(parser.playlists);
|
||||
setSelectedVideo(parser.selectedPlaylistId);
|
||||
setVideoId(null);
|
||||
setCurrentVideoState(-1);
|
||||
setVideoEnd(null);
|
||||
setVideoStart(null);
|
||||
});
|
||||
|
||||
useMessageEvent<YoutubeControlVideoMessageEvent>(YoutubeControlVideoMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if((objectId === -1) || (objectId !== parser.furniId)) return;
|
||||
|
||||
switch(parser.commandId)
|
||||
{
|
||||
case 1:
|
||||
setCurrentVideoState(YoutubeVideoPlaybackStateEnum.PLAYING);
|
||||
break;
|
||||
case 2:
|
||||
setCurrentVideoState(YoutubeVideoPlaybackStateEnum.PAUSED);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
useFurniRemovedEvent(((objectId !== -1) && (category !== -1)), event =>
|
||||
{
|
||||
if((event.id !== objectId) || (event.category !== category)) return;
|
||||
|
||||
onClose();
|
||||
});
|
||||
|
||||
return { objectId, videoId, videoStart, videoEnd, currentVideoState, selectedVideo, playlists, onClose, previous, next, pause, play, selectVideo };
|
||||
};
|
||||
|
||||
export const useFurnitureYoutubeWidget = useFurnitureYoutubeWidgetState;
|
||||
@@ -0,0 +1,12 @@
|
||||
export * from './furniture';
|
||||
export * from './useAvatarInfoWidget';
|
||||
export * from './useChatInputWidget';
|
||||
export * from './useChatWidget';
|
||||
export * from './useDoorbellWidget';
|
||||
export * from './useFilterWordsWidget';
|
||||
export * from './useFriendRequestWidget';
|
||||
export * from './useFurniChooserWidget';
|
||||
export * from './usePetPackageWidget';
|
||||
export * from './usePollWidget';
|
||||
export * from './useUserChooserWidget';
|
||||
export * from './useWordQuizWidget';
|
||||
@@ -0,0 +1,357 @@
|
||||
import { GetRoomEngine, GetSessionDataManager, RoomEngineObjectEvent, RoomEngineUseProductEvent, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomSessionPetInfoUpdateEvent, RoomSessionPetStatusUpdateEvent, RoomSessionUserDataUpdateEvent } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { AvatarInfoFurni, AvatarInfoName, AvatarInfoPet, AvatarInfoRentableBot, AvatarInfoUser, AvatarInfoUtilities, CanManipulateFurniture, FurniCategory, IAvatarInfo, IsOwnerOfFurniture, RoomWidgetUpdateRoomObjectEvent, UseProductItem } from '../../../api';
|
||||
import { useNitroEvent, useUiEvent } from '../../events';
|
||||
import { useFriends } from '../../friends';
|
||||
import { useWired } from '../../wired';
|
||||
import { useObjectDeselectedEvent, useObjectRollOutEvent, useObjectRollOverEvent, useObjectSelectedEvent } from '../engine';
|
||||
import { useRoom } from '../useRoom';
|
||||
|
||||
const useAvatarInfoWidgetState = () =>
|
||||
{
|
||||
const [ avatarInfo, setAvatarInfo ] = useState<IAvatarInfo>(null);
|
||||
const [ activeNameBubble, setActiveNameBubble ] = useState<AvatarInfoName>(null);
|
||||
const [ nameBubbles, setNameBubbles ] = useState<AvatarInfoName[]>([]);
|
||||
const [ productBubbles, setProductBubbles ] = useState<UseProductItem[]>([]);
|
||||
const [ confirmingProduct, setConfirmingProduct ] = useState<UseProductItem>(null);
|
||||
const [ pendingPetId, setPendingPetId ] = useState<number>(-1);
|
||||
const [ isDecorating, setIsDecorating ] = useState(false);
|
||||
const { friends = [] } = useFriends();
|
||||
const { selectObjectForWired = null } = useWired();
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const removeNameBubble = (index: number) =>
|
||||
{
|
||||
setNameBubbles(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
newValue.splice(index, 1);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
};
|
||||
|
||||
const removeProductBubble = (index: number) =>
|
||||
{
|
||||
setProductBubbles(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
const item = newValue.splice(index, 1)[0];
|
||||
|
||||
if(confirmingProduct === item) setConfirmingProduct(null);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
};
|
||||
|
||||
const updateConfirmingProduct = (product: UseProductItem) =>
|
||||
{
|
||||
setConfirmingProduct(product);
|
||||
setProductBubbles([]);
|
||||
};
|
||||
|
||||
const getObjectName = (objectId: number, category: number) =>
|
||||
{
|
||||
const name = AvatarInfoUtilities.getObjectName(objectId, category);
|
||||
|
||||
if(!name) return;
|
||||
|
||||
setActiveNameBubble(name);
|
||||
|
||||
if(category !== RoomObjectCategory.UNIT) setProductBubbles([]);
|
||||
};
|
||||
|
||||
const getObjectInfo = (objectId: number, category: number) =>
|
||||
{
|
||||
let info: IAvatarInfo = null;
|
||||
|
||||
switch(category)
|
||||
{
|
||||
case RoomObjectCategory.FLOOR:
|
||||
case RoomObjectCategory.WALL:
|
||||
info = AvatarInfoUtilities.getFurniInfo(objectId, category);
|
||||
|
||||
if(info) selectObjectForWired(objectId, category);
|
||||
break;
|
||||
case RoomObjectCategory.UNIT: {
|
||||
const userData = roomSession.userDataManager.getUserDataByIndex(objectId);
|
||||
|
||||
if(!userData) break;
|
||||
|
||||
switch(userData.type)
|
||||
{
|
||||
case RoomObjectType.PET:
|
||||
roomSession.userDataManager.requestPetInfo(userData.webID);
|
||||
setPendingPetId(userData.webID);
|
||||
break;
|
||||
case RoomObjectType.USER:
|
||||
info = AvatarInfoUtilities.getUserInfo(category, userData);
|
||||
break;
|
||||
case RoomObjectType.BOT:
|
||||
info = AvatarInfoUtilities.getBotInfo(category, userData);
|
||||
break;
|
||||
case RoomObjectType.RENTABLE_BOT:
|
||||
info = AvatarInfoUtilities.getRentableBotInfo(category, userData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!info) return;
|
||||
|
||||
setAvatarInfo(info);
|
||||
};
|
||||
|
||||
const processUsableRoomObject = (objectId: number) =>
|
||||
{
|
||||
};
|
||||
|
||||
const refreshPetInfo = () =>
|
||||
{
|
||||
// roomSession.userDataManager.requestPetInfo(petData.id);
|
||||
};
|
||||
|
||||
useNitroEvent<RoomSessionUserDataUpdateEvent>(RoomSessionUserDataUpdateEvent.USER_DATA_UPDATED, event =>
|
||||
{
|
||||
if(!event.addedUsers.length) return;
|
||||
|
||||
let addedNameBubbles: AvatarInfoName[] = [];
|
||||
|
||||
event.addedUsers.forEach(user =>
|
||||
{
|
||||
if(user.webID === GetSessionDataManager().userId || user.type !== RoomObjectType.USER) return;
|
||||
|
||||
if(friends.find(friend => (friend.id === user.webID)))
|
||||
{
|
||||
addedNameBubbles.push(new AvatarInfoName(user.roomIndex, RoomObjectCategory.UNIT, user.webID, user.name, user.type, true));
|
||||
}
|
||||
});
|
||||
|
||||
if(!addedNameBubbles.length) return;
|
||||
|
||||
setNameBubbles(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
addedNameBubbles.forEach(bubble =>
|
||||
{
|
||||
const oldIndex = newValue.findIndex(oldBubble => (oldBubble.id === bubble.id));
|
||||
|
||||
if(oldIndex > -1) newValue.splice(oldIndex, 1);
|
||||
|
||||
newValue.push(bubble);
|
||||
});
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useNitroEvent<RoomSessionPetInfoUpdateEvent>(RoomSessionPetInfoUpdateEvent.PET_INFO, event =>
|
||||
{
|
||||
const petData = event.petInfo;
|
||||
|
||||
if(!petData) return;
|
||||
|
||||
if(petData.id !== pendingPetId) return;
|
||||
|
||||
const petInfo = AvatarInfoUtilities.getPetInfo(petData);
|
||||
|
||||
if(!petInfo) return;
|
||||
|
||||
setAvatarInfo(petInfo);
|
||||
setPendingPetId(-1);
|
||||
});
|
||||
|
||||
useNitroEvent<RoomSessionPetStatusUpdateEvent>(RoomSessionPetStatusUpdateEvent.PET_STATUS_UPDATE, event =>
|
||||
{
|
||||
/* var _local_2:Boolean;
|
||||
var _local_3:Boolean;
|
||||
var _local_4:Boolean;
|
||||
var _local_5:Boolean;
|
||||
var _local_6:RoomUserData;
|
||||
var _local_7:_Str_4828;
|
||||
if (((!(this._container == null)) && (!(this._container.events == null))))
|
||||
{
|
||||
_local_2 = k.canBreed;
|
||||
_local_3 = k.canHarvest;
|
||||
_local_4 = k.canRevive;
|
||||
_local_5 = k.hasBreedingPermission;
|
||||
_local_6 = this._Str_19958(k.petId);
|
||||
if (_local_6 == null)
|
||||
{
|
||||
Logger.log((("Could not find pet with the id: " + k.petId) + " given by petStatusUpdate"));
|
||||
return;
|
||||
}
|
||||
_local_7 = new _Str_4828(_local_6.roomObjectId, _local_2, _local_3, _local_4, _local_5);
|
||||
this._container.events.dispatchEvent(_local_7); */
|
||||
});
|
||||
|
||||
useNitroEvent<RoomEngineUseProductEvent>(RoomEngineUseProductEvent.USE_PRODUCT_FROM_INVENTORY, event =>
|
||||
{
|
||||
// this._Str_23199((k as RoomEngineUseProductEvent).inventoryStripId, (k as RoomEngineUseProductEvent).furnitureTypeId);
|
||||
});
|
||||
|
||||
useNitroEvent<RoomEngineUseProductEvent>(RoomEngineUseProductEvent.USE_PRODUCT_FROM_ROOM, event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, event.objectId, RoomObjectCategory.FLOOR);
|
||||
|
||||
if(!roomObject || !IsOwnerOfFurniture(roomObject)) return;
|
||||
|
||||
const ownerId = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_OWNER_ID);
|
||||
const typeId = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_TYPE_ID);
|
||||
const furniData = GetSessionDataManager().getFloorItemData(typeId);
|
||||
const parts = furniData.customParams.split(' ');
|
||||
const part = (parts.length ? parseInt(parts[0]) : -1);
|
||||
|
||||
if(part === -1) return;
|
||||
|
||||
const useProductBubbles: UseProductItem[] = [];
|
||||
const roomObjects = GetRoomEngine().getRoomObjects(roomSession.roomId, RoomObjectCategory.UNIT);
|
||||
|
||||
for(const roomObject of roomObjects)
|
||||
{
|
||||
const userData = roomSession.userDataManager.getUserDataByIndex(roomObject.id);
|
||||
|
||||
let replace = false;
|
||||
|
||||
if(!userData || (userData.type !== RoomObjectType.PET))
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if(userData.ownerId === ownerId)
|
||||
{
|
||||
if(userData.hasSaddle && (furniData.specialType === FurniCategory.PET_SADDLE)) replace = true;
|
||||
|
||||
const figureParts = userData.figure.split(' ');
|
||||
const figurePart = (figureParts.length ? parseInt(figureParts[0]) : -1);
|
||||
|
||||
if(figurePart === part)
|
||||
{
|
||||
if(furniData.specialType === FurniCategory.MONSTERPLANT_REVIVAL)
|
||||
{
|
||||
if(!userData.canRevive) continue;
|
||||
}
|
||||
|
||||
if(furniData.specialType === FurniCategory.MONSTERPLANT_REBREED)
|
||||
{
|
||||
if((userData.petLevel < 7) || userData.canRevive || userData.canBreed) continue;
|
||||
}
|
||||
|
||||
if(furniData.specialType === FurniCategory.MONSTERPLANT_FERTILIZE)
|
||||
{
|
||||
if((userData.petLevel >= 7) || userData.canRevive) continue;
|
||||
}
|
||||
|
||||
useProductBubbles.push(new UseProductItem(userData.roomIndex, RoomObjectCategory.UNIT, userData.name, event.objectId, roomObject.id, -1, replace));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setConfirmingProduct(null);
|
||||
|
||||
if(useProductBubbles.length) setProductBubbles(useProductBubbles);
|
||||
});
|
||||
|
||||
useNitroEvent<RoomEngineObjectEvent>(RoomEngineObjectEvent.REQUEST_MANIPULATION, event =>
|
||||
{
|
||||
if(!CanManipulateFurniture(roomSession, event.objectId, event.category)) return;
|
||||
|
||||
setIsDecorating(true);
|
||||
});
|
||||
|
||||
useObjectSelectedEvent(event =>
|
||||
{
|
||||
getObjectInfo(event.id, event.category);
|
||||
});
|
||||
|
||||
useObjectDeselectedEvent(event =>
|
||||
{
|
||||
setAvatarInfo(null);
|
||||
setProductBubbles([]);
|
||||
});
|
||||
|
||||
useObjectRollOverEvent(event =>
|
||||
{
|
||||
if(avatarInfo || (event.category !== RoomObjectCategory.UNIT)) return;
|
||||
|
||||
getObjectName(event.id, event.category);
|
||||
});
|
||||
|
||||
useObjectRollOutEvent(event =>
|
||||
{
|
||||
if(!activeNameBubble || (event.category !== RoomObjectCategory.UNIT) || (activeNameBubble.roomIndex !== event.id)) return;
|
||||
|
||||
setActiveNameBubble(null);
|
||||
});
|
||||
|
||||
useUiEvent<RoomWidgetUpdateRoomObjectEvent>([
|
||||
RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED,
|
||||
RoomWidgetUpdateRoomObjectEvent.USER_REMOVED
|
||||
], event =>
|
||||
{
|
||||
if(activeNameBubble && (activeNameBubble.category === event.category) && (activeNameBubble.roomIndex === event.id)) setActiveNameBubble(null);
|
||||
|
||||
if(event.category === RoomObjectCategory.UNIT)
|
||||
{
|
||||
let index = nameBubbles.findIndex(bubble => (bubble.roomIndex === event.id));
|
||||
|
||||
if(index > -1) setNameBubbles(prevValue => prevValue.filter(bubble => (bubble.roomIndex === event.id)));
|
||||
|
||||
index = productBubbles.findIndex(bubble => (bubble.id === event.id));
|
||||
|
||||
if(index > -1) setProductBubbles(prevValue => prevValue.filter(bubble => (bubble.id !== event.id)));
|
||||
}
|
||||
|
||||
else if(event.category === RoomObjectCategory.FLOOR)
|
||||
{
|
||||
const index = productBubbles.findIndex(bubble => (bubble.id === event.id));
|
||||
|
||||
if(index > -1) setProductBubbles(prevValue => prevValue.filter(bubble => (bubble.requestRoomObjectId !== event.id)));
|
||||
}
|
||||
|
||||
if(avatarInfo)
|
||||
{
|
||||
if(avatarInfo instanceof AvatarInfoFurni)
|
||||
{
|
||||
if(avatarInfo.id === event.id) setAvatarInfo(null);
|
||||
}
|
||||
|
||||
else if((avatarInfo instanceof AvatarInfoUser) || (avatarInfo instanceof AvatarInfoRentableBot) || (avatarInfo instanceof AvatarInfoPet))
|
||||
{
|
||||
if(avatarInfo.roomIndex === event.id) setAvatarInfo(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!avatarInfo) return;
|
||||
|
||||
setActiveNameBubble(null);
|
||||
setNameBubbles([]);
|
||||
setProductBubbles([]);
|
||||
}, [ avatarInfo ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!activeNameBubble) return;
|
||||
|
||||
setNameBubbles([]);
|
||||
}, [ activeNameBubble ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!roomSession) return;
|
||||
|
||||
roomSession.isDecorating = isDecorating;
|
||||
}, [ roomSession, isDecorating ]);
|
||||
|
||||
return { avatarInfo, setAvatarInfo, activeNameBubble, setActiveNameBubble, nameBubbles, productBubbles, confirmingProduct, isDecorating, setIsDecorating, removeNameBubble, removeProductBubble, updateConfirmingProduct, getObjectName };
|
||||
};
|
||||
|
||||
export const useAvatarInfoWidget = useAvatarInfoWidgetState;
|
||||
@@ -0,0 +1,282 @@
|
||||
import { AvatarExpressionEnum, CreateLinkEvent, GetEventDispatcher, GetRoomEngine, GetSessionDataManager, GetTicker, HabboClubLevelEnum, RoomControllerLevel, RoomEngineObjectEvent, RoomObjectCategory, RoomRotatingEffect, RoomSessionChatEvent, RoomSettingsComposer, RoomShakingEffect, RoomZoomEvent, TextureUtils } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ChatMessageTypeEnum, GetClubMemberLevel, GetConfigurationValue, LocalizeText, SendMessageComposer } from '../../../api';
|
||||
import { useNitroEvent } from '../../events';
|
||||
import { useNotification } from '../../notification';
|
||||
import { useObjectSelectedEvent } from '../engine';
|
||||
import { useRoom } from '../useRoom';
|
||||
|
||||
const useChatInputWidgetState = () =>
|
||||
{
|
||||
const [ selectedUsername, setSelectedUsername ] = useState('');
|
||||
const [ isTyping, setIsTyping ] = useState<boolean>(false);
|
||||
const [ typingStartedSent, setTypingStartedSent ] = useState(false);
|
||||
const [ isIdle, setIsIdle ] = useState(false);
|
||||
const [ floodBlocked, setFloodBlocked ] = useState(false);
|
||||
const [ floodBlockedSeconds, setFloodBlockedSeconds ] = useState(0);
|
||||
const { showNitroAlert = null, showConfirm = null } = useNotification();
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const sendChat = (text: string, chatType: number, recipientName: string = '', styleId: number = 0) =>
|
||||
{
|
||||
if(text === '') return null;
|
||||
|
||||
const parts = text.split(' ');
|
||||
|
||||
if(parts.length > 0)
|
||||
{
|
||||
const firstPart = parts[0];
|
||||
let secondPart = '';
|
||||
|
||||
if(parts.length > 1) secondPart = parts[1];
|
||||
|
||||
if((firstPart.charAt(0) === ':') && (secondPart === 'x'))
|
||||
{
|
||||
const selectedAvatarId = GetRoomEngine().selectedAvatarId;
|
||||
|
||||
if(selectedAvatarId > -1)
|
||||
{
|
||||
const userData = roomSession.userDataManager.getUserDataByIndex(selectedAvatarId);
|
||||
|
||||
if(userData)
|
||||
{
|
||||
secondPart = userData.name;
|
||||
text = text.replace(' x', (' ' + userData.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(firstPart.toLowerCase())
|
||||
{
|
||||
case ':shake':
|
||||
RoomShakingEffect.init(2500, 5000);
|
||||
RoomShakingEffect.turnVisualizationOn();
|
||||
|
||||
return null;
|
||||
|
||||
case ':rotate':
|
||||
RoomRotatingEffect.init(2500, 5000);
|
||||
RoomRotatingEffect.turnVisualizationOn();
|
||||
|
||||
return null;
|
||||
case ':d':
|
||||
case ';d':
|
||||
if(GetClubMemberLevel() === HabboClubLevelEnum.VIP)
|
||||
{
|
||||
roomSession.sendExpressionMessage(AvatarExpressionEnum.LAUGH.ordinal);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'o/':
|
||||
case '_o/':
|
||||
roomSession.sendExpressionMessage(AvatarExpressionEnum.WAVE.ordinal);
|
||||
|
||||
return null;
|
||||
case ':kiss':
|
||||
if(GetClubMemberLevel() === HabboClubLevelEnum.VIP)
|
||||
{
|
||||
roomSession.sendExpressionMessage(AvatarExpressionEnum.BLOW.ordinal);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
break;
|
||||
case ':jump':
|
||||
if(GetClubMemberLevel() === HabboClubLevelEnum.VIP)
|
||||
{
|
||||
roomSession.sendExpressionMessage(AvatarExpressionEnum.JUMP.ordinal);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
break;
|
||||
case ':idle':
|
||||
roomSession.sendExpressionMessage(AvatarExpressionEnum.IDLE.ordinal);
|
||||
|
||||
return null;
|
||||
case '_b':
|
||||
roomSession.sendExpressionMessage(AvatarExpressionEnum.RESPECT.ordinal);
|
||||
|
||||
return null;
|
||||
case ':sign':
|
||||
roomSession.sendSignMessage(parseInt(secondPart));
|
||||
|
||||
return null;
|
||||
case ':iddqd':
|
||||
case ':flip':
|
||||
GetEventDispatcher().dispatchEvent(new RoomZoomEvent(roomSession.roomId, -1, true));
|
||||
|
||||
return null;
|
||||
case ':zoom':
|
||||
GetEventDispatcher().dispatchEvent(new RoomZoomEvent(roomSession.roomId, parseInt(secondPart)));
|
||||
|
||||
return null;
|
||||
case ':screenshot':
|
||||
const texture = GetRoomEngine().createTextureFromRoom(roomSession.roomId, 1);
|
||||
|
||||
(async () =>
|
||||
{
|
||||
const image = new Image();
|
||||
|
||||
image.src = await TextureUtils.generateImageUrl(texture);
|
||||
|
||||
const newWindow = window.open('');
|
||||
newWindow.document.write(image.outerHTML);
|
||||
})();
|
||||
return null;
|
||||
case ':pickall':
|
||||
if(roomSession.isRoomOwner || GetSessionDataManager().isModerator)
|
||||
{
|
||||
showConfirm(LocalizeText('room.confirm.pick_all'), () =>
|
||||
{
|
||||
GetSessionDataManager().sendSpecialCommandMessage(':pickall');
|
||||
},
|
||||
null, null, null, LocalizeText('generic.alert.title'));
|
||||
}
|
||||
|
||||
return null;
|
||||
case ':ejectall':
|
||||
if(roomSession.isRoomOwner || GetSessionDataManager().isModerator || roomSession.controllerLevel >= RoomControllerLevel.GUEST)
|
||||
{
|
||||
showConfirm(LocalizeText('room.confirm.eject_all'), () =>
|
||||
{
|
||||
GetSessionDataManager().sendSpecialCommandMessage(':ejectall');
|
||||
},
|
||||
null, null, null, LocalizeText('generic.alert.title'));
|
||||
}
|
||||
return null;
|
||||
case ':furni':
|
||||
CreateLinkEvent('furni-chooser/');
|
||||
return null;
|
||||
case ':chooser':
|
||||
CreateLinkEvent('user-chooser/');
|
||||
return null;
|
||||
case ':floor':
|
||||
case ':bcfloor':
|
||||
if(roomSession.controllerLevel >= RoomControllerLevel.ROOM_OWNER) CreateLinkEvent('floor-editor/show');
|
||||
|
||||
return null;
|
||||
case ':togglefps': {
|
||||
if(GetTicker().maxFPS > 0) GetTicker().maxFPS = 0;
|
||||
else GetTicker().maxFPS = GetConfigurationValue('system.animation.fps');
|
||||
|
||||
return null;
|
||||
}
|
||||
case ':client':
|
||||
case ':nitro':
|
||||
case ':billsonnn':
|
||||
showNitroAlert();
|
||||
return null;
|
||||
case ':settings':
|
||||
if(roomSession.isRoomOwner || GetSessionDataManager().isModerator)
|
||||
{
|
||||
SendMessageComposer(new RoomSettingsComposer(roomSession.roomId));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
switch(chatType)
|
||||
{
|
||||
case ChatMessageTypeEnum.CHAT_DEFAULT:
|
||||
roomSession.sendChatMessage(text, styleId);
|
||||
break;
|
||||
case ChatMessageTypeEnum.CHAT_SHOUT:
|
||||
roomSession.sendShoutMessage(text, styleId);
|
||||
break;
|
||||
case ChatMessageTypeEnum.CHAT_WHISPER:
|
||||
roomSession.sendWhisperMessage(recipientName, text, styleId);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
useNitroEvent<RoomSessionChatEvent>(RoomSessionChatEvent.FLOOD_EVENT, event =>
|
||||
{
|
||||
setFloodBlocked(true);
|
||||
setFloodBlockedSeconds(parseFloat(event.message));
|
||||
});
|
||||
|
||||
useObjectSelectedEvent(event =>
|
||||
{
|
||||
if(event.category !== RoomObjectCategory.UNIT) return;
|
||||
|
||||
const userData = roomSession.userDataManager.getUserDataByIndex(event.id);
|
||||
|
||||
if(!userData) return;
|
||||
|
||||
setSelectedUsername(userData.name);
|
||||
});
|
||||
|
||||
useNitroEvent<RoomEngineObjectEvent>(RoomEngineObjectEvent.DESELECTED, event => setSelectedUsername(''));
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!floodBlocked) return;
|
||||
|
||||
let seconds = 0;
|
||||
|
||||
const interval = setInterval(() =>
|
||||
{
|
||||
setFloodBlockedSeconds(prevValue =>
|
||||
{
|
||||
seconds = ((prevValue || 0) - 1);
|
||||
|
||||
return seconds;
|
||||
});
|
||||
|
||||
if(seconds < 0)
|
||||
{
|
||||
clearInterval(interval);
|
||||
|
||||
setFloodBlocked(false);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [ floodBlocked ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!isIdle) return;
|
||||
|
||||
let timeout: ReturnType<typeof setTimeout> = null;
|
||||
|
||||
if(isIdle)
|
||||
{
|
||||
timeout = setTimeout(() =>
|
||||
{
|
||||
setIsIdle(false);
|
||||
setIsTyping(false);
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [ isIdle ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(isTyping)
|
||||
{
|
||||
if(!typingStartedSent)
|
||||
{
|
||||
setTypingStartedSent(true);
|
||||
|
||||
roomSession.sendChatTypingMessage(isTyping);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(typingStartedSent)
|
||||
{
|
||||
setTypingStartedSent(false);
|
||||
|
||||
roomSession.sendChatTypingMessage(isTyping);
|
||||
}
|
||||
}
|
||||
}, [ roomSession, isTyping, typingStartedSent ]);
|
||||
|
||||
return { selectedUsername, floodBlocked, floodBlockedSeconds, setIsTyping, setIsIdle, sendChat };
|
||||
};
|
||||
|
||||
export const useChatInputWidget = useChatInputWidgetState;
|
||||
@@ -0,0 +1,191 @@
|
||||
import { GetGuestRoomResultEvent, GetRoomEngine, PetFigureData, RoomChatSettings, RoomChatSettingsEvent, RoomDragEvent, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomSessionChatEvent, RoomUserData, SystemChatStyleEnum } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { ChatBubbleMessage, ChatBubbleUtilities, ChatEntryType, ChatHistoryCurrentDate, GetConfigurationValue, GetRoomObjectScreenLocation, IRoomChatSettings, LocalizeText, PlaySound, RoomChatFormatter } from '../../../api';
|
||||
import { useMessageEvent, useNitroEvent } from '../../events';
|
||||
import { useRoom } from '../useRoom';
|
||||
import { useChatHistory } from './../../chat-history';
|
||||
|
||||
const useChatWidgetState = () =>
|
||||
{
|
||||
const [chatMessages, setChatMessages] = useState<ChatBubbleMessage[]>([]);
|
||||
const [chatSettings, setChatSettings] = useState<IRoomChatSettings>({
|
||||
mode: RoomChatSettings.CHAT_MODE_FREE_FLOW,
|
||||
weight: RoomChatSettings.CHAT_BUBBLE_WIDTH_NORMAL,
|
||||
speed: RoomChatSettings.CHAT_SCROLL_SPEED_NORMAL,
|
||||
distance: 50,
|
||||
protection: RoomChatSettings.FLOOD_FILTER_NORMAL
|
||||
});
|
||||
const { roomSession = null } = useRoom();
|
||||
const { addChatEntry } = useChatHistory();
|
||||
const isDisposed = useRef(false);
|
||||
|
||||
const getScrollSpeed = useMemo(() =>
|
||||
{
|
||||
if(!chatSettings) return 6000;
|
||||
|
||||
switch(chatSettings.speed)
|
||||
{
|
||||
case RoomChatSettings.CHAT_SCROLL_SPEED_FAST:
|
||||
return 3000;
|
||||
case RoomChatSettings.CHAT_SCROLL_SPEED_NORMAL:
|
||||
return 6000;
|
||||
case RoomChatSettings.CHAT_SCROLL_SPEED_SLOW:
|
||||
return 12000;
|
||||
}
|
||||
}, [chatSettings]);
|
||||
|
||||
useNitroEvent<RoomSessionChatEvent>(RoomSessionChatEvent.CHAT_EVENT, async event =>
|
||||
{
|
||||
const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, event.objectId, RoomObjectCategory.UNIT);
|
||||
const bubbleLocation = roomObject ? GetRoomObjectScreenLocation(roomSession.roomId, roomObject?.id, RoomObjectCategory.UNIT) : { x: 0, y: 0 };
|
||||
const userData = roomObject ? roomSession.userDataManager.getUserDataByIndex(event.objectId) : new RoomUserData(-1);
|
||||
|
||||
let username = '';
|
||||
let avatarColor = 0;
|
||||
let imageUrl: string = null;
|
||||
let chatType = event.chatType;
|
||||
let styleId = event.style;
|
||||
let userType = 0;
|
||||
let petType = -1;
|
||||
let text = event.message;
|
||||
|
||||
if(userData)
|
||||
{
|
||||
userType = userData.type;
|
||||
|
||||
const figure = userData.figure;
|
||||
|
||||
switch(userType)
|
||||
{
|
||||
case RoomObjectType.PET:
|
||||
imageUrl = await ChatBubbleUtilities.getPetImage(figure, 2, true, 64, roomObject.model.getValue<string>(RoomObjectVariable.FIGURE_POSTURE));
|
||||
petType = new PetFigureData(figure).typeId;
|
||||
break;
|
||||
case RoomObjectType.USER:
|
||||
imageUrl = await ChatBubbleUtilities.getUserImage(figure);
|
||||
break;
|
||||
case RoomObjectType.RENTABLE_BOT:
|
||||
case RoomObjectType.BOT:
|
||||
styleId = SystemChatStyleEnum.BOT;
|
||||
break;
|
||||
}
|
||||
|
||||
avatarColor = ChatBubbleUtilities.AVATAR_COLOR_CACHE.get(figure);
|
||||
username = userData.name;
|
||||
}
|
||||
|
||||
switch(chatType)
|
||||
{
|
||||
case RoomSessionChatEvent.CHAT_TYPE_RESPECT:
|
||||
text = LocalizeText('widgets.chatbubble.respect', ['username'], [username]);
|
||||
|
||||
if(GetConfigurationValue('respect.options')['enabled']) PlaySound(GetConfigurationValue('respect.options')['sound']);
|
||||
|
||||
break;
|
||||
case RoomSessionChatEvent.CHAT_TYPE_PETREVIVE:
|
||||
case RoomSessionChatEvent.CHAT_TYPE_PET_REBREED_FERTILIZE:
|
||||
case RoomSessionChatEvent.CHAT_TYPE_PET_SPEED_FERTILIZE: {
|
||||
let textKey = 'widget.chatbubble.petrevived';
|
||||
|
||||
if(chatType === RoomSessionChatEvent.CHAT_TYPE_PET_REBREED_FERTILIZE)
|
||||
{
|
||||
textKey = 'widget.chatbubble.petrefertilized;';
|
||||
}
|
||||
|
||||
else if(chatType === RoomSessionChatEvent.CHAT_TYPE_PET_SPEED_FERTILIZE)
|
||||
{
|
||||
textKey = 'widget.chatbubble.petspeedfertilized';
|
||||
}
|
||||
|
||||
let targetUserName: string = null;
|
||||
|
||||
const newRoomObject = GetRoomEngine().getRoomObject(roomSession.roomId, event.extraParam, RoomObjectCategory.UNIT);
|
||||
|
||||
if(newRoomObject)
|
||||
{
|
||||
const newUserData = roomSession.userDataManager.getUserDataByIndex(roomObject.id);
|
||||
|
||||
if(newUserData) targetUserName = newUserData.name;
|
||||
}
|
||||
|
||||
text = LocalizeText(textKey, ['petName', 'userName'], [username, targetUserName]);
|
||||
break;
|
||||
}
|
||||
case RoomSessionChatEvent.CHAT_TYPE_PETRESPECT:
|
||||
text = LocalizeText('widget.chatbubble.petrespect', ['petname'], [username]);
|
||||
break;
|
||||
case RoomSessionChatEvent.CHAT_TYPE_PETTREAT:
|
||||
text = LocalizeText('widget.chatbubble.pettreat', ['petname'], [username]);
|
||||
break;
|
||||
case RoomSessionChatEvent.CHAT_TYPE_HAND_ITEM_RECEIVED:
|
||||
text = LocalizeText('widget.chatbubble.handitem', ['username', 'handitem'], [username, LocalizeText(('handitem' + event.extraParam))]);
|
||||
break;
|
||||
case RoomSessionChatEvent.CHAT_TYPE_MUTE_REMAINING: {
|
||||
const hours = ((event.extraParam > 0) ? Math.floor((event.extraParam / 3600)) : 0).toString();
|
||||
const minutes = ((event.extraParam > 0) ? Math.floor((event.extraParam % 3600) / 60) : 0).toString();
|
||||
const seconds = (event.extraParam % 60).toString();
|
||||
|
||||
text = LocalizeText('widget.chatbubble.mutetime', ['hours', 'minutes', 'seconds'], [hours, minutes, seconds]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const formattedText = RoomChatFormatter(text);
|
||||
const color = (avatarColor && (('#' + (avatarColor.toString(16).padStart(6, '0'))) || null));
|
||||
|
||||
const chatMessage = new ChatBubbleMessage(
|
||||
userData.roomIndex,
|
||||
RoomObjectCategory.UNIT,
|
||||
roomSession.roomId,
|
||||
text,
|
||||
formattedText,
|
||||
username,
|
||||
{ x: bubbleLocation.x, y: bubbleLocation.y },
|
||||
chatType,
|
||||
styleId,
|
||||
imageUrl,
|
||||
color);
|
||||
|
||||
setChatMessages(prevValue => [...prevValue, chatMessage]);
|
||||
addChatEntry({ id: -1, webId: userData.webID, entityId: userData.roomIndex, name: username, imageUrl, style: styleId, chatType: chatType, entityType: userData.type, message: formattedText, timestamp: ChatHistoryCurrentDate(), type: ChatEntryType.TYPE_CHAT, roomId: roomSession.roomId, color });
|
||||
});
|
||||
|
||||
useNitroEvent<RoomDragEvent>(RoomDragEvent.ROOM_DRAG, event =>
|
||||
{
|
||||
if(!chatMessages.length || (event.roomId !== roomSession.roomId)) return;
|
||||
|
||||
const offsetX = event.offsetX;
|
||||
|
||||
chatMessages.forEach(chat => (chat.elementRef && (chat.left += offsetX)));
|
||||
});
|
||||
|
||||
useMessageEvent<GetGuestRoomResultEvent>(GetGuestRoomResultEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser.roomEnter) return;
|
||||
|
||||
setChatSettings(parser.chat);
|
||||
});
|
||||
|
||||
useMessageEvent<RoomChatSettingsEvent>(RoomChatSettingsEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setChatSettings(parser.chat);
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
isDisposed.current = false;
|
||||
|
||||
return () =>
|
||||
{
|
||||
isDisposed.current = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { chatMessages, setChatMessages, chatSettings, getScrollSpeed };
|
||||
};
|
||||
|
||||
export const useChatWidget = useChatWidgetState;
|
||||
@@ -0,0 +1,44 @@
|
||||
import { RoomSessionDoorbellEvent } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { GetRoomSession } from '../../../api';
|
||||
import { useNitroEvent } from '../../events';
|
||||
|
||||
const useDoorbellWidgetState = () =>
|
||||
{
|
||||
const [ users, setUsers ] = useState<string[]>([]);
|
||||
|
||||
const addUser = (userName: string) =>
|
||||
{
|
||||
if(users.indexOf(userName) >= 0) return;
|
||||
|
||||
setUsers([ ...users, userName ]);
|
||||
};
|
||||
|
||||
const removeUser = (userName: string) =>
|
||||
{
|
||||
const index = users.indexOf(userName);
|
||||
|
||||
if(index === -1) return;
|
||||
|
||||
const newUsers = [ ...users ];
|
||||
|
||||
newUsers.splice(index, 1);
|
||||
|
||||
setUsers(newUsers);
|
||||
};
|
||||
|
||||
const answer = (userName: string, flag: boolean) =>
|
||||
{
|
||||
GetRoomSession().sendDoorbellApprovalMessage(userName, flag);
|
||||
|
||||
removeUser(userName);
|
||||
};
|
||||
|
||||
useNitroEvent<RoomSessionDoorbellEvent>(RoomSessionDoorbellEvent.DOORBELL, event => addUser(event.userName));
|
||||
useNitroEvent<RoomSessionDoorbellEvent>(RoomSessionDoorbellEvent.RSDE_REJECTED, event => removeUser(event.userName));
|
||||
useNitroEvent<RoomSessionDoorbellEvent>(RoomSessionDoorbellEvent.RSDE_ACCEPTED, event => removeUser(event.userName));
|
||||
|
||||
return { users, addUser, removeUser, answer };
|
||||
};
|
||||
|
||||
export const useDoorbellWidget = useDoorbellWidgetState;
|
||||
@@ -0,0 +1,23 @@
|
||||
import { RoomFilterSettingsMessageEvent } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { useMessageEvent } from '../../events';
|
||||
|
||||
const useFilterWordsWidgetState = () =>
|
||||
{
|
||||
const [ wordsFilter, setWordsFilter ] = useState<string[]>(null);
|
||||
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
||||
|
||||
const onClose = () => setIsVisible(false);
|
||||
|
||||
useMessageEvent<RoomFilterSettingsMessageEvent>(RoomFilterSettingsMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setIsVisible(true);
|
||||
setWordsFilter(parser.words);
|
||||
});
|
||||
|
||||
return { wordsFilter, isVisible, setWordsFilter, onClose };
|
||||
};
|
||||
|
||||
export const useFilterWordsWidget = useFilterWordsWidgetState;
|
||||
@@ -0,0 +1,81 @@
|
||||
import { RoomObjectCategory, RoomObjectUserType } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { GetRoomSession, MessengerRequest } from '../../../api';
|
||||
import { useFriends } from '../../friends';
|
||||
import { useUserAddedEvent, useUserRemovedEvent } from '../engine';
|
||||
|
||||
const useFriendRequestWidgetState = () =>
|
||||
{
|
||||
const [ activeRequests, setActiveRequests ] = useState<{ roomIndex: number, request: MessengerRequest }[]>([]);
|
||||
const { requests = [], dismissedRequestIds = [], setDismissedRequestIds = null } = useFriends();
|
||||
|
||||
const displayedRequests = useMemo(() => activeRequests.filter(request => (dismissedRequestIds.indexOf(request.request.requesterUserId) === -1)), [ activeRequests, dismissedRequestIds ]);
|
||||
|
||||
const hideFriendRequest = (userId: number) =>
|
||||
{
|
||||
setDismissedRequestIds(prevValue =>
|
||||
{
|
||||
if(prevValue.indexOf(userId) >= 0) return prevValue;
|
||||
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
newValue.push(userId);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
};
|
||||
|
||||
useUserAddedEvent(true, event =>
|
||||
{
|
||||
if(event.category !== RoomObjectCategory.UNIT) return;
|
||||
|
||||
const userData = GetRoomSession().userDataManager.getUserDataByIndex(event.id);
|
||||
|
||||
if(!userData || (userData.type !== RoomObjectUserType.getTypeNumber(RoomObjectUserType.USER))) return;
|
||||
|
||||
const request = requests.find(request => (request.requesterUserId === userData.webID));
|
||||
|
||||
if(!request || activeRequests.find(request => (request.request.requesterUserId === userData.webID))) return;
|
||||
|
||||
const newValue = [ ...activeRequests ];
|
||||
|
||||
newValue.push({ roomIndex: userData.roomIndex, request });
|
||||
|
||||
setActiveRequests(newValue);
|
||||
});
|
||||
|
||||
useUserRemovedEvent(true, event =>
|
||||
{
|
||||
if(event.category !== RoomObjectCategory.UNIT) return;
|
||||
|
||||
const index = activeRequests.findIndex(request => (request.roomIndex === event.id));
|
||||
|
||||
if(index === -1) return;
|
||||
|
||||
const newValue = [ ...activeRequests ];
|
||||
|
||||
newValue.splice(index, 1);
|
||||
|
||||
setActiveRequests(newValue);
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const newDisplayedRequests: { roomIndex: number, request: MessengerRequest }[] = [];
|
||||
|
||||
for(const request of requests)
|
||||
{
|
||||
const userData = GetRoomSession().userDataManager.getUserData(request.requesterUserId);
|
||||
|
||||
if(!userData) continue;
|
||||
|
||||
newDisplayedRequests.push({ roomIndex: userData.roomIndex, request });
|
||||
}
|
||||
|
||||
setActiveRequests(newDisplayedRequests);
|
||||
}, [ requests ]);
|
||||
|
||||
return { displayedRequests, hideFriendRequest };
|
||||
};
|
||||
|
||||
export const useFriendRequestWidget = useFriendRequestWidgetState;
|
||||
@@ -0,0 +1,132 @@
|
||||
import { GetRoomEngine, GetSessionDataManager, RoomObjectCategory, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { GetRoomSession, LocalizeText, RoomObjectItem } from '../../../api';
|
||||
import { useFurniAddedEvent, useFurniRemovedEvent } from '../engine';
|
||||
import { useRoom } from '../useRoom';
|
||||
|
||||
const useFurniChooserWidgetState = () =>
|
||||
{
|
||||
const [ items, setItems ] = useState<RoomObjectItem[]>(null);
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const onClose = () => setItems(null);
|
||||
|
||||
const selectItem = (item: RoomObjectItem) => item && GetRoomEngine().selectRoomObject(GetRoomSession().roomId, item.id, item.category);
|
||||
|
||||
const populateChooser = () =>
|
||||
{
|
||||
const sessionDataManager = GetSessionDataManager();
|
||||
const wallObjects = GetRoomEngine().getRoomObjects(roomSession.roomId, RoomObjectCategory.WALL);
|
||||
const floorObjects = GetRoomEngine().getRoomObjects(roomSession.roomId, RoomObjectCategory.FLOOR);
|
||||
|
||||
const wallItems = wallObjects.map(roomObject =>
|
||||
{
|
||||
if(roomObject.id < 0) return null;
|
||||
|
||||
let name = roomObject.type;
|
||||
|
||||
if(name.startsWith('poster'))
|
||||
{
|
||||
name = LocalizeText(`poster_${ name.replace('poster', '') }_name`);
|
||||
}
|
||||
else
|
||||
{
|
||||
const typeId = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_TYPE_ID);
|
||||
const furniData = sessionDataManager.getWallItemData(typeId);
|
||||
|
||||
if(furniData && furniData.name.length) name = furniData.name;
|
||||
}
|
||||
|
||||
return new RoomObjectItem(roomObject.id, RoomObjectCategory.WALL, name);
|
||||
});
|
||||
|
||||
const floorItems = floorObjects.map(roomObject =>
|
||||
{
|
||||
if(roomObject.id < 0) return null;
|
||||
|
||||
let name = roomObject.type;
|
||||
|
||||
const typeId = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_TYPE_ID);
|
||||
const furniData = sessionDataManager.getFloorItemData(typeId);
|
||||
|
||||
if(furniData && furniData.name.length) name = furniData.name;
|
||||
|
||||
return new RoomObjectItem(roomObject.id, RoomObjectCategory.FLOOR, name);
|
||||
});
|
||||
|
||||
setItems([ ...wallItems, ...floorItems ].sort((a, b) => ((a.name < b.name) ? -1 : 1)));
|
||||
};
|
||||
|
||||
useFurniAddedEvent(!!items, event =>
|
||||
{
|
||||
if(event.id < 0) return;
|
||||
|
||||
const roomObject = GetRoomEngine().getRoomObject(GetRoomSession().roomId, event.id, event.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
let item: RoomObjectItem = null;
|
||||
|
||||
switch(event.category)
|
||||
{
|
||||
case RoomObjectCategory.WALL: {
|
||||
let name = roomObject.type;
|
||||
|
||||
if(name.startsWith('poster'))
|
||||
{
|
||||
name = LocalizeText(`poster_${ name.replace('poster', '') }_name`);
|
||||
}
|
||||
else
|
||||
{
|
||||
const typeId = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_TYPE_ID);
|
||||
const furniData = GetSessionDataManager().getWallItemData(typeId);
|
||||
|
||||
if(furniData && furniData.name.length) name = furniData.name;
|
||||
}
|
||||
|
||||
item = new RoomObjectItem(roomObject.id, RoomObjectCategory.WALL, name);
|
||||
|
||||
break;
|
||||
}
|
||||
case RoomObjectCategory.FLOOR: {
|
||||
let name = roomObject.type;
|
||||
|
||||
const typeId = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_TYPE_ID);
|
||||
const furniData = GetSessionDataManager().getFloorItemData(typeId);
|
||||
|
||||
if(furniData && furniData.name.length) name = furniData.name;
|
||||
|
||||
item = new RoomObjectItem(roomObject.id, RoomObjectCategory.FLOOR, name);
|
||||
}
|
||||
}
|
||||
|
||||
setItems(prevValue => [ ...prevValue, item ].sort((a, b) => ((a.name < b.name) ? -1 : 1)));
|
||||
});
|
||||
|
||||
useFurniRemovedEvent(!!items, event =>
|
||||
{
|
||||
if(event.id < 0) return;
|
||||
|
||||
setItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
for(let i = 0; i < newValue.length; i++)
|
||||
{
|
||||
const existingValue = newValue[i];
|
||||
|
||||
if((existingValue.id !== event.id) || (existingValue.category !== event.category)) continue;
|
||||
|
||||
newValue.splice(i, 1);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
return { items, onClose, selectItem, populateChooser };
|
||||
};
|
||||
|
||||
export const useFurniChooserWidget = useFurniChooserWidgetState;
|
||||
@@ -0,0 +1,75 @@
|
||||
import { GetRoomEngine, OpenPetPackageMessageComposer, RoomObjectCategory, RoomSessionPetPackageEvent } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { LocalizeText, SendMessageComposer } from '../../../api';
|
||||
import { useNitroEvent } from '../../events';
|
||||
|
||||
const usePetPackageWidgetState = () =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
||||
const [ objectId, setObjectId ] = useState<number>(-1);
|
||||
const [ objectType, setObjectType ] = useState<string>('');
|
||||
const [ petName, setPetName ] = useState<string>('');
|
||||
const [ errorResult, setErrorResult ] = useState<string>('');
|
||||
|
||||
const onClose = () =>
|
||||
{
|
||||
setErrorResult('');
|
||||
setPetName('');
|
||||
setObjectType('');
|
||||
setObjectId(-1);
|
||||
setIsVisible(false);
|
||||
};
|
||||
|
||||
const onConfirm = () =>
|
||||
{
|
||||
SendMessageComposer(new OpenPetPackageMessageComposer(objectId, petName));
|
||||
};
|
||||
|
||||
const onChangePetName = (petName: string) =>
|
||||
{
|
||||
setPetName(petName);
|
||||
if(errorResult.length > 0) setErrorResult('');
|
||||
};
|
||||
|
||||
const getErrorResultForCode = (errorCode: number) =>
|
||||
{
|
||||
if(!errorCode || errorCode === 0) return;
|
||||
|
||||
switch(errorCode)
|
||||
{
|
||||
case 1:
|
||||
return LocalizeText('catalog.alert.petname.long');
|
||||
case 2:
|
||||
return LocalizeText('catalog.alert.petname.short');
|
||||
case 3:
|
||||
return LocalizeText('catalog.alert.petname.chars');
|
||||
case 4:
|
||||
default:
|
||||
return LocalizeText('catalog.alert.petname.bobba');
|
||||
}
|
||||
};
|
||||
|
||||
useNitroEvent<RoomSessionPetPackageEvent>(RoomSessionPetPackageEvent.RSOPPE_OPEN_PET_PACKAGE_REQUESTED, event =>
|
||||
{
|
||||
if(!event) return;
|
||||
|
||||
const roomObject = GetRoomEngine().getRoomObject(event.session.roomId, event.objectId, RoomObjectCategory.FLOOR);
|
||||
|
||||
setObjectId(event.objectId);
|
||||
setObjectType(roomObject.type);
|
||||
setIsVisible(true);
|
||||
});
|
||||
|
||||
useNitroEvent<RoomSessionPetPackageEvent>(RoomSessionPetPackageEvent.RSOPPE_OPEN_PET_PACKAGE_RESULT, event =>
|
||||
{
|
||||
if(!event) return;
|
||||
|
||||
if(event.nameValidationStatus === 0) onClose();
|
||||
|
||||
if(event.nameValidationStatus !== 0) setErrorResult(getErrorResultForCode(event.nameValidationStatus));
|
||||
});
|
||||
|
||||
return { isVisible, errorResult, petName, objectType, onChangePetName, onConfirm, onClose };
|
||||
};
|
||||
|
||||
export const usePetPackageWidget = usePetPackageWidgetState;
|
||||
@@ -0,0 +1,52 @@
|
||||
import { RoomSessionPollEvent } from '@nitrots/nitro-renderer';
|
||||
import { DispatchUiEvent, RoomWidgetPollUpdateEvent } from '../../../api';
|
||||
import { useNitroEvent } from '../../events';
|
||||
import { useRoom } from '../useRoom';
|
||||
|
||||
const usePollWidgetState = () =>
|
||||
{
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const startPoll = (pollId: number) => roomSession.sendPollStartMessage(pollId);
|
||||
|
||||
const rejectPoll = (pollId: number) => roomSession.sendPollRejectMessage(pollId);
|
||||
|
||||
const answerPoll = (pollId: number, questionId: number, answers: string[]) => roomSession.sendPollAnswerMessage(pollId, questionId, answers);
|
||||
|
||||
useNitroEvent<RoomSessionPollEvent>(RoomSessionPollEvent.OFFER, event =>
|
||||
{
|
||||
const pollEvent = new RoomWidgetPollUpdateEvent(RoomWidgetPollUpdateEvent.OFFER, event.id);
|
||||
|
||||
pollEvent.summary = event.summary;
|
||||
pollEvent.headline = event.headline;
|
||||
|
||||
DispatchUiEvent(pollEvent);
|
||||
});
|
||||
|
||||
useNitroEvent<RoomSessionPollEvent>(RoomSessionPollEvent.ERROR, event =>
|
||||
{
|
||||
const pollEvent = new RoomWidgetPollUpdateEvent(RoomWidgetPollUpdateEvent.ERROR, event.id);
|
||||
|
||||
pollEvent.summary = event.summary;
|
||||
pollEvent.headline = event.headline;
|
||||
|
||||
DispatchUiEvent(pollEvent);
|
||||
});
|
||||
|
||||
useNitroEvent<RoomSessionPollEvent>(RoomSessionPollEvent.CONTENT, event =>
|
||||
{
|
||||
const pollEvent = new RoomWidgetPollUpdateEvent(RoomWidgetPollUpdateEvent.CONTENT, event.id);
|
||||
|
||||
pollEvent.startMessage = event.startMessage;
|
||||
pollEvent.endMessage = event.endMessage;
|
||||
pollEvent.numQuestions = event.numQuestions;
|
||||
pollEvent.questionArray = event.questionArray;
|
||||
pollEvent.npsPoll = event.npsPoll;
|
||||
|
||||
DispatchUiEvent(pollEvent);
|
||||
});
|
||||
|
||||
return { startPoll, rejectPoll, answerPoll };
|
||||
};
|
||||
|
||||
export const usePollWidget = usePollWidgetState;
|
||||
@@ -0,0 +1,80 @@
|
||||
import { GetRoomEngine, RoomObjectCategory } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { GetRoomSession, RoomObjectItem } from '../../../api';
|
||||
import { useUserAddedEvent, useUserRemovedEvent } from '../engine';
|
||||
import { useRoom } from '../useRoom';
|
||||
|
||||
const useUserChooserWidgetState = () =>
|
||||
{
|
||||
const [ items, setItems ] = useState<RoomObjectItem[]>(null);
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const onClose = () => setItems(null);
|
||||
|
||||
const selectItem = (item: RoomObjectItem) => item && GetRoomEngine().selectRoomObject(GetRoomSession().roomId, item.id, item.category);
|
||||
|
||||
const populateChooser = () =>
|
||||
{
|
||||
const roomSession = GetRoomSession();
|
||||
const roomObjects = GetRoomEngine().getRoomObjects(roomSession.roomId, RoomObjectCategory.UNIT);
|
||||
|
||||
setItems(roomObjects
|
||||
.map(roomObject =>
|
||||
{
|
||||
if(roomObject.id < 0) return null;
|
||||
|
||||
const userData = roomSession.userDataManager.getUserDataByIndex(roomObject.id);
|
||||
|
||||
if(!userData) return null;
|
||||
|
||||
return new RoomObjectItem(userData.roomIndex, RoomObjectCategory.UNIT, userData.name);
|
||||
})
|
||||
.sort((a, b) => ((a.name < b.name) ? -1 : 1)));
|
||||
};
|
||||
|
||||
useUserAddedEvent(!!items, event =>
|
||||
{
|
||||
if(event.id < 0) return;
|
||||
|
||||
const userData = GetRoomSession().userDataManager.getUserDataByIndex(event.id);
|
||||
|
||||
if(!userData) return;
|
||||
|
||||
setItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
newValue.push(new RoomObjectItem(userData.roomIndex, RoomObjectCategory.UNIT, userData.name));
|
||||
newValue.sort((a, b) => ((a.name < b.name) ? -1 : 1));
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
useUserRemovedEvent(!!items, event =>
|
||||
{
|
||||
if(event.id < 0) return;
|
||||
|
||||
setItems(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
for(let i = 0; i < newValue.length; i++)
|
||||
{
|
||||
const existingValue = newValue[i];
|
||||
|
||||
if((existingValue.id !== event.id) || (existingValue.category !== event.category)) continue;
|
||||
|
||||
newValue.splice(i, 1);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
|
||||
return { items, onClose, selectItem, populateChooser };
|
||||
};
|
||||
|
||||
export const useUserChooserWidget = useUserChooserWidgetState;
|
||||
@@ -0,0 +1,149 @@
|
||||
import { AvatarAction, GetRoomEngine, IQuestion, RoomSessionWordQuizEvent } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { VoteValue } from '../../../api';
|
||||
import { useNitroEvent } from '../../events';
|
||||
import { useRoom } from '../useRoom';
|
||||
import { usePollWidget } from './usePollWidget';
|
||||
|
||||
const DEFAULT_DISPLAY_DELAY = 4000;
|
||||
const SIGN_FADE_DELAY = 3;
|
||||
|
||||
const useWordQuizWidgetState = () =>
|
||||
{
|
||||
const [ pollId, setPollId ] = useState(-1);
|
||||
const [ question, setQuestion ] = useState<IQuestion>(null);
|
||||
const [ answerSent, setAnswerSent ] = useState(false);
|
||||
const [ questionClearTimeout, setQuestionClearTimeout ] = useState<ReturnType<typeof setTimeout>>(null);
|
||||
const [ answerCounts, setAnswerCounts ] = useState<Map<string, number>>(new Map());
|
||||
const [ userAnswers, setUserAnswers ] = useState<Map<number, VoteValue>>(new Map());
|
||||
const { answerPoll = null } = usePollWidget();
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const clearQuestion = () =>
|
||||
{
|
||||
setPollId(-1);
|
||||
setQuestion(null);
|
||||
};
|
||||
|
||||
const vote = (vote: string) =>
|
||||
{
|
||||
if(answerSent || !question) return;
|
||||
|
||||
answerPoll(pollId, question.id, [ vote ]);
|
||||
|
||||
setAnswerSent(true);
|
||||
};
|
||||
|
||||
useNitroEvent<RoomSessionWordQuizEvent>(RoomSessionWordQuizEvent.ANSWERED, event =>
|
||||
{
|
||||
const userData = roomSession.userDataManager.getUserData(event.userId);
|
||||
|
||||
if(!userData) return;
|
||||
|
||||
setAnswerCounts(event.answerCounts);
|
||||
|
||||
setUserAnswers(prevValue =>
|
||||
{
|
||||
if(!prevValue.has(userData.roomIndex))
|
||||
{
|
||||
const newValue = new Map(userAnswers);
|
||||
|
||||
newValue.set(userData.roomIndex, { value: event.value, secondsLeft: SIGN_FADE_DELAY });
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
return prevValue;
|
||||
});
|
||||
|
||||
GetRoomEngine().updateRoomObjectUserGesture(roomSession.roomId, userData.roomIndex, AvatarAction.getGestureId((event.value === '0') ? AvatarAction.GESTURE_SAD : AvatarAction.GESTURE_SMILE));
|
||||
});
|
||||
|
||||
useNitroEvent<RoomSessionWordQuizEvent>(RoomSessionWordQuizEvent.FINISHED, event =>
|
||||
{
|
||||
if(question && (question.id === event.questionId))
|
||||
{
|
||||
setAnswerCounts(event.answerCounts);
|
||||
setAnswerSent(true);
|
||||
|
||||
setQuestionClearTimeout(prevValue =>
|
||||
{
|
||||
if(prevValue) clearTimeout(prevValue);
|
||||
|
||||
return setTimeout(() => clearQuestion(), DEFAULT_DISPLAY_DELAY);
|
||||
});
|
||||
}
|
||||
|
||||
setUserAnswers(new Map());
|
||||
});
|
||||
|
||||
useNitroEvent<RoomSessionWordQuizEvent>(RoomSessionWordQuizEvent.QUESTION, event =>
|
||||
{
|
||||
setPollId(event.id);
|
||||
setQuestion(event.question);
|
||||
setAnswerSent(false);
|
||||
setAnswerCounts(new Map());
|
||||
setUserAnswers(new Map());
|
||||
|
||||
setQuestionClearTimeout(prevValue =>
|
||||
{
|
||||
if(prevValue) clearTimeout(prevValue);
|
||||
|
||||
if(event.duration > 0)
|
||||
{
|
||||
const delay = event.duration < 1000 ? DEFAULT_DISPLAY_DELAY : event.duration;
|
||||
|
||||
return setTimeout(() => clearQuestion(), delay);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const checkSignFade = () =>
|
||||
{
|
||||
setUserAnswers(prevValue =>
|
||||
{
|
||||
const keysToRemove: number[] = [];
|
||||
|
||||
prevValue.forEach((value, key) =>
|
||||
{
|
||||
value.secondsLeft--;
|
||||
|
||||
if(value.secondsLeft <= 0) keysToRemove.push(key);
|
||||
});
|
||||
|
||||
if(keysToRemove.length === 0) return prevValue;
|
||||
|
||||
const copy = new Map(prevValue);
|
||||
|
||||
keysToRemove.forEach(key => copy.delete(key));
|
||||
|
||||
return copy;
|
||||
});
|
||||
};
|
||||
|
||||
const interval = setInterval(() => checkSignFade(), 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
return () =>
|
||||
{
|
||||
setQuestionClearTimeout(prevValue =>
|
||||
{
|
||||
if(prevValue) clearTimeout(prevValue);
|
||||
|
||||
return null;
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { question, answerSent, answerCounts, userAnswers, vote };
|
||||
};
|
||||
|
||||
export const useWordQuizWidget = useWordQuizWidgetState;
|
||||
@@ -0,0 +1 @@
|
||||
export * from './useSessionInfo';
|
||||
@@ -0,0 +1,63 @@
|
||||
import { FigureUpdateEvent, GetSessionDataManager, RoomUnitChatStyleComposer, UserInfoDataParser, UserInfoEvent, UserSettingsEvent } from '@nitrots/nitro-renderer';
|
||||
import { useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { SendMessageComposer } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
|
||||
const useSessionInfoState = () =>
|
||||
{
|
||||
const [ userInfo, setUserInfo ] = useState<UserInfoDataParser>(null);
|
||||
const [ userFigure, setUserFigure ] = useState<string>(null);
|
||||
const [ chatStyleId, setChatStyleId ] = useState<number>(0);
|
||||
const [ userRespectRemaining, setUserRespectRemaining ] = useState<number>(0);
|
||||
const [ petRespectRemaining, setPetRespectRemaining ] = useState<number>(0);
|
||||
|
||||
const updateChatStyleId = (styleId: number) =>
|
||||
{
|
||||
setChatStyleId(styleId);
|
||||
|
||||
SendMessageComposer(new RoomUnitChatStyleComposer(styleId));
|
||||
};
|
||||
|
||||
const respectUser = (userId: number) =>
|
||||
{
|
||||
GetSessionDataManager().giveRespect(userId);
|
||||
|
||||
setUserRespectRemaining(GetSessionDataManager().respectsLeft);
|
||||
};
|
||||
|
||||
const respectPet = (petId: number) =>
|
||||
{
|
||||
GetSessionDataManager().givePetRespect(petId);
|
||||
|
||||
setPetRespectRemaining(GetSessionDataManager().respectsPetLeft);
|
||||
};
|
||||
|
||||
useMessageEvent<UserInfoEvent>(UserInfoEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setUserInfo(parser.userInfo);
|
||||
setUserFigure(parser.userInfo.figure);
|
||||
setUserRespectRemaining(parser.userInfo.respectsRemaining);
|
||||
setPetRespectRemaining(parser.userInfo.respectsPetRemaining);
|
||||
});
|
||||
|
||||
useMessageEvent<FigureUpdateEvent>(FigureUpdateEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setUserFigure(parser.figure);
|
||||
});
|
||||
|
||||
useMessageEvent<UserSettingsEvent>(UserSettingsEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setChatStyleId(parser.chatType);
|
||||
});
|
||||
|
||||
return { userInfo, userFigure, chatStyleId, userRespectRemaining, petRespectRemaining, respectUser, respectPet, updateChatStyleId };
|
||||
};
|
||||
|
||||
export const useSessionInfo = () => useBetween(useSessionInfoState);
|
||||
@@ -0,0 +1,45 @@
|
||||
import { NitroLogger } from '@nitrots/nitro-renderer';
|
||||
import { Dispatch, SetStateAction, useState } from 'react';
|
||||
import { GetLocalStorage, SetLocalStorage } from '../api';
|
||||
|
||||
const userId = new URLSearchParams(window.location.search).get('userid') || 0;
|
||||
|
||||
const useLocalStorageState = <T>(key: string, initialValue: T): [ T, Dispatch<SetStateAction<T>>] =>
|
||||
{
|
||||
key = userId ? `${ key }.${ userId }` : key;
|
||||
|
||||
const [ storedValue, setStoredValue ] = useState<T>(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
const item = typeof window !== 'undefined' ? GetLocalStorage<T>(key) as T : undefined;
|
||||
return item ?? initialValue;
|
||||
}
|
||||
|
||||
catch(error)
|
||||
{
|
||||
return initialValue;
|
||||
}
|
||||
});
|
||||
|
||||
const setValue = (value: T) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
||||
|
||||
setStoredValue(valueToStore);
|
||||
|
||||
if(typeof window !== 'undefined') SetLocalStorage(key, valueToStore);
|
||||
}
|
||||
|
||||
catch(error)
|
||||
{
|
||||
NitroLogger.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return [ storedValue, setValue ];
|
||||
}
|
||||
|
||||
export const useLocalStorage = useLocalStorageState;
|
||||
@@ -0,0 +1,44 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
export const useSharedVisibility = () =>
|
||||
{
|
||||
const [ activeIds, setActiveIds ] = useState<number[]>([]);
|
||||
|
||||
const isVisible = useMemo(() => !!activeIds.length, [ activeIds ]);
|
||||
|
||||
const activate = useCallback(() =>
|
||||
{
|
||||
let id = -1;
|
||||
|
||||
setActiveIds(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
id = newValue.length ? (newValue[(newValue.length - 1)] + 1) : 0;
|
||||
|
||||
newValue.push(id);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
return id;
|
||||
}, []);
|
||||
|
||||
const deactivate = useCallback((id: number) =>
|
||||
{
|
||||
setActiveIds(prevValue =>
|
||||
{
|
||||
const newValue = [ ...prevValue ];
|
||||
|
||||
const index = newValue.indexOf(id);
|
||||
|
||||
if(index === -1) return prevValue;
|
||||
|
||||
newValue.splice(index, 1);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}, []);
|
||||
|
||||
return { isVisible, activate, deactivate };
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './useWired';
|
||||
@@ -0,0 +1,140 @@
|
||||
import { ConditionDefinition, OpenMessageComposer, Triggerable, TriggerDefinition, UpdateActionMessageComposer, UpdateConditionMessageComposer, UpdateTriggerMessageComposer, WiredActionDefinition, WiredFurniActionEvent, WiredFurniConditionEvent, WiredFurniTriggerEvent, WiredOpenEvent, WiredSaveSuccessEvent } from '@nitrots/nitro-renderer';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useBetween } from 'use-between';
|
||||
import { IsOwnerOfFloorFurniture, LocalizeText, SendMessageComposer, WiredFurniType, WiredSelectionVisualizer } from '../../api';
|
||||
import { useMessageEvent } from '../events';
|
||||
import { useNotification } from '../notification';
|
||||
|
||||
const useWiredState = () =>
|
||||
{
|
||||
const [ trigger, setTrigger ] = useState<Triggerable>(null);
|
||||
const [ intParams, setIntParams ] = useState<number[]>([]);
|
||||
const [ stringParam, setStringParam ] = useState<string>('');
|
||||
const [ furniIds, setFurniIds ] = useState<number[]>([]);
|
||||
const [ actionDelay, setActionDelay ] = useState<number>(0);
|
||||
const [ allowsFurni, setAllowsFurni ] = useState<number>(WiredFurniType.STUFF_SELECTION_OPTION_NONE);
|
||||
const { showConfirm = null } = useNotification();
|
||||
|
||||
const saveWired = () =>
|
||||
{
|
||||
const save = (trigger: Triggerable) =>
|
||||
{
|
||||
if(!trigger) return;
|
||||
|
||||
if(trigger instanceof WiredActionDefinition)
|
||||
{
|
||||
SendMessageComposer(new UpdateActionMessageComposer(trigger.id, intParams, stringParam, furniIds, actionDelay, trigger.stuffTypeSelectionCode));
|
||||
}
|
||||
|
||||
else if(trigger instanceof TriggerDefinition)
|
||||
{
|
||||
SendMessageComposer(new UpdateTriggerMessageComposer(trigger.id, intParams, stringParam, furniIds, trigger.stuffTypeSelectionCode));
|
||||
}
|
||||
|
||||
else if(trigger instanceof ConditionDefinition)
|
||||
{
|
||||
SendMessageComposer(new UpdateConditionMessageComposer(trigger.id, intParams, stringParam, furniIds, trigger.stuffTypeSelectionCode));
|
||||
}
|
||||
};
|
||||
|
||||
if(!IsOwnerOfFloorFurniture(trigger.id))
|
||||
{
|
||||
showConfirm(LocalizeText('wiredfurni.nonowner.change.confirm.body'), () =>
|
||||
{
|
||||
save(trigger);
|
||||
}, null, null, null, LocalizeText('wiredfurni.nonowner.change.confirm.title'));
|
||||
}
|
||||
else
|
||||
{
|
||||
save(trigger);
|
||||
}
|
||||
};
|
||||
|
||||
const selectObjectForWired = (objectId: number, category: number) =>
|
||||
{
|
||||
if(!trigger || !allowsFurni) return;
|
||||
|
||||
if(objectId <= 0) return;
|
||||
|
||||
setFurniIds(prevValue =>
|
||||
{
|
||||
const newFurniIds = [ ...prevValue ];
|
||||
|
||||
const index = prevValue.indexOf(objectId);
|
||||
|
||||
if(index >= 0)
|
||||
{
|
||||
newFurniIds.splice(index, 1);
|
||||
|
||||
WiredSelectionVisualizer.hide(objectId);
|
||||
}
|
||||
|
||||
else if(newFurniIds.length < trigger.maximumItemSelectionCount)
|
||||
{
|
||||
newFurniIds.push(objectId);
|
||||
|
||||
WiredSelectionVisualizer.show(objectId);
|
||||
}
|
||||
|
||||
return newFurniIds;
|
||||
});
|
||||
};
|
||||
|
||||
useMessageEvent<WiredOpenEvent>(WiredOpenEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
SendMessageComposer(new OpenMessageComposer(parser.stuffId));
|
||||
});
|
||||
|
||||
useMessageEvent<WiredSaveSuccessEvent>(WiredSaveSuccessEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setTrigger(null);
|
||||
});
|
||||
|
||||
useMessageEvent<WiredFurniActionEvent>(WiredFurniActionEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setTrigger(parser.definition);
|
||||
});
|
||||
|
||||
useMessageEvent<WiredFurniConditionEvent>(WiredFurniConditionEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setTrigger(parser.definition);
|
||||
});
|
||||
|
||||
useMessageEvent<WiredFurniTriggerEvent>(WiredFurniTriggerEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setTrigger(parser.definition);
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!trigger) return;
|
||||
|
||||
return () =>
|
||||
{
|
||||
setIntParams([]);
|
||||
setStringParam('');
|
||||
setActionDelay(0);
|
||||
setFurniIds(prevValue =>
|
||||
{
|
||||
if(prevValue && prevValue.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prevValue);
|
||||
|
||||
return [];
|
||||
});
|
||||
setAllowsFurni(WiredFurniType.STUFF_SELECTION_OPTION_NONE);
|
||||
};
|
||||
}, [ trigger ]);
|
||||
|
||||
return { trigger, setTrigger, intParams, setIntParams, stringParam, setStringParam, furniIds, setFurniIds, actionDelay, setActionDelay, setAllowsFurni, saveWired, selectObjectForWired };
|
||||
};
|
||||
|
||||
export const useWired = () => useBetween(useWiredState);
|
||||
Reference in New Issue
Block a user