Skip to content

Commit

Permalink
Merge pull request #11 from Nonhuman-Nonsense/albin
Browse files Browse the repository at this point in the history
Albin
  • Loading branch information
albin-karlsson authored Apr 25, 2024
2 parents 56001e3 + 0c28951 commit f51c5fb
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 31 deletions.
8 changes: 7 additions & 1 deletion client/src/components/AudioOutput.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import React, { useEffect, useRef } from "react";

function AudioOutput({ currentAudioMessage, onFinishedPlaying }) {
function AudioOutput({ currentAudioMessage, onFinishedPlaying, stopAudio }) {
const audioRef = useRef(null);
const urlRef = useRef(null);
const checkPlaybackIntervalRef = useRef(null);

useEffect(() => {
audioRef.current && audioRef.current.pause();

console.log("Stopping audio...");
}, [stopAudio]);

useEffect(() => {
// Initialize the audio element if it does not exist
if (!audioRef.current) {
Expand Down
20 changes: 17 additions & 3 deletions client/src/components/ConversationControls.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
import React from "react";

function ConversationControls({ isPaused, onPauseResume, onSkipForward }) {
function ConversationControls({
isPaused,
onPauseResume,
onSkipForward,
onRaiseHandOrNevermind,
onSubmit,
isRaisedHand,
humanInterjection,
}) {
return (
<div style={{ pointerEvents: "auto" }}>
<button onClick={onPauseResume}>{isPaused ? "Resume" : "Pause"}</button>
<button onClick={onSkipForward}>Skip forward</button>
{/* <button onClick={onPauseResume}>{isPaused ? "Resume" : "Pause"}</button> */}
{!humanInterjection && (
<button onClick={onSkipForward}>Skip forward</button>
)}
{humanInterjection && <button onClick={onSubmit}>Submit</button>}
<button onClick={onRaiseHandOrNevermind}>
{isRaisedHand ? "Nevermind" : "Raise hand"}
</button>
</div>
);
}
Expand Down
100 changes: 91 additions & 9 deletions client/src/components/Council.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,25 @@ import Overlay from "./Overlay";
import CouncilOverlays from "./CouncilOverlays";
import Navbar from "./Navbar";
import Output from "./Output";
import ConversationControls from "./ConversationControls";
import useWindowSize from "../hooks/useWindowSize";
import HumanInput from "./HumanInput";

function Council({ options }) {
const { foods, humanName, topic } = options;
const [activeOverlay, setActiveOverlay] = useState("");
const { width: screenWidth } = useWindowSize();
const [textMessages, setTextMessages] = useState([]); // State to store conversation updates
const [audioMessages, setAudioMessages] = useState([]); // To store multiple ArrayBuffers
const [isReady, setIsReady] = useState(false);
const [isRaisedHand, setIsRaisedHand] = useState(false);
const [humanInterjection, setHumanInterjection] = useState(false);
const [skipForward, setSkipForward] = useState(false);
const [newTopic, setNewTopic] = useState("");
const [interjectionCounter, setInterjectionCounter] = useState(-1000);
const [interjectionReplyRecieved, setInterjectionReplyRecieved] =
useState(false);

const socketRef = useRef(null); // Using useRef to persist socket instance

const foodsContainerStyle = {
Expand All @@ -28,39 +39,92 @@ function Council({ options }) {
};

useEffect(() => {
initializeConversation(); // Call the function to start the conversation when component mounts
}, []);

// Function to initialize or restart the conversation
const initializeConversation = (customTopic) => {
const topicToSend = customTopic || topic; // Use custom topic if provided, else use default topic

socketRef.current = io();

// Send initial data to start the conversation
let promptsAndOptions = {
const promptsAndOptions = {
options: {
...globalOptions,
humanName,
raiseHandPrompt: false,
neverMindPrompt: false,
},
name: "New room",
topic,
topic: topicToSend,
characters: foods,
};

socketRef.current.emit("start_conversation", promptsAndOptions);

// Listen for conversation text updates
socketRef.current.on("conversation_update", (textMessage) => {
setInterjectionCounter((prev) => prev + 1);
setTextMessages((prev) => [...prev, textMessage]);
});

// Listen for audio updates
socketRef.current.on("audio_update", (audioMessage) => {
setInterjectionCounter((prev) => prev + 1);
setAudioMessages((prevAudioMessages) => [
...prevAudioMessages,
audioMessage,
]);
});
};

useEffect(() => {
if (interjectionCounter === 2) {
setInterjectionReplyRecieved(true);
setHumanInterjection(false);
setIsRaisedHand(false);
}
}, [interjectionCounter]);

return () => {
socketRef.current.disconnect();
function handleOnResetInterjectionReply() {
setInterjectionReplyRecieved(false);
}

function handleOnIsReady() {
setIsReady(true);
}

function handleOnSkipForward() {
setSkipForward(!skipForward);
}

function handleOnSubmit() {
const promptsAndOptions = {
options: {
...globalOptions,
humanName,
raiseHandPrompt: newTopic,
neverMindPrompt: false,
},
name: "New room",
topic: newTopic,
characters: foods,
};
}, []);

setInterjectionCounter(() => 0);

socketRef.current.emit("raise_hand", promptsAndOptions);
}

function handleOnRaiseHandOrNevermind() {
setIsRaisedHand((prev) => !prev);
}

function handleOnHumanInterjection(value) {
setHumanInterjection(value);
}

function handleOnAddNewTopic(newTopic) {
setNewTopic(newTopic);
}

function displayResetWarning() {
setActiveOverlay("reset");
Expand All @@ -82,12 +146,30 @@ function Council({ options }) {
className="text-container"
style={{ justifyContent: "end" }}
>
{/* Render the Output component regardless of the overlay */}
{humanInterjection && (
<HumanInput onAddNewTopic={handleOnAddNewTopic} />
)}
<Output
textMessages={textMessages}
audioMessages={audioMessages}
isActiveOverlay={activeOverlay !== ""}
isRaisedHand={isRaisedHand}
onIsReady={handleOnIsReady}
onHumanInterjection={handleOnHumanInterjection}
humanInterjection={humanInterjection}
skipForward={skipForward}
interjectionReplyRecieved={interjectionReplyRecieved}
onResetInterjectionReply={handleOnResetInterjectionReply}
/>
{isReady && (
<ConversationControls
onSkipForward={handleOnSkipForward}
onRaiseHandOrNevermind={handleOnRaiseHandOrNevermind}
onSubmit={handleOnSubmit}
isRaisedHand={isRaisedHand}
humanInterjection={humanInterjection}
/>
)}
</div>
<div style={foodsContainerStyle}>
{foods.map((food, index) => (
Expand Down
19 changes: 19 additions & 0 deletions client/src/components/HumanInput.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";

function HumanInput({ onAddNewTopic }) {
function handleOnInput(e) {
onAddNewTopic(e.target.value);
}

return (
<div style={{ pointerEvents: "auto" }}>
<textarea
onInput={handleOnInput}
rows="2"
placeholder="your input"
/>
</div>
);
}

export default HumanInput;
104 changes: 89 additions & 15 deletions client/src/components/Output.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,123 @@ import React, { useState, useEffect } from "react";
import TextOutput from "./TextOutput";
import AudioOutput from "./AudioOutput";

function Output({ textMessages, audioMessages, isActiveOverlay }) {
function Output({
textMessages,
audioMessages,
isActiveOverlay,
isRaisedHand,
onIsReady,
onHumanInterjection,
humanInterjection,
skipForward,
interjectionReplyRecieved,
onResetInterjectionReply,
}) {
const [currentMessageIndex, setCurrentMessageIndex] = useState(0);
const [currentTextMessage, setCurrentTextMessage] = useState(null);
const [currentAudioMessage, setCurrentAudioMessage] = useState(null);
const [stopAudio, setStopAudio] = useState(false);

useEffect(() => {
tryFindTextAndAudio();
}, [currentMessageIndex, textMessages, audioMessages]);
if (interjectionReplyRecieved) {
console.log(
"Should be about dancing: ",
textMessages[textMessages.length - 1].text
);

function tryFindTextAndAudio() {
setCurrentTextMessage(textMessages[textMessages.length - 1]);
setCurrentAudioMessage(audioMessages[audioMessages.length - 1]);

onResetInterjectionReply();
}
}, [interjectionReplyRecieved]);

useEffect(() => {
if (currentTextMessage && currentAudioMessage) {
proceedToNextMessage();
}
}, [skipForward]);

// useEffect for checking for raised hand when changing message index (inbetween food talking)
useEffect(() => {
// Check to see if the hand is raised
if (isRaisedHand) {
setStopAudio(!stopAudio);
onHumanInterjection(true);
} else {
findTextAndAudio();
}
}, [currentMessageIndex]);

// useEffect for nevermind when adding new input
useEffect(() => {
if (
!isRaisedHand &&
currentTextMessage === null &&
currentAudioMessage === null &&
!interjectionReplyRecieved
) {
onHumanInterjection(false);
findTextAndAudio();
}
}, [isRaisedHand]);

useEffect(() => {
console.log("Amount of text messages :", textMessages.length);
console.log("Amount of voice messages :", audioMessages.length);

console.log("Text messages:", textMessages);
console.log("Audio messages:", audioMessages);

findTextAndAudio();
}, [textMessages, audioMessages]);

function findTextAndAudio() {
const textMessage = textMessages[currentMessageIndex];
const audioMessage = audioMessages.find(
(a) => a.message_index === currentMessageIndex
);
const audioMessage = audioMessages.find((a) => a.id === textMessage.id);

if (
textMessage &&
audioMessage &&
!currentTextMessage &&
!currentAudioMessage
!currentAudioMessage &&
!isRaisedHand &&
!interjectionReplyRecieved
) {
console.log("Both found!");

// Set isReady to true in the parent component to render the controls (for skipping forward et.c.)
onIsReady();

setCurrentTextMessage((prev) => textMessage);
setCurrentAudioMessage((prev) => audioMessage);
}
}

function handleOnFinishedPlaying() {
setCurrentTextMessage((prev) => null);
setCurrentAudioMessage((prev) => null);
function proceedToNextMessage() {
setCurrentTextMessage(() => null);
setCurrentAudioMessage(() => null);

console.log("Current index: ", currentMessageIndex);
setCurrentMessageIndex((prev) => prev + 1);
}

function handleOnFinishedPlaying() {
proceedToNextMessage();
}

return (
<>
<TextOutput
currentTextMessage={currentTextMessage}
currentAudioMessage={currentAudioMessage}
/>
{!humanInterjection && (
<TextOutput
currentTextMessage={currentTextMessage}
currentAudioMessage={currentAudioMessage}
/>
)}
<AudioOutput
currentAudioMessage={currentAudioMessage}
onFinishedPlaying={handleOnFinishedPlaying}
stopAudio={stopAudio}
/>
</>
);
Expand Down
11 changes: 8 additions & 3 deletions client/src/components/TextOutput.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react";

function TextOutput({ currentTextMessage }) {
function TextOutput({ currentTextMessage, currentAudioMessage }) {
const [currentSnippetIndex, setCurrentSnippetIndex] = useState(0);
const [currentSnippet, setCurrentSnippet] = useState("");

Expand All @@ -10,7 +10,7 @@ function TextOutput({ currentTextMessage }) {
}, [currentTextMessage]);

useEffect(() => {
if (currentTextMessage && currentTextMessage.text) {
if (currentTextMessage && currentTextMessage.text && currentAudioMessage) {
const text = currentTextMessage.text;
// Split the text into sentences, ignoring periods followed by a number
const sentences = text.split(/(?<=[.!?])(?=\s+(?![0-9]))/);
Expand All @@ -23,7 +23,12 @@ function TextOutput({ currentTextMessage }) {
}, calculateDisplayTime(currentSnippet) * 1000);
return () => clearInterval(interval);
}
}, [currentTextMessage, currentSnippetIndex]);
}, [
currentTextMessage,
currentSnippetIndex,
currentAudioMessage,
currentSnippet,
]);

// Calculate the display time based on the number of characters in the snippet
const calculateDisplayTime = (text) => {
Expand Down

0 comments on commit f51c5fb

Please sign in to comment.