diff --git a/src/assets/images/inventory/trading/locked-icon.png b/src/assets/images/inventory/trading/locked-icon.png new file mode 100644 index 0000000..7d9a3cb Binary files /dev/null and b/src/assets/images/inventory/trading/locked-icon.png differ diff --git a/src/assets/images/inventory/trading/unlocked-icon.png b/src/assets/images/inventory/trading/unlocked-icon.png new file mode 100644 index 0000000..39bd355 Binary files /dev/null and b/src/assets/images/inventory/trading/unlocked-icon.png differ diff --git a/src/components/camera/CameraWidgetView.scss b/src/components/camera/CameraWidgetView.scss index aecd0b1..8de2902 100644 --- a/src/components/camera/CameraWidgetView.scss +++ b/src/components/camera/CameraWidgetView.scss @@ -25,6 +25,23 @@ filter: brightness(0.8); } } + + .info-camera { + top: 8px; + right: 36px; + cursor: pointer; + background-image: url("@/assets/images/boxes/card/questionmark.png"); + width: 19px; + height: 20px; + + &:hover { + background-image: url("@/assets/images/boxes/card/questionmark_hover.png"); + + &:active { + background-image: url("@/assets/images/boxes/card/questionmark_click.png"); + } + } + } .camera-area { position: absolute; diff --git a/src/components/camera/CameraWidgetView.tsx b/src/components/camera/CameraWidgetView.tsx index 00ab0a8..91f9d0e 100644 --- a/src/components/camera/CameraWidgetView.tsx +++ b/src/components/camera/CameraWidgetView.tsx @@ -44,10 +44,29 @@ export const CameraWidgetView: FC<{}> = props => } const checkoutPictureUrl = (pictureUrl: string) => - { - setSavedPictureUrl(pictureUrl); - setMode(MODE_CHECKOUT); - } + { + const expectedPrefix = 'data:image/'; + + if (!pictureUrl.startsWith(expectedPrefix)) { + return; + } + + const sanitizedUrl = sanitizeImageUrl(pictureUrl); + if (!sanitizedUrl) { + return; + } + + setSavedPictureUrl(sanitizedUrl); + setMode(MODE_CHECKOUT); + } + + const sanitizeImageUrl = (imageUrl: string): string | null => { + if (!imageUrl.startsWith('data:image/')) { + return null; + } + + return imageUrl; + } useRoomSessionManagerEvent(RoomSessionEvent.ENDED, event => setMode(MODE_NONE)); diff --git a/src/components/camera/views/CameraWidgetCaptureView.tsx b/src/components/camera/views/CameraWidgetCaptureView.tsx index 308bb83..c3fc015 100644 --- a/src/components/camera/views/CameraWidgetCaptureView.tsx +++ b/src/components/camera/views/CameraWidgetCaptureView.tsx @@ -1,7 +1,7 @@ import { NitroRectangle, TextureUtils } from '@nitrots/nitro-renderer'; import { FC, useRef } from 'react'; import { FaTimes } from 'react-icons/fa'; -import { CameraPicture, GetRoomEngine, GetRoomSession, LocalizeText, PlaySound, SoundNames } from '../../../api'; +import { CameraPicture, CreateLinkEvent, GetRoomEngine, GetRoomSession, LocalizeText, PlaySound, SoundNames } from '../../../api'; import { Column, DraggableWindow, Flex } from '../../../common'; import { useCamera, useNotification } from '../../../hooks'; @@ -62,6 +62,7 @@ export const CameraWidgetCaptureView: FC = props = { selectedPicture && }
+
CreateLinkEvent('habbopages/camera') }>
diff --git a/src/components/camera/views/CameraWidgetCheckoutView.tsx b/src/components/camera/views/CameraWidgetCheckoutView.tsx index 6ee92f5..23d581b 100644 --- a/src/components/camera/views/CameraWidgetCheckoutView.tsx +++ b/src/components/camera/views/CameraWidgetCheckoutView.tsx @@ -1,8 +1,8 @@ -import { CameraPublishStatusMessageEvent, CameraPurchaseOKMessageEvent, CameraStorageUrlMessageEvent, PublishPhotoMessageComposer, PurchasePhotoMessageComposer } from '@nitrots/nitro-renderer'; +import { CameraPublishStatusMessageEvent, CameraPurchaseOKMessageEvent, CameraStorageUrlMessageEvent, NotEnoughBalanceMessageEvent, PublishPhotoMessageComposer, PurchasePhotoMessageComposer } from '@nitrots/nitro-renderer'; import { FC, useEffect, useMemo, useState } from 'react'; import { CreateLinkEvent, GetConfiguration, GetRoomEngine, LocalizeText, SendMessageComposer } from '../../../api'; import { Button, Column, Flex, LayoutCurrencyIcon, LayoutImage, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../common'; -import { useMessageEvent } from '../../../hooks'; +import { useMessageEvent, useNotification } from '../../../hooks'; export interface CameraWidgetCheckoutViewProps { @@ -21,6 +21,7 @@ export const CameraWidgetCheckoutView: FC = props const [ wasPicturePublished, setWasPicturePublished ] = useState(false); const [ isWaiting, setIsWaiting ] = useState(false); const [ publishCooldown, setPublishCooldown ] = useState(0); + const { simpleAlert } = useNotification(); const publishDisabled = useMemo(() => GetConfiguration('camera.publish.disabled', false), []); @@ -34,6 +35,8 @@ export const CameraWidgetCheckoutView: FC = props { const parser = event.getParser(); + if (!parser.ok) simpleAlert(LocalizeText('camera.publish.wait', [ 'minutes' ], [ Math.floor(parser.secondsToWait / 60).toString().replace('-', '') ]), null, null, null, LocalizeText('camera.purchase.pleasewait')); + setPublishUrl(parser.extraDataId); setPublishCooldown(parser.secondsToWait); setWasPicturePublished(parser.ok); @@ -47,6 +50,19 @@ export const CameraWidgetCheckoutView: FC = props setPictureUrl(GetConfiguration('camera.url') + '/' + parser.url); }); + useMessageEvent(NotEnoughBalanceMessageEvent, event => + { + const parser = event.getParser(); + + if (!parser) return null; + + if (parser.notEnoughCredits && !parser.notEnoughActivityPoints) simpleAlert(LocalizeText('catalog.alert.notenough.credits.description'), null, null, null, LocalizeText('catalog.alert.notenough.title')); + + if (!parser.notEnoughCredits && parser.notEnoughActivityPoints) simpleAlert(LocalizeText(`catalog.alert.notenough.activitypoints.description.${ parser.activityPointType }`), null, null, null, LocalizeText(`catalog.alert.notenough.activitypoints.title.${ parser.activityPointType }`)); + + setIsWaiting(false); + }); + const processAction = (type: string, value: string | number = null) => { switch(type) diff --git a/src/components/camera/views/CameraWidgetShowPhotoView.tsx b/src/components/camera/views/CameraWidgetShowPhotoView.tsx index 0825c60..c01ea07 100644 --- a/src/components/camera/views/CameraWidgetShowPhotoView.tsx +++ b/src/components/camera/views/CameraWidgetShowPhotoView.tsx @@ -1,6 +1,7 @@ +import { RoomObjectCategory, RoomObjectVariable } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; import { FaArrowLeft, FaArrowRight } from 'react-icons/fa'; -import { GetUserProfile, IPhotoData, LocalizeText } from '../../../api'; +import { GetRoomEngine, GetUserProfile, IPhotoData, LocalizeText } from '../../../api'; import { Flex, Grid, Text } from '../../../common'; export interface CameraWidgetShowPhotoViewProps @@ -13,7 +14,6 @@ export const CameraWidgetShowPhotoView: FC = pro { const { currentIndex = -1, currentPhotos = null } = props; const [ imageIndex, setImageIndex ] = useState(0); - const currentImage = (currentPhotos && currentPhotos.length) ? currentPhotos[imageIndex] : null; const next = () => @@ -21,9 +21,7 @@ export const CameraWidgetShowPhotoView: FC = pro setImageIndex(prevValue => { let newIndex = (prevValue + 1); - if(newIndex >= currentPhotos.length) newIndex = 0; - return newIndex; }); } @@ -33,36 +31,35 @@ export const CameraWidgetShowPhotoView: FC = pro setImageIndex(prevValue => { let newIndex = (prevValue - 1); - if(newIndex < 0) newIndex = (currentPhotos.length - 1); - return newIndex; }); } - - useEffect(() => + + const getUserData = (roomId: number, objectId: number, type: string): number | string => { - setImageIndex(currentIndex); - }, [ currentIndex ]); + const roomObject = GetRoomEngine().getRoomObject(roomId, objectId, RoomObjectCategory.WALL); + if (!roomObject) return; + return type == 'username' ? roomObject.model.getValue(RoomObjectVariable.FURNITURE_OWNER_NAME) : roomObject.model.getValue(RoomObjectVariable.FURNITURE_OWNER_ID); + } + + useEffect(() => { setImageIndex(currentIndex); }, [ currentIndex ]); if(!currentImage) return null; return ( - { !currentImage.w && - { LocalizeText('camera.loading') } } + { !currentImage.w && { LocalizeText('camera.loading') } } - { currentImage.m && currentImage.m.length && - { currentImage.m } } + { currentImage.m && currentImage.m.length && { currentImage.m } } - { (currentImage.n || '') } - { new Date(currentImage.t * 1000).toLocaleDateString() } + { new Date(currentImage.t * 1000).toLocaleDateString(undefined, { day: 'numeric', month: 'long', year: 'numeric' }) } + GetUserProfile(Number(getUserData(currentImage.s, Number(currentImage.u), 'id')))}> { getUserData(currentImage.s, Number(currentImage.u), 'username') } { (currentPhotos.length > 1) && - GetUserProfile(currentImage.oi) }>{ currentImage.o } } diff --git a/src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx b/src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx index f4f9d24..8e4e262 100644 --- a/src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx +++ b/src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx @@ -1,9 +1,14 @@ -import { NotificationBubbleItem, NotificationBubbleType } from "../../../../api"; +import { + NotificationBubbleItem, + NotificationBubbleType, +} from "../../../../api"; import { NotificationClubGiftBubbleView } from "./NotificationClubGiftBubbleView"; import { NotificationDefaultBubbleView } from "./NotificationDefaultBubbleView"; -export const GetBubbleLayout = ( item: NotificationBubbleItem, onClose: () => void ) => { - +export const GetBubbleLayout = ( + item: NotificationBubbleItem, + onClose: () => void +) => { if (!item) return null; const props = { key: item.id, item, onClose }; @@ -14,4 +19,4 @@ export const GetBubbleLayout = ( item: NotificationBubbleItem, onClose: () => vo default: return ; } -}; \ No newline at end of file +};