Groups updates

This commit is contained in:
MyNameIsBatman 2021-08-31 00:22:20 -03:00
parent dcf18449b8
commit 820a147120
16 changed files with 318 additions and 41 deletions

View File

@ -0,0 +1,7 @@
import { GroupJoinComposer } from '@nitrots/nitro-renderer';
import { SendMessageHook } from '../../hooks';
export function TryJoinGroup(groupId: number): void
{
SendMessageHook(new GroupJoinComposer(groupId));
}

View File

@ -4,6 +4,7 @@ import { CatalogLayoutDefaultView } from './default/CatalogLayoutDefaultView';
import { CatalogLayoutFrontpage4View } from './frontpage4/CatalogLayoutFrontpage4View'; import { CatalogLayoutFrontpage4View } from './frontpage4/CatalogLayoutFrontpage4View';
import { CatalogLayouGuildCustomFurniView } from './guild-custom-furni/CatalogLayoutGuildCustomFurniView'; import { CatalogLayouGuildCustomFurniView } from './guild-custom-furni/CatalogLayoutGuildCustomFurniView';
import { CatalogLayouGuildForumView } from './guild-forum/CatalogLayoutGuildForumView'; import { CatalogLayouGuildForumView } from './guild-forum/CatalogLayoutGuildForumView';
import { CatalogLayouGuildFrontpageView } from './guild-frontpage/CatalogLayoutGuildFrontpageView';
import { CatalogLayoutInfoLoyaltyView } from './info-loyalty/CatalogLayoutInfoLoyaltyView'; import { CatalogLayoutInfoLoyaltyView } from './info-loyalty/CatalogLayoutInfoLoyaltyView';
import { CatalogLayoutPetView } from './pets/CatalogLayoutPetView'; import { CatalogLayoutPetView } from './pets/CatalogLayoutPetView';
import { CatalogLayoutPets2View } from './pets2/CatalogLayoutPets2View'; import { CatalogLayoutPets2View } from './pets2/CatalogLayoutPets2View';
@ -30,7 +31,7 @@ export const GetCatalogLayout = (pageParser: CatalogPageMessageParser, roomPrevi
case 'vip_buy': case 'vip_buy':
return <CatalogLayoutVipBuyView roomPreviewer={ roomPreviewer } pageParser={ pageParser } />; return <CatalogLayoutVipBuyView roomPreviewer={ roomPreviewer } pageParser={ pageParser } />;
case 'guild_frontpage': case 'guild_frontpage':
return null; return <CatalogLayouGuildFrontpageView roomPreviewer={ roomPreviewer } pageParser={ pageParser } />;
case 'guild_forum': case 'guild_forum':
return <CatalogLayouGuildForumView roomPreviewer={ roomPreviewer } pageParser={ pageParser } />; return <CatalogLayouGuildForumView roomPreviewer={ roomPreviewer } pageParser={ pageParser } />;
case 'guild_custom_furni': case 'guild_custom_furni':

View File

@ -1,7 +1,8 @@
import { CatalogGroupsComposer } from '@nitrots/nitro-renderer'; import { CatalogGroupsComposer, StringDataType } from '@nitrots/nitro-renderer';
import { FC, useEffect } from 'react'; import { FC, useEffect, useState } from 'react';
import { LocalizeText } from '../../../../../../api'; import { LocalizeText } from '../../../../../../api';
import { SendMessageHook } from '../../../../../../hooks/messages'; import { SendMessageHook } from '../../../../../../hooks/messages';
import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView';
import { GetOfferName } from '../../../../common/CatalogUtilities'; import { GetOfferName } from '../../../../common/CatalogUtilities';
import { useCatalogContext } from '../../../../context/CatalogContext'; import { useCatalogContext } from '../../../../context/CatalogContext';
import { CatalogRoomPreviewerView } from '../../../catalog-room-previewer/CatalogRoomPreviewerView'; import { CatalogRoomPreviewerView } from '../../../catalog-room-previewer/CatalogRoomPreviewerView';
@ -18,11 +19,32 @@ export const CatalogLayouGuildCustomFurniView: FC<CatalogLayoutGuildCustomFurniV
const product = ((activeOffer && activeOffer.products[0]) || null); const product = ((activeOffer && activeOffer.products[0]) || null);
const [ selectedGroupIndex, setSelectedGroupIndex ] = useState<number>(0);
useEffect(() => useEffect(() =>
{ {
SendMessageHook(new CatalogGroupsComposer()); SendMessageHook(new CatalogGroupsComposer());
}, [ pageParser ]); }, [ pageParser ]);
useEffect(() =>
{
if(!groups[selectedGroupIndex]) return;
const productData = [];
productData.push('0');
productData.push(groups[selectedGroupIndex].groupId);
productData.push(groups[selectedGroupIndex].badgeCode);
productData.push(groups[selectedGroupIndex].colorA);
productData.push(groups[selectedGroupIndex].colorB);
const stringDataType = new StringDataType();
stringDataType.setValue(productData);
roomPreviewer.updateObjectStuffData(stringDataType);
}, [ selectedGroupIndex, activeOffer ]);
if(!groups) return null;
return ( return (
<div className="row h-100 nitro-catalog-layout-guild-custom-furni"> <div className="row h-100 nitro-catalog-layout-guild-custom-furni">
<div className="d-flex flex-column col-7 h-100"> <div className="d-flex flex-column col-7 h-100">
@ -30,13 +52,30 @@ export const CatalogLayouGuildCustomFurniView: FC<CatalogLayoutGuildCustomFurniV
</div> </div>
{ product && { product &&
<div className="col position-relative d-flex flex-column"> <div className="col position-relative d-flex flex-column">
{ groups[selectedGroupIndex] && <div className="position-absolute" style={{ width: '50px', height: '50px', zIndex: 1 }}>
<BadgeImageView badgeCode={ groups[selectedGroupIndex].badgeCode } isGroup={ true } />
</div> }
<CatalogRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } /> <CatalogRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
<div className="fs-6 text-black mt-1 overflow-hidden">{ GetOfferName(activeOffer) }</div> <div className="fs-6 text-black mt-1 overflow-hidden">{ GetOfferName(activeOffer) }</div>
{ groups.length === 0 && <div className="bg-muted text-center rounded p-1 text-black mt-auto"> { groups.length === 0 && <div className="bg-muted text-center rounded p-1 text-black mt-auto">
{ LocalizeText('catalog.guild_selector.members_only') } { LocalizeText('catalog.guild_selector.members_only') }
<button className="btn btn-sm btn-primary mt-1">{ LocalizeText('catalog.guild_selector.find_groups') }</button> <button className="btn btn-sm btn-primary mt-1">{ LocalizeText('catalog.guild_selector.find_groups') }</button>
</div> } </div> }
{ groups.length > 0 && <CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } /> } { groups.length > 0 && <>
<div className="d-flex mb-2">
<div className="rounded d-flex overflow-hidden me-1 border">
<div className="h-100" style={{ width: '20px', backgroundColor: '#' + groups[selectedGroupIndex].colorA }}></div>
<div className="h-100" style={{ width: '20px', backgroundColor: '#' + groups[selectedGroupIndex].colorB }}></div>
</div>
<select className="form-select form-select-sm" value={ selectedGroupIndex } onChange={ (e) => setSelectedGroupIndex(parseInt(e.target.value)) }>
{ groups.map((group, index) =>
{
return <option key={ index } value={ index }>{ group.groupName }</option>;
}) }
</select>
</div>
<CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } extra={ groups[selectedGroupIndex] ? groups[selectedGroupIndex].groupId.toString() : '' } />
</> }
</div> } </div> }
</div> </div>
); );

