npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@visit-x/vxmessenger

v1.0.7

Published

This project is about providing messaging and video interactions for the customers of VISIT-X, the german leading Live Sex Cams & Sexchat Community.

Readme

VXMESSENGER

This project is about providing messaging and video interactions for the customers of VISIT-X, the german leading Live Sex Cams & Sexchat Community.

BREAKING CHANGES: Starting from the 1.0.0 version:

1. Updated Styles Import Path

The styles entry point has changed.

Before:

import '@visit-x/vxmessenger/vxm_build/styles.css';

After:

import '@visit-x/vxmessenger/styles.css';

2. Build Output Directory Renamed

The package’s compiled output directory has been renamed:

Old: vxm_build/

New: dist/

Update any bundler configs, tooling or direct imports relying on the old path.

3. Default Build Format Switched to ESM

The default exported build format is now ESM instead of UMD.

If you still need UMD (not recommended due to React 19 dropping official UMD builds), you can use dist/index.umd.js

Usage and integration

NPM

The VXMessenger package provides multiple build options (described in exports fields of package.json): ESM, UMD, CJS builds. Your bundler should automatically chose the most relevant option.

Caution: currently there is no support for SSR rendering, please use client-only import if your framework is server-side.

Use via CDN

To connect the messenger, you need to use a link to the latest version (js, css) or a specific one.

https://vxmessenger.visit-x.net/releases/{version}/styles.css
https://vxmessenger.visit-x.net/releases/{version}/vxmessenger.js
{version} - version number, for example 0.0.1

The files deployed to CDN include:

  • styles.css - Styles required by messenger to look correct.
  • vxmessenger.js - Chunked VXMessenger build. The recommended integration option, see the example below.
  • vxmessenger.esm.js - Standalone (NO chunks) ES-Module bundle of package.
  • vxmessenger.umd.js - Standalone UMD bundle of package.

Important: The React should already be available on your page by the time you import VXMessenger (i.e., import React BEFORE VXmessenger)

<script type="importmap">
	{
	  "imports": {
		"react": "https://esm.sh/[email protected]",
		"react-dom": "https://esm.sh/[email protected]",
		"react-dom/": "https://esm.sh/[email protected]/"
	  }
	}
</script>
<script type="module">
	import React from "react";
	import ReactDomClient from "react-dom/client";
	import VXMessenger from 'https://vxmessenger.visit-x.net/releases/{version}/vxmessenger.js' // chunked version of messenger

	const messengerProps = {}; // your connection and other props go here

	const Messenger = React.createElement(VXMessenger, messengerProps, null);
	ReactDomClient.createRoot(document.getElementById('root')).render(Messenger)
</script>

Caution: Package integrations may have issues in projects created with CRA after ejecting due to unsupported webpack build configurations. The possible workaround would be using the webpack resolve alias:

module.exports = {
  // ...
  resolve: {
    alias: {
      '@visit-x/vxmessenger': path.resolve(__dirname, 'node_modules/@visit-x/vxmessenger/dist/index.umd.js')
    }
  }
};

