mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-20 15:36:18 +00:00
🆕 Added the youtube player (not the furni)
This commit is contained in:
@@ -17,5 +17,7 @@
|
|||||||
"widget.room.chat.hide_avatars": "Verberg avatars",
|
"widget.room.chat.hide_avatars": "Verberg avatars",
|
||||||
"widget.room.chat.hide_balloon": "Verberg Spreekballon",
|
"widget.room.chat.hide_balloon": "Verberg Spreekballon",
|
||||||
"widget.room.chat.show_balloon": "Spreekballon",
|
"widget.room.chat.show_balloon": "Spreekballon",
|
||||||
"widget.room.chat.clear_history": "leeg geschiedenis"
|
"widget.room.chat.clear_history": "leeg geschiedenis",
|
||||||
|
"widget.room.youtube.shared": "YouTube word gedeeld",
|
||||||
|
"widget.room.youtube.open_video": "Open de video"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { GetConfigurationValue } from '../nitro/GetConfigurationValue';
|
||||||
|
import { LocalizeText } from './LocalizeText';
|
||||||
|
|
||||||
const allowedColours: Map<string, string> = new Map();
|
const allowedColours: Map<string, string> = new Map();
|
||||||
|
|
||||||
allowedColours.set('r', 'red');
|
allowedColours.set('r', 'red');
|
||||||
@@ -44,6 +47,16 @@ export const RoomChatFormatter = (content: string) =>
|
|||||||
content = encodeHTML(content);
|
content = encodeHTML(content);
|
||||||
//content = (joypixels.shortnameToUnicode(content) as string)
|
//content = (joypixels.shortnameToUnicode(content) as string)
|
||||||
|
|
||||||
|
if(!GetConfigurationValue<boolean>('youtube.publish.disabled', false))
|
||||||
|
{
|
||||||
|
const labelShared = LocalizeText('widget.room.youtube.shared');
|
||||||
|
const labelOpen = LocalizeText('widget.room.youtube.open_video');
|
||||||
|
content = content.replace(
|
||||||
|
/(?:http:\/\/|https:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?.*v=|shorts\/)?([a-zA-Z0-9_-]{11})/g,
|
||||||
|
`<div style="margin:2px 0"><strong>📺 ${ labelShared }</strong></div><div><a href="https://youtu.be/$1" target="_blank" style="background-color:red;color:white;padding:3px 8px;border-radius:4px;text-decoration:none;font-size:12px">▶ ${ labelOpen }</a></div>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if(content.startsWith('@') && content.indexOf('@', 1) > -1)
|
if(content.startsWith('@') && content.indexOf('@', 1) > -1)
|
||||||
{
|
{
|
||||||
let match = null;
|
let match = null;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { ToolbarView } from './toolbar/ToolbarView';
|
|||||||
import { UserProfileView } from './user-profile/UserProfileView';
|
import { UserProfileView } from './user-profile/UserProfileView';
|
||||||
import { UserSettingsView } from './user-settings/UserSettingsView';
|
import { UserSettingsView } from './user-settings/UserSettingsView';
|
||||||
import { WiredView } from './wired/WiredView';
|
import { WiredView } from './wired/WiredView';
|
||||||
|
import { YoutubeTvView } from './youtube-tv/YoutubeTvView';
|
||||||
|
|
||||||
export const MainView: FC<{}> = props =>
|
export const MainView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
@@ -114,6 +115,7 @@ export const MainView: FC<{}> = props =>
|
|||||||
<CampaignView />
|
<CampaignView />
|
||||||
<GameCenterView />
|
<GameCenterView />
|
||||||
<FloorplanEditorView />
|
<FloorplanEditorView />
|
||||||
|
<YoutubeTvView />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ import { AddLinkEventTracker, ILinkEventTracker, RemoveLinkEventTracker } from '
|
|||||||
import { FC, useEffect, useMemo, useRef, useState } from 'react';
|
import { FC, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { ChatEntryType, LocalizeText } from '../../api';
|
import { ChatEntryType, LocalizeText } from '../../api';
|
||||||
import { Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common';
|
import { Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common';
|
||||||
import { useChatHistory } from '../../hooks';
|
import { useChatHistory, useOnClickChat } from '../../hooks';
|
||||||
import { NitroInput } from '../../layout';
|
import { NitroInput } from '../../layout';
|
||||||
|
|
||||||
export const ChatHistoryView: FC<{}> = props => {
|
export const ChatHistoryView: FC<{}> = props => {
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
const [searchText, setSearchText] = useState<string>('');
|
const [searchText, setSearchText] = useState<string>('');
|
||||||
const {chatHistory = []} = useChatHistory();
|
const {chatHistory = []} = useChatHistory();
|
||||||
|
const { onClickChat } = useOnClickChat();
|
||||||
const elementRef = useRef<HTMLDivElement>(null);
|
const elementRef = useRef<HTMLDivElement>(null);
|
||||||
const isFirstRender = useRef(true);
|
const isFirstRender = useRef(true);
|
||||||
const prevChatLength = useRef<number>(0);
|
const prevChatLength = useRef<number>(0);
|
||||||
@@ -94,7 +95,7 @@ export const ChatHistoryView: FC<{}> = props => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="chat-content">
|
<div className="chat-content">
|
||||||
<b className="mr-1 username" dangerouslySetInnerHTML={{__html: `${row.name}: `}} />
|
<b className="mr-1 username" dangerouslySetInnerHTML={{__html: `${row.name}: `}} />
|
||||||
<span className="message" dangerouslySetInnerHTML={{__html: `${row.message}`}} />
|
<span className="message" dangerouslySetInnerHTML={{__html: `${row.message}`}} onClick={ onClickChat } />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { GetRoomEngine, RoomChatSettings, RoomObjectCategory } from '@nitrots/nitro-renderer';
|
import { GetRoomEngine, RoomChatSettings, RoomObjectCategory } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useEffect, useMemo, useRef, useState } from 'react';
|
import { FC, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { ChatBubbleMessage } from '../../../../api';
|
import { ChatBubbleMessage } from '../../../../api';
|
||||||
|
import { useOnClickChat } from '../../../../hooks';
|
||||||
|
|
||||||
interface ChatWidgetMessageViewProps
|
interface ChatWidgetMessageViewProps
|
||||||
{
|
{
|
||||||
@@ -18,6 +19,7 @@ export const ChatWidgetMessageView: FC<ChatWidgetMessageViewProps> = ({
|
|||||||
const [ isVisible, setIsVisible ] = useState(false);
|
const [ isVisible, setIsVisible ] = useState(false);
|
||||||
const [ isReady, setIsReady ] = useState(false);
|
const [ isReady, setIsReady ] = useState(false);
|
||||||
const elementRef = useRef<HTMLDivElement>(null);
|
const elementRef = useRef<HTMLDivElement>(null);
|
||||||
|
const { onClickChat } = useOnClickChat();
|
||||||
|
|
||||||
const getBubbleWidth = useMemo(() =>
|
const getBubbleWidth = useMemo(() =>
|
||||||
{
|
{
|
||||||
@@ -90,7 +92,7 @@ export const ChatWidgetMessageView: FC<ChatWidgetMessageViewProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="chat-content py-[5px] px-[6px] ml-[27px] leading-none min-h-[25px]">
|
<div className="chat-content py-[5px] px-[6px] ml-[27px] leading-none min-h-[25px]">
|
||||||
<b className="username" dangerouslySetInnerHTML={ { __html: `${ chat.username }: ` } } />
|
<b className="username" dangerouslySetInnerHTML={ { __html: `${ chat.username }: ` } } />
|
||||||
<span className="message" dangerouslySetInnerHTML={ { __html: `${ chat.formattedText }` } } />
|
<span className="message" dangerouslySetInnerHTML={ { __html: `${ chat.formattedText }` } } onClick={ onClickChat } />
|
||||||
</div>
|
</div>
|
||||||
<div className="pointer absolute left-[50%] translate-x-[-50%] w-[9px] h-[6px] bottom-[-5px]" />
|
<div className="pointer absolute left-[50%] translate-x-[-50%] w-[9px] h-[6px] bottom-[-5px]" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { AddLinkEventTracker, RemoveLinkEventTracker } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { GetConfigurationValue } from '../../api';
|
||||||
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../common';
|
||||||
|
|
||||||
|
export const YoutubeTvView: FC<{}> = props =>
|
||||||
|
{
|
||||||
|
const [ videoId, setVideoId ] = useState<string>('');
|
||||||
|
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const close = () => setIsVisible(false);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
const linkTracker: ILinkEventTracker = {
|
||||||
|
linkReceived: (url: string) =>
|
||||||
|
{
|
||||||
|
const parts = url.split('/');
|
||||||
|
|
||||||
|
if(parts.length < 3) return;
|
||||||
|
|
||||||
|
switch(parts[1])
|
||||||
|
{
|
||||||
|
case 'show':
|
||||||
|
setVideoId(parts[2]);
|
||||||
|
setIsVisible(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
eventUrlPrefix: 'youtube-tv/'
|
||||||
|
};
|
||||||
|
|
||||||
|
AddLinkEventTracker(linkTracker);
|
||||||
|
|
||||||
|
return () => RemoveLinkEventTracker(linkTracker);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const originUrl = useMemo(() => GetConfigurationValue<string>('url.prefix', ''), []);
|
||||||
|
|
||||||
|
if(!isVisible) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NitroCardView className="w-[560px] h-[345px]" uniqueKey="youtube-tv">
|
||||||
|
<NitroCardHeaderView headerText="YouTube TV" onCloseClick={ close } />
|
||||||
|
<NitroCardContentView grow gap={ 0 } overflow="hidden">
|
||||||
|
{ (videoId.length > 0) &&
|
||||||
|
<iframe
|
||||||
|
className="w-full h-full border-0"
|
||||||
|
allowFullScreen
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
|
src={ `https://www.youtube.com/embed/${ videoId }?autoplay=1&mute=0&controls=1&origin=${ originUrl }&playsinline=1&showinfo=0&rel=0&iv_load_policy=3&modestbranding=1&disablekb=1&enablejsapi=1&widgetid=3` }
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</NitroCardContentView>
|
||||||
|
</NitroCardView>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './YoutubeTvView';
|
||||||
@@ -23,3 +23,4 @@ export * from './useLocalStorage';
|
|||||||
export * from './useSharedVisibility';
|
export * from './useSharedVisibility';
|
||||||
export * from './wired';
|
export * from './wired';
|
||||||
export * from './useChatWindow';
|
export * from './useChatWindow';
|
||||||
|
export * from './useOnClickChat';
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { CreateLinkEvent } from '@nitrots/nitro-renderer';
|
||||||
|
import { useBetween } from 'use-between';
|
||||||
|
import { LocalizeText } from '../api';
|
||||||
|
import { useNotification } from './notification';
|
||||||
|
|
||||||
|
const YOUTUBE_REGEX = /(?:http:\/\/|https:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?.*v=|shorts\/)?([a-zA-Z0-9_-]{11})/;
|
||||||
|
|
||||||
|
const useOnClickChatState = () =>
|
||||||
|
{
|
||||||
|
const { showConfirm = null } = useNotification();
|
||||||
|
|
||||||
|
const onClickChat = (event: React.MouseEvent<HTMLElement>) =>
|
||||||
|
{
|
||||||
|
if(!(event.target instanceof HTMLAnchorElement) || !event.target.href) return;
|
||||||
|
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const url = event.target.href;
|
||||||
|
const youtubeMatch = url.match(YOUTUBE_REGEX);
|
||||||
|
|
||||||
|
if(youtubeMatch)
|
||||||
|
{
|
||||||
|
CreateLinkEvent('youtube-tv/show/' + youtubeMatch[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
showConfirm(LocalizeText('chat.confirm.openurl', [ 'url' ], [ url ]), () =>
|
||||||
|
{
|
||||||
|
window.open(url, '_blank');
|
||||||
|
}, null, null, null, LocalizeText('generic.alert.title'), null, 'link');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { onClickChat };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useOnClickChat = () => useBetween(useOnClickChatState);
|
||||||
Reference in New Issue
Block a user