View File

@ -0,0 +1,28 @@
import { FC } from 'react';
import { CreateLinkEvent, LocalizeText } from '../../../../../../api';
import { GetCatalogPageImage, GetCatalogPageText } from '../../../../common/CatalogUtilities';
import { useCatalogContext } from '../../../../context/CatalogContext';
import { CatalogLayoutGuildFrontpageViewProps } from './CatalogLayoutGuildFrontpageView.types';
export const CatalogLayouGuildFrontpageView: FC<CatalogLayoutGuildFrontpageViewProps> = props =>
{
const { pageParser = null } = props;
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
return (
<div className="row h-100 nitro-catalog-layout-guild-custom-furni">
<div className="col-7 overflow-auto h-100 d-flex flex-column bg-muted rounded py-1 px-2 text-black">
<div dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 2) } } />
<div dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 0) } } />
<div dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 1) } } />
<div className="d-block mb-2">
<img alt="" src={ GetCatalogPageImage(pageParser, 1) } />
</div>
</div>
<div className="col position-relative d-flex flex-column justify-content-center" onClick={ () => CreateLinkEvent('groups/create') }>
<button className="btn btn-sm btn-primary mt-1">{ LocalizeText('catalog.start.guild.purchase.button') }</button>
</div>
</div>
);
}

