Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OV-347: Connect Generate Script With Studio Page #381

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6f1c54e
OV-347: * initial step to connect script generation with studio
lfelix3011 Sep 19, 2024
3662d04
OV-347: * move logic to slice insted fo componets, move lofic to add …
lfelix3011 Sep 19, 2024
2f7b0a1
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 20, 2024
147990a
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 20, 2024
06025f3
OV-347: * remove typing efect of generate script
lfelix3011 Sep 20, 2024
ae70f16
OV-347: * manage state when creating video script from ai
lfelix3011 Sep 20, 2024
48731c7
OV-347: + avatars to scene
lfelix3011 Sep 20, 2024
79e2426
OV-347: * adjust names of methods and add default avatar to scene
lfelix3011 Sep 20, 2024
8cd3a99
OV-347: * move general logic of videos created with ai to studio page
lfelix3011 Sep 21, 2024
662eb58
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 23, 2024
1d95554
OV-347: * create constant for magic values, use absolute path, move l…
lfelix3011 Sep 23, 2024
713eece
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 24, 2024
cef9e6f
OV-347: * duplicated code in merge, and Math.ceil insted of round
lfelix3011 Sep 24, 2024
3f1e1fb
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 25, 2024
f69a23a
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 25, 2024
ffbaa7c
OV-347: * use of videoScripts from redux and not pass as property
lfelix3011 Sep 25, 2024
2355bda
OV-347: * remove folders to equal structure of the others
lfelix3011 Sep 25, 2024
72e8260
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 26, 2024
39bd790
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,19 +1,43 @@
import { Box, Loader, VStack } from '~/bundles/common/components/components.js';
import {
Box,
Button,
Flex,
Loader,
Navigate,
VStack,
} from '~/bundles/common/components/components.js';
import { EMPTY_VALUE } from '~/bundles/common/constants/constants.js';
import { DataStatus } from '~/bundles/common/enums/data-status.enum.js';
import { useAppSelector } from '~/bundles/common/hooks/hooks.js';
import { AppRoute } from '~/bundles/common/enums/enums.js';
import {
useAppDispatch,
useAppSelector,
useCallback,
useEffect,
useMemo,
useState,
} from '~/bundles/common/hooks/hooks.js';
import { IconName } from '~/bundles/common/icons/icons.js';
import { actions as studioActions } from '~/bundles/studio/store/studio.js';

import { GenerateScriptPlaceholderContent } from '../generate-script-placeholder-content/generate-script-placeholder-content.js';
import { GenerateScriptScene } from '../generate-script-scene/generate-script-scene.js';
import styles from './styles.module.css';

