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,53 @@
|
||||
import { GetRoomEngine, GetSessionDataManager } from '@nitrots/nitro-renderer';
|
||||
import { FC } from 'react';
|
||||
import { CalendarItemState, GetConfigurationValue, ICalendarItem } from '../../api';
|
||||
import { Column, Flex, LayoutImage } from '../../common';
|
||||
|
||||
interface CalendarItemViewProps
|
||||
{
|
||||
itemId: number;
|
||||
state: number;
|
||||
active?: boolean;
|
||||
product?: ICalendarItem;
|
||||
onClick: (itemId: number) => void;
|
||||
}
|
||||
|
||||
export const CalendarItemView: FC<CalendarItemViewProps> = props =>
|
||||
{
|
||||
const { itemId = -1, state = null, product = null, active = false, onClick = null } = props;
|
||||
|
||||
const getFurnitureIcon = (name: string) =>
|
||||
{
|
||||
let furniData = GetSessionDataManager().getFloorItemDataByName(name);
|
||||
let url = null;
|
||||
|
||||
if(furniData) url = GetRoomEngine().getFurnitureFloorIconUrl(furniData.id);
|
||||
else
|
||||
{
|
||||
furniData = GetSessionDataManager().getWallItemDataByName(name);
|
||||
|
||||
if(furniData) url = GetRoomEngine().getFurnitureWallIconUrl(furniData.id);
|
||||
}
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
return (
|
||||
<Column center fit pointer className={ `campaign-spritesheet campaign-day-generic-bg rounded calendar-item ${ active ? 'active' : '' }` } onClick={ () => onClick(itemId) }>
|
||||
{ (state === CalendarItemState.STATE_UNLOCKED) &&
|
||||
<Flex center className="campaign-spritesheet unlocked-bg">
|
||||
<Flex center className="campaign-spritesheet campaign-opened">
|
||||
{ product &&
|
||||
<LayoutImage imageUrl={ product.customImage ? GetConfigurationValue<string>('image.library.url') + product.customImage : getFurnitureIcon(product.productName) } /> }
|
||||
</Flex>
|
||||
</Flex> }
|
||||
{ (state !== CalendarItemState.STATE_UNLOCKED) &&
|
||||
<Flex center className="campaign-spritesheet locked-bg">
|
||||
{ (state === CalendarItemState.STATE_LOCKED_AVAILABLE) &&
|
||||
<div className="campaign-spritesheet available" /> }
|
||||
{ ((state === CalendarItemState.STATE_LOCKED_EXPIRED) || (state === CalendarItemState.STATE_LOCKED_FUTURE)) &&
|
||||
<div className="campaign-spritesheet unavailable" /> }
|
||||
</Flex> }
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,144 @@
|
||||
import { GetSessionDataManager } from '@nitrots/nitro-renderer';
|
||||
import { FC, useState } from 'react';
|
||||
import { CalendarItemState, ICalendarItem, LocalizeText } from '../../api';
|
||||
import { Button, Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common';
|
||||
import { CalendarItemView } from './CalendarItemView';
|
||||
|
||||
interface CalendarViewProps
|
||||
{
|
||||
onClose(): void;
|
||||
openPackage(id: number, asStaff: boolean): void;
|
||||
receivedProducts: Map<number, ICalendarItem>;
|
||||
campaignName: string;
|
||||
currentDay: number;
|
||||
numDays: number;
|
||||
openedDays: number[];
|
||||
missedDays: number[];
|
||||
}
|
||||
|
||||
const TOTAL_SHOWN_ITEMS = 5;
|
||||
|
||||
export const CalendarView: FC<CalendarViewProps> = props =>
|
||||
{
|
||||
const { onClose = null, campaignName = null, currentDay = null, numDays = null, missedDays = null, openedDays = null, openPackage = null, receivedProducts = null } = props;
|
||||
const [ selectedDay, setSelectedDay ] = useState(currentDay);
|
||||
const [ index, setIndex ] = useState(Math.max(0, (selectedDay - 1)));
|
||||
|
||||
const getDayState = (day: number) =>
|
||||
{
|
||||
if(openedDays.includes(day)) return CalendarItemState.STATE_UNLOCKED;
|
||||
|
||||
if(day > currentDay) return CalendarItemState.STATE_LOCKED_FUTURE;
|
||||
|
||||
if(missedDays.includes(day)) return CalendarItemState.STATE_LOCKED_EXPIRED;
|
||||
|
||||
return CalendarItemState.STATE_LOCKED_AVAILABLE;
|
||||
};
|
||||
|
||||
const dayMessage = (day: number) =>
|
||||
{
|
||||
const state = getDayState(day);
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case CalendarItemState.STATE_UNLOCKED:
|
||||
return LocalizeText('campaign.calendar.info.unlocked');
|
||||
case CalendarItemState.STATE_LOCKED_FUTURE:
|
||||
return LocalizeText('campaign.calendar.info.future');
|
||||
case CalendarItemState.STATE_LOCKED_EXPIRED:
|
||||
return LocalizeText('campaign.calendar.info.expired');
|
||||
default:
|
||||
return LocalizeText('campaign.calendar.info.available.desktop');
|
||||
}
|
||||
};
|
||||
|
||||
const onClickNext = () =>
|
||||
{
|
||||
const nextDay = (selectedDay + 1);
|
||||
|
||||
if(nextDay === numDays) return;
|
||||
|
||||
setSelectedDay(nextDay);
|
||||
|
||||
if((index + TOTAL_SHOWN_ITEMS) < (nextDay + 1)) setIndex(index + 1);
|
||||
};
|
||||
|
||||
const onClickPrev = () =>
|
||||
{
|
||||
const prevDay = (selectedDay - 1);
|
||||
|
||||
if(prevDay < 0) return;
|
||||
|
||||
setSelectedDay(prevDay);
|
||||
|
||||
if(index > prevDay) setIndex(index - 1);
|
||||
};
|
||||
|
||||
const onClickItem = (item: number) =>
|
||||
{
|
||||
if(selectedDay === item)
|
||||
{
|
||||
const state = getDayState(item);
|
||||
|
||||
if(state === CalendarItemState.STATE_LOCKED_AVAILABLE) openPackage(item, false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedDay(item);
|
||||
};
|
||||
|
||||
const forceOpen = () =>
|
||||
{
|
||||
const id = selectedDay;
|
||||
const state = getDayState(id);
|
||||
|
||||
if(state !== CalendarItemState.STATE_UNLOCKED) openPackage(id, true);
|
||||
};
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-campaign-calendar" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={ LocalizeText(`campaign.calendar.${ campaignName }.title`) } onCloseClick={ onClose } />
|
||||
<NitroCardContentView>
|
||||
<Grid alignItems="center" fullHeight={ false } justifyContent="between">
|
||||
<Column size={ 1 } />
|
||||
<Column size={ 10 }>
|
||||
<div className="flex items-center gap-1 justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text fontSize={ 3 }>{ LocalizeText('campaign.calendar.heading.day', [ 'number' ], [ (selectedDay + 1).toString() ]) }</Text>
|
||||
<Text>{ dayMessage(selectedDay) }</Text>
|
||||
</div>
|
||||
<div>
|
||||
{ GetSessionDataManager().isModerator &&
|
||||
<Button variant="danger" onClick={ forceOpen }>Force open</Button> }
|
||||
</div>
|
||||
</div>
|
||||
</Column>
|
||||
<Column size={ 1 } />
|
||||
</Grid>
|
||||
<div className="flex h-full gap-2">
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="campaign-spritesheet prev cursor-pointer" onClick={ onClickPrev } />
|
||||
</div>
|
||||
<Column center fullWidth>
|
||||
<Grid fit columnCount={ TOTAL_SHOWN_ITEMS } gap={ 1 }>
|
||||
{ [ ...Array(TOTAL_SHOWN_ITEMS) ].map((e, i) =>
|
||||
{
|
||||
const day = (index + i);
|
||||
|
||||
return (
|
||||
<Column key={ i } overflow="hidden">
|
||||
<CalendarItemView active={ (selectedDay === day) } itemId={ day } product={ receivedProducts.has(day) ? receivedProducts.get(day) : null } state={ getDayState(day) } onClick={ onClickItem } />
|
||||
</Column>
|
||||
);
|
||||
}) }
|
||||
</Grid>
|
||||
</Column>
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="campaign-spritesheet next cursor-pointer" onClick={ onClickNext } />
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,101 @@
|
||||
import { AddLinkEventTracker, CampaignCalendarData, CampaignCalendarDataMessageEvent, CampaignCalendarDoorOpenedMessageEvent, ILinkEventTracker, OpenCampaignCalendarDoorAsStaffComposer, OpenCampaignCalendarDoorComposer, RemoveLinkEventTracker } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { CalendarItem, SendMessageComposer } from '../../api';
|
||||
import { useMessageEvent } from '../../hooks';
|
||||
import { CalendarView } from './CalendarView';
|
||||
|
||||
export const CampaignView: FC<{}> = props =>
|
||||
{
|
||||
const [ calendarData, setCalendarData ] = useState<CampaignCalendarData>(null);
|
||||
const [ lastOpenAttempt, setLastOpenAttempt ] = useState<number>(-1);
|
||||
const [ receivedProducts, setReceivedProducts ] = useState<Map<number, CalendarItem>>(new Map());
|
||||
const [ isCalendarOpen, setCalendarOpen ] = useState(false);
|
||||
|
||||
const openPackage = (id: number, asStaff = false) =>
|
||||
{
|
||||
if(!calendarData) return;
|
||||
|
||||
setLastOpenAttempt(id);
|
||||
|
||||
if(asStaff)
|
||||
{
|
||||
SendMessageComposer(new OpenCampaignCalendarDoorAsStaffComposer(calendarData.campaignName, id));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
SendMessageComposer(new OpenCampaignCalendarDoorComposer(calendarData.campaignName, id));
|
||||
}
|
||||
};
|
||||
|
||||
useMessageEvent<CampaignCalendarDataMessageEvent>(CampaignCalendarDataMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser) return;
|
||||
|
||||
setCalendarData(parser.calendarData);
|
||||
});
|
||||
|
||||
useMessageEvent<CampaignCalendarDoorOpenedMessageEvent>(CampaignCalendarDoorOpenedMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser) return;
|
||||
|
||||
const lastAttempt = lastOpenAttempt;
|
||||
|
||||
if(parser.doorOpened)
|
||||
{
|
||||
setCalendarData(prev =>
|
||||
{
|
||||
const copy = prev.clone();
|
||||
copy.openedDays.push(lastOpenAttempt);
|
||||
|
||||
return copy;
|
||||
});
|
||||
|
||||
setReceivedProducts(prev =>
|
||||
{
|
||||
const copy = new Map(prev);
|
||||
copy.set(lastAttempt, new CalendarItem(parser.productName, parser.customImage,parser.furnitureClassName));
|
||||
|
||||
return copy;
|
||||
});
|
||||
}
|
||||
|
||||
setLastOpenAttempt(-1);
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const linkTracker: ILinkEventTracker = {
|
||||
linkReceived: (url: string) =>
|
||||
{
|
||||
const value = url.split('/');
|
||||
|
||||
if(value.length < 2) return;
|
||||
|
||||
switch(value[1])
|
||||
{
|
||||
case 'calendar':
|
||||
setCalendarOpen(true);
|
||||
break;
|
||||
}
|
||||
},
|
||||
eventUrlPrefix: 'openView/'
|
||||
};
|
||||
|
||||
AddLinkEventTracker(linkTracker);
|
||||
|
||||
return () => RemoveLinkEventTracker(linkTracker);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{ (calendarData && isCalendarOpen) &&
|
||||
<CalendarView campaignName={ calendarData.campaignName } currentDay={ calendarData.currentDay } missedDays={ calendarData.missedDays } numDays={ calendarData.campaignDays } openedDays={ calendarData.openedDays } openPackage={ openPackage } receivedProducts={ receivedProducts } onClose={ () => setCalendarOpen(false) } />
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user