View File

@ -0,0 +1,4 @@
import { CatalogLayoutProps } from '../CatalogLayout.types';
export interface CatalogLayoutGuildFrontpageViewProps extends CatalogLayoutProps
{}

View File

@ -0,0 +1,27 @@
import { GroupBadgePartsEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react';
import { CreateMessageHook } from '../../hooks';
import { useGroupsContext } from './context/GroupsContext';
import { GroupsActions } from './context/GroupsContext.types';
export const GroupsMessageHandler: FC<{}> = props =>
{
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
const onGroupBadgePartsEvent = useCallback((event: GroupBadgePartsEvent) =>
{
const parser = event.getParser();
dispatchGroupsState({
type: GroupsActions.SET_BADGE_PARTS,
payload: {
arrayMaps: [ parser.bases, parser.symbols ],
stringMaps: [ parser.partColors, parser.colorsA, parser.colorsB ]
}
})
}, [ dispatchGroupsState ]);
CreateMessageHook(GroupBadgePartsEvent, onGroupBadgePartsEvent);
return null;
};

View File

@ -1,11 +1,50 @@
import { FC } from 'react'; import { GroupBadgePartsComposer, ILinkEventTracker } from '@nitrots/nitro-renderer';
import { GroupInformationBoxView } from './views/information-standalone/GroupInformationStandaloneView'; import { FC, useCallback, useEffect, useReducer } from 'react';
import { AddEventLinkTracker, RemoveLinkEventTracker } from '../../api';
import { SendMessageHook } from '../../hooks';
import { GroupsContextProvider } from './context/GroupsContext';
import { GroupsReducer, initialGroups } from './context/GroupsContext.types';
import { GroupsMessageHandler } from './GroupsMessageHandler';
import { GroupInformationStandaloneView } from './views/information-standalone/GroupInformationStandaloneView';
export const GroupsView: FC<{}> = props => export const GroupsView: FC<{}> = props =>
{ {
const [ groupsState, dispatchGroupsState ] = useReducer(GroupsReducer, initialGroups);
useEffect(() =>
{
SendMessageHook(new GroupBadgePartsComposer());
}, []);
const linkReceived = useCallback((url: string) =>
{
const parts = url.split('/');
if(parts.length < 2) return;
switch(parts[1])
{
case 'create':
return;
}
}, []);
useEffect(() =>
{
const linkTracker: ILinkEventTracker = {
linkReceived,
eventUrlPrefix: 'groups/'
};
AddEventLinkTracker(linkTracker);
return () => RemoveLinkEventTracker(linkTracker);
}, [ linkReceived ]);
return ( return (
<> <GroupsContextProvider value={ { groupsState, dispatchGroupsState } }>
<GroupInformationBoxView /> <GroupsMessageHandler />
</> <GroupInformationStandaloneView />
</GroupsContextProvider>
); );
}; };

View File

@ -0,0 +1,14 @@
import { createContext, FC, useContext } from 'react';
import { GroupsContextProps, IGroupsContext } from './GroupsContext.types';
const GroupsContext = createContext<IGroupsContext>({
groupsState: null,
dispatchGroupsState: null
});
export const GroupsContextProvider: FC<GroupsContextProps> = props =>
{
return <GroupsContext.Provider value={ props.value }>{ props.children }</GroupsContext.Provider>
}
export const useGroupsContext = () => useContext(GroupsContext);

View File

@ -0,0 +1,61 @@
import { Dispatch, ProviderProps, Reducer } from 'react';
export interface IGroupsContext
{
groupsState: IGroupsState;
dispatchGroupsState: Dispatch<IGroupsAction>;
}
export interface GroupsContextProps extends ProviderProps<IGroupsContext>
{
}
export interface IGroupsState
{
badgeBases: Map<number, string[]>;
badgeSymbols: Map<number, string[]>;
badgePartColors: Map<number, string>;
groupColorsA: Map<number, string>;
groupColorsB: Map<number, string>;
}
export interface IGroupsAction
{
type: string;
payload: {
arrayMaps?: Map<number, string[]>[];
stringMaps?: Map<number, string>[];
}
}
export class GroupsActions
{
public static SET_BADGE_PARTS: string = 'GA_SET_BADGE_PARTS';
}
export const initialGroups: IGroupsState = {
badgeBases: null,
badgeSymbols: null,
badgePartColors: null,
groupColorsA: null,
groupColorsB: null
};
export const GroupsReducer: Reducer<IGroupsState, IGroupsAction> = (state, action) =>
{
switch(action.type)
{
case GroupsActions.SET_BADGE_PARTS: {
const badgeBases = (action.payload.arrayMaps[0] || state.badgeBases || null);
const badgeSymbols = (action.payload.arrayMaps[1] || state.badgeSymbols || null);
const badgePartColors = (action.payload.stringMaps[0] || state.badgePartColors || null);
const groupColorsA = (action.payload.stringMaps[1] || state.groupColorsA || null);
const groupColorsB = (action.payload.stringMaps[2] || state.groupColorsB || null);
return { ...state, badgeBases, badgeSymbols, badgePartColors, groupColorsA, groupColorsB };
}
default:
return state;
}
}

View File

@ -5,7 +5,7 @@ import { CreateMessageHook } from '../../../../hooks';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
import { GroupInformationView } from '../information/GroupInformationView'; import { GroupInformationView } from '../information/GroupInformationView';
export const GroupInformationBoxView: FC<{}> = props => export const GroupInformationStandaloneView: FC<{}> = props =>
{ {
const [ groupInformation, setGroupInformation ] = useState<GroupInformationParser>(null); const [ groupInformation, setGroupInformation ] = useState<GroupInformationParser>(null);
@ -15,10 +15,9 @@ export const GroupInformationBoxView: FC<{}> = props =>
if(!parser.flag) return; if(!parser.flag) return;
if(groupInformation) setGroupInformation(null); setGroupInformation(null);
setGroupInformation(parser); setGroupInformation(parser);
}, [ groupInformation ]); }, []);
CreateMessageHook(GroupInformationEvent, onGroupInformationEvent); CreateMessageHook(GroupInformationEvent, onGroupInformationEvent);