Properties

  • id?: string - unique identification for the configured instance of messenger.

  • language: string - case insensitive (en, de, es, EN, DE, ES). This value is mandatory.

    Note: Please note that when obtaining "clientId" + "server" pair (in case one wants to start the messenger this way), the same language should be passed to the appropriate service. Otherwise, some of the messages we are getting ( and displaying) from the vchat-core will come in default language (en), producing an unpleasant mix.

  • initialVideoChatType?: VideoChatType - if the parent platform sets this value, the newly-opened messenger will start directly in the given active stream. This feature is independent to clientId, as it can be used with both clientId or webtoken. This property can be used when the client clicks on a "Start voyeur" button from the parent platform and the messenger will start with initialVideoChatType with "VOYEUR_CHAT". If no value is set, the messenger will start in its minified state, without any stream.

  • theme?: Partial<Theme> - if this value is set, the parent platform can completely configure messenger's appearance to the client. It contains information about the font (type, dimensions), the dimensions for certain elements and all the colors used in the messenger. Each color property is a string and expects the color in hexa value. If no value is set for colors or dimensions, the fallback theme will be the design from visit-x.

  • preferenceStore?: PreferenceStore - this property represents the custom implementation for handling preferences of the VXMessenger. This property is optional, if no property is passed to VXMessenger the default implementation will apply.

  • connection: ConnectionWithToken | ConnectionWithChat - this property represents the data that initiates the messenger's workflow - webtoken type of connection or clientId and server type. The first type of connection ( ConnectionWithToken) will contain the webtoken, userKey and partner's information (key and id) and will use the vxcontrol-client-lib as main API. The second type of connection (ConnectionWithChat) will contain the clientId and server and won't use the vxcontrol-client-lib, only the vchat-core. This second type of connection is going to be used only when the messenger must start in an active stream, therefore, the initialVideoChatType property should have a value set.

  • features?: Features - contains all the toggleable features of the messenger, in order to ensure flexibility. All properties are of type boolean and if the value is true, that implies that the functionality is going to be present in the messenger. Our toggleable features are:

    • hasAudioMessages?: boolean - the messenger will have the functionality of sending audio messages to the model. Desktop only. ( default false)
    • hasBehaviorOnClickOutside?: boolean - if this flag is true, the messenger will close when clicking outside. (default true)
    • hasCam2Cam?: boolean - when having an active stream, cam2cam button is rendered. (default true)
    • hasClipping?: boolean - has the ability of buy a private chat clip. (default true)
    • hasCloseButton?: boolean - the X button in the top right corner is rendered. (default true)
    • hasEmoticons?: boolean - has the ability of sending emoticons. (default true)
    • hasExitPopups?: boolean - any type of exit (for instance when closing the videochat) would use messenger's implemented popups to inform the client. Some content partners would desire using their own popups/behaviors to be executed. (default true)
    • hasFavorite?: boolean - the favorite icon (near model's name) is rendered. (default true)
    • hasFocusTrap?: boolean - when enabled, manages keyboard focus within the messenger window and all nested modals, preventing focus from escaping to external page elements. (default true)
    • hasFullscreen?: boolean - when having an active stream, the client has the fullscreen option. (default true)
    • hasFullscreenLayoutOnLandscape?: boolean - when the stream starts and the container has landscape sizes, the messenger will be using the fullscreen layout. (default false)
    • hasGifts?: boolean - has the ability of sending gifts. (default true)
    • hasGiftsSectionOpened: boolean | GiftCategory - to start messenger with opened gift section set this value to true. To open any gifts tab assign some special to this value (for example, GiftCategory.LOVE). GiftCategory - is enum and exported directly from messenger.
    • hasGroupChatClientName?: boolean - in a group chat session, a message sent by the client will contain its own name/alias. (default false)
    • hasGroupChatIndicator?: boolean - renders the number of participants in a group chat. Desktop only. (default true)
    • hasHeader?: boolean - has header of Messenger. (default true)
    • hasHistoryMessages?: boolean - when opening the messenger, in the chatbox we will render the past conversations between the client and the model. (default true)
    • hasImageUpload?: boolean - has the ability of uploading images. (default true)
    • hasLiveChat?: boolean - has the ability to start Livechat. (default true)
    • hasLivePreview?: boolean - has the ability to start Live preview. (default true)
    • hasMessageTranslation?: boolean - has the ability of translate model`s messages. (default false)
    • canSendMediaWithTextFeature?: boolean - has the ability of sending media with text. (default false)
    • hasMessagesGrouped2?: boolean - adds a message about the end of the previous livechat session. (default true)
    • hasMessagesGrouped?: boolean - the history messages are grouped together based on date. (default true)
    • hasMessaging?: boolean - has ability to send messages. (default true)
    • hasModelMotto?: boolean - model's motto is rendered. (default true)
    • hasModelName?: boolean - model's name is rendered. (default true)
    • hasModelProfilePicture?: boolean - model's profile picture is rendered. (default true)
    • hasNewMobileMessengerUI?: boolean - using new mobile UI for messenger (default false)
    • hasNewMobileUI?: boolean - using new mobile UI for video chat (default true)
    • hasPreloaderPaymentText?: boolean - if this flag is true, there will be no text displayed above loading pre-loader. (default true)
    • hasPrivateChat?: boolean - when having an active stream, private chat button is rendered. (default true)
    • hasProductionDebugEnabled?: boolean - we will have access to production debug information in order to solve live errors more efficiently. (default false)
    • hasSendMessageButton?: boolean - on true, the button for sending messages to the model will be rendered. (default true)
    • hasSettings?: boolean - on true, the settings button in header will be rendered. (default true)
    • hasTips?: boolean - if it has the ability of sending tips and the anonymous tip functionality when starting a voyeur chat session. (default false)
    • hasToyControl?: boolean - if it has the toy control feature activated. (default true)
    • hasUnreadMessageCounter?: boolean - adds an unread message counter to the scroll down button. (default false)
    • hasVideoCalls?: boolean - has the ability of call to model. (default false)
    • hasVoyeur?: boolean - if voyeur button is rendered. (default true)
    • isMessageTypeHeader?: boolean - text chat messenger has a customized header, to be applied on visit-x integration only. (default false)
    • shouldUseTusUpload?: boolean - use resumable uploads. (default false)
    • shouldVipOnly?: boolean - only VIP users can write models. (default false)
    • showCensoredContent?: boolean - if it has the ability to view censored content. (default true)
  • options?: Options - contains very specific configurable properties when desiring a certain behavior for the messenger.

    • updateBrowserLink?: string - if this value is set, the parent platform can personalize the message for Cam2Cam button when is used on Internet Explorer. When the messenger is rendered on Internet Explorer, the client can't use the Cam2Cam feature, therefore the button is disabled with some information about this issue. Part of this information is the update browser link, which can be set through this property.

    • analyticsKey?: string - represents your Google Analytics key and if this value is set, you will be able to visualize the default trackers implemented in our library.

    • preloader?: string - represents image's CDN address that is going to be used as a pre-loader for starting a stream. This property is used for branding the messenger for a content partner. If no value is set, the fallback preloader is going to be a three-dots animation.

    • tip?: ITipOption - content partners starting a chat-based video session have the tipping feature active, but in order for it to work, some information are needed from the mother platform: amount of a tip, associated currency and an image associated to the tip (defaults some green bank-notes). That info will be rendered on the confirmation tip pop-up.

    • initialVideochatVolume?: number - when the stream will start, the initial volume will be set by this property. The value must be any number from 0 to 1. If the value is set on 0, the stream will start muted. If no value is set, the stream will start normally.

    • censoredContentImage?: string - represents image's CDN address that is going to be used as the cover for censored content. If no value is set, the default image is going to be shown.

    • useInjectableHlsPlayer?: boolean - allows integrators to provide their own implementation of the HLS player. If not provided, will call a onClickVideo hook with (hls: string, poster: string, isTranscoding?: boolean) arguments (this is default behavior). When set to true, you can customize the inlined player by providing custom implementation for component. See hooks.renderHlsPlayer for custom implementation.

  • hooks?: Hooks - functions, in which the parent platform can implement a behavior on certain events. They represent a way of communication between the messenger and the parent platform. All the functions are nullable.

    • onClickProfile? - will execute the logic inside the function when client clicks on model's name or profile picture;

    • onClickImage? - will execute this logic when user click an image, instead of simply opening the image in the default viewer; useful for platforms willing to have their own image viewers

    • onClickVideo? - will be called when user is clicking a video message; if not specified, video won't play, messenger has no player embedded inside. Is not called when options.useInjectableHlsPlayer is set to true.

    • onExit? - will execute the logic inside the function when the messenger will close, irrespective to its cause. It will contain an exitCode in order to reflect the reason for which the messenger has been closed;

    • onRecharge? - will execute the logic inside the function if messenger's recharge mechanism is not working;

    • onVideoChatAction? - will execute the logic inside the function when any type of stream changes its type from active to inactive or from inactive to active. It will return a flag if the video is open and, optionally, a value for chatId when closing the stream.

    • onExitPopupAction? - will execute the logic inside the function when exit pop-ups (if having this feature set on) are displayed or hidden;

    • onAgeVerification? - will execute the logic inside the function when messenger would need to verify the age of the client. The messenger as an application does not know how to apply age verification, so it's going to rely on the parent platform;

    • onModelStatusChange? - will execute the logic inside the function when the model goes from online to offline or from offline to online. Any change in model's online status will trigger this function;

    • onSupportedVideoChatTypes? - will execute the logic inside the function when the messenger is opened. It will return the array of possible streams with the model. For instance, ["LIVECHAT", "LIVE_PREVIEW", "VOYEUR_CHAT"] if all the streams are possible.

    • onFailedChatConfig? - will execute the logic inside the function when the messenger has been started with the clientId and server as connection, but starting the stream has failed and returned with an error. This error is going to be returned as a parameter;

    • onFailedWebToken? - will execute the logic inside the function when the messenger finds the provided web token not usable (or not usable anymore - e.g. token has expired);

    • onBecomeVIP? - functionality for setting the current client as a VIP member to your platform.

    • onNewIncomingMessage? - will execute the logic inside the function when new message from the model was received.

    • onClickLobbyChat - will be executed when the user clicks on "Live Show"

    • renderHlsPlayer? - can be used to render custom HLS player inlined in the messenger. Has to return a React component or plain HTML element. If not provided, messenger will fall back to HTML5 <video> element, which does not have a 100% support for HLS within various browsers.

    • onMediaOfferPurchaseRequest? - triggers a media purchase hook by clicking on the purchase button, if the hook is set, the messenger does not request a purchase

Interfaces and enumerators for messenger's properties

ConnectionWithToken

interface ConnectionWithToken {
	webToken: string;
	partner: Partner;
	userKey?: string;
}

VideoChatType

enum VideoChatType {
	LIVECHAT = 'LIVECHAT',
	FREECHAT = 'FREECHAT',
	LIVE_PREVIEW = 'LIVE_PREVIEW',
	VOYEUR_CHAT = 'VOYEUR_CHAT',
	VOYEUR_PRIVATE_CHAT = 'VOYEUR_PRIVATE_CHAT',
	PRIVATE_CHAT = 'PRIVATE_CHAT',
	CAM2CAM = 'CAM2CAM',
	NO_DOWNSTREAM = 'NO_DOWNSTREAM',
	GROUP_CHAT = 'GROUP_CHAT',
	CONTENT_PARTNERS_CHAT = 'CONTENT_PARTNERS_CHAT',
	VIDEO_CALL = 'VIDEO_CALL',
}

ConnectionWithChat

interface ConnectionWithChat {
	clientId: string;
	server: string;
}

Partner

interface Partner {
	id: string;
	key: string;
}

GiftCategory

enum GiftCategory {
	INVENTORY = -1,
	LOVE = 1,
	WINTER = 2,
	SUMMER = 3,
	FETISH = 4,
	BUNDLES = Infinity,
}

Features

interface Features {
	hasAudioMessages: boolean;
	hasBehaviorOnClickOutside: boolean;
	hasCam2Cam: boolean;
	hasClipping: boolean;
	hasCloseButton: boolean;
	hasEmoticons: boolean;
	hasExitPopups: boolean;
	hasFavorite: boolean;
	hasFocusTrap: boolean;
	hasFullscreen: boolean;
	hasFullscreenLayoutOnLandscape: boolean;
	hasGifts: boolean;
	hasGiftsSectionOpened: EnumGiftCategory | boolean;
	hasGroupChatClientName: boolean;
	hasGroupChatIndicator: boolean;
	hasHeader: boolean;
	hasHistoryMessages: boolean;
	hasImageUpload: boolean;
	hasLiveChat: boolean;
	hasLivePreview: boolean;
	hasMessageTranslation: boolean;
	hasMessagesGrouped2: boolean;
	hasMessagesGrouped: boolean;
	hasMessaging: boolean;
	hasModelMotto: boolean;
	hasModelName: boolean;
	hasModelProfilePicture: boolean;
	hasNewMobileMessengerUI: boolean;
	hasNewMobileUI: boolean;
	hasPreloaderPaymentText: boolean;
	hasPrivateChat: boolean;
	hasProductionDebugEnabled: boolean;
	hasSendMessageButton: boolean;
	hasSettings: boolean;
	hasTips: boolean;
	hasToyControl: boolean;
	hasUnreadMessageCounter: boolean;
	hasVideoCalls: boolean;
	hasVoyeur: boolean;
	isMessageTypeHeader: boolean;
	shouldUseTusUpload: boolean;
	shouldVipOnly: boolean;
	showCensoredContent: boolean;
}

ImperativeHandlers

interface ImperativeHandlers {
	toggleFullscreen: (event: SyntheticEvent) => void;
}

PreferenceStore

interface PreferenceStore {
	getPreference: (preferenceName: PreferenceName) => boolean | number | undefined;
	setPreference: (preferenceName: PreferenceName, value: boolean | number, instanceId?: string) => void;
}

Options

interface Options {
	tip?: TipOption;
	preloader?: string;
	analyticsKey?: string;
	updateBrowserLink?: string;
	switchToPortraitWidth?: number;
	initialVideochatVolume?: number;
	censoredContentImage?: string;
	useInjectableHlsPlayer?: boolean;
}
interface TipOption {
	value: number;
	currency: CurrencyName.EURO;
	imageUrl?: string;
}

Theme

interface Theme {
	name: string;
	typography: {
		fontFamily: string;
		secondaryFontFamily: string;
		weight: {
			thin: number;
			light: number;
			regular: number;
			medium: number;
			lightBold: number;
			bold: number;
		};
	};

	layout: {
		textMode: {
			minWidth: number;
			minHeight: number;
		};
		messageSection: {
			responsiveBreakpointWidth: number;
		};
		scrollBar: number;
	};

	colors: {
		messengerShadow: string;
		overlay: string;
		baseButtonIcon: string;
		recordingIcon: string;
		audioPlayerBackground: string;
		audioPlayerTime: string;
		audioPlayerPlay: string;
		audioPlayerKnob: string;
		audioPlayerRail: string;
		audioPlayerControlDisabled: string;
		fontSwitcherIcon: string;
		sendMessageButton: string;
		cancelRecordingHover: string;
		cam2CamBackgroundColor: string;
		cam2CamDisabledColor: string;
		cam2CamHoverColor: string;
		messengerWrapper: string;
		messengerClose: string;
		messengerSettings: string;
		messengerLoader: string;
		messageEditorBackgroundColor: string;
		messageEditorPlaceholderColor: string;
		messageEditorTextColor: string;
		mentionBackground: string;
		mentionHeaderText: string;
		mentionHeaderBackground: string;
		mentionHeaderBorderColor: string;
		mentionSectionBackground: string;
		paidMessageBuyButtonText: string;
		paidMessageBuyButtonBackground: string;
		paidMessageBuyButtonDisabledBackground: string;
		paidMessageLabelText: string;
		videoMessageTimeText: string;
		headerText: string;
		headerShadow: string;
		typing: {
			text: string;
			background: string;
			shadow: string;
		};
		typingBackground: string;
		imageLoader: string;

		settings: {
			text: string;
			textDisabled: string;
			shadow: string;
			divider: string;
			delete: string;
			selectedCameraIcon: string;
			ban: string;
			overlay: string;
		};

		groupChat: {
			modelInfoText: string;
			pictureBorder: string;

			userMessageText: string;
			userMessageBackground: string;
			usernameColorSet: string[];

			modelMessageText: string;
			modelMessageBackground: string;
			modelMessageUsername: string;

			clientMessageText: string;
			clientMessageBackground: string;
			clientName: string;

			participants: {
				bgColor: string;
				textColor: string;
			};
		};

		vipLivePreviewButton: {
			background: string;
			text: string;
		};

		vipOnly: {
			background: string;
			text: string;
		};

		chatBoxContainer: string;
		imageUrlContent: string;
		groupColor: string;
		groupTextColor: string;
		profileLink: string;
		profileLinkHover: string;
		profileMotto: string;
		messengerButtonInvertedBackground: string;
		messengerButtonHover: string;
		messengerButtonInvertedText: string;
		windowBackground: string;
		windowItemsShadow: string;
		windowPaginatorBackground: string;
		clientMessageBackground: string;
		fullscreenSystemMessageBackground: string;
		fullscreenSystemMessageText: string;
		modelMessageBackground: string;
		messageText: string;
		messageCircle: {
			background: string;
			shadow: string;
		};
		messagePlayButton: string;
		messageTextDeleted: string;
		messageTimeAgo: string;
		messageReadIndicator: string;
		messageUploadOverlay: string;
		videoChatBackground: string;
		videoChatControlsBackground: string;
		videoChatControlsBackgroundHover: string;
		videoChatControlsText: string;
		privateTicketShow: {
			background: string;
			divider: string;
		};

		onlineBulletColor: string;
		offlineBulletColor: string;

		purchaseButton: {
			defaultBackground: string;
			paidBackground: string;
			disabledBackground: string;

			defaultText: string;
			paidText: string;
		};

		selectedButton: {
			defaultBackground: string;
			defaultBackgroundHover: string;

			blackBackground: string;
			blackBackgroundHover: string;

			defaultDropDownBackground: string;
			blueBackgroundHover: string;
			greyBackgroundHover: string;

			text: string;
			clickHighlight: string;
		};
		videoCallButton: {
			background: string;
			text: string;
		};
		closeButton: {
			primaryColor: string;
			secondaryColor: string;
		};

		initiallyMutedButtonBackground: string;
		initiallyMutedToolTipContentText: string;

		checkedStrokeColor: string;
		checkedFillColor: string;

		dropDownContentBgColor: string;

		timerFillColor: string;
		timerUnfillColor: string;
		timerBgColor: string;
		timerTextColor: string;

		livePreviewTimer: {
			fillColor: string;
			unfillColor: string;
			bgColor: string;
			textColor: string;
		};

		editor: {
			background: string;
			text: string;
			placeholder: string;
		};

		giftPageTitle: string;
		giftPageText: string;
		giftOldPrice: string;
		giftPaginatorBorder: string;
		giftPageTitleBold: string;

		avsCam2CamPopup: {
			backgroundColor: string;
			smallTextColor: string;
			mainTextColor: string;
			button: {
				standard: string;
				onHover: string;
				textColor: string;
			};
		};

		dialogModal: {
			background: string;
			titleText: string;
			text: string;
			agreeButton: {
				standard: string;
				hover: string;
				text: string;
			};
			cancelButton: {
				standard: string;
				border: string;
				hover: string;
				text: string;
			};
		};

		preChatPopup: {
			backgroundColor: string;
			textColor: string;
		};

		actionResponse: {
			warningColor: string;
			errorColor: string;
			successfulColor: string;
			neutralColor: string;
		};

		userPrompt: {
			tipColor: string;
			tipPriceColor: string;
			tipButtonColor: string;
			tipButtonBackgroundColor: string;
			tipButtonBackgroundHoverColor: string;
		};
		modal: {
			closeButtonColor: string;
			backgroundColor: string;
		};

		toySection: {
			primaryColor: string;
			secondaryColor: string;
			lock: string;
			text: string;
			hoverTooltipText: string;
			headerSectionBackground: string;
			headerSectionBorderColor: string;
			optionText: string;
			headerSectionLink: string;
			inactiveToyMessageText: string;
		};

		informationDialog: {
			title: string;
		};

		messageTypeHeader: {
			modelTitle: string;
			title: string;
			subtitle: string;
		};

		expirationInfo: {
			background: string;
			title: string;
		};

		cameraSelection: {
			option: {
				defaultColor: string;
				selectedColor: string;
			};
		};

		mobileMenuWrapper: {
			backgroundColor: string;
			borderColor: string;
			itemBorderColor: string;
			lastItemBorderColor: string;
			itemWrapperColor: string;
		};

		noteButton: string;
		favoriteButton: string;
		giftsCloseButton: string;
		ban: {
			text: string;
			timer: string;
			divider: string;
		};

		messageMenu: {
			background: string;
			delete: string;
			shadow: string;
			longPressOverlay: string;
			longPressBackground: string;
		};
		reactions: {
			background: string;
			hoveredBackground: string;
			deleteText: string;
			primaryText: string;
		};
		contextMenu: {
			background: string;
		};
		progress: {
			background: string;
			bar: string;
		};
		uploadProgress: {
			circle: string;
			text: string;
			background: string;
		};
		scrollBar: {
			normal: string;
			hovered: string;
		};
		alert: {
			error: string;
			success: string;
			text: string;
		};
		dropZone: {
			accept: string;
			reject: string;
			text: string;
			background: string;
		};
		clipping: {
			time: {
				background: string;
				text: string;
			};
		};
		loading: {
			circle: {
				background: string;
				color: string;
				text: string;
			};
			text: string;
		};
	};
}

Hooks

interface Hooks {
	onClickProfile?: () => void;
	onClickImage?: (src: string, srcSet: string) => void;
	onClickVideo?: (hlsUrl: string, posterUrl: string) => void;
	onExit: (code?: ExitCode, message?: string, chatFlags?: string[], chatType?: EndChatType) => void;
	onRecharge?: () => void;
	onVideoChatAction?: (isVideoChatOpen: boolean, chatId?: string) => void;
	onExitPopupAction?: (isExitPopupOpen: boolean, code?: ExitCode, message?: string, chatType?: EndChatType) => void;
	onAgeVerification?: () => void;
	onModelStatusChange?: (isOnline: boolean) => void;
	onSupportedVideoChatTypes?: (videoChatTypes: VideoChatType[]) => void;
	onFailedChatConfig?: (error: any) => void;
	onFailedWebToken?: (error?: any) => void;
	onBecomeVIP?: () => void;
	onClickLobbyChat?: () => void;
	renderHlsPlayer?: (props: {
		hls?: string;
		originSrc?: string;
		poster?: string;
		isTranscoding?: boolean;
	}) => React.ReactNode;
	onKeyUp?: (props: {key: string; CmdOrCtrl: boolean}) => void;
	onMessageReport?: (props: {messageId?: string}) => void;
	onMessageSend?: (props: {text: string}) => Promise<boolean>;
	onMediaOfferPurchaseRequest?: (args: {messageId: string; channelId: string}) => void;
}

Methods exposed by VXMessenger

  • closeMessenger - will clean up the messenger: close opened sockets and reset the Redux store. Note: Before sending a new clientId and server, we advise calling the "closeMessenger" function, otherwise the messenger won't react to the newly received values. The correct steps would be:
    1. Send initial clientId & server to the vxmessenger
    2. Call the closeMessenger function for a clean new state of the messenger, when you want to send new connection values
    3. Send the new clientId & server to the vxmessenger

Integration

For displaying the messenger on iOS devices, an integration tip is:

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1,user-scalable=0" />

Simple React Example

import React from 'react';

import VXMessenger, {Language, VideoChatType} from '@visit-x/vxmessenger';
import '@visit-x/vxmessenger/styles.css';

import './App.css';

const props = {
	connection: {
		clientId: '00000000000000000000000000000000',
		server: 'ds.farm1.campoints.net',
	},
	language: Language.EN,
	initialVideoChatType: VideoChatType.LIVECHAT,
	features: {
		hasModelProfilePicture: false,
		hasModelMotto: false,
		hasModelName: false,
		hasCam2Cam: true,
		hasPrivateChat: false,
		hasExitPopups: false,
		hasFavorite: false,
		hasFullscreen: false,
		hasCloseButton: false,
	},
	hooks: {
		onClickProfile: function () {
			console.log('Clicked on profile');
		},
		onExit: function (exitCode, exitMessage, chatFlags) {
			console.log('Chat exited', {exitCode, exitMessage, chatFlags});
		},
		onRecharge: function () {
			console.log('onRecharge');
		},
		onVideoChatAction: function (isVideoOpen, chatId) {
			console.log('onVideoChatAction:isVideoOpen', isVideoOpen);
			console.log('onVideoChatAction:chatId', chatId);
		},
		onAgeVerification: function () {
			console.log('onAgeVerification');
		},
		onModelStatusChange: function (isOnline) {
			console.log('Model status changed to ', isOnline);
		},
		onSupportedVideoChatTypes: function (videoChatTypes) {
			console.log(videoChatTypes);
		},
		onFailedChatConfig: function (error) {
			console.log('Supplied chat conf has failed with the following reason: ', error);
		},
	},
};

function App() {
	return (
		<div className="App">
			<VXMessenger {...props} />
		</div>
	);
}

export default App;

Also, we could modify recent example to get access to fullscreen toggle:

import React, {useCallback, useRef} from 'react';

import VXMessenger, {ImperativeHandlers, Language, VideoChatType} from '@visit-x/vxmessenger';
import '@visit-x/vxmessenger/styles.css';

//...

function App() {
	const vxMessengerRef = useRef<ImperativeHandlers>(null);

	return (
		<div className="App">
			<VXMessenger {...props} ref={vxMessengerRef} />
			<button
				onClick={useCallback((event) => {
					vxMessengerRef?.current?.toggleFullscreen(event);
				}, [])}
			>
				Toggle fullscreen mode
			</button>
		</div>
	);
}

Please pay attention to event. It is required as an argument for toggleFullscreen because user has to interact with the page or a UI element in order for this feature to work.

Simple RequireJS Example

<!DOCTYPE html>
<html lang="en">
	<meta charset="utf-8" />
	<meta content="width=device-width, initial-scale=1, maximum-scale=1,user-scalable=0" name="viewport" />
	<title>VXMessenger - Simple RequireJS Example</title>
	<link href="https://vxmessenger.visit-x.net/releases/latest/styles.css" rel="stylesheet" type="text/css" />
	<script
		src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"
		type="application/javascript"
	></script>
	<script src="https://cdn.polyfill.io/v2/polyfill.min.js" type="application/javascript"></script>
	<script type="application/javascript">
		requirejs.config({
			baseUrl: '.',
			paths: {
				react: 'https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min',
				'react-dom': 'https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min',
				vxmessenger: 'https://vxmessenger.visit-x.net/releases/latest/vxmessenger.min',
				hooks: 'scripts/hooks',
				features: 'scripts/features',
				options: 'scripts/options',
				theme: 'scripts/theme',
			},
		});
		requirejs(['react', 'hooks', 'features', 'options', 'theme'], function (React, hooks, features, options, theme) {
			requirejs(['react-dom', 'vxmessenger'], function (ReactDom, VXMessenger) {
				const props = {
					connection: {
						webToken: '00000000000000000000',
						partner: {
							id: '0000',
							key: '00000000',
						},
						userKey: '000000',
					},
					language: 'en',
					hooks: hooks,
					features: features,
					options: options,
					theme: theme,
				};

				const Messenger = React.createElement(VXMessenger.default, props, null);

				ReactDom.render(Messenger, document.getElementById('root'));
			});
		});
	</script>

	<body>
		<div id="root">Loading...</div>
	</body>
</html>

Simple NextJs example

layout.tsx

import type {Metadata} from 'next';
import {Geist, Geist_Mono} from 'next/font/google';
import React from 'react';

import '@visit-x/vxmessenger/styles.css';

export const metadata: Metadata = {
	title: 'Create Next App',
	description: 'Generated by create next app',
};

export default function RootLayout({
	children,
}: Readonly<{
	children: React.ReactNode;
}>) {
	return (
		<html lang="en">
			<body>{children}</body>
		</html>
	);
}

page.tsx

'use client';

import dynamic from 'next/dynamic';
import React from 'react';
import {SupportedLanguage} from 'cmd-control-client-lib';

const VXmessanger = dynamic(() => import('@visit-x/vxmessenger'), {
	loading: () => <p>Loading Messenger...</p>,
	ssr: false,
});

export default function Home() {
	<VXmessanger
		connection={getConnection()}
		language={SupportedLanguage.DE}
		features={features}
		hooks={getHooks()}
		theme={getTheme()}
	/>
}

Exit Codes

If you want to configure certain behaviors on the "onExit" hook based on the incoming exit code, this information can be found inside the resources presented down below.

  • https://npm.runkit.com/cmd-control-client-lib/dist/protocol/command/resultcode.d.ts
  • https://npm.runkit.com/cmd-control-client-lib/dist/protocol/exit-code.d.ts

Tips & Tricks

  • Pressing CTRL+ALT+V (Windows) or CTRL+OPTIONS+V (Mac) will show the current VXMessenger's version for about 5 seconds; For mobile devices, seven consecutive clicks will do the job.

Built With

Libraries

Tooling