🆙 Init V3

This commit is contained in:
DuckieTM
2026-01-31 09:10:52 +01:00
commit 7feb10ab15
1733 changed files with 53405 additions and 0 deletions
@@ -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>
);
};
+144
View File
@@ -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>
);
};
+101
View File
@@ -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) } />
}
</>
);
};