View File

@ -1,6 +1,7 @@
import { GroupDeleteComposer, GroupInformationComposer, GroupJoinComposer, GroupRemoveMemberComposer } from '@nitrots/nitro-renderer'; import { GroupDeleteComposer, GroupRemoveMemberComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react'; import { FC, useCallback } from 'react';
import { CreateLinkEvent, GetSessionDataManager, LocalizeText, TryVisitRoom } from '../../../../api'; import { CreateLinkEvent, GetSessionDataManager, LocalizeText, TryVisitRoom } from '../../../../api';
import { TryJoinGroup } from '../../../../api/groups/TryJoinGroup';
import { SendMessageHook } from '../../../../hooks'; import { SendMessageHook } from '../../../../hooks';
import { CatalogPageName } from '../../../catalog/common/CatalogPageName'; import { CatalogPageName } from '../../../catalog/common/CatalogPageName';
import { BadgeImageView } from '../../../shared/badge-image/BadgeImageView'; import { BadgeImageView } from '../../../shared/badge-image/BadgeImageView';
@ -12,18 +13,16 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
{ {
const { groupInformation = null, onClose = null } = props; const { groupInformation = null, onClose = null } = props;
const tryJoinGroup = useCallback(() => const joinGroup = useCallback(() =>
{ {
if(!groupInformation) return; if(!groupInformation) return;
SendMessageHook(new GroupJoinComposer(groupInformation.id)); TryJoinGroup(groupInformation.id);
SendMessageHook(new GroupInformationComposer(groupInformation.id, false));
}, [ groupInformation ]); }, [ groupInformation ]);
const tryLeaveGroup = useCallback(() => const leaveGroup = useCallback(() =>
{ {
SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId)); SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId));
SendMessageHook(new GroupInformationComposer(groupInformation.id, false));
if(onClose) onClose(); if(onClose) onClose();
}, [ groupInformation, onClose ]); }, [ groupInformation, onClose ]);
@ -67,10 +66,10 @@ export const GroupInformationView: FC<GroupInformationViewProps> = props =>
{ {
if(groupInformation.type === GroupType.PRIVATE && groupInformation.membershipType === GroupMembershipType.NOT_MEMBER) return; if(groupInformation.type === GroupType.PRIVATE && groupInformation.membershipType === GroupMembershipType.NOT_MEMBER) return;
if(groupInformation.membershipType === GroupMembershipType.MEMBER) return tryLeaveGroup(); if(groupInformation.membershipType === GroupMembershipType.MEMBER) return leaveGroup();
return tryJoinGroup(); return joinGroup();
}, [ groupInformation, tryLeaveGroup, tryJoinGroup ]); }, [ groupInformation, leaveGroup, joinGroup ]);
const handleAction = useCallback((action: string) => const handleAction = useCallback((action: string) =>
{ {

View File

@ -3,5 +3,6 @@ import { GroupInformationParser } from '@nitrots/nitro-renderer';
export interface GroupInformationViewProps export interface GroupInformationViewProps
{ {
groupInformation: GroupInformationParser; groupInformation: GroupInformationParser;
onJoin?: () => void;
onClose?: () => void; onClose?: () => void;
} }

View File

@ -70,9 +70,9 @@ export const GroupRoomInformationView: FC<{}> = props =>
const getButtonText = useCallback(() => const getButtonText = useCallback(() =>
{ {
if(groupInformation.type === GroupType.PRIVATE) return ''; if(isRealOwner()) return 'group.manage';
if(isRealOwner()) return 'group.youareowner'; if(groupInformation.type === GroupType.PRIVATE) return '';
if(groupInformation.membershipType === GroupMembershipType.MEMBER) return 'group.leave'; if(groupInformation.membershipType === GroupMembershipType.MEMBER) return 'group.leave';
@ -112,8 +112,8 @@ export const GroupRoomInformationView: FC<{}> = props =>
{ groupInformation.title } { groupInformation.title }
</div> </div>
</div> </div>
{ groupInformation.type !== GroupType.PRIVATE && !isRealOwner() && { (groupInformation.type !== GroupType.PRIVATE || isRealOwner()) &&
<button className="btn btn-sm btn-primary w-100 mt-1" disabled={ groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING || isRealOwner() } onClick={ handleButtonClick }> <button className="btn btn-sm btn-primary w-100 mt-1" disabled={ groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING } onClick={ handleButtonClick }>
{ LocalizeText(getButtonText()) } { LocalizeText(getButtonText()) }
</button> </button>
} }

View File

@ -1,7 +1,7 @@
import { RoomMuteComposer, RoomSettingsComposer, RoomStaffPickComposer, SecurityLevel, UserHomeRoomComposer } from '@nitrots/nitro-renderer'; import { RoomMuteComposer, RoomSettingsComposer, RoomStaffPickComposer, SecurityLevel, UserHomeRoomComposer } from '@nitrots/nitro-renderer';
import classNames from 'classnames'; import classNames from 'classnames';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import { GetConfiguration, GetSessionDataManager, LocalizeText } from '../../../../api'; import { GetConfiguration, GetGroupInformation, GetSessionDataManager, LocalizeText } from '../../../../api';
import { NavigatorEvent } from '../../../../events'; import { NavigatorEvent } from '../../../../events';
import { RoomWidgetThumbnailEvent } from '../../../../events/room-widgets/thumbnail'; import { RoomWidgetThumbnailEvent } from '../../../../events/room-widgets/thumbnail';
import { dispatchUiEvent } from '../../../../hooks/events'; import { dispatchUiEvent } from '../../../../hooks/events';
@ -82,6 +82,7 @@ export const NavigatorRoomInfoView: FC<NavigatorRoomInfoViewProps> = props =>
dispatchUiEvent(new RoomWidgetThumbnailEvent(RoomWidgetThumbnailEvent.TOGGLE_THUMBNAIL)); dispatchUiEvent(new RoomWidgetThumbnailEvent(RoomWidgetThumbnailEvent.TOGGLE_THUMBNAIL));
return; return;
case 'open_group_info': case 'open_group_info':
GetGroupInformation(roomInfoData.enteredGuestRoom.habboGroupId);
return; return;
case 'toggle_room_link': case 'toggle_room_link':
dispatchUiEvent(new NavigatorEvent(NavigatorEvent.TOGGLE_ROOM_LINK)); dispatchUiEvent(new NavigatorEvent(NavigatorEvent.TOGGLE_ROOM_LINK));
@ -137,7 +138,7 @@ export const NavigatorRoomInfoView: FC<NavigatorRoomInfoViewProps> = props =>
</div> </div>
<div>{ roomInfoData.enteredGuestRoom.description }</div> <div>{ roomInfoData.enteredGuestRoom.description }</div>
<div className="room-thumbnail border mt-1 mb-2"> <div className="room-thumbnail border mt-1 mb-2">
<i className="icon icon-camera-small position-absolute b-0 r-0 m-1 cursor-pointer" onClick={ () => processAction('open_room_thumbnail_camera') } /> { hasPermission('settings') && <i className="icon icon-camera-small position-absolute b-0 r-0 m-1 cursor-pointer" onClick={ () => processAction('open_room_thumbnail_camera') } /> }
{ roomThumbnail && <img alt="" src={ roomThumbnail } /> } { roomThumbnail && <img alt="" src={ roomThumbnail } /> }
</div> </div>
{ roomInfoData.enteredGuestRoom.habboGroupId > 0 && <div className="d-flex align-items-center mb-2 cursor-pointer" onClick={ () => processAction('open_group_info') }> { roomInfoData.enteredGuestRoom.habboGroupId > 0 && <div className="d-flex align-items-center mb-2 cursor-pointer" onClick={ () => processAction('open_group_info') }>

View File

@ -1,6 +1,9 @@
import { ContextMenuEnum, RoomEngineTriggerWidgetEvent, RoomObjectCategory } from '@nitrots/nitro-renderer'; import { ContextMenuEnum, FurnitureGroupInfoComposer, GroupFurniContextMenuInfoMessageParser, RoomEngineTriggerWidgetEvent, RoomObjectCategory, RoomObjectVariable } from '@nitrots/nitro-renderer';
import { GroupFurniContextMenuInfoMessageEvent } from '@nitrots/nitro-renderer/src/nitro/communication/messages/incoming/room/furniture/GroupFurniContextMenuInfoMessageEvent';
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { GetRoomEngine, IsOwnerOfFurniture, LocalizeText, RoomWidgetFurniActionMessage } from '../../../../../api'; import { GetRoomEngine, IsOwnerOfFurniture, LocalizeText, RoomWidgetFurniActionMessage, TryVisitRoom } from '../../../../../api';
import { TryJoinGroup } from '../../../../../api/groups/TryJoinGroup';
import { CreateMessageHook, SendMessageHook } from '../../../../../hooks';
import { useRoomEngineEvent } from '../../../../../hooks/events'; import { useRoomEngineEvent } from '../../../../../hooks/events';
import { useRoomContext } from '../../../context/RoomContext'; import { useRoomContext } from '../../../context/RoomContext';
import { ContextMenuView } from '../../context-menu/ContextMenuView'; import { ContextMenuView } from '../../context-menu/ContextMenuView';
@ -18,6 +21,9 @@ export const FurnitureContextMenuView: FC<{}> = props =>
const [ mode, setMode ] = useState<string>(null); const [ mode, setMode ] = useState<string>(null);
const [ confirmMode, setConfirmMode ] = useState<string>(null); const [ confirmMode, setConfirmMode ] = useState<string>(null);
const [ confirmingObjectId, setConfirmingObjectId ] = useState(-1); const [ confirmingObjectId, setConfirmingObjectId ] = useState(-1);
const [ groupData, setGroupData ] = useState<GroupFurniContextMenuInfoMessageParser>(null);
const [ isGroupMember, setIsGroupMember ] = useState<boolean>(false);
const { roomSession = null, widgetHandler = null } = useRoomContext(); const { roomSession = null, widgetHandler = null } = useRoomContext();
const close = useCallback(() => const close = useCallback(() =>
@ -71,6 +77,9 @@ export const FurnitureContextMenuView: FC<{}> = props =>
case ContextMenuEnum.PURCHASABLE_CLOTHING: case ContextMenuEnum.PURCHASABLE_CLOTHING:
if(IsOwnerOfFurniture(object)) setMode(ContextMenuEnum.PURCHASABLE_CLOTHING); if(IsOwnerOfFurniture(object)) setMode(ContextMenuEnum.PURCHASABLE_CLOTHING);
return; return;
case ContextMenuEnum.GROUP_FURNITURE:
SendMessageHook(new FurnitureGroupInfoComposer(object.id, object.model.getValue<number>(RoomObjectVariable.FURNITURE_GUILD_CUSTOMIZED_GUILD_ID)));
return;
} }
return; return;
@ -85,6 +94,18 @@ export const FurnitureContextMenuView: FC<{}> = props =>
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_MONSTERPLANT_SEED_PLANT_CONFIRMATION_DIALOG, onRoomEngineTriggerWidgetEvent); useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_MONSTERPLANT_SEED_PLANT_CONFIRMATION_DIALOG, onRoomEngineTriggerWidgetEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_PURCHASABLE_CLOTHING_CONFIRMATION_DIALOG, onRoomEngineTriggerWidgetEvent); useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_PURCHASABLE_CLOTHING_CONFIRMATION_DIALOG, onRoomEngineTriggerWidgetEvent);
const onGroupFurniContextMenuInfoMessageEvent = useCallback((event: GroupFurniContextMenuInfoMessageEvent) =>
{
const parser = event.getParser();
setGroupData(null);
setGroupData(parser);
setIsGroupMember(parser.userIsMember);
setMode(ContextMenuEnum.GROUP_FURNITURE);
}, []);
CreateMessageHook(GroupFurniContextMenuInfoMessageEvent, onGroupFurniContextMenuInfoMessageEvent);
const processAction = useCallback((name: string) => const processAction = useCallback((name: string) =>
{ {
if(name) if(name)
@ -105,11 +126,18 @@ export const FurnitureContextMenuView: FC<{}> = props =>
setConfirmMode(PURCHASABLE_CLOTHING_CONFIRMATION); setConfirmMode(PURCHASABLE_CLOTHING_CONFIRMATION);
setConfirmingObjectId(objectId); setConfirmingObjectId(objectId);
break; break;
case 'join_group':
TryJoinGroup(groupData.guildId);
setIsGroupMember(true);
return;
case 'go_to_group_homeroom':
if(groupData) TryVisitRoom(groupData.guildHomeRoomId);
break;
} }
} }
close(); close();
}, [ roomSession, widgetHandler, objectId, close ]); }, [ roomSession, widgetHandler, objectId, groupData, close ]);
return ( return (
<> <>
@ -153,6 +181,18 @@ export const FurnitureContextMenuView: FC<{}> = props =>
{ LocalizeText('widget.generic_usable.button.use') } { LocalizeText('widget.generic_usable.button.use') }
</ContextMenuListItemView> </ContextMenuListItemView>
</> } </> }
{ (mode === ContextMenuEnum.GROUP_FURNITURE) && groupData &&
<>
<ContextMenuHeaderView>
{ groupData.guildName }
</ContextMenuHeaderView>
{ !isGroupMember && <ContextMenuListItemView onClick={ event => processAction('join_group') }>
{ LocalizeText('widget.furniture.button.join.group') }
</ContextMenuListItemView> }
<ContextMenuListItemView onClick={ event => processAction('go_to_group_homeroom') }>
{ LocalizeText('widget.furniture.button.go.to.group.home.room') }
</ContextMenuListItemView>
</> }
</ContextMenuView> } </ContextMenuView> }
</> </>
) )

