diff --git a/packages/uiweb/src/lib/components/chat/MessageInput/MessageInput.tsx b/packages/uiweb/src/lib/components/chat/MessageInput/MessageInput.tsx index 9aaa33add..3204f325d 100644 --- a/packages/uiweb/src/lib/components/chat/MessageInput/MessageInput.tsx +++ b/packages/uiweb/src/lib/components/chat/MessageInput/MessageInput.tsx @@ -24,13 +24,15 @@ import { ConnectButtonComp } from '../ConnectButton'; import { Modal, ModalHeader } from '../reusables/Modal'; import { ThemeContext } from '../theme/ThemeProvider'; -import { PUBLIC_GOOGLE_TOKEN, device } from '../../../config'; +import { FRAMES_REGISTRY_URL, PUBLIC_GOOGLE_TOKEN, device } from '../../../config'; import usePushUser from '../../../hooks/usePushUser'; import { MODAL_BACKGROUND_TYPE, MODAL_POSITION_TYPE, type FileMessageContent } from '../../../types'; import { GIFType, Group, IChatTheme, MessageInputProps } from '../exportedTypes'; import { checkIfAccessVerifiedGroup } from '../helpers'; import { InfoContainer } from '../reusables'; -import { IChatInfoResponse } from '../types'; + +import { IChatInfoResponse, FrameCommand } from '../types'; +import { extractDynamicArgs } from '../../../utilities/getFrameUrlDynamicParams'; /** * @interface IThemeProps @@ -85,7 +87,8 @@ export const MessageInput: React.FC = ({ const [isRules, setIsRules] = useState(false); const [isMember, setIsMember] = useState(false); const [formattedChatId, setFormattedChatId] = useState(''); - + const [allFrameCommands, setAllFrameCommands] = useState([]); // all available frame commands + const [filteredFrameCommands, setFilteredFrameCommands] = useState([]); // filtered frame commands based on typed command const { getGroupByIDnew } = useGetGroupByIDnew(); const [groupInfo, setGroupInfo] = useState(null); @@ -119,6 +122,22 @@ export const MessageInput: React.FC = ({ const onChangeTypedMessage = (val: string) => { setTypedMessage(val); + if (val === '') setFilteredFrameCommands([]); + // Check if the message contains a slash and process accordingly + if (val.includes('/')) { + const parts = val.split(' '); + + const commandIndex = parts.findIndex((part) => part.startsWith('/')); + + if (commandIndex !== -1) { + // Filter commands that start with the typed command part + const matchingCommands = allFrameCommands.filter((command) => command.command.startsWith(parts[commandIndex])); + + if (matchingCommands.length > 0) { + setFilteredFrameCommands(matchingCommands); + } + } + } }; useClickAway(modalRef, () => { setShowEmojis(false); @@ -249,6 +268,20 @@ export const MessageInput: React.FC = ({ } }, [chatAcceptStream]); + // fetch all available frame commands on mount + useEffect(() => { + const fetchAllFrameCommands = async () => { + console.log('fetching all frame commands'); + const response = await fetch(FRAMES_REGISTRY_URL); + const data = await response.json(); + if (data) { + console.log('data', data); + setAllFrameCommands(data); + } + }; + fetchAllFrameCommands(); + }, []); + const transformGroupDetails = (item: any): void => { if (groupInfo?.chatId === item?.chatId) { const updatedGroupInfo = groupInfo; @@ -387,11 +420,32 @@ export const MessageInput: React.FC = ({ const sendPushMessage = async (content: string, type: string) => { try { + if (content.includes('/')) { + const parts = content.split(' '); + const commandIndex = parts.findIndex((part) => part.startsWith('/')); + const frameCommand = parts[commandIndex]; + const matchingCommand = allFrameCommands.find((command) => command.command === frameCommand); + if (matchingCommand) { + let url = matchingCommand.url; + const dynamicArgs = extractDynamicArgs(url); + const dynamicArgsValues = parts.slice(commandIndex + 1, parts.length); + + dynamicArgs.forEach((arg, index) => { + url = url.replace(`\${${arg}}`, dynamicArgsValues[index]); + }); + const textBeforeCommand = parts.slice(0, commandIndex).join(' '); + content = `${textBeforeCommand} ${url} ${dynamicArgsValues.slice(dynamicArgs.length).join(' ')}`; + } + + setFilteredFrameCommands([]); + } + const sendMessageResponse = await sendMessage({ message: content, chatId: formattedChatId, messageType: type as any, }); + if (sendMessageResponse && typeof sendMessageResponse === 'string' && sendMessageResponse.includes('403')) { setAccessControl(chatId, true); setVerified(false); @@ -401,7 +455,6 @@ export const MessageInput: React.FC = ({ console.log(error); } }; - const sendTextMsg = async () => { if (typedMessage.trim() !== '') { await sendPushMessage(typedMessage as string, 'Text'); @@ -433,7 +486,53 @@ export const MessageInput: React.FC = ({ justifyContent="space-between" alignItems="center" className={chatInfo?.list === 'REQUESTS' ? 'hide' : ''} + position="relative" > + {filteredFrameCommands && filteredFrameCommands.length > 0 && ( +
+ {filteredFrameCommands.map((command, index) => ( +
+ + {command.command} + {`${ + extractDynamicArgs(command.url).length > 0 ? ` [${extractDynamicArgs(command.url).join('] [')}]` : `` + }`} + + + {command.description ?? 'This Frame does something '} + +
+ ))} +
+ )} = ({ /> )} + ; recipient?: string; } + +export interface FrameCommand { + owner: string; + command: string; + url: string; + description?: string; +} export * from './tokenGatedGroupCreationType'; diff --git a/packages/uiweb/src/lib/config/constants.ts b/packages/uiweb/src/lib/config/constants.ts index c1a13971d..b125a3541 100644 --- a/packages/uiweb/src/lib/config/constants.ts +++ b/packages/uiweb/src/lib/config/constants.ts @@ -99,7 +99,9 @@ export const allowedNetworks = { 123, // for fuse testnet 80085, // for berachain testnet 2442, // polygon zkevm + 111557560 // cyber connect testnet + ], staging: [ // 42, //for kovan @@ -111,7 +113,9 @@ export const allowedNetworks = { 123, // for fuse testnet 80085, // for berachain testnet 2442, // polygon zkevm + 111557560 // cyber connect testnet + ], local: [ 11155111, // for eth sepolia @@ -122,7 +126,9 @@ export const allowedNetworks = { 123, // for fuse testnet 80085, // for berachain testnet 2442, // polygon zkevm + 111557560 // cyber connect testnet + ], }; @@ -152,8 +158,7 @@ export const FILE_ICON = (extension: string) => // Livekit Server URLs export const LIVEKIT_SERVER_URL = 'https://spacev2-demo-17wvllxz.livekit.cloud'; -export const LIVEKIT_SERVER_WEBSOCKET_URL = - 'wss://spacev2-demo-17wvllxz.livekit.cloud'; -export const LIVEKIT_TOKEN_GENERATOR_SERVER_URL = - 'https://ms-lk-server.onrender.com'; +export const LIVEKIT_SERVER_WEBSOCKET_URL = 'wss://spacev2-demo-17wvllxz.livekit.cloud'; +export const LIVEKIT_TOKEN_GENERATOR_SERVER_URL = 'https://ms-lk-server.onrender.com'; export const GUEST_MODE_ACCOUNT = '0x0000000000000000000000000000000000000001'; +export const FRAMES_REGISTRY_URL = 'https://frames.push.org/api/register-frame'; diff --git a/packages/uiweb/src/lib/utilities/getFrameUrlDynamicParams.ts b/packages/uiweb/src/lib/utilities/getFrameUrlDynamicParams.ts new file mode 100644 index 000000000..8e7e5e796 --- /dev/null +++ b/packages/uiweb/src/lib/utilities/getFrameUrlDynamicParams.ts @@ -0,0 +1,11 @@ +export function extractDynamicArgs(str: string) { + const regex = /\$\{([^}]+)\}/g; + const matches = []; + let match; + + while ((match = regex.exec(str)) !== null) { + matches.push(match[1]); + } + + return matches; +}