mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-20 07:26:19 +00:00
🆙 Security Fix - Will not go into details
This commit is contained in:
@@ -67,11 +67,20 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
|
|||||||
{
|
{
|
||||||
if(event.badgeId !== badgeCode) return;
|
if(event.badgeId !== badgeCode) return;
|
||||||
|
|
||||||
const element = await TextureUtils.generateImage(new NitroSprite(event.image));
|
if(isGroup)
|
||||||
|
{
|
||||||
console.log ('boe');
|
const element = await TextureUtils.generateImage(new NitroSprite(event.image));
|
||||||
|
|
||||||
element.onload = () => setImageElement(element);
|
element.onload = () => setImageElement(element);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const badgeUrl = GetConfigurationValue<string>('badge.asset.url').replace('%badgename%', badgeCode.toString());
|
||||||
|
const img = new Image();
|
||||||
|
|
||||||
|
img.onload = () => setImageElement(img);
|
||||||
|
img.src = badgeUrl;
|
||||||
|
}
|
||||||
|
|
||||||
didSetBadge = true;
|
didSetBadge = true;
|
||||||
|
|
||||||
@@ -84,13 +93,23 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
|
|||||||
|
|
||||||
if(texture && !didSetBadge)
|
if(texture && !didSetBadge)
|
||||||
{
|
{
|
||||||
(async () =>
|
if(isGroup)
|
||||||
{
|
{
|
||||||
const element = await TextureUtils.generateImage(new NitroSprite(texture));
|
(async () =>
|
||||||
|
{
|
||||||
|
const element = await TextureUtils.generateImage(new NitroSprite(texture));
|
||||||
|
|
||||||
element.onload = () => setImageElement(element);
|
element.onload = () => setImageElement(element);
|
||||||
})();
|
})();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const badgeUrl = GetConfigurationValue<string>('badge.asset.url').replace('%badgename%', badgeCode.toString());
|
||||||
|
const img = new Image();
|
||||||
|
|
||||||
|
img.onload = () => setImageElement(img);
|
||||||
|
img.src = badgeUrl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => GetEventDispatcher().removeEventListener(BadgeImageReadyEvent.IMAGE_READY, onBadgeImageReadyEvent);
|
return () => GetEventDispatcher().removeEventListener(BadgeImageReadyEvent.IMAGE_READY, onBadgeImageReadyEvent);
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export const MainView: FC<{}> = props =>
|
|||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{ landingViewVisible &&
|
{ landingViewVisible &&
|
||||||
<motion.div
|
<motion.div
|
||||||
className="w-full h-full"
|
className="w-full h-full"
|
||||||
initial={ { opacity: 0 }}
|
initial={ { opacity: 0 }}
|
||||||
animate={ { opacity: 1 }}
|
animate={ { opacity: 1 }}
|
||||||
exit={ { opacity: 0 }}>
|
exit={ { opacity: 0 }}>
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { CatalogEvent, CatalogInitGiftEvent, CatalogPurchasedEvent } from '../..
|
|||||||
import { useCatalog, useFriends, useMessageEvent, useUiEvent } from '../../../../hooks';
|
import { useCatalog, useFriends, useMessageEvent, useUiEvent } from '../../../../hooks';
|
||||||
import { classNames } from '../../../../layout';
|
import { classNames } from '../../../../layout';
|
||||||
|
|
||||||
|
let isBuyingGift = false;
|
||||||
|
|
||||||
export const CatalogGiftView: FC<{}> = props =>
|
export const CatalogGiftView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
||||||
@@ -32,6 +34,7 @@ export const CatalogGiftView: FC<{}> = props =>
|
|||||||
|
|
||||||
const onClose = useCallback(() =>
|
const onClose = useCallback(() =>
|
||||||
{
|
{
|
||||||
|
isBuyingGift = false;
|
||||||
setIsVisible(false);
|
setIsVisible(false);
|
||||||
setPageId(0);
|
setPageId(0);
|
||||||
setOfferId(0);
|
setOfferId(0);
|
||||||
@@ -122,6 +125,10 @@ export const CatalogGiftView: FC<{}> = props =>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(isBuyingGift) return;
|
||||||
|
|
||||||
|
isBuyingGift = true;
|
||||||
|
|
||||||
SendMessageComposer(new PurchaseFromCatalogAsGiftComposer(pageId, offerId, extraData, receiverName, message, colourId, selectedBoxIndex, selectedRibbonIndex, showMyFace));
|
SendMessageComposer(new PurchaseFromCatalogAsGiftComposer(pageId, offerId, extraData, receiverName, message, colourId, selectedBoxIndex, selectedRibbonIndex, showMyFace));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -136,6 +143,7 @@ export const CatalogGiftView: FC<{}> = props =>
|
|||||||
switch(event.type)
|
switch(event.type)
|
||||||
{
|
{
|
||||||
case CatalogPurchasedEvent.PURCHASE_SUCCESS:
|
case CatalogPurchasedEvent.PURCHASE_SUCCESS:
|
||||||
|
isBuyingGift = false;
|
||||||
onClose();
|
onClose();
|
||||||
return;
|
return;
|
||||||
case CatalogEvent.INIT_GIFT:
|
case CatalogEvent.INIT_GIFT:
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { useCatalog, useMessageEvent, useNavigator, useRoomPromote } from '../..
|
|||||||
import { NitroInput } from '../../../../../layout';
|
import { NitroInput } from '../../../../../layout';
|
||||||
import { CatalogLayoutProps } from './CatalogLayout.types';
|
import { CatalogLayoutProps } from './CatalogLayout.types';
|
||||||
|
|
||||||
|
let isPurchasingAd = false;
|
||||||
|
|
||||||
export const CatalogLayoutRoomAdsView: FC<CatalogLayoutProps> = props =>
|
export const CatalogLayoutRoomAdsView: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { page = null } = props;
|
const { page = null } = props;
|
||||||
@@ -45,6 +47,10 @@ export const CatalogLayoutRoomAdsView: FC<CatalogLayoutProps> = props =>
|
|||||||
|
|
||||||
const purchaseAd = () =>
|
const purchaseAd = () =>
|
||||||
{
|
{
|
||||||
|
if(isPurchasingAd) return;
|
||||||
|
|
||||||
|
isPurchasingAd = true;
|
||||||
|
|
||||||
const pageId = page.pageId;
|
const pageId = page.pageId;
|
||||||
const offerId = page.offers.length >= 1 ? page.offers[0].offerId : -1;
|
const offerId = page.offers.length >= 1 ? page.offers[0].offerId : -1;
|
||||||
const flatId = roomId;
|
const flatId = roomId;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ClubOfferData, GetClubOffersMessageComposer, PurchaseFromCatalogComposer } from '@nitrots/nitro-renderer';
|
import { ClubOfferData, GetClubOffersMessageComposer, PurchaseFromCatalogComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { CatalogPurchaseState, LocalizeText, SendMessageComposer } from '../../../../../api';
|
import { CatalogPurchaseState, LocalizeText, SendMessageComposer } from '../../../../../api';
|
||||||
import { AutoGrid, Button, Column, Flex, Grid, LayoutCurrencyIcon, LayoutGridItem, LayoutLoadingSpinnerView, Text } from '../../../../../common';
|
import { AutoGrid, Button, Column, Flex, Grid, LayoutCurrencyIcon, LayoutGridItem, LayoutLoadingSpinnerView, Text } from '../../../../../common';
|
||||||
import { CatalogEvent, CatalogPurchaseFailureEvent, CatalogPurchasedEvent } from '../../../../../events';
|
import { CatalogEvent, CatalogPurchaseFailureEvent, CatalogPurchasedEvent } from '../../../../../events';
|
||||||
@@ -13,15 +13,18 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
|
|||||||
const { currentPage = null, catalogOptions = null } = useCatalog();
|
const { currentPage = null, catalogOptions = null } = useCatalog();
|
||||||
const { purse = null, getCurrencyAmount = null } = usePurse();
|
const { purse = null, getCurrencyAmount = null } = usePurse();
|
||||||
const { clubOffers = null } = catalogOptions;
|
const { clubOffers = null } = catalogOptions;
|
||||||
|
const isPurchasingRef = useRef<boolean>(false);
|
||||||
|
|
||||||
const onCatalogEvent = useCallback((event: CatalogEvent) =>
|
const onCatalogEvent = useCallback((event: CatalogEvent) =>
|
||||||
{
|
{
|
||||||
switch(event.type)
|
switch(event.type)
|
||||||
{
|
{
|
||||||
case CatalogPurchasedEvent.PURCHASE_SUCCESS:
|
case CatalogPurchasedEvent.PURCHASE_SUCCESS:
|
||||||
|
isPurchasingRef.current = false;
|
||||||
setPurchaseState(CatalogPurchaseState.NONE);
|
setPurchaseState(CatalogPurchaseState.NONE);
|
||||||
return;
|
return;
|
||||||
case CatalogPurchaseFailureEvent.PURCHASE_FAILED:
|
case CatalogPurchaseFailureEvent.PURCHASE_FAILED:
|
||||||
|
isPurchasingRef.current = false;
|
||||||
setPurchaseState(CatalogPurchaseState.FAILED);
|
setPurchaseState(CatalogPurchaseState.FAILED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -83,8 +86,9 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
|
|||||||
|
|
||||||
const purchaseSubscription = useCallback(() =>
|
const purchaseSubscription = useCallback(() =>
|
||||||
{
|
{
|
||||||
if(!pendingOffer) return;
|
if(!pendingOffer || isPurchasingRef.current) return;
|
||||||
|
|
||||||
|
isPurchasingRef.current = true;
|
||||||
setPurchaseState(CatalogPurchaseState.PURCHASE);
|
setPurchaseState(CatalogPurchaseState.PURCHASE);
|
||||||
SendMessageComposer(new PurchaseFromCatalogComposer(currentPage.pageId, pendingOffer.offerId, null, 1));
|
SendMessageComposer(new PurchaseFromCatalogComposer(currentPage.pageId, pendingOffer.offerId, null, 1));
|
||||||
}, [ pendingOffer, currentPage ]);
|
}, [ pendingOffer, currentPage ]);
|
||||||
|
|||||||
+15
-1
@@ -1,5 +1,5 @@
|
|||||||
import { CancelMarketplaceOfferMessageComposer, GetMarketplaceOwnOffersMessageComposer, MarketplaceCancelOfferResultEvent, MarketplaceOwnOffersEvent, RedeemMarketplaceOfferCreditsMessageComposer } from '@nitrots/nitro-renderer';
|
import { CancelMarketplaceOfferMessageComposer, GetMarketplaceOwnOffersMessageComposer, MarketplaceCancelOfferResultEvent, MarketplaceOwnOffersEvent, RedeemMarketplaceOfferCreditsMessageComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { LocalizeText, MarketplaceOfferData, MarketPlaceOfferState, NotificationAlertType, SendMessageComposer } from '../../../../../../api';
|
import { LocalizeText, MarketplaceOfferData, MarketPlaceOfferState, NotificationAlertType, SendMessageComposer } from '../../../../../../api';
|
||||||
import { Button, Column, Text } from '../../../../../../common';
|
import { Button, Column, Text } from '../../../../../../common';
|
||||||
import { useMessageEvent, useNotification } from '../../../../../../hooks';
|
import { useMessageEvent, useNotification } from '../../../../../../hooks';
|
||||||
@@ -11,6 +11,8 @@ export const CatalogLayoutMarketplaceOwnItemsView: FC<CatalogLayoutProps> = prop
|
|||||||
const [ creditsWaiting, setCreditsWaiting ] = useState(0);
|
const [ creditsWaiting, setCreditsWaiting ] = useState(0);
|
||||||
const [ offers, setOffers ] = useState<MarketplaceOfferData[]>([]);
|
const [ offers, setOffers ] = useState<MarketplaceOfferData[]>([]);
|
||||||
const { simpleAlert = null } = useNotification();
|
const { simpleAlert = null } = useNotification();
|
||||||
|
const isRedeemingRef = useRef<boolean>(false);
|
||||||
|
const pendingCancelsRef = useRef<Set<number>>(new Set());
|
||||||
|
|
||||||
useMessageEvent<MarketplaceOwnOffersEvent>(MarketplaceOwnOffersEvent, event =>
|
useMessageEvent<MarketplaceOwnOffersEvent>(MarketplaceOwnOffersEvent, event =>
|
||||||
{
|
{
|
||||||
@@ -54,6 +56,10 @@ export const CatalogLayoutMarketplaceOwnItemsView: FC<CatalogLayoutProps> = prop
|
|||||||
|
|
||||||
const redeemSoldOffers = useCallback(() =>
|
const redeemSoldOffers = useCallback(() =>
|
||||||
{
|
{
|
||||||
|
if(isRedeemingRef.current) return;
|
||||||
|
|
||||||
|
isRedeemingRef.current = true;
|
||||||
|
|
||||||
setOffers(prevValue =>
|
setOffers(prevValue =>
|
||||||
{
|
{
|
||||||
const idsToDelete = soldOffers.map(value => value.offerId);
|
const idsToDelete = soldOffers.map(value => value.offerId);
|
||||||
@@ -62,11 +68,19 @@ export const CatalogLayoutMarketplaceOwnItemsView: FC<CatalogLayoutProps> = prop
|
|||||||
});
|
});
|
||||||
|
|
||||||
SendMessageComposer(new RedeemMarketplaceOfferCreditsMessageComposer());
|
SendMessageComposer(new RedeemMarketplaceOfferCreditsMessageComposer());
|
||||||
|
|
||||||
|
setTimeout(() => isRedeemingRef.current = false, 3000);
|
||||||
}, [ soldOffers ]);
|
}, [ soldOffers ]);
|
||||||
|
|
||||||
const takeItemBack = (offerData: MarketplaceOfferData) =>
|
const takeItemBack = (offerData: MarketplaceOfferData) =>
|
||||||
{
|
{
|
||||||
|
if(pendingCancelsRef.current.has(offerData.offerId)) return;
|
||||||
|
|
||||||
|
pendingCancelsRef.current.add(offerData.offerId);
|
||||||
|
|
||||||
SendMessageComposer(new CancelMarketplaceOfferMessageComposer(offerData.offerId));
|
SendMessageComposer(new CancelMarketplaceOfferMessageComposer(offerData.offerId));
|
||||||
|
|
||||||
|
setTimeout(() => pendingCancelsRef.current.delete(offerData.offerId), 2000);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
|
|||||||
+7
-1
@@ -1,5 +1,5 @@
|
|||||||
import { BuyMarketplaceOfferMessageComposer, GetMarketplaceOffersMessageComposer, MarketplaceBuyOfferResultEvent, MarketPlaceOffersEvent } from '@nitrots/nitro-renderer';
|
import { BuyMarketplaceOfferMessageComposer, GetMarketplaceOffersMessageComposer, MarketplaceBuyOfferResultEvent, MarketPlaceOffersEvent } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useMemo, useState } from 'react';
|
import { FC, useCallback, useMemo, useRef, useState } from 'react';
|
||||||
import { IMarketplaceSearchOptions, LocalizeText, MarketplaceOfferData, MarketplaceSearchType, NotificationAlertType, SendMessageComposer } from '../../../../../../api';
|
import { IMarketplaceSearchOptions, LocalizeText, MarketplaceOfferData, MarketplaceSearchType, NotificationAlertType, SendMessageComposer } from '../../../../../../api';
|
||||||
import { Button, Column, Text } from '../../../../../../common';
|
import { Button, Column, Text } from '../../../../../../common';
|
||||||
import { useMessageEvent, useNotification, usePurse } from '../../../../../../hooks';
|
import { useMessageEvent, useNotification, usePurse } from '../../../../../../hooks';
|
||||||
@@ -23,6 +23,7 @@ export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplac
|
|||||||
const [ lastSearch, setLastSearch ] = useState<IMarketplaceSearchOptions>({ minPrice: -1, maxPrice: -1, query: '', type: 3 });
|
const [ lastSearch, setLastSearch ] = useState<IMarketplaceSearchOptions>({ minPrice: -1, maxPrice: -1, query: '', type: 3 });
|
||||||
const { getCurrencyAmount = null } = usePurse();
|
const { getCurrencyAmount = null } = usePurse();
|
||||||
const { simpleAlert = null, showConfirm = null } = useNotification();
|
const { simpleAlert = null, showConfirm = null } = useNotification();
|
||||||
|
const isBuyingRef = useRef<boolean>(false);
|
||||||
|
|
||||||
const requestOffers = useCallback((options: IMarketplaceSearchOptions) =>
|
const requestOffers = useCallback((options: IMarketplaceSearchOptions) =>
|
||||||
{
|
{
|
||||||
@@ -56,6 +57,9 @@ export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplac
|
|||||||
|
|
||||||
showConfirm(LocalizeText('catalog.marketplace.confirm_header'), () =>
|
showConfirm(LocalizeText('catalog.marketplace.confirm_header'), () =>
|
||||||
{
|
{
|
||||||
|
if(isBuyingRef.current) return;
|
||||||
|
|
||||||
|
isBuyingRef.current = true;
|
||||||
SendMessageComposer(new BuyMarketplaceOfferMessageComposer(offerId));
|
SendMessageComposer(new BuyMarketplaceOfferMessageComposer(offerId));
|
||||||
},
|
},
|
||||||
null, null, null, LocalizeText('catalog.marketplace.confirm_title'));
|
null, null, null, LocalizeText('catalog.marketplace.confirm_title'));
|
||||||
@@ -83,6 +87,8 @@ export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplac
|
|||||||
{
|
{
|
||||||
const parser = event.getParser();
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
isBuyingRef.current = false;
|
||||||
|
|
||||||
if(!parser) return;
|
if(!parser) return;
|
||||||
|
|
||||||
switch(parser.result)
|
switch(parser.result)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { CatalogPostMarketplaceOfferEvent } from '../../../../../../events';
|
|||||||
import { useCatalog, useMessageEvent, useNotification, useUiEvent } from '../../../../../../hooks';
|
import { useCatalog, useMessageEvent, useNotification, useUiEvent } from '../../../../../../hooks';
|
||||||
import { NitroInput } from '../../../../../../layout';
|
import { NitroInput } from '../../../../../../layout';
|
||||||
|
|
||||||
|
let isPostingMarketplaceOffer = false;
|
||||||
|
|
||||||
export const MarketplacePostOfferView: FC<{}> = props =>
|
export const MarketplacePostOfferView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ item, setItem ] = useState<FurnitureItem>(null);
|
const [ item, setItem ] = useState<FurnitureItem>(null);
|
||||||
@@ -65,10 +67,15 @@ export const MarketplacePostOfferView: FC<{}> = props =>
|
|||||||
|
|
||||||
const postItem = () =>
|
const postItem = () =>
|
||||||
{
|
{
|
||||||
if(!item || (askingPrice < marketplaceConfiguration.minimumPrice)) return;
|
if(!item || (askingPrice < marketplaceConfiguration.minimumPrice) || isPostingMarketplaceOffer) return;
|
||||||
|
|
||||||
showConfirm(LocalizeText('inventory.marketplace.confirm_offer.info', [ 'furniname', 'price' ], [ getFurniTitle, askingPrice.toString() ]), () =>
|
showConfirm(LocalizeText('inventory.marketplace.confirm_offer.info', [ 'furniname', 'price' ], [ getFurniTitle, askingPrice.toString() ]), () =>
|
||||||
{
|
{
|
||||||
|
if(isPostingMarketplaceOffer) return;
|
||||||
|
|
||||||
|
isPostingMarketplaceOffer = true;
|
||||||
|
setTimeout(() => isPostingMarketplaceOffer = false, 5000);
|
||||||
|
|
||||||
SendMessageComposer(new MakeOfferMessageComposer(askingPrice, item.isWallItem ? 2 : 1, item.id));
|
SendMessageComposer(new MakeOfferMessageComposer(askingPrice, item.isWallItem ? 2 : 1, item.id));
|
||||||
setItem(null);
|
setItem(null);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { useCatalog, useNotification, usePurse } from '../../../../../../hooks';
|
|||||||
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
import { VipGiftItem } from './VipGiftItemView';
|
import { VipGiftItem } from './VipGiftItemView';
|
||||||
|
|
||||||
|
let isSelectingGift = false;
|
||||||
|
|
||||||
export const CatalogLayoutVipGiftsView: FC<CatalogLayoutProps> = props =>
|
export const CatalogLayoutVipGiftsView: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { purse = null } = usePurse();
|
const { purse = null } = usePurse();
|
||||||
@@ -30,6 +32,10 @@ export const CatalogLayoutVipGiftsView: FC<CatalogLayoutProps> = props =>
|
|||||||
{
|
{
|
||||||
showConfirm(LocalizeText('catalog.club_gift.confirm'), () =>
|
showConfirm(LocalizeText('catalog.club_gift.confirm'), () =>
|
||||||
{
|
{
|
||||||
|
if(isSelectingGift) return;
|
||||||
|
|
||||||
|
isSelectingGift = true;
|
||||||
|
|
||||||
SendMessageComposer(new SelectClubGiftComposer(localizationId));
|
SendMessageComposer(new SelectClubGiftComposer(localizationId));
|
||||||
|
|
||||||
setCatalogOptions(prevValue =>
|
setCatalogOptions(prevValue =>
|
||||||
@@ -38,6 +44,8 @@ export const CatalogLayoutVipGiftsView: FC<CatalogLayoutProps> = props =>
|
|||||||
|
|
||||||
return { ...prevValue };
|
return { ...prevValue };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setTimeout(() => isSelectingGift = false, 5000);
|
||||||
}, null);
|
}, null);
|
||||||
}, [ setCatalogOptions, showConfirm ]);
|
}, [ setCatalogOptions, showConfirm ]);
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ interface CatalogPurchaseWidgetViewProps
|
|||||||
purchaseCallback?: () => void;
|
purchaseCallback?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isPurchasingCatalogItem = false;
|
||||||
|
|
||||||
export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = props =>
|
export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { noGiftOption = false, purchaseCallback = null } = props;
|
const { noGiftOption = false, purchaseCallback = null } = props;
|
||||||
@@ -25,15 +27,19 @@ export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = pro
|
|||||||
switch(event.type)
|
switch(event.type)
|
||||||
{
|
{
|
||||||
case CatalogPurchasedEvent.PURCHASE_SUCCESS:
|
case CatalogPurchasedEvent.PURCHASE_SUCCESS:
|
||||||
|
isPurchasingCatalogItem = false;
|
||||||
setPurchaseState(CatalogPurchaseState.NONE);
|
setPurchaseState(CatalogPurchaseState.NONE);
|
||||||
return;
|
return;
|
||||||
case CatalogPurchaseFailureEvent.PURCHASE_FAILED:
|
case CatalogPurchaseFailureEvent.PURCHASE_FAILED:
|
||||||
|
isPurchasingCatalogItem = false;
|
||||||
setPurchaseState(CatalogPurchaseState.FAILED);
|
setPurchaseState(CatalogPurchaseState.FAILED);
|
||||||
return;
|
return;
|
||||||
case CatalogPurchaseNotAllowedEvent.NOT_ALLOWED:
|
case CatalogPurchaseNotAllowedEvent.NOT_ALLOWED:
|
||||||
|
isPurchasingCatalogItem = false;
|
||||||
setPurchaseState(CatalogPurchaseState.FAILED);
|
setPurchaseState(CatalogPurchaseState.FAILED);
|
||||||
return;
|
return;
|
||||||
case CatalogPurchaseSoldOutEvent.SOLD_OUT:
|
case CatalogPurchaseSoldOutEvent.SOLD_OUT:
|
||||||
|
isPurchasingCatalogItem = false;
|
||||||
setPurchaseState(CatalogPurchaseState.SOLD_OUT);
|
setPurchaseState(CatalogPurchaseState.SOLD_OUT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -62,7 +68,7 @@ export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = pro
|
|||||||
|
|
||||||
const purchase = (isGift: boolean = false) =>
|
const purchase = (isGift: boolean = false) =>
|
||||||
{
|
{
|
||||||
if(!currentOffer) return;
|
if(!currentOffer || isPurchasingCatalogItem) return;
|
||||||
|
|
||||||
if(GetClubMemberLevel() < currentOffer.clubLevel)
|
if(GetClubMemberLevel() < currentOffer.clubLevel)
|
||||||
{
|
{
|
||||||
@@ -78,6 +84,7 @@ export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = pro
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isPurchasingCatalogItem = true;
|
||||||
setPurchaseState(CatalogPurchaseState.PURCHASE);
|
setPurchaseState(CatalogPurchaseState.PURCHASE);
|
||||||
|
|
||||||
if(purchaseCallback)
|
if(purchaseCallback)
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { FriendlyTime, GetConfigurationValue, LocalizeText, SendMessageComposer
|
|||||||
import { Button, Column, Flex, LayoutCurrencyIcon, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
|
import { Button, Column, Flex, LayoutCurrencyIcon, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
|
||||||
import { usePurse } from '../../../../hooks';
|
import { usePurse } from '../../../../hooks';
|
||||||
|
|
||||||
|
let isBuyingOffer = false;
|
||||||
|
|
||||||
export const OfferWindowView = (props: { offer: TargetedOfferData, setOpen: Dispatch<SetStateAction<boolean>> }) =>
|
export const OfferWindowView = (props: { offer: TargetedOfferData, setOpen: Dispatch<SetStateAction<boolean>> }) =>
|
||||||
{
|
{
|
||||||
const { offer = null, setOpen = null } = props;
|
const { offer = null, setOpen = null } = props;
|
||||||
@@ -37,8 +39,14 @@ export const OfferWindowView = (props: { offer: TargetedOfferData, setOpen: Disp
|
|||||||
|
|
||||||
const buyOffer = () =>
|
const buyOffer = () =>
|
||||||
{
|
{
|
||||||
|
if(isBuyingOffer) return;
|
||||||
|
|
||||||
|
isBuyingOffer = true;
|
||||||
|
|
||||||
SendMessageComposer(new PurchaseTargetedOfferComposer(offer.id, amount));
|
SendMessageComposer(new PurchaseTargetedOfferComposer(offer.id, amount));
|
||||||
SendMessageComposer(new GetTargetedOfferComposer());
|
SendMessageComposer(new GetTargetedOfferComposer());
|
||||||
|
|
||||||
|
setTimeout(() => isBuyingOffer = false, 5000);
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!offer) return;
|
if(!offer) return;
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ interface GroupCreatorViewProps
|
|||||||
|
|
||||||
const TABS: number[] = [ 1, 2, 3, 4 ];
|
const TABS: number[] = [ 1, 2, 3, 4 ];
|
||||||
|
|
||||||
|
let isBuyingGroup = false;
|
||||||
|
|
||||||
export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
|
export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { onClose = null } = props;
|
const { onClose = null } = props;
|
||||||
@@ -34,7 +36,10 @@ export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
|
|||||||
|
|
||||||
const buyGroup = () =>
|
const buyGroup = () =>
|
||||||
{
|
{
|
||||||
if(!groupData) return;
|
if(!groupData || isBuyingGroup) return;
|
||||||
|
|
||||||
|
isBuyingGroup = true;
|
||||||
|
setTimeout(() => isBuyingGroup = false, 5000);
|
||||||
|
|
||||||
const badge = [];
|
const badge = [];
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { AddLinkEventTracker, GetSessionDataManager, GroupAdminGiveComposer, GroupAdminTakeComposer, GroupConfirmMemberRemoveEvent, GroupConfirmRemoveMemberComposer, GroupMemberParser, GroupMembersComposer, GroupMembersEvent, GroupMembershipAcceptComposer, GroupMembershipDeclineComposer, GroupMembersParser, GroupRank, GroupRemoveMemberComposer, ILinkEventTracker, RemoveLinkEventTracker } from '@nitrots/nitro-renderer';
|
import { AddLinkEventTracker, GetSessionDataManager, GroupAdminGiveComposer, GroupAdminTakeComposer, GroupConfirmMemberRemoveEvent, GroupConfirmRemoveMemberComposer, GroupMemberParser, GroupMembersComposer, GroupMembersEvent, GroupMembershipAcceptComposer, GroupMembershipDeclineComposer, GroupMembersParser, GroupRank, GroupRemoveMemberComposer, ILinkEventTracker, RemoveLinkEventTracker } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useState } from 'react';
|
import { FC, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
|
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
|
||||||
import { GetUserProfile, LocalizeText, SendMessageComposer } from '../../../api';
|
import { GetUserProfile, LocalizeText, SendMessageComposer } from '../../../api';
|
||||||
import { Button, Column, Flex, Grid, LayoutAvatarImageView, LayoutBadgeImageView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../common';
|
import { Button, Column, Flex, Grid, LayoutAvatarImageView, LayoutBadgeImageView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../common';
|
||||||
@@ -16,6 +16,7 @@ export const GroupMembersView: FC<{}> = props =>
|
|||||||
const [ searchQuery, setSearchQuery ] = useState<string>('');
|
const [ searchQuery, setSearchQuery ] = useState<string>('');
|
||||||
const [ removingMemberName, setRemovingMemberName ] = useState<string>(null);
|
const [ removingMemberName, setRemovingMemberName ] = useState<string>(null);
|
||||||
const { showConfirm = null } = useNotification();
|
const { showConfirm = null } = useNotification();
|
||||||
|
const pendingActionsRef = useRef<Set<string>>(new Set());
|
||||||
|
|
||||||
const getRankDescription = (member: GroupMemberParser) =>
|
const getRankDescription = (member: GroupMemberParser) =>
|
||||||
{
|
{
|
||||||
@@ -42,6 +43,11 @@ export const GroupMembersView: FC<{}> = props =>
|
|||||||
{
|
{
|
||||||
if(!membersData.admin || (member.rank === GroupRank.OWNER)) return;
|
if(!membersData.admin || (member.rank === GroupRank.OWNER)) return;
|
||||||
|
|
||||||
|
const key = `admin_${member.id}`;
|
||||||
|
if(pendingActionsRef.current.has(key)) return;
|
||||||
|
pendingActionsRef.current.add(key);
|
||||||
|
setTimeout(() => pendingActionsRef.current.delete(key), 2000);
|
||||||
|
|
||||||
if(member.rank !== GroupRank.ADMIN) SendMessageComposer(new GroupAdminGiveComposer(membersData.groupId, member.id));
|
if(member.rank !== GroupRank.ADMIN) SendMessageComposer(new GroupAdminGiveComposer(membersData.groupId, member.id));
|
||||||
else SendMessageComposer(new GroupAdminTakeComposer(membersData.groupId, member.id));
|
else SendMessageComposer(new GroupAdminTakeComposer(membersData.groupId, member.id));
|
||||||
|
|
||||||
@@ -52,6 +58,11 @@ export const GroupMembersView: FC<{}> = props =>
|
|||||||
{
|
{
|
||||||
if(!membersData.admin || (member.rank !== GroupRank.REQUESTED)) return;
|
if(!membersData.admin || (member.rank !== GroupRank.REQUESTED)) return;
|
||||||
|
|
||||||
|
const key = `accept_${member.id}`;
|
||||||
|
if(pendingActionsRef.current.has(key)) return;
|
||||||
|
pendingActionsRef.current.add(key);
|
||||||
|
setTimeout(() => pendingActionsRef.current.delete(key), 2000);
|
||||||
|
|
||||||
SendMessageComposer(new GroupMembershipAcceptComposer(membersData.groupId, member.id));
|
SendMessageComposer(new GroupMembershipAcceptComposer(membersData.groupId, member.id));
|
||||||
|
|
||||||
refreshMembers();
|
refreshMembers();
|
||||||
@@ -61,6 +72,11 @@ export const GroupMembersView: FC<{}> = props =>
|
|||||||
{
|
{
|
||||||
if(!membersData.admin) return;
|
if(!membersData.admin) return;
|
||||||
|
|
||||||
|
const key = `remove_${member.id}`;
|
||||||
|
if(pendingActionsRef.current.has(key)) return;
|
||||||
|
pendingActionsRef.current.add(key);
|
||||||
|
setTimeout(() => pendingActionsRef.current.delete(key), 2000);
|
||||||
|
|
||||||
if(member.rank === GroupRank.REQUESTED)
|
if(member.rank === GroupRank.REQUESTED)
|
||||||
{
|
{
|
||||||
SendMessageComposer(new GroupMembershipDeclineComposer(membersData.groupId, member.id));
|
SendMessageComposer(new GroupMembershipDeclineComposer(membersData.groupId, member.id));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IssueMessageData, ReleaseIssuesMessageComposer } from '@nitrots/nitro-renderer';
|
import { IssueMessageData, ReleaseIssuesMessageComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC } from 'react';
|
import { FC, useRef } from 'react';
|
||||||
import { SendMessageComposer } from '../../../../api';
|
import { SendMessageComposer } from '../../../../api';
|
||||||
import { Button, Column, Grid } from '../../../../common';
|
import { Button, Column, Grid } from '../../../../common';
|
||||||
|
|
||||||
@@ -12,6 +12,17 @@ interface ModToolsMyIssuesTabViewProps
|
|||||||
export const ModToolsMyIssuesTabView: FC<ModToolsMyIssuesTabViewProps> = props =>
|
export const ModToolsMyIssuesTabView: FC<ModToolsMyIssuesTabViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { myIssues = null, handleIssue = null } = props;
|
const { myIssues = null, handleIssue = null } = props;
|
||||||
|
const pendingReleasesRef = useRef<Set<number>>(new Set());
|
||||||
|
|
||||||
|
const releaseIssue = (issueId: number) =>
|
||||||
|
{
|
||||||
|
if(pendingReleasesRef.current.has(issueId)) return;
|
||||||
|
|
||||||
|
pendingReleasesRef.current.add(issueId);
|
||||||
|
SendMessageComposer(new ReleaseIssuesMessageComposer([ issueId ]));
|
||||||
|
|
||||||
|
setTimeout(() => pendingReleasesRef.current.delete(issueId), 2000);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column gap={ 0 } overflow="hidden">
|
<Column gap={ 0 } overflow="hidden">
|
||||||
@@ -36,7 +47,7 @@ export const ModToolsMyIssuesTabView: FC<ModToolsMyIssuesTabViewProps> = props =
|
|||||||
<Button variant="primary" onClick={ event => handleIssue(issue.issueId) }>Handle</Button>
|
<Button variant="primary" onClick={ event => handleIssue(issue.issueId) }>Handle</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2">
|
<div className="col-span-2">
|
||||||
<Button variant="danger" onClick={ event => SendMessageComposer(new ReleaseIssuesMessageComposer([ issue.issueId ])) }>Release</Button>
|
<Button variant="danger" onClick={ () => releaseIssue(issue.issueId) }>Release</Button>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IssueMessageData, PickIssuesMessageComposer } from '@nitrots/nitro-renderer';
|
import { IssueMessageData, PickIssuesMessageComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC } from 'react';
|
import { FC, useRef } from 'react';
|
||||||
import { SendMessageComposer } from '../../../../api';
|
import { SendMessageComposer } from '../../../../api';
|
||||||
import { Button, Column, Grid } from '../../../../common';
|
import { Button, Column, Grid } from '../../../../common';
|
||||||
|
|
||||||
@@ -11,6 +11,17 @@ interface ModToolsOpenIssuesTabViewProps
|
|||||||
export const ModToolsOpenIssuesTabView: FC<ModToolsOpenIssuesTabViewProps> = props =>
|
export const ModToolsOpenIssuesTabView: FC<ModToolsOpenIssuesTabViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { openIssues = null } = props;
|
const { openIssues = null } = props;
|
||||||
|
const pendingPicksRef = useRef<Set<number>>(new Set());
|
||||||
|
|
||||||
|
const pickIssue = (issueId: number) =>
|
||||||
|
{
|
||||||
|
if(pendingPicksRef.current.has(issueId)) return;
|
||||||
|
|
||||||
|
pendingPicksRef.current.add(issueId);
|
||||||
|
SendMessageComposer(new PickIssuesMessageComposer([ issueId ], false, 0, 'pick issue button'));
|
||||||
|
|
||||||
|
setTimeout(() => pendingPicksRef.current.delete(issueId), 2000);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column gap={ 0 } overflow="hidden">
|
<Column gap={ 0 } overflow="hidden">
|
||||||
@@ -31,7 +42,7 @@ export const ModToolsOpenIssuesTabView: FC<ModToolsOpenIssuesTabViewProps> = pro
|
|||||||
<div className="col-span-3">{ issue.reportedUserName }</div>
|
<div className="col-span-3">{ issue.reportedUserName }</div>
|
||||||
<div className="col-span-4">{ new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString() }</div>
|
<div className="col-span-4">{ new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString() }</div>
|
||||||
<div className="col-span-3">
|
<div className="col-span-3">
|
||||||
<Button variant="success" onClick={ event => SendMessageComposer(new PickIssuesMessageComposer([ issue.issueId ], false, 0, 'pick issue button')) }>Pick Issue</Button>
|
<Button variant="success" onClick={ () => pickIssue(issue.issueId) }>Pick Issue</Button>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CallForHelpTopicData, DefaultSanctionMessageComposer, ModAlertMessageComposer, ModBanMessageComposer, ModKickMessageComposer, ModMessageMessageComposer, ModMuteMessageComposer, ModTradingLockMessageComposer } from '@nitrots/nitro-renderer';
|
import { CallForHelpTopicData, DefaultSanctionMessageComposer, ModAlertMessageComposer, ModBanMessageComposer, ModKickMessageComposer, ModMessageMessageComposer, ModMuteMessageComposer, ModTradingLockMessageComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useMemo, useState } from 'react';
|
import { FC, useMemo, useRef, useState } from 'react';
|
||||||
import { ISelectedUser, LocalizeText, ModActionDefinition, NotificationAlertType, SendMessageComposer } from '../../../../api';
|
import { ISelectedUser, LocalizeText, ModActionDefinition, NotificationAlertType, SendMessageComposer } from '../../../../api';
|
||||||
import { Button, DraggableWindowPosition, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
|
import { Button, DraggableWindowPosition, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
|
||||||
import { useModTools, useNotification } from '../../../../hooks';
|
import { useModTools, useNotification } from '../../../../hooks';
|
||||||
@@ -33,6 +33,7 @@ export const ModToolsUserModActionView: FC<ModToolsUserModActionViewProps> = pro
|
|||||||
const [ message, setMessage ] = useState<string>('');
|
const [ message, setMessage ] = useState<string>('');
|
||||||
const { cfhCategories = null, settings = null } = useModTools();
|
const { cfhCategories = null, settings = null } = useModTools();
|
||||||
const { simpleAlert = null } = useNotification();
|
const { simpleAlert = null } = useNotification();
|
||||||
|
const isSendingRef = useRef<boolean>(false);
|
||||||
|
|
||||||
const topics = useMemo(() =>
|
const topics = useMemo(() =>
|
||||||
{
|
{
|
||||||
@@ -53,6 +54,8 @@ export const ModToolsUserModActionView: FC<ModToolsUserModActionViewProps> = pro
|
|||||||
|
|
||||||
const sendDefaultSanction = () =>
|
const sendDefaultSanction = () =>
|
||||||
{
|
{
|
||||||
|
if(isSendingRef.current) return;
|
||||||
|
|
||||||
let errorMessage: string = null;
|
let errorMessage: string = null;
|
||||||
|
|
||||||
const category = topics[selectedTopic];
|
const category = topics[selectedTopic];
|
||||||
@@ -63,6 +66,8 @@ export const ModToolsUserModActionView: FC<ModToolsUserModActionViewProps> = pro
|
|||||||
|
|
||||||
const messageOrDefault = (message.trim().length === 0) ? LocalizeText(`help.cfh.topic.${ category.id }`) : message;
|
const messageOrDefault = (message.trim().length === 0) ? LocalizeText(`help.cfh.topic.${ category.id }`) : message;
|
||||||
|
|
||||||
|
isSendingRef.current = true;
|
||||||
|
|
||||||
SendMessageComposer(new DefaultSanctionMessageComposer(user.userId, selectedTopic, messageOrDefault));
|
SendMessageComposer(new DefaultSanctionMessageComposer(user.userId, selectedTopic, messageOrDefault));
|
||||||
|
|
||||||
onCloseClick();
|
onCloseClick();
|
||||||
@@ -70,6 +75,8 @@ export const ModToolsUserModActionView: FC<ModToolsUserModActionViewProps> = pro
|
|||||||
|
|
||||||
const sendSanction = () =>
|
const sendSanction = () =>
|
||||||
{
|
{
|
||||||
|
if(isSendingRef.current) return;
|
||||||
|
|
||||||
let errorMessage: string = null;
|
let errorMessage: string = null;
|
||||||
|
|
||||||
const category = topics[selectedTopic];
|
const category = topics[selectedTopic];
|
||||||
@@ -145,6 +152,8 @@ export const ModToolsUserModActionView: FC<ModToolsUserModActionViewProps> = pro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSendingRef.current = true;
|
||||||
|
|
||||||
onCloseClick();
|
onCloseClick();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import { Button, Flex, Grid, LayoutCurrencyIcon, LayoutGridItem, Text } from '..
|
|||||||
import { useNavigator } from '../../../hooks';
|
import { useNavigator } from '../../../hooks';
|
||||||
import { NitroInput } from '../../../layout';
|
import { NitroInput } from '../../../layout';
|
||||||
|
|
||||||
|
let isCreatingRoom = false;
|
||||||
|
let createRoomTimeout: ReturnType<typeof setTimeout> = null;
|
||||||
|
|
||||||
export const NavigatorRoomCreatorView: FC<{}> = props =>
|
export const NavigatorRoomCreatorView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ maxVisitorsList, setMaxVisitorsList ] = useState<number[]>(null);
|
const [ maxVisitorsList, setMaxVisitorsList ] = useState<number[]>(null);
|
||||||
@@ -16,6 +19,7 @@ export const NavigatorRoomCreatorView: FC<{}> = props =>
|
|||||||
const [ tradesSetting, setTradesSetting ] = useState<number>(0);
|
const [ tradesSetting, setTradesSetting ] = useState<number>(0);
|
||||||
const [ roomModels, setRoomModels ] = useState<IRoomModel[]>([]);
|
const [ roomModels, setRoomModels ] = useState<IRoomModel[]>([]);
|
||||||
const [ selectedModelName, setSelectedModelName ] = useState<string>('');
|
const [ selectedModelName, setSelectedModelName ] = useState<string>('');
|
||||||
|
const [ isCreating, setIsCreating ] = useState<boolean>(isCreatingRoom);
|
||||||
const { categories = null } = useNavigator();
|
const { categories = null } = useNavigator();
|
||||||
|
|
||||||
const hcDisabled = GetConfigurationValue<boolean>('hc.disabled', false);
|
const hcDisabled = GetConfigurationValue<boolean>('hc.disabled', false);
|
||||||
@@ -31,7 +35,19 @@ export const NavigatorRoomCreatorView: FC<{}> = props =>
|
|||||||
|
|
||||||
const createRoom = () =>
|
const createRoom = () =>
|
||||||
{
|
{
|
||||||
|
if(isCreatingRoom) return;
|
||||||
|
|
||||||
|
isCreatingRoom = true;
|
||||||
|
setIsCreating(true);
|
||||||
|
|
||||||
SendMessageComposer(new CreateFlatMessageComposer(name, description, 'model_' + selectedModelName, Number(category), Number(visitorsCount), tradesSetting));
|
SendMessageComposer(new CreateFlatMessageComposer(name, description, 'model_' + selectedModelName, Number(category), Number(visitorsCount), tradesSetting));
|
||||||
|
|
||||||
|
if(createRoomTimeout) clearTimeout(createRoomTimeout);
|
||||||
|
createRoomTimeout = setTimeout(() =>
|
||||||
|
{
|
||||||
|
isCreatingRoom = false;
|
||||||
|
setIsCreating(false);
|
||||||
|
}, 5000);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
@@ -117,7 +133,7 @@ export const NavigatorRoomCreatorView: FC<{}> = props =>
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Button fullWidth disabled={ (!name || (name.length < 3)) } variant={ (!name || (name.length < 3)) ? 'danger' : 'success' } onClick={ createRoom }>{ LocalizeText('navigator.createroom.create') }</Button>
|
<Button fullWidth disabled={ isCreating || !name || (name.length < 3) } variant={ (isCreating || !name || (name.length < 3)) ? 'danger' : 'success' } onClick={ createRoom }>{ LocalizeText('navigator.createroom.create') }</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+15
-4
@@ -1,5 +1,5 @@
|
|||||||
import { FlatControllerAddedEvent, FlatControllerRemovedEvent, FlatControllersEvent, RemoveAllRightsMessageComposer, RoomGiveRightsComposer, RoomTakeRightsComposer, RoomUsersWithRightsComposer } from '@nitrots/nitro-renderer';
|
import { FlatControllerAddedEvent, FlatControllerRemovedEvent, FlatControllersEvent, RemoveAllRightsMessageComposer, RoomGiveRightsComposer, RoomTakeRightsComposer, RoomUsersWithRightsComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useRef, useState } from 'react';
|
||||||
import { IRoomData, LocalizeText, SendMessageComposer } from '../../../../api';
|
import { IRoomData, LocalizeText, SendMessageComposer } from '../../../../api';
|
||||||
import { Button, Column, Flex, Grid, Text, UserProfileIconView } from '../../../../common';
|
import { Button, Column, Flex, Grid, Text, UserProfileIconView } from '../../../../common';
|
||||||
import { useFriends, useMessageEvent } from '../../../../hooks';
|
import { useFriends, useMessageEvent } from '../../../../hooks';
|
||||||
@@ -18,6 +18,17 @@ export const NavigatorRoomSettingsRightsTabView: FC<NavigatorRoomSettingsTabView
|
|||||||
const { roomData = null } = props;
|
const { roomData = null } = props;
|
||||||
const [ usersWithRights, setUsersWithRights ] = useState<Map<number, string>>(new Map());
|
const [ usersWithRights, setUsersWithRights ] = useState<Map<number, string>>(new Map());
|
||||||
const { onlineFriends = [], offlineFriends = [] } = useFriends();
|
const { onlineFriends = [], offlineFriends = [] } = useFriends();
|
||||||
|
const pendingActionsRef = useRef<Set<string>>(new Set());
|
||||||
|
|
||||||
|
const guardedSend = (key: string, composer: any) =>
|
||||||
|
{
|
||||||
|
if(pendingActionsRef.current.has(key)) return;
|
||||||
|
|
||||||
|
pendingActionsRef.current.add(key);
|
||||||
|
SendMessageComposer(composer);
|
||||||
|
|
||||||
|
setTimeout(() => pendingActionsRef.current.delete(key), 2000);
|
||||||
|
};
|
||||||
|
|
||||||
const allFriendsRaw = [ ...onlineFriends, ...offlineFriends ];
|
const allFriendsRaw = [ ...onlineFriends, ...offlineFriends ];
|
||||||
|
|
||||||
@@ -115,7 +126,7 @@ export const NavigatorRoomSettingsRightsTabView: FC<NavigatorRoomSettingsTabView
|
|||||||
<Text
|
<Text
|
||||||
pointer
|
pointer
|
||||||
grow
|
grow
|
||||||
onClick={ () => SendMessageComposer(new RoomTakeRightsComposer(id)) }>
|
onClick={ () => guardedSend(`take_${id}`, new RoomTakeRightsComposer(id)) }>
|
||||||
{ name }
|
{ name }
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -127,7 +138,7 @@ export const NavigatorRoomSettingsRightsTabView: FC<NavigatorRoomSettingsTabView
|
|||||||
<Button
|
<Button
|
||||||
variant="danger"
|
variant="danger"
|
||||||
disabled={ !filteredUsersWithRights.size }
|
disabled={ !filteredUsersWithRights.size }
|
||||||
onClick={ () => roomData && SendMessageComposer(new RemoveAllRightsMessageComposer(roomData.roomId)) }>
|
onClick={ () => roomData && guardedSend('removeAll', new RemoveAllRightsMessageComposer(roomData.roomId)) }>
|
||||||
{ LocalizeText('navigator.flatctrls.clear') }
|
{ LocalizeText('navigator.flatctrls.clear') }
|
||||||
</Button>
|
</Button>
|
||||||
</Column>
|
</Column>
|
||||||
@@ -154,7 +165,7 @@ export const NavigatorRoomSettingsRightsTabView: FC<NavigatorRoomSettingsTabView
|
|||||||
<Text
|
<Text
|
||||||
pointer
|
pointer
|
||||||
grow
|
grow
|
||||||
onClick={ () => SendMessageComposer(new RoomGiveRightsComposer(friend.id)) }>
|
onClick={ () => guardedSend(`give_${friend.id}`, new RoomGiveRightsComposer(friend.id)) }>
|
||||||
{ friend.name }
|
{ friend.name }
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
Reference in New Issue
Block a user