View File

@ -1,7 +1,7 @@
import { CrackableDataType, RoomControllerLevel, RoomObjectCategory, RoomObjectVariable, RoomWidgetEnumItemExtradataParameter, RoomWidgetFurniInfoUsagePolicyEnum, SetObjectDataMessageComposer, StringDataType, UserProfileComposer } from '@nitrots/nitro-renderer'; import { CrackableDataType, GroupInformationComposer, GroupInformationEvent, RoomControllerLevel, RoomObjectCategory, RoomObjectVariable, RoomWidgetEnumItemExtradataParameter, RoomWidgetFurniInfoUsagePolicyEnum, SetObjectDataMessageComposer, StringDataType } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import { CreateLinkEvent, GetRoomEngine, LocalizeText, RoomWidgetFurniActionMessage } from '../../../../../../api'; import { CreateLinkEvent, GetGroupInformation, GetRoomEngine, LocalizeText, RoomWidgetFurniActionMessage } from '../../../../../../api';
import { SendMessageHook } from '../../../../../../hooks'; import { CreateMessageHook, SendMessageHook } from '../../../../../../hooks';
import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView'; import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView';
import { LimitedEditionCompactPlateView } from '../../../../../shared/limited-edition/compact-plate/LimitedEditionCompactPlateView'; import { LimitedEditionCompactPlateView } from '../../../../../shared/limited-edition/compact-plate/LimitedEditionCompactPlateView';
import { RarityLevelView } from '../../../../../shared/rarity-level/RarityLevelView'; import { RarityLevelView } from '../../../../../shared/rarity-level/RarityLevelView';
@ -31,6 +31,7 @@ export const InfoStandWidgetFurniView: FC<InfoStandWidgetFurniViewProps> = props
const [ crackableHits, setCrackableHits ] = useState(0); const [ crackableHits, setCrackableHits ] = useState(0);
const [ crackableTarget, setCrackableTarget ] = useState(0); const [ crackableTarget, setCrackableTarget ] = useState(0);
const [ godMode, setGodMode ] = useState(false); const [ godMode, setGodMode ] = useState(false);
const [ groupName, setGroupName ] = useState<string>(null);
useEffect(() => useEffect(() =>
{ {
@ -128,12 +129,23 @@ export const InfoStandWidgetFurniView: FC<InfoStandWidgetFurniViewProps> = props
setCrackableHits(crackableHits); setCrackableHits(crackableHits);
setCrackableTarget(crackableTarget); setCrackableTarget(crackableTarget);
setGodMode(godMode); setGodMode(godMode);
setGroupName(null);
if(furniData.groupId) SendMessageHook(new GroupInformationComposer(furniData.groupId, false));
}, [ roomSession, furniData ]); }, [ roomSession, furniData ]);
const openFurniGroupInfo = useCallback(() => const onGroupInformationEvent = useCallback((event: GroupInformationEvent) =>
{ {
const parser = event.getParser();
}, []); if(!furniData || furniData.groupId !== parser.id || parser.flag) return;
if(groupName) setGroupName(null);
setGroupName(parser.title);
}, [ furniData, groupName ]);
CreateMessageHook(GroupInformationEvent, onGroupInformationEvent);
const onFurniSettingChange = useCallback((index: number, value: string) => const onFurniSettingChange = useCallback((index: number, value: string) =>
{ {
@ -223,9 +235,13 @@ export const InfoStandWidgetFurniView: FC<InfoStandWidgetFurniViewProps> = props
widgetHandler.processWidgetMessage(new RoomWidgetFurniActionMessage(messageType, furniData.id, furniData.category, furniData.purchaseOfferId, objectData)); widgetHandler.processWidgetMessage(new RoomWidgetFurniActionMessage(messageType, furniData.id, furniData.category, furniData.purchaseOfferId, objectData));
}, [ widgetHandler, furniData, pickupMode, customKeys, customValues, getFurniSettingsAsString ]); }, [ widgetHandler, furniData, pickupMode, customKeys, customValues, getFurniSettingsAsString ]);
const openProfile = useCallback(() => const getGroupBadgeCode = useCallback(() =>
{ {
SendMessageHook(new UserProfileComposer(furniData.ownerId)); const stringDataType = (furniData.stuffData as StringDataType);
if(!stringDataType || !(stringDataType instanceof StringDataType)) return null;
return stringDataType.getValue(2);
}, [ furniData ]); }, [ furniData ]);
if(!furniData) return null; if(!furniData) return null;
@ -258,11 +274,12 @@ export const InfoStandWidgetFurniView: FC<InfoStandWidgetFurniViewProps> = props
<hr className="m-0 my-1" /> <hr className="m-0 my-1" />
<div className="small text-wrap">{ LocalizeText('infostand.crackable_furni.hits_remaining', [ 'hits', 'target' ], [ crackableHits.toString(), crackableTarget.toString() ]) }</div> <div className="small text-wrap">{ LocalizeText('infostand.crackable_furni.hits_remaining', [ 'hits', 'target' ], [ crackableHits.toString(), crackableTarget.toString() ]) }</div>
</> } </> }
{ (furniData.groupId > 0) && { furniData.groupId > 0 &&
<> <>
<hr className="m-0 my-1" /> <hr className="m-0 my-1" />
<div className="badge badge-secondary mb-0" onClick={ openFurniGroupInfo }> <div className="d-flex align-items-center cursor-pointer text-decoration-underline gap-2" onClick={ () => GetGroupInformation(furniData.groupId) }>
<BadgeImageView badgeCode={ (furniData.stuffData as StringDataType).getValue(2) } /> <BadgeImageView badgeCode={ getGroupBadgeCode() } isGroup={ true } />
<div>{ groupName }</div>
</div> </div>
</> } </> }
{ godMode && { godMode &&