const GenerateScriptPlaceholder: React.FC = () => {
const { dataStatus, videoScripts, videoScriptErrorMessage } =
useAppSelector(({ chat }) => ({
type Properties = {
onClose: () => void;
};

const GenerateScriptPlaceholder: React.FC<Properties> = ({ onClose }) => {
const dispatch = useAppDispatch();
const [shouldRedirect, setShouldRedirect] = useState(false);
const [isScriptAdded, setIsScriptAdded] = useState(false);
const { dataStatus, avatars, videoScripts, videoScriptErrorMessage } =
useAppSelector(({ chat, studio }) => ({
dataStatus: chat.dataStatus,
videoScripts: chat.videoScripts,
videoScriptErrorMessage: chat.videoScriptErrorMessage,
avatars: studio.avatars,
}));

const renderLoadingState = (): React.ReactNode => (
Expand Down Expand Up @@ -54,13 +78,54 @@ const GenerateScriptPlaceholder: React.FC = () => {
if (videoScripts.length === EMPTY_VALUE) {
return renderEmptyState();
}

return renderScripts();
};

const isScriptAvailable: boolean = useMemo(() => {
return (
dataStatus !== DataStatus.PENDING &&
videoScripts.length > EMPTY_VALUE
);
}, [dataStatus, videoScripts]);

const goToStudio = useCallback(() => {
dispatch(studioActions.addGeneratedVideoScript(videoScripts));
setIsScriptAdded(true);
}, [dispatch, videoScripts]);

useEffect(() => {
if (avatars.length === EMPTY_VALUE) {
void dispatch(studioActions.loadAvatars());
}
}, [dispatch, avatars.length]);

useEffect(() => {
if (isScriptAdded) {
setShouldRedirect(true);
onClose();
}
}, [isScriptAdded, onClose]);

if (shouldRedirect) {
return <Navigate to={AppRoute.STUDIO} replace />;
}

return (
<VStack className={styles['script-placeholder-container']}>
{getContent()}
</VStack>
<Flex className={styles['script-placeholder-container']}>
<VStack className={styles['script-placeholder-content']}>
{getContent()}
</VStack>

{isScriptAvailable && (
<Button
type="button"
label="Create Video"
className={styles['script-placeholder-button']}
onClick={goToStudio}
/>
)}
</Flex>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
.script-placeholder-container {
width: 100%;
flex-direction: column;
align-items: flex-end;
}
.script-placeholder-content {
width: 100%;
height: 100vh;
min-height: 480px;
Expand All @@ -7,3 +12,7 @@
padding: 40px;
gap: 10px;
}
.script-placeholder-button {
width: 150px;
margin: 35px;
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,19 @@
import { Box, Heading } from '~/bundles/common/components/components.js';
import { useCallback, useState } from '~/bundles/common/hooks/hooks.js';
import { type VideoScript } from '~/bundles/common/types/types.js';

import styles from './styles.module.css';

type Properties = {
videoScript: VideoScript;
};

const GenerateScriptScene: React.FC<Properties> = ({ videoScript }) => {
const { title, description } = videoScript;
const [animationDone, setAnimationDone] = useState(false);

const handleAnimationEnd = useCallback(() => {
setAnimationDone(true);
}, [setAnimationDone]);

return (
<Box mb="10" width="100%">
<Heading as="h4" variant="H4" color="typography.600">
{title}
</Heading>
<Box
as="p"
className={
animationDone
? styles['typing-effect--done']
: styles['typing-effect']
}
onAnimationEnd={handleAnimationEnd}
>
{description}
</Box>
<Box as="p">{description}</Box>
</Box>
);
};
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import { GenerateScriptForm } from '../generate-script-form/generate-script-form
import { GenerateScriptPlaceholder } from '../generate-script-placeholder/generate-script-placeholder.js';
import styles from './styles.module.css';

const GenerateScriptView: React.FC = () => {
type Properties = {
onClose: () => void;
};
const GenerateScriptView: React.FC<Properties> = ({ onClose }) => {
const dispatch = useAppDispatch();
const { messages } = useAppSelector(({ chat }) => ({
messages: chat.messages.filter(
Expand Down Expand Up @@ -68,7 +71,7 @@ const GenerateScriptView: React.FC = () => {
<GenerateScriptForm
onSubmit={handleGenerateVideoScriptSubmit}
/>
<GenerateScriptPlaceholder />
<GenerateScriptPlaceholder onClose={onClose} />
</HStack>
</TabPanel>
</TabPanels>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { getVideoScriptMessageFromPayload } from './generate-script-message-template.js';
export { sanitizeJsonString } from './sanitize-json-string.js';

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const VideoModalContent: React.FC<Properties> = ({ onClose }) => {
<VideoPreview onClose={onClose} />
</TabPanel>
<TabPanel p="0px 24px">
<GenerateScriptView />
<GenerateScriptView onClose={onClose} />
</TabPanel>
</TabPanels>
</Tabs>
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/bundles/common/constants/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
export { FPS } from './fps.constant.js';
export { SOCKET_TRANSPORT_WEBSOCKETS } from './socket-trasnport.constants.js';
export { EMPTY_VALUE, LAST_ELEMENT_INDEX } from 'shared';
export {
EMPTY_VALUE,
FIRST_INDEX,
LAST_ELEMENT_INDEX,
LAST_INDEX_OFFSET,
} from 'shared';
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ import {
import { type MenuItem } from './types/types.js';

const VideoMenu: React.FC = () => {
const activeItem = useAppSelector(({ studio }) => studio.ui.menuActiveItem);
const { selectedItem, activeItem } = useAppSelector(({ studio }) => ({
selectedItem: studio.ui.selectedItem,
activeItem: studio.ui.menuActiveItem,
}));

const dispatch = useAppDispatch();
const [isChatOpen, setIsChatOpen] = useState(false);
Expand All @@ -54,8 +57,6 @@ const VideoMenu: React.FC = () => {
setIsChatOpen(false);
}, []);

const { selectedItem } = useAppSelector(({ studio }) => studio.ui);

useEffect(() => {
if (selectedItem?.type === 'script') {
setActiveItem('script');
Expand All @@ -64,7 +65,6 @@ const VideoMenu: React.FC = () => {
}, [selectedItem, setActiveItem, dispatch]);

// TODO: Uncomment menu items after demo

const menuItems: Record<ValueOf<typeof MenuItems>, MenuItem> = {
// templates: {
// label: 'Templates',
Expand Down
34 changes: 34 additions & 0 deletions frontend/src/bundles/studio/helpers/add-scene.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { v4 as uuidv4 } from 'uuid';

import { DEFAULT_SCENE_DURATION } from '~/bundles/studio/constants/constants.js';
import { RowNames } from '~/bundles/studio/enums/enums.js';
import {
type addSceneRequest,
type addSceneResponse,
type SelectedItem,
} from '~/bundles/studio/types/types.js';

import { calculateTotalMilliseconds } from './helpers.js';

const addScene = ({ scenes, rangeEnd }: addSceneRequest): addSceneResponse => {
const scene = {
id: uuidv4(),
duration: DEFAULT_SCENE_DURATION,
};
const updatedcenes = [...scenes, scene];

const selectedItem: SelectedItem = { id: scene.id, type: RowNames.SCENE };
const totalMilliseconds = calculateTotalMilliseconds(
updatedcenes,
rangeEnd,
);
rangeEnd = totalMilliseconds;

return {
scene,
selectedItem,
rangeEnd,
};
};

export { addScene };
46 changes: 46 additions & 0 deletions frontend/src/bundles/studio/helpers/add-script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { v4 as uuidv4 } from 'uuid';

import {
DEFAULT_SCRIPT_DURATION,
DEFAULT_VOICE,
} from '~/bundles/studio/constants/constants.js';
import { PlayIconNames, RowNames } from '~/bundles/studio/enums/enums.js';
import {
type addScriptRequest,
type addScriptResponse,
type Script,
type SelectedItem,
} from '~/bundles/studio/types/types.js';

import { calculateTotalMilliseconds } from './helpers.js';

const addScript = ({
text,
scripts,
rangeEnd,
}: addScriptRequest): addScriptResponse => {
const script: Script = {
id: uuidv4(),
duration: DEFAULT_SCRIPT_DURATION,
text: text,
voice: DEFAULT_VOICE,
iconName: PlayIconNames.READY,
url: null,
};
const updatedScripts = [...scripts, script];

const selectedItem: SelectedItem = { id: script.id, type: RowNames.SCRIPT };
const totalMilliseconds = calculateTotalMilliseconds(
updatedScripts,
rangeEnd,
);
rangeEnd = totalMilliseconds;

return {
script,
selectedItem,
rangeEnd,
};
};

export { addScript };
26 changes: 26 additions & 0 deletions frontend/src/bundles/studio/helpers/create-default-avatar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {
type AvatarGetResponseDto,
type SceneAvatar,
} from '~/bundles/studio/types/types.js';

const createDefaultAvatarFromRequest = (
avatar: AvatarGetResponseDto | undefined,
): SceneAvatar | undefined => {
if (!avatar) {
return;
}

const avatarStyle = avatar.styles[0];
if (!avatarStyle) {
return;
}

return {
id: avatar.id,
name: avatar.name,
style: avatarStyle.style,
url: avatarStyle.imgUrl,
};
};

export { createDefaultAvatarFromRequest };
3 changes: 3 additions & 0 deletions frontend/src/bundles/studio/helpers/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export { addScene } from './add-scene.js';
export { addScript } from './add-script.js';
export { createDefaultAvatarFromRequest } from './create-default-avatar.js';
export { getDestinationPointerValue } from './get-destination-pointer-value.js';
export { getElementEnd } from './get-element-end.js';
export { getNewItemIndexBySpan } from './get-new-item-index.js';
Expand Down
Loading
Loading