mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-23 06:40:50 +01:00
More catalog updates
This commit is contained in:
parent
9a2a9df3b1
commit
35efd87188
@ -17,7 +17,7 @@ import { CatalogReducer, initialCatalog } from './reducers/CatalogReducer';
|
||||
import { CatalogGiftView } from './views/gift/CatalogGiftView';
|
||||
import { ACTIVE_PAGES, CatalogNavigationView } from './views/navigation/CatalogNavigationView';
|
||||
import { CatalogPageView } from './views/page/CatalogPageView';
|
||||
import { MarketplacePostOfferView } from './views/page/layout/marketplace/post-offer/MarketplacePostOfferView';
|
||||
import { MarketplacePostOfferView } from './views/page/layout/marketplace/MarketplacePostOfferView';
|
||||
|
||||
export const CatalogView: FC<{}> = props =>
|
||||
{
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { CatalogPageMessageParser } from '@nitrots/nitro-renderer';
|
||||
import { FC } from 'react';
|
||||
import { NitroLayoutFlexColumn } from '../../../../layout';
|
||||
import { GetCatalogPageImage, GetCatalogPageText } from '../../common/CatalogUtilities';
|
||||
import { CatalogPageDetailsViewProps } from './CatalogPageDetailsView.types';
|
||||
|
||||
export interface CatalogPageDetailsViewProps
|
||||
{
|
||||
pageParser: CatalogPageMessageParser;
|
||||
}
|
||||
|
||||
export const CatalogPageDetailsView: FC<CatalogPageDetailsViewProps> = props =>
|
||||
{
|
||||
|
@ -1,6 +0,0 @@
|
||||
import { CatalogPageMessageParser } from '@nitrots/nitro-renderer';
|
||||
|
||||
export interface CatalogPageDetailsViewProps
|
||||
{
|
||||
pageParser: CatalogPageMessageParser;
|
||||
}
|
@ -28,5 +28,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
@import './marketplace/marketplace-item/MarketplaceItemView';
|
||||
@import './marketplace/post-offer/MarketplacePostOfferView';
|
||||
.nitro-catalog-layout-marketplace-grid {
|
||||
|
||||
.layout-grid-item {
|
||||
height: 75px !important;
|
||||
max-height: 75px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.nitro-catalog-layout-marketplace-post-offer {
|
||||
width: $marketplace-post-offer-width;
|
||||
height: $marketplace-post-offer-height;
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ import { CatalogLayouGuildCustomFurniView } from './guild-custom-furni/CatalogLa
|
||||
import { CatalogLayouGuildForumView } from './guild-forum/CatalogLayoutGuildForumView';
|
||||
import { CatalogLayouGuildFrontpageView } from './guild-frontpage/CatalogLayoutGuildFrontpageView';
|
||||
import { CatalogLayoutInfoLoyaltyView } from './info-loyalty/CatalogLayoutInfoLoyaltyView';
|
||||
import { CatalogLayoutMarketplaceOwnItemsView } from './marketplace/own-items/CatalogLayoutMarketplaceOwnItemsView';
|
||||
import { CatalogLayoutMarketplacePublicItemsView } from './marketplace/public-items/CatalogLayoutMarketplacePublicItemsView';
|
||||
import { CatalogLayoutMarketplaceOwnItemsView } from './marketplace/CatalogLayoutMarketplaceOwnItemsView';
|
||||
import { CatalogLayoutMarketplacePublicItemsView } from './marketplace/CatalogLayoutMarketplacePublicItemsView';
|
||||
import { CatalogLayoutPetView } from './pets/CatalogLayoutPetView';
|
||||
import { CatalogLayoutPets2View } from './pets2/CatalogLayoutPets2View';
|
||||
import { CatalogLayoutPets3View } from './pets3/CatalogLayoutPets3View';
|
||||
|
@ -0,0 +1,102 @@
|
||||
import { FC, useCallback, useMemo } from 'react';
|
||||
import { GetRoomEngine, LocalizeText } from '../../../../../../api';
|
||||
import { Button } from '../../../../../../common/Button';
|
||||
import { Column } from '../../../../../../common/Column';
|
||||
import { LayoutGridItem } from '../../../../../../common/layout/LayoutGridItem';
|
||||
import { LayoutImage } from '../../../../../../common/layout/LayoutImage';
|
||||
import { Text } from '../../../../../../common/Text';
|
||||
import { MarketplaceOfferData } from './common/MarketplaceOfferData';
|
||||
import { MarketPlaceOfferState } from './common/MarketplaceOfferState';
|
||||
|
||||
export interface MarketplaceItemViewProps
|
||||
{
|
||||
offerData: MarketplaceOfferData;
|
||||
type?: number;
|
||||
onClick(offerData: MarketplaceOfferData): void;
|
||||
}
|
||||
|
||||
export const OWN_OFFER = 1;
|
||||
export const PUBLIC_OFFER = 2;
|
||||
|
||||
export const CatalogLayoutMarketplaceItemView: FC<MarketplaceItemViewProps> = props =>
|
||||
{
|
||||
const { offerData = null, type = PUBLIC_OFFER, onClick = null } = props;
|
||||
|
||||
const getImageUrlForOffer = useCallback( () =>
|
||||
{
|
||||
if(!offerData) return '';
|
||||
|
||||
switch(offerData.furniType)
|
||||
{
|
||||
case MarketplaceOfferData.TYPE_FLOOR:
|
||||
return GetRoomEngine().getFurnitureFloorIconUrl(offerData.furniId);
|
||||
case MarketplaceOfferData.TYPE_WALL:
|
||||
return GetRoomEngine().getFurnitureWallIconUrl(offerData.furniId, offerData.extraData);
|
||||
}
|
||||
|
||||
return '';
|
||||
}, [offerData]);
|
||||
|
||||
const getMarketplaceOfferTitle = useMemo(() =>
|
||||
{
|
||||
if(!offerData) return '';
|
||||
|
||||
// desc
|
||||
return LocalizeText(((offerData.furniType === 2) ? 'wallItem' : 'roomItem') + `.name.${ offerData.furniId }`);
|
||||
}, [ offerData ]);
|
||||
|
||||
const offerTime = useCallback( () =>
|
||||
{
|
||||
if(!offerData) return '';
|
||||
|
||||
if(offerData.status === MarketPlaceOfferState.SOLD) return LocalizeText('catalog.marketplace.offer.sold');
|
||||
|
||||
if(offerData.timeLeftMinutes <= 0) return LocalizeText('catalog.marketplace.offer.expired');
|
||||
|
||||
const time = Math.max(1, offerData.timeLeftMinutes);
|
||||
const hours = Math.floor(time / 60);
|
||||
const minutes = time - (hours * 60);
|
||||
|
||||
let text = minutes + ' ' + LocalizeText('catalog.marketplace.offer.minutes');
|
||||
if(hours > 0)
|
||||
{
|
||||
text = hours + ' ' + LocalizeText('catalog.marketplace.offer.hours') + ' ' + text;
|
||||
}
|
||||
|
||||
return LocalizeText('catalog.marketplace.offer.time_left', ['time'], [text] );
|
||||
}, [offerData]);
|
||||
|
||||
return (
|
||||
<LayoutGridItem center={ false } column={ false } alignItems="center" className="p-1">
|
||||
<LayoutImage imageUrl={ getImageUrlForOffer() } fit={ false } style={ { width: 50, height: 50 } } />
|
||||
<Column grow gap={ 0 }>
|
||||
<Text fontWeight="bold">{ getMarketplaceOfferTitle }</Text>
|
||||
{ (type === OWN_OFFER) &&
|
||||
<>
|
||||
<Text>{ LocalizeText('catalog.marketplace.offer.price_own_item', [ 'price' ], [ offerData.price.toString() ]) }</Text>
|
||||
<Text>{ offerTime() }</Text>
|
||||
</> }
|
||||
{ (type === PUBLIC_OFFER) &&
|
||||
<>
|
||||
<Text>{ LocalizeText('catalog.marketplace.offer.price_public_item', ['price', 'average'], [offerData.price.toString(), offerData.averagePrice.toString() ]) }</Text>
|
||||
<Text>{ LocalizeText('catalog.marketplace.offer_count', ['count'], [offerData.offerCount.toString()]) }</Text>
|
||||
</> }
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
{ ((type === OWN_OFFER) && (offerData.status !== MarketPlaceOfferState.SOLD)) &&
|
||||
<Button variant="secondary" size="sm" onClick={ () => onClick(offerData) }>
|
||||
{ LocalizeText('catalog.marketplace.offer.pick') }
|
||||
</Button> }
|
||||
{ type === PUBLIC_OFFER &&
|
||||
<>
|
||||
<Button variant="secondary" size="sm" onClick={ () => onClick(offerData) }>
|
||||
{ LocalizeText('buy') }
|
||||
</Button>
|
||||
<Button variant="secondary" size="sm" disabled>
|
||||
{ LocalizeText('catalog.marketplace.view_more') }
|
||||
</Button>
|
||||
</> }
|
||||
</Column>
|
||||
</LayoutGridItem>
|
||||
);
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
import { CancelMarketplaceOfferMessageComposer, GetMarketplaceOwnOffersMessageComposer, MarketplaceCancelOfferResultEvent, MarketplaceOwnOffersEvent, RedeemMarketplaceOfferCreditsMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useMemo, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../../../api';
|
||||
import { Button } from '../../../../../../common/Button';
|
||||
import { Column } from '../../../../../../common/Column';
|
||||
import { Grid } from '../../../../../../common/Grid';
|
||||
import { Text } from '../../../../../../common/Text';
|
||||
import { BatchUpdates, CreateMessageHook, SendMessageHook, UseMountEffect } from '../../../../../../hooks';
|
||||
import { NotificationAlertType } from '../../../../../../views/notification-center/common/NotificationAlertType';
|
||||
import { NotificationUtilities } from '../../../../../../views/notification-center/common/NotificationUtilities';
|
||||
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||
import { CatalogLayoutMarketplaceItemView, OWN_OFFER } from './CatalogLayoutMarketplaceItemView';
|
||||
import { MarketplaceOfferData } from './common/MarketplaceOfferData';
|
||||
import { MarketPlaceOfferState } from './common/MarketplaceOfferState';
|
||||
|
||||
export const CatalogLayoutMarketplaceOwnItemsView: FC<CatalogLayoutProps> = props =>
|
||||
{
|
||||
const [ creditsWaiting, setCreditsWaiting ] = useState(0);
|
||||
const [ offers, setOffers ] = useState<MarketplaceOfferData[]>([]);
|
||||
|
||||
const onMarketPlaceOwnOffersEvent = useCallback((event: MarketplaceOwnOffersEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser) return;
|
||||
|
||||
const offers = parser.offers.map(offer =>
|
||||
{
|
||||
const newOffer = new MarketplaceOfferData(offer.offerId, offer.furniId, offer.furniType, offer.extraData, offer.stuffData, offer.price, offer.status, offer.averagePrice, offer.offerCount);
|
||||
|
||||
newOffer.timeLeftMinutes = offer.timeLeftMinutes;
|
||||
|
||||
return newOffer;
|
||||
});
|
||||
|
||||
BatchUpdates(() =>
|
||||
{
|
||||
setCreditsWaiting(parser.creditsWaiting);
|
||||
setOffers(offers);
|
||||
});
|
||||
}, []);
|
||||
|
||||
CreateMessageHook(MarketplaceOwnOffersEvent, onMarketPlaceOwnOffersEvent);
|
||||
|
||||
const onMarketplaceCancelOfferResultEvent = useCallback((event:MarketplaceCancelOfferResultEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser) return;
|
||||
|
||||
if(!parser.success)
|
||||
{
|
||||
NotificationUtilities.simpleAlert(LocalizeText('catalog.marketplace.cancel_failed'), NotificationAlertType.DEFAULT, null, null, LocalizeText('catalog.marketplace.operation_failed.topic'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setOffers(prevValue => prevValue.filter(value => (value.offerId !== parser.offerId)));
|
||||
}, []);
|
||||
|
||||
CreateMessageHook(MarketplaceCancelOfferResultEvent, onMarketplaceCancelOfferResultEvent);
|
||||
|
||||
const soldOffers = useMemo(() =>
|
||||
{
|
||||
return offers.filter(value => (value.status === MarketPlaceOfferState.SOLD));
|
||||
}, [ offers ]);
|
||||
|
||||
const redeemSoldOffers = useCallback(() =>
|
||||
{
|
||||
setOffers(prevValue =>
|
||||
{
|
||||
const idsToDelete = soldOffers.map(value => value.offerId);
|
||||
|
||||
return prevValue.filter(value => (idsToDelete.indexOf(value.offerId) === -1));
|
||||
})
|
||||
|
||||
SendMessageHook(new RedeemMarketplaceOfferCreditsMessageComposer());
|
||||
}, [ soldOffers ]);
|
||||
|
||||
const takeItemBack = (offerData: MarketplaceOfferData) =>
|
||||
{
|
||||
SendMessageHook(new CancelMarketplaceOfferMessageComposer(offerData.offerId));
|
||||
};
|
||||
|
||||
UseMountEffect(() =>
|
||||
{
|
||||
SendMessageHook(new GetMarketplaceOwnOffersMessageComposer());
|
||||
});
|
||||
|
||||
return (
|
||||
<Column>
|
||||
{ (creditsWaiting <= 0) &&
|
||||
<Text center className="bg-muted rounded p-1">
|
||||
{ LocalizeText('catalog.marketplace.redeem.no_sold_items') }
|
||||
</Text> }
|
||||
{ (creditsWaiting > 0) &&
|
||||
<Column center gap={ 1 } className="bg-muted rounded p-2">
|
||||
<Text>
|
||||
{ LocalizeText('catalog.marketplace.redeem.get_credits', ['count', 'credits'], [ soldOffers.length.toString(), creditsWaiting.toString() ]) }
|
||||
</Text>
|
||||
<Button size="sm" className="mt-1" onClick={ redeemSoldOffers }>
|
||||
{ LocalizeText('catalog.marketplace.offer.redeem') }
|
||||
</Button>
|
||||
</Column> }
|
||||
<Column gap={ 1 } overflow="hidden">
|
||||
<Text truncate shrink fontWeight="bold">
|
||||
{ LocalizeText('catalog.marketplace.items_found', [ 'count' ], [ offers.length.toString() ]) }
|
||||
</Text>
|
||||
<Grid columnCount={ 1 } overflow="auto" className="nitro-catalog-layout-marketplace-grid">
|
||||
{ (offers.length > 0) && offers.map(offer => <CatalogLayoutMarketplaceItemView key={ offer.offerId } offerData={ offer } type={ OWN_OFFER } onClick={ takeItemBack } />) }
|
||||
</Grid>
|
||||
</Column>
|
||||
</Column>
|
||||
);
|
||||
}
|
@ -1,17 +1,21 @@
|
||||
import { BuyMarketplaceOfferMessageComposer, GetMarketplaceOffersMessageComposer, MarketplaceBuyOfferResultEvent, MarketPlaceOffersEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useMemo, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../../../../api';
|
||||
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../../../../hooks';
|
||||
import { NitroCardGridView } from '../../../../../../../layout';
|
||||
import { NotificationAlertType } from '../../../../../../../views/notification-center/common/NotificationAlertType';
|
||||
import { NotificationUtilities } from '../../../../../../../views/notification-center/common/NotificationUtilities';
|
||||
import { GetCurrencyAmount } from '../../../../../../../views/purse/common/CurrencyHelper';
|
||||
import { CatalogLayoutProps } from '../../CatalogLayout.types';
|
||||
import { IMarketplaceSearchOptions } from '../common/IMarketplaceSearchOptions';
|
||||
import { MarketplaceOfferData } from '../common/MarketplaceOfferData';
|
||||
import { MarketplaceSearchType } from '../common/MarketplaceSearchType';
|
||||
import { MarketplaceItemView, PUBLIC_OFFER } from '../marketplace-item/MarketplaceItemView';
|
||||
import { SearchFormView } from './SearchFormView';
|
||||
import { LocalizeText } from '../../../../../../api';
|
||||
import { Button } from '../../../../../../common/Button';
|
||||
import { ButtonGroup } from '../../../../../../common/ButtonGroup';
|
||||
import { Column } from '../../../../../../common/Column';
|
||||
import { Grid } from '../../../../../../common/Grid';
|
||||
import { Text } from '../../../../../../common/Text';
|
||||
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../../../hooks';
|
||||
import { NotificationAlertType } from '../../../../../../views/notification-center/common/NotificationAlertType';
|
||||
import { NotificationUtilities } from '../../../../../../views/notification-center/common/NotificationUtilities';
|
||||
import { GetCurrencyAmount } from '../../../../../../views/purse/common/CurrencyHelper';
|
||||
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||
import { CatalogLayoutMarketplaceItemView, PUBLIC_OFFER } from './CatalogLayoutMarketplaceItemView';
|
||||
import { SearchFormView } from './CatalogLayoutMarketplaceSearchFormView';
|
||||
import { IMarketplaceSearchOptions } from './common/IMarketplaceSearchOptions';
|
||||
import { MarketplaceOfferData } from './common/MarketplaceOfferData';
|
||||
import { MarketplaceSearchType } from './common/MarketplaceSearchType';
|
||||
|
||||
const SORT_TYPES_VALUE = [1, 2];
|
||||
const SORT_TYPES_ACTIVITY = [3, 4, 5, 6];
|
||||
@ -141,26 +145,30 @@ export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplac
|
||||
CreateMessageHook(MarketPlaceOffersEvent, onMarketPlaceOffersEvent);
|
||||
CreateMessageHook(MarketplaceBuyOfferResultEvent, onMarketplaceBuyOfferResultEvent);
|
||||
|
||||
return (<>
|
||||
<div className="btn-group" role="group">
|
||||
<button type="button" className={`btn btn-primary ${searchType === MarketplaceSearchType.BY_ACTIVITY ? 'active' : ''}`} onClick={() => setSearchType(MarketplaceSearchType.BY_ACTIVITY)}>
|
||||
{ LocalizeText('catalog.marketplace.search_by_activity') }
|
||||
</button>
|
||||
<button type="button" className={`btn btn-primary ${searchType === MarketplaceSearchType.BY_VALUE ? 'active' : ''}`} onClick={() => setSearchType(MarketplaceSearchType.BY_VALUE)}>
|
||||
{ LocalizeText('catalog.marketplace.search_by_value') }
|
||||
</button>
|
||||
<button type="button" className={`btn btn-primary ${searchType === MarketplaceSearchType.ADVANCED ? 'active' : ''}`} onClick={() => setSearchType(MarketplaceSearchType.ADVANCED)}>
|
||||
{ LocalizeText('catalog.marketplace.search_advanced') }
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<SearchFormView sortTypes={ getSortTypes } searchType={ searchType } onSearch={ requestOffers }/>
|
||||
|
||||
<div className='text-black'>{LocalizeText('catalog.marketplace.items_found', ['count'], [offers.size.toString()])}</div>
|
||||
<NitroCardGridView columns={1} className='text-black'>
|
||||
{
|
||||
Array.from(offers.values()).map( (entry, index) => <MarketplaceItemView key={ index } offerData={ entry } type={ PUBLIC_OFFER } onClick={purchaseItem} />)
|
||||
}
|
||||
</NitroCardGridView>
|
||||
</>);
|
||||
return (
|
||||
<>
|
||||
<ButtonGroup>
|
||||
<Button size="sm" active={ (searchType === MarketplaceSearchType.BY_ACTIVITY) } onClick={ () => setSearchType(MarketplaceSearchType.BY_ACTIVITY) }>
|
||||
{ LocalizeText('catalog.marketplace.search_by_activity') }
|
||||
</Button>
|
||||
<Button size="sm" active={ (searchType === MarketplaceSearchType.BY_VALUE) } onClick={ () => setSearchType(MarketplaceSearchType.BY_VALUE) }>
|
||||
{ LocalizeText('catalog.marketplace.search_by_value') }
|
||||
</Button>
|
||||
<Button size="sm" active={ (searchType === MarketplaceSearchType.ADVANCED) } onClick={ () => setSearchType(MarketplaceSearchType.ADVANCED) }>
|
||||
{ LocalizeText('catalog.marketplace.search_advanced') }
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<SearchFormView sortTypes={ getSortTypes } searchType={ searchType } onSearch={ requestOffers } />
|
||||
<Column gap={ 1 } overflow="hidden">
|
||||
<Text truncate shrink fontWeight="bold">
|
||||
{ LocalizeText('catalog.marketplace.items_found', [ 'count' ], [ offers.size.toString() ]) }
|
||||
</Text>
|
||||
<Grid columnCount={ 1 } className="nitro-catalog-layout-marketplace-grid" overflow="auto">
|
||||
{
|
||||
Array.from(offers.values()).map( (entry, index) => <CatalogLayoutMarketplaceItemView key={ index } offerData={ entry } type={ PUBLIC_OFFER } onClick={purchaseItem} />)
|
||||
}
|
||||
</Grid>
|
||||
</Column>
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../../../api';
|
||||
import { Button } from '../../../../../../common/Button';
|
||||
import { Column } from '../../../../../../common/Column';
|
||||
import { Flex } from '../../../../../../common/Flex';
|
||||
import { Text } from '../../../../../../common/Text';
|
||||
import { IMarketplaceSearchOptions } from './common/IMarketplaceSearchOptions';
|
||||
import { MarketplaceSearchType } from './common/MarketplaceSearchType';
|
||||
|
||||
export interface SearchFormViewProps
|
||||
{
|
||||
searchType: number;
|
||||
sortTypes: number[];
|
||||
onSearch(options: IMarketplaceSearchOptions): void;
|
||||
}
|
||||
|
||||
export const SearchFormView: FC<SearchFormViewProps> = props =>
|
||||
{
|
||||
const { searchType = null, sortTypes = null, onSearch = null } = props;
|
||||
const [ sortType, setSortType ] = useState(sortTypes ? sortTypes[0] : 3); // first item of SORT_TYPES_ACTIVITY
|
||||
const [ searchQuery, setSearchQuery ] = useState('');
|
||||
const [ min, setMin ] = useState(0);
|
||||
const [ max, setMax ] = useState(0);
|
||||
|
||||
const onSortTypeChange = useCallback((sortType: number) =>
|
||||
{
|
||||
setSortType(sortType);
|
||||
|
||||
if((searchType === MarketplaceSearchType.BY_ACTIVITY) || (searchType === MarketplaceSearchType.BY_VALUE)) onSearch({ minPrice: -1, maxPrice: -1, query: '', type: sortType });
|
||||
}, [ onSearch, searchType ]);
|
||||
|
||||
const onClickSearch = useCallback(() =>
|
||||
{
|
||||
const minPrice = ((min > 0) ? min : -1);
|
||||
const maxPrice = ((max > 0) ? max : -1);
|
||||
|
||||
onSearch({ minPrice: minPrice, maxPrice: maxPrice, type: sortType, query: searchQuery })
|
||||
}, [ max, min, onSearch, searchQuery, sortType ]);
|
||||
|
||||
useEffect( () =>
|
||||
{
|
||||
if(!sortTypes || !sortTypes.length) return;
|
||||
|
||||
const sortType = sortTypes[0];
|
||||
|
||||
setSortType(sortType);
|
||||
|
||||
if(searchType === MarketplaceSearchType.BY_ACTIVITY || MarketplaceSearchType.BY_VALUE === searchType) onSearch({ minPrice: -1, maxPrice: -1, query: '', type: sortType });
|
||||
}, [onSearch, searchType, sortTypes]);
|
||||
|
||||
return (
|
||||
<Column gap={ 1 }>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<Text className="col-3">{ LocalizeText('catalog.marketplace.sort_order') }</Text>
|
||||
<select className="form-select form-select-sm" value={ sortType } onChange={ event => onSortTypeChange(parseInt(event.target.value)) }>
|
||||
{ sortTypes.map(type => <option key={ type } value={ type }>{ LocalizeText(`catalog.marketplace.sort.${ type }`) }</option>) }
|
||||
</select>
|
||||
</Flex>
|
||||
{ searchType === MarketplaceSearchType.ADVANCED &&
|
||||
<>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<Text className="col-3">{ LocalizeText('catalog.marketplace.search_name') }</Text>
|
||||
<input className="form-control form-control-sm" type="text" value={ searchQuery } onChange={ event => setSearchQuery(event.target.value) }/>
|
||||
</Flex>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<Text className="col-3">{ LocalizeText('catalog.marketplace.search_price') }</Text>
|
||||
<Flex fullWidth gap={ 1 }>
|
||||
<input className="form-control form-control-sm" type="number" min={ 0 } value={ min } onChange={ event => setMin(event.target.valueAsNumber) } />
|
||||
<input className="form-control form-control-sm" type="number" min={ 0 } value={ max } onChange={ event => setMax(event.target.valueAsNumber) } />
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Button variant="secondary" size="sm" className="mx-auto" onClick={ onClickSearch }>{ LocalizeText('generic.search') }</Button>
|
||||
</> }
|
||||
</Column>
|
||||
);
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
import { ImageResult, MakeOfferMessageComposer, Vector3d } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { GetRoomEngine, LocalizeText } from '../../../../../../api';
|
||||
import { Base } from '../../../../../../common/Base';
|
||||
import { Button } from '../../../../../../common/Button';
|
||||
import { Column } from '../../../../../../common/Column';
|
||||
import { Grid } from '../../../../../../common/Grid';
|
||||
import { LayoutImage } from '../../../../../../common/layout/LayoutImage';
|
||||
import { Text } from '../../../../../../common/Text';
|
||||
import { CatalogPostMarketplaceOfferEvent } from '../../../../../../events/catalog/CatalogPostMarketplaceOfferEvent';
|
||||
import { BatchUpdates, SendMessageHook, useUiEvent } from '../../../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../../layout';
|
||||
import { NotificationUtilities } from '../../../../../../views/notification-center/common/NotificationUtilities';
|
||||
import { FurnitureItem } from '../../../../../inventory/common/FurnitureItem';
|
||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||
|
||||
export const MarketplacePostOfferView : FC<{}> = props =>
|
||||
{
|
||||
const [ item, setItem ] = useState<FurnitureItem>(null);
|
||||
const [ askingPrice, setAskingPrice ] = useState(0);
|
||||
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
||||
|
||||
const close = useCallback(() =>
|
||||
{
|
||||
BatchUpdates(() =>
|
||||
{
|
||||
setItem(null);
|
||||
setAskingPrice(0);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onCatalogPostMarketplaceOfferEvent = useCallback( (event: CatalogPostMarketplaceOfferEvent) =>
|
||||
{
|
||||
setItem(event.item);
|
||||
}, []);
|
||||
|
||||
useUiEvent(CatalogPostMarketplaceOfferEvent.POST_MARKETPLACE, onCatalogPostMarketplaceOfferEvent);
|
||||
|
||||
const getItemImage = useCallback( () =>
|
||||
{
|
||||
if(!item) return '';
|
||||
|
||||
let object: ImageResult;
|
||||
|
||||
if(!item.isWallItem)
|
||||
{
|
||||
object = GetRoomEngine().getFurnitureFloorImage(item.type, new Vector3d(90,0,0), 64, this, 4293848814, item.extra.toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
object = GetRoomEngine().getFurnitureWallImage(item.type, new Vector3d(90,0,0), 64, this, 4293848814, item.extra.toString());
|
||||
}
|
||||
|
||||
if(object)
|
||||
{
|
||||
const image = object.getImage();
|
||||
|
||||
if(image) return image.src;
|
||||
}
|
||||
return '';
|
||||
}, [item]);
|
||||
|
||||
const getFurniTitle = useCallback( () =>
|
||||
{
|
||||
if(!item) return '';
|
||||
|
||||
const localizationKey = item.isWallItem ? 'wallItem.name.' + item.type : 'roomItem.name.' + item.type;
|
||||
|
||||
return LocalizeText(localizationKey);
|
||||
}, [item]);
|
||||
|
||||
const getFurniDescription = useCallback( () =>
|
||||
{
|
||||
if(!item) return '';
|
||||
|
||||
const localizationKey = item.isWallItem ? 'wallItem.desc.' + item.type : 'roomItem.desc.' + item.type;
|
||||
|
||||
return LocalizeText(localizationKey);
|
||||
}, [item]);
|
||||
|
||||
const postItem = useCallback( () =>
|
||||
{
|
||||
if(isNaN(askingPrice) || askingPrice <= 0 || !item) return;
|
||||
|
||||
NotificationUtilities.confirm(LocalizeText('inventory.marketplace.confirm_offer.info', ['furniname', 'price'], [getFurniTitle(), askingPrice.toString()]), () =>
|
||||
{
|
||||
SendMessageHook(new MakeOfferMessageComposer(askingPrice, item.isWallItem ? 2 : 1, item.id));
|
||||
setItem(null);
|
||||
},
|
||||
() => { setItem(null)}, null, null, LocalizeText('inventory.marketplace.confirm_offer.title'));
|
||||
}, [askingPrice, getFurniTitle, item]);
|
||||
|
||||
return ( item &&
|
||||
<NitroCardView className="nitro-catalog-layout-marketplace-post-offer" simple={ true }>
|
||||
<NitroCardHeaderView headerText={ LocalizeText('inventory.marketplace.make_offer.title') } onCloseClick={ close } />
|
||||
<NitroCardContentView overflow="hidden">
|
||||
<Grid fullHeight>
|
||||
<Column center className="bg-muted rounded p-2" size={ 4 } overflow="hidden">
|
||||
<LayoutImage imageUrl={ getItemImage() } />
|
||||
</Column>
|
||||
<Column size={ 8 } justifyContent="between" overflow="hidden">
|
||||
<Column grow gap={ 1 }>
|
||||
<Text fontWeight="bold">{ getFurniTitle() }</Text>
|
||||
<Text truncate shrink>{ getFurniDescription() }</Text>
|
||||
</Column>
|
||||
<Column overflow="auto">
|
||||
<Text italics>
|
||||
{ LocalizeText('inventory.marketplace.make_offer.expiration_info', ['time'], [catalogState.marketplaceConfiguration.offerTime.toString()]) }
|
||||
</Text>
|
||||
<div className="input-group has-validation">
|
||||
<input className="form-control form-control-sm" type="number" min={ 0 } value={ askingPrice } onChange={ event => setAskingPrice(event.target.valueAsNumber) } placeholder={ LocalizeText('inventory.marketplace.make_offer.price_request') } />
|
||||
{ ((askingPrice < catalogState.marketplaceConfiguration.minimumPrice) || isNaN(askingPrice)) &&
|
||||
<Base className="invalid-feedback d-block">
|
||||
{ LocalizeText('inventory.marketplace.make_offer.min_price', [ 'minprice' ], [ catalogState.marketplaceConfiguration.minimumPrice.toString() ]) }
|
||||
</Base> }
|
||||
{ ((askingPrice > catalogState.marketplaceConfiguration.maximumPrice) && !isNaN(askingPrice)) &&
|
||||
<Base className="invalid-feedback d-block">
|
||||
{ LocalizeText('inventory.marketplace.make_offer.max_price', [ 'maxprice' ], [ catalogState.marketplaceConfiguration.maximumPrice.toString() ]) }
|
||||
</Base> }
|
||||
{ (!((askingPrice < catalogState.marketplaceConfiguration.minimumPrice) || (askingPrice > catalogState.marketplaceConfiguration.maximumPrice) || isNaN(askingPrice))) &&
|
||||
<Base className="invalid-feedback d-block">
|
||||
{ LocalizeText('inventory.marketplace.make_offer.final_price', [ 'commission', 'finalprice' ], [ catalogState.marketplaceConfiguration.commission.toString(), (askingPrice + catalogState.marketplaceConfiguration.commission).toString() ]) }
|
||||
</Base> }
|
||||
</div>
|
||||
<Button size="sm" disabled={ ((askingPrice < catalogState.marketplaceConfiguration.minimumPrice) || (askingPrice > catalogState.marketplaceConfiguration.maximumPrice) || isNaN(askingPrice)) } onClick={ postItem }>
|
||||
{ LocalizeText('inventory.marketplace.make_offer.post') }
|
||||
</Button>
|
||||
</Column>
|
||||
</Column>
|
||||
</Grid>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
)
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
.marketplace-item {
|
||||
max-height: 70px;
|
||||
height: 70px;
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
import { FC, useCallback } from 'react';
|
||||
import { GetRoomEngine, LocalizeText } from '../../../../../../../api';
|
||||
import { NitroCardGridItemView } from '../../../../../../../layout';
|
||||
import { MarketplaceOfferData } from '../common/MarketplaceOfferData';
|
||||
import { MarketPlaceOfferState } from '../common/MarketplaceOfferState';
|
||||
|
||||
export const OWN_OFFER = 1;
|
||||
export const PUBLIC_OFFER = 2;
|
||||
|
||||
export interface MarketplaceItemViewProps
|
||||
{
|
||||
offerData: MarketplaceOfferData;
|
||||
type?: number;
|
||||
onClick(offerData: MarketplaceOfferData): void;
|
||||
}
|
||||
|
||||
export const MarketplaceItemView: FC<MarketplaceItemViewProps> = props =>
|
||||
{
|
||||
const { offerData = null, type = PUBLIC_OFFER, onClick = null } = props;
|
||||
|
||||
const getImageUrlForOffer = useCallback( () =>
|
||||
{
|
||||
if(!offerData) return '';
|
||||
|
||||
switch(offerData.furniType)
|
||||
{
|
||||
case MarketplaceOfferData.TYPE_FLOOR:
|
||||
return GetRoomEngine().getFurnitureFloorIconUrl(offerData.furniId);
|
||||
case MarketplaceOfferData.TYPE_WALL:
|
||||
return GetRoomEngine().getFurnitureWallIconUrl(offerData.furniId, offerData.extraData);
|
||||
}
|
||||
|
||||
return '';
|
||||
}, [offerData]);
|
||||
|
||||
const getMarketplaceOfferTitle = useCallback(() =>
|
||||
{
|
||||
if(!offerData) return '';
|
||||
|
||||
const localizationKey = offerData.furniType === 2 ? 'wallItem.name.' + offerData.furniId: 'roomItem.name.' + offerData.furniId;
|
||||
|
||||
return LocalizeText(localizationKey);
|
||||
}, [offerData]);
|
||||
|
||||
const getMarketplaceOfferDescription = useCallback( () =>
|
||||
{
|
||||
if(!offerData) return '';
|
||||
|
||||
const localizationKey = offerData.furniType === 2 ? 'wallItem.desc.' + offerData.furniId : 'roomItem.desc.' + offerData.furniId;
|
||||
|
||||
return LocalizeText(localizationKey);
|
||||
}, [offerData]);
|
||||
|
||||
const offerTime = useCallback( () =>
|
||||
{
|
||||
if(!offerData) return '';
|
||||
|
||||
if(offerData.status === MarketPlaceOfferState.SOLD) return LocalizeText('catalog.marketplace.offer.sold');
|
||||
|
||||
if(offerData.timeLeftMinutes <= 0) return LocalizeText('catalog.marketplace.offer.expired');
|
||||
|
||||
const time = Math.max(1, offerData.timeLeftMinutes);
|
||||
const hours = Math.floor(time / 60);
|
||||
const minutes = time - (hours * 60);
|
||||
|
||||
let text = minutes + ' ' + LocalizeText('catalog.marketplace.offer.minutes');
|
||||
if(hours > 0)
|
||||
{
|
||||
text = hours + ' ' + LocalizeText('catalog.marketplace.offer.hours') + ' ' + text;
|
||||
}
|
||||
|
||||
return LocalizeText('catalog.marketplace.offer.time_left', ['time'], [text] );
|
||||
}, [offerData]);
|
||||
|
||||
return (
|
||||
<NitroCardGridItemView className='w-100 marketplace-item align-items-center'>
|
||||
<img src={ getImageUrlForOffer() } className='mx-3' alt='' />
|
||||
<div className='h-100 flex-grow-1 justify-content-center '>
|
||||
<div className='fw-bold'>{getMarketplaceOfferTitle()}</div>
|
||||
<div className='fst-italic fs-6'>{getMarketplaceOfferDescription()}</div>
|
||||
|
||||
{ type === OWN_OFFER && <>
|
||||
<div>{ LocalizeText('catalog.marketplace.offer.price_own_item', ['price'], [offerData.price.toString()])}</div>
|
||||
<div>{ offerTime() }</div>
|
||||
</>
|
||||
}
|
||||
{ type === PUBLIC_OFFER && <>
|
||||
<div>{ LocalizeText('catalog.marketplace.offer.price_public_item', ['price', 'average'], [offerData.price.toString(), offerData.averagePrice.toString() ]) }</div>
|
||||
<div>{ LocalizeText('catalog.marketplace.offer_count', ['count'], [offerData.offerCount.toString()]) }</div>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
<div className='btn-group-vertical mx-1 gap-2'>
|
||||
{ (type === OWN_OFFER && offerData.status !== MarketPlaceOfferState.SOLD) && <button className='btn btn-secondary btn-sm' onClick={ () => onClick(offerData) }>{ LocalizeText('catalog.marketplace.offer.pick') }</button>}
|
||||
{ type === PUBLIC_OFFER && <>
|
||||
<button className='btn btn-secondary btn-sm' onClick={ () => onClick(offerData) } >{ LocalizeText('buy') }</button>
|
||||
<button className='btn btn-secondary btn-sm' disabled={true}>{ LocalizeText('catalog.marketplace.view_more') }</button>
|
||||
</>}
|
||||
</div>
|
||||
</NitroCardGridItemView>)
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
import { CancelMarketplaceOfferMessageComposer, GetMarketplaceOwnOffersMessageComposer, MarketplaceCancelOfferResultEvent, MarketplaceOwnOffersEvent, RedeemMarketplaceOfferCreditsMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../../../../api';
|
||||
import { BatchUpdates, CreateMessageHook, SendMessageHook, UseMountEffect } from '../../../../../../../hooks';
|
||||
import { NitroCardGridView } from '../../../../../../../layout';
|
||||
import { NitroLayoutBase } from '../../../../../../../layout/base';
|
||||
import { NotificationAlertType } from '../../../../../../../views/notification-center/common/NotificationAlertType';
|
||||
import { NotificationUtilities } from '../../../../../../../views/notification-center/common/NotificationUtilities';
|
||||
import { CatalogLayoutProps } from '../../CatalogLayout.types';
|
||||
import { MarketplaceOfferData } from '../common/MarketplaceOfferData';
|
||||
import { MarketPlaceOfferState } from '../common/MarketplaceOfferState';
|
||||
import { MarketplaceItemView, OWN_OFFER } from '../marketplace-item/MarketplaceItemView';
|
||||
|
||||
export interface CatalogLayoutMarketplaceOwnItemsViewProps extends CatalogLayoutProps
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
export const CatalogLayoutMarketplaceOwnItemsView: FC<CatalogLayoutMarketplaceOwnItemsViewProps> = props =>
|
||||
{
|
||||
const [ creditsWaiting, setCreditsWaiting ] = useState(0);
|
||||
const [ offers, setOffers ] = useState(new Map<number, MarketplaceOfferData>());
|
||||
|
||||
UseMountEffect(() =>
|
||||
{
|
||||
SendMessageHook(new GetMarketplaceOwnOffersMessageComposer());
|
||||
});
|
||||
|
||||
const onMarketPlaceOwnOffersEvent = useCallback((event: MarketplaceOwnOffersEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser) return;
|
||||
|
||||
const latestOffers = new Map<number, MarketplaceOfferData>();
|
||||
parser.offers.forEach(entry =>
|
||||
{
|
||||
const offerEntry = new MarketplaceOfferData(entry.offerId, entry.furniId, entry.furniType, entry.extraData, entry.stuffData, entry.price, entry.status, entry.averagePrice, entry.offerCount);
|
||||
offerEntry.timeLeftMinutes = entry.timeLeftMinutes;
|
||||
latestOffers.set(entry.offerId, offerEntry);
|
||||
});
|
||||
|
||||
BatchUpdates(() =>
|
||||
{
|
||||
setCreditsWaiting(parser.creditsWaiting);
|
||||
setOffers(latestOffers);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onMarketplaceCancelOfferResultEvent = useCallback((event:MarketplaceCancelOfferResultEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser) return;
|
||||
|
||||
if(!parser.success)
|
||||
{
|
||||
NotificationUtilities.simpleAlert(LocalizeText('catalog.marketplace.cancel_failed'), NotificationAlertType.DEFAULT, null, null, LocalizeText('catalog.marketplace.operation_failed.topic'));
|
||||
return;
|
||||
}
|
||||
|
||||
setOffers( prev =>
|
||||
{
|
||||
const newVal = new Map(prev);
|
||||
newVal.delete(parser.offerId);
|
||||
return newVal;
|
||||
});
|
||||
}, []);
|
||||
|
||||
CreateMessageHook(MarketplaceOwnOffersEvent, onMarketPlaceOwnOffersEvent);
|
||||
CreateMessageHook(MarketplaceCancelOfferResultEvent, onMarketplaceCancelOfferResultEvent);
|
||||
|
||||
const redeemSoldOffers = useCallback(() =>
|
||||
{
|
||||
setOffers(prev =>
|
||||
{
|
||||
const newVal = new Map(prev);
|
||||
|
||||
const idsToDelete = [];
|
||||
|
||||
for(const offer of newVal.values())
|
||||
{
|
||||
if(offer.status === MarketPlaceOfferState.SOLD)
|
||||
{
|
||||
idsToDelete.push(offer.offerId);
|
||||
}
|
||||
}
|
||||
|
||||
for(const offerId of idsToDelete)
|
||||
{
|
||||
newVal.delete(offerId);
|
||||
}
|
||||
return newVal;
|
||||
})
|
||||
|
||||
SendMessageHook(new RedeemMarketplaceOfferCreditsMessageComposer());
|
||||
}, []);
|
||||
|
||||
const takeItemBack = useCallback( (offerData: MarketplaceOfferData) =>
|
||||
{
|
||||
SendMessageHook(new CancelMarketplaceOfferMessageComposer(offerData.offerId));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{ (creditsWaiting <= 0) && <NitroLayoutBase className='text-black'>{LocalizeText('catalog.marketplace.redeem.no_sold_items')}</NitroLayoutBase>}
|
||||
|
||||
{ (creditsWaiting > 0) && <NitroLayoutBase className='text-black'>{LocalizeText('catalog.marketplace.redeem.get_credits', ['count', 'credits'], [Array.from(offers.values()).filter(value => value.status === MarketPlaceOfferState.SOLD).length.toString(), creditsWaiting.toString()])}</NitroLayoutBase>}
|
||||
|
||||
<button className='btn btn-primary btn-sm mx-auto' disabled={creditsWaiting <= 0} onClick={redeemSoldOffers}>{LocalizeText('catalog.marketplace.offer.redeem')}</button>
|
||||
|
||||
<div className='text-black'>{LocalizeText('catalog.marketplace.items_found', ['count'], [offers.size.toString()])}</div>
|
||||
<NitroCardGridView columns={1} className='text-black'>
|
||||
{
|
||||
Array.from(offers.values()).map( (entry, index) => <MarketplaceItemView key={ index } offerData={ entry } type={ OWN_OFFER } onClick={takeItemBack} />)
|
||||
}
|
||||
</NitroCardGridView>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
.nitro-marketplace-post-offer {
|
||||
width: 300px;
|
||||
height: 365px;
|
||||
|
||||
.item-image-container {
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
import { ImageResult, MakeOfferMessageComposer, Vector3d } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { GetRoomEngine, LocalizeText } from '../../../../../../../api';
|
||||
import { CatalogPostMarketplaceOfferEvent } from '../../../../../../../events/catalog/CatalogPostMarketplaceOfferEvent';
|
||||
import { SendMessageHook, useUiEvent } from '../../../../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutFlex } from '../../../../../../../layout';
|
||||
import { NotificationUtilities } from '../../../../../../../views/notification-center/common/NotificationUtilities';
|
||||
import { FurnitureItem } from '../../../../../../inventory/common/FurnitureItem';
|
||||
import { useCatalogContext } from '../../../../../context/CatalogContext';
|
||||
|
||||
export const MarketplacePostOfferView : FC<{}> = props =>
|
||||
{
|
||||
const [ item, setItem ] = useState<FurnitureItem>(null);
|
||||
const [ askingPrice, setAskingPrice ] = useState(0);
|
||||
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
||||
|
||||
const close = useCallback(() =>
|
||||
{
|
||||
setItem(null);
|
||||
setAskingPrice(0);
|
||||
}, []);
|
||||
|
||||
const onCatalogPostMarketplaceOfferEvent = useCallback( (event: CatalogPostMarketplaceOfferEvent) =>
|
||||
{
|
||||
setItem(event.item);
|
||||
}, []);
|
||||
|
||||
useUiEvent(CatalogPostMarketplaceOfferEvent.POST_MARKETPLACE, onCatalogPostMarketplaceOfferEvent);
|
||||
|
||||
const getItemImage = useCallback( () =>
|
||||
{
|
||||
if(!item) return '';
|
||||
|
||||
let object: ImageResult;
|
||||
|
||||
if(!item.isWallItem)
|
||||
{
|
||||
object = GetRoomEngine().getFurnitureFloorImage(item.type, new Vector3d(90,0,0), 64, this, 4293848814, item.extra.toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
object = GetRoomEngine().getFurnitureWallImage(item.type, new Vector3d(90,0,0), 64, this, 4293848814, item.extra.toString());
|
||||
}
|
||||
|
||||
if(object)
|
||||
{
|
||||
const image = object.getImage();
|
||||
|
||||
if(image) return image.src;
|
||||
}
|
||||
return '';
|
||||
}, [item]);
|
||||
|
||||
const getFurniTitle = useCallback( () =>
|
||||
{
|
||||
if(!item) return '';
|
||||
|
||||
const localizationKey = item.isWallItem ? 'wallItem.name.' + item.type : 'roomItem.name.' + item.type;
|
||||
|
||||
return LocalizeText(localizationKey);
|
||||
}, [item]);
|
||||
|
||||
const getFurniDescription = useCallback( () =>
|
||||
{
|
||||
if(!item) return '';
|
||||
|
||||
const localizationKey = item.isWallItem ? 'wallItem.desc.' + item.type : 'roomItem.desc.' + item.type;
|
||||
|
||||
return LocalizeText(localizationKey);
|
||||
}, [item]);
|
||||
|
||||
const postItem = useCallback( () =>
|
||||
{
|
||||
if(isNaN(askingPrice) || askingPrice <= 0 || !item) return;
|
||||
|
||||
NotificationUtilities.confirm(LocalizeText('inventory.marketplace.confirm_offer.info', ['furniname', 'price'], [getFurniTitle(), askingPrice.toString()]), () =>
|
||||
{
|
||||
SendMessageHook(new MakeOfferMessageComposer(askingPrice, item.isWallItem ? 2 : 1, item.id));
|
||||
setItem(null);
|
||||
},
|
||||
() => { setItem(null)}, null, null, LocalizeText('inventory.marketplace.confirm_offer.title'));
|
||||
}, [askingPrice, getFurniTitle, item]);
|
||||
|
||||
return ( item &&
|
||||
<NitroCardView uniqueKey="catalog-mp-post-offer" className="nitro-marketplace-post-offer" simple={ true }>
|
||||
<NitroCardHeaderView headerText={ LocalizeText('inventory.marketplace.make_offer.title') } onCloseClick={ close } />
|
||||
<NitroCardContentView className="text-black">
|
||||
<NitroLayoutFlex>
|
||||
<div className="item-image-container mx-3" style={{ backgroundImage: `url(${getItemImage()})` }} />
|
||||
<div className='h-100 flex-grow-1 justify-content-center '>
|
||||
<div className='fw-bold'>{getFurniTitle()}</div>
|
||||
<div className='fs-6'>{getFurniDescription()}</div>
|
||||
</div>
|
||||
</NitroLayoutFlex>
|
||||
<div className='mx-2 fst-italic text-break mb-3'>
|
||||
{ LocalizeText('inventory.marketplace.make_offer.expiration_info', ['time'], [catalogState.marketplaceConfiguration.offerTime.toString()]) }
|
||||
</div>
|
||||
<div className="d-flex flex-row text-black mb-3">
|
||||
<div className="mr-2 align-self-center fw-bold" style={ { whiteSpace: 'nowrap' } }>{ LocalizeText('inventory.marketplace.make_offer.price_request') }</div>
|
||||
<input className="form-control form-control-sm" type="number" min={0} value={ askingPrice } onChange={ event => setAskingPrice(event.target.valueAsNumber) } />
|
||||
</div>
|
||||
<div className="alert alert-light" role="alert">
|
||||
{ (askingPrice < catalogState.marketplaceConfiguration.minimumPrice || isNaN(askingPrice)) && LocalizeText('inventory.marketplace.make_offer.min_price', ['minprice'], [catalogState.marketplaceConfiguration.minimumPrice.toString()]) }
|
||||
{ askingPrice > catalogState.marketplaceConfiguration.maximumPrice && !isNaN(askingPrice) &&
|
||||
LocalizeText('inventory.marketplace.make_offer.max_price', ['maxprice'], [catalogState.marketplaceConfiguration.maximumPrice.toString()])
|
||||
}
|
||||
{ !(askingPrice < catalogState.marketplaceConfiguration.minimumPrice || askingPrice > catalogState.marketplaceConfiguration.maximumPrice || isNaN(askingPrice)) && LocalizeText('inventory.marketplace.make_offer.final_price', ['commission', 'finalprice'], [catalogState.marketplaceConfiguration.commission.toString(), (askingPrice + catalogState.marketplaceConfiguration.commission).toString()])}
|
||||
</div>
|
||||
<div className="btn-group btn-group-sm mt-3" role="group">
|
||||
<button className='btn btn-primary' disabled={askingPrice < catalogState.marketplaceConfiguration.minimumPrice || askingPrice > catalogState.marketplaceConfiguration.maximumPrice || isNaN(askingPrice)} onClick={ postItem }>
|
||||
{ LocalizeText('inventory.marketplace.make_offer.post') }
|
||||
</button>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
)
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../../../../api';
|
||||
import { IMarketplaceSearchOptions } from '../common/IMarketplaceSearchOptions';
|
||||
import { MarketplaceSearchType } from '../common/MarketplaceSearchType';
|
||||
|
||||
export interface SearchFormViewProps
|
||||
{
|
||||
searchType: number;
|
||||
sortTypes: number[];
|
||||
onSearch(options: IMarketplaceSearchOptions): void;
|
||||
}
|
||||
|
||||
export const SearchFormView: FC<SearchFormViewProps> = props =>
|
||||
{
|
||||
const { searchType = null, sortTypes = null, onSearch = null } = props;
|
||||
const [ sortType, setSortType ] = useState(sortTypes ? sortTypes[0] : 3); // first item of SORT_TYPES_ACTIVITY
|
||||
const [ searchQuery, setSearchQuery ] = useState('');
|
||||
const [ min, setMin ] = useState(0);
|
||||
const [ max, setMax ] = useState(0);
|
||||
|
||||
const onSortTypeChange = useCallback((sortType: number) =>
|
||||
{
|
||||
setSortType(sortType);
|
||||
if(searchType === MarketplaceSearchType.BY_ACTIVITY || searchType === MarketplaceSearchType.BY_VALUE)
|
||||
onSearch({ minPrice: -1, maxPrice: -1, query: '', type: sortType });
|
||||
}, [onSearch, searchType]);
|
||||
|
||||
const onClickSearch = useCallback(() =>
|
||||
{
|
||||
const minPrice = min > 0 ? min : -1;
|
||||
const maxPrice = max > 0 ? max : -1;
|
||||
|
||||
onSearch({ minPrice: minPrice, maxPrice: maxPrice, type: sortType, query: searchQuery })
|
||||
}, [max, min, onSearch, searchQuery, sortType]);
|
||||
|
||||
useEffect( () =>
|
||||
{
|
||||
if(!sortTypes || !sortTypes.length) return;
|
||||
|
||||
const sortType = sortTypes[0];
|
||||
setSortType(sortType);
|
||||
|
||||
if(searchType === MarketplaceSearchType.BY_ACTIVITY || MarketplaceSearchType.BY_VALUE === searchType)
|
||||
onSearch({ minPrice: -1, maxPrice: -1, query: '', type: sortType });
|
||||
}, [onSearch, searchType, sortTypes]);
|
||||
|
||||
return (<>
|
||||
<div className="d-flex flex-row text-black">
|
||||
<div className="mr-2 align-self-center col-4" style={ { whiteSpace: 'nowrap' } }>{ LocalizeText('catalog.marketplace.sort_order') }</div>
|
||||
<select className="form-control form-control-sm" value={sortType} onChange={ (event) => onSortTypeChange(parseInt(event.target.value)) }>
|
||||
{ sortTypes.map( (type, index) => <option key={index} value={type}>{ LocalizeText(`catalog.marketplace.sort.${type}`) }</option>)}
|
||||
</select>
|
||||
</div>
|
||||
{ searchType === MarketplaceSearchType.ADVANCED && <>
|
||||
<div className="d-flex flex-row text-black">
|
||||
<div className="mr-2 align-self-center col-4" style={ { whiteSpace: 'nowrap' } }>{ LocalizeText('catalog.marketplace.search_name') }</div>
|
||||
<input className="form-control form-control-sm" type="text" value={ searchQuery} onChange={event => setSearchQuery(event.target.value)}/>
|
||||
</div>
|
||||
|
||||
<div className="d-flex flex-row text-black">
|
||||
<div className="mr-2 align-self-center col-4" style={ { whiteSpace: 'nowrap' } }>{ LocalizeText('catalog.marketplace.search_price') }</div>
|
||||
<input className="form-control form-control-sm" type="number" min={0} value={ min } onChange={ event => setMin(event.target.valueAsNumber) } />
|
||||
<input className="form-control form-control-sm" type="number" min={0} value={ max } onChange={ event => setMax(event.target.valueAsNumber) } />
|
||||
</div>
|
||||
|
||||
<button className="btn btn-secondary btn-sm float-end mx-auto" onClick={onClickSearch}>{ LocalizeText('generic.search') }</button>
|
||||
</>
|
||||
}
|
||||
</>);
|
||||
}
|
@ -67,8 +67,8 @@ export const CatalogLayoutPetPurchaseView: FC<CatalogLayoutPetPurchaseViewProps>
|
||||
</Column>
|
||||
</Flex>
|
||||
<Column gap={ 1 }>
|
||||
<CatalogPurchaseButtonView className="btn-sm w-100" offer={ offer } pageId={ pageId } extra={ extraData } quantity={ 1 } isPurchaseAllowed={ nameApproved } beforePurchase={ beforePurchase } />
|
||||
{ offer.giftable && <CatalogPurchaseGiftButtonView className="btn-sm w-100 mt-1" offer={ offer } pageId={ pageId } extra={ extraData } disabled={ nameApproved } /> }
|
||||
<CatalogPurchaseButtonView offer={ offer } pageId={ pageId } extra={ extraData } quantity={ 1 } isPurchaseAllowed={ nameApproved } beforePurchase={ beforePurchase } />
|
||||
{ offer.giftable && <CatalogPurchaseGiftButtonView offer={ offer } pageId={ pageId } extra={ extraData } disabled={ nameApproved } /> }
|
||||
</Column>
|
||||
</Column>
|
||||
);
|
||||
|
@ -117,6 +117,6 @@ export const CatalogPurchaseButtonView: FC<CatalogPurchaseButtonViewProps> = pro
|
||||
return <Button variant="danger" size="sm" disabled { ...rest }>{ LocalizeText('generic.failed') }</Button>;
|
||||
case CatalogPurchaseState.NONE:
|
||||
default:
|
||||
return <Button variant="success" size="sm" onClick={ event => setPurchaseState(CatalogPurchaseState.CONFIRM) }>{ LocalizeText('buy') }</Button>
|
||||
return <Button variant="success" size="sm" onClick={ event => setPurchaseState(CatalogPurchaseState.CONFIRM) } { ...rest }>{ LocalizeText('buy') }</Button>
|
||||
}
|
||||
}
|
||||
|
@ -21,5 +21,5 @@ export const CatalogPurchaseGiftButtonView: FC<CatalogPurchaseGiftButtonViewProp
|
||||
dispatchUiEvent(new CatalogInitGiftEvent(pageId, offer.offerId, extra));
|
||||
}
|
||||
|
||||
return <Button variant="secondary" onClick={ initGift } { ...rest }>{ LocalizeText('catalog.purchase_confirmation.gift') }</Button>;
|
||||
return <Button variant="secondary" size="sm" onClick={ initGift } { ...rest }>{ LocalizeText('catalog.purchase_confirmation.gift') }</Button>;
|
||||
}
|
||||
|
@ -82,8 +82,8 @@ export const CatalogPurchaseView: FC<CatalogPurchaseViewProps> = props =>
|
||||
</Column>
|
||||
</Flex>
|
||||
<Column gap={ 1 }>
|
||||
<CatalogPurchaseButtonView className="btn-sm w-100" offer={ offer } pageId={ pageId } extra={ extraData } quantity={ quantity } disabled={ disabled } />
|
||||
{ offer.giftable && <CatalogPurchaseGiftButtonView className="btn-sm w-100 mt-1" offer={ offer } pageId={ pageId } extra={ extraData } disabled={ disabled } /> }
|
||||
<CatalogPurchaseButtonView offer={ offer } pageId={ pageId } extra={ extraData } quantity={ quantity } disabled={ disabled } />
|
||||
{ offer.giftable && <CatalogPurchaseGiftButtonView offer={ offer } pageId={ pageId } extra={ extraData } disabled={ disabled } /> }
|
||||
</Column>
|
||||
</Column>
|
||||
);
|
||||
|
@ -1,9 +1,16 @@
|
||||
import { FC } from 'react';
|
||||
import { Dispatch, FC, SetStateAction } from 'react';
|
||||
import { LocalizeText } from '../../../../api';
|
||||
import { NitroLayoutButton, NitroLayoutFlex } from '../../../../layout';
|
||||
import { NitroLayoutBase } from '../../../../layout/base';
|
||||
import { Base } from '../../../../common/Base';
|
||||
import { Button } from '../../../../common/Button';
|
||||
import { Flex } from '../../../../common/Flex';
|
||||
import { Text } from '../../../../common/Text';
|
||||
import { useCatalogContext } from '../../context/CatalogContext';
|
||||
import { CatalogSelectGroupViewProps } from './CatalogSelectGroupView.types';
|
||||
|
||||
export interface CatalogSelectGroupViewProps
|
||||
{
|
||||
selectedGroupIndex: number;
|
||||
setSelectedGroupIndex: Dispatch<SetStateAction<number>>;
|
||||
}
|
||||
|
||||
export const CatalogSelectGroupView: FC<CatalogSelectGroupViewProps> = props =>
|
||||
{
|
||||
@ -14,27 +21,24 @@ export const CatalogSelectGroupView: FC<CatalogSelectGroupViewProps> = props =>
|
||||
if(!groups || !groups.length)
|
||||
{
|
||||
return (
|
||||
<NitroLayoutBase className="flex-grow-1 bg-muted rounded text-black text-center p-1">
|
||||
<Text grow center className="bg-muted rounded p-1">
|
||||
{ LocalizeText('catalog.guild_selector.members_only') }
|
||||
<NitroLayoutButton className="mt-1" variant="primary" size="sm">
|
||||
<Button variant="primary" size="sm" className="mt-1">
|
||||
{ LocalizeText('catalog.guild_selector.find_groups') }
|
||||
</NitroLayoutButton>
|
||||
</NitroLayoutBase>
|
||||
</Button>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<NitroLayoutFlex>
|
||||
<NitroLayoutFlex className="rounded border me-1" overflow="hidden">
|
||||
<NitroLayoutBase className="h-100" style={ { width: '20px', backgroundColor: '#' + groups[selectedGroupIndex].colorA } } />
|
||||
<NitroLayoutBase className="h-100" style={ { width: '20px', backgroundColor: '#' + groups[selectedGroupIndex].colorB } } />
|
||||
</NitroLayoutFlex>
|
||||
<Flex gap={ 1 }>
|
||||
<Flex overflow="hidden" className="rounded border">
|
||||
<Base fullHeight style={ { width: '20px', backgroundColor: '#' + groups[selectedGroupIndex].colorA } } />
|
||||
<Base fullHeight style={ { width: '20px', backgroundColor: '#' + groups[selectedGroupIndex].colorB } } />
|
||||
</Flex>
|
||||
<select className="form-select form-select-sm" value={ selectedGroupIndex } onChange={ event => setSelectedGroupIndex(parseInt(event.target.value)) }>
|
||||
{ groups.map((group, index) =>
|
||||
{
|
||||
return <option key={ index } value={ index }>{ group.groupName }</option>;
|
||||
}) }
|
||||
{ groups.map((group, index) => <option key={ index } value={ index }>{ group.groupName }</option>) }
|
||||
</select>
|
||||
</NitroLayoutFlex>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
export interface CatalogSelectGroupViewProps
|
||||
{
|
||||
selectedGroupIndex: number;
|
||||
setSelectedGroupIndex: Dispatch<SetStateAction<number>>;
|
||||
}
|
Loading…
Reference in New Issue
Block a user