From 7470ebffeb4616c17c8fa3b0b8612a06a79f8855 Mon Sep 17 00:00:00 2001 From: desmondwong1215 Date: Sat, 22 Feb 2025 20:54:59 +0800 Subject: [PATCH 01/16] first-commit --- .husky/pre-push | 0 .tool-versions | 0 scripts/coverage-fix.sh | 0 scripts/test-coveralls.sh | 0 scripts/test.sh | 0 src/styles/_sourcecast.scss | 0 src/styles/_sourcereel.scss | 0 src/styles/_workspace.scss | 0 src/styles/index.scss | 0 9 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 .husky/pre-push mode change 100755 => 100644 .tool-versions mode change 100755 => 100644 scripts/coverage-fix.sh mode change 100755 => 100644 scripts/test-coveralls.sh mode change 100755 => 100644 scripts/test.sh mode change 100755 => 100644 src/styles/_sourcecast.scss mode change 100755 => 100644 src/styles/_sourcereel.scss mode change 100755 => 100644 src/styles/_workspace.scss mode change 100755 => 100644 src/styles/index.scss diff --git a/.husky/pre-push b/.husky/pre-push old mode 100755 new mode 100644 diff --git a/.tool-versions b/.tool-versions old mode 100755 new mode 100644 diff --git a/scripts/coverage-fix.sh b/scripts/coverage-fix.sh old mode 100755 new mode 100644 diff --git a/scripts/test-coveralls.sh b/scripts/test-coveralls.sh old mode 100755 new mode 100644 diff --git a/scripts/test.sh b/scripts/test.sh old mode 100755 new mode 100644 diff --git a/src/styles/_sourcecast.scss b/src/styles/_sourcecast.scss old mode 100755 new mode 100644 diff --git a/src/styles/_sourcereel.scss b/src/styles/_sourcereel.scss old mode 100755 new mode 100644 diff --git a/src/styles/_workspace.scss b/src/styles/_workspace.scss old mode 100755 new mode 100644 diff --git a/src/styles/index.scss b/src/styles/index.scss old mode 100755 new mode 100644 From 94a6da7de0092bd04b6b6f204ac39fea04b0a1e0 Mon Sep 17 00:00:00 2001 From: desmondwong1215 Date: Sat, 22 Feb 2025 21:00:12 +0800 Subject: [PATCH 02/16] First commmit --- .husky/pre-push | 0 .tool-versions | 0 scripts/coverage-fix.sh | 0 scripts/test-coveralls.sh | 0 scripts/test.sh | 0 src/features/stories/storiesComponents/UserBlogContent.tsx | 1 + src/styles/_sourcecast.scss | 0 src/styles/_sourcereel.scss | 0 src/styles/_workspace.scss | 0 src/styles/index.scss | 0 10 files changed, 1 insertion(+) mode change 100644 => 100755 .husky/pre-push mode change 100644 => 100755 .tool-versions mode change 100644 => 100755 scripts/coverage-fix.sh mode change 100644 => 100755 scripts/test-coveralls.sh mode change 100644 => 100755 scripts/test.sh mode change 100644 => 100755 src/styles/_sourcecast.scss mode change 100644 => 100755 src/styles/_sourcereel.scss mode change 100644 => 100755 src/styles/_workspace.scss mode change 100644 => 100755 src/styles/index.scss diff --git a/.husky/pre-push b/.husky/pre-push old mode 100644 new mode 100755 diff --git a/.tool-versions b/.tool-versions old mode 100644 new mode 100755 diff --git a/scripts/coverage-fix.sh b/scripts/coverage-fix.sh old mode 100644 new mode 100755 diff --git a/scripts/test-coveralls.sh b/scripts/test-coveralls.sh old mode 100644 new mode 100755 diff --git a/scripts/test.sh b/scripts/test.sh old mode 100644 new mode 100755 diff --git a/src/features/stories/storiesComponents/UserBlogContent.tsx b/src/features/stories/storiesComponents/UserBlogContent.tsx index 2cabf33bc4..30f373b2c0 100644 --- a/src/features/stories/storiesComponents/UserBlogContent.tsx +++ b/src/features/stories/storiesComponents/UserBlogContent.tsx @@ -108,6 +108,7 @@ const UserBlogContent: React.FC = ({ fileContent }) => { return content ? (
{renderStoryMarkdown(content)}
+
Hello world!
) : (
diff --git a/src/styles/_sourcecast.scss b/src/styles/_sourcecast.scss old mode 100644 new mode 100755 diff --git a/src/styles/_sourcereel.scss b/src/styles/_sourcereel.scss old mode 100644 new mode 100755 diff --git a/src/styles/_workspace.scss b/src/styles/_workspace.scss old mode 100644 new mode 100755 diff --git a/src/styles/index.scss b/src/styles/index.scss old mode 100644 new mode 100755 From eba871a2a936bebcaf611d014089bffb19a4bd4a Mon Sep 17 00:00:00 2001 From: desmondwong1215 Date: Sat, 22 Feb 2025 21:11:52 +0800 Subject: [PATCH 03/16] first commit --- .husky/pre-push | 0 .tool-versions | 0 scripts/coverage-fix.sh | 0 scripts/test-coveralls.sh | 0 scripts/test.sh | 0 src/styles/_sourcecast.scss | 0 src/styles/_sourcereel.scss | 0 src/styles/_workspace.scss | 0 src/styles/index.scss | 0 9 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 .husky/pre-push mode change 100755 => 100644 .tool-versions mode change 100755 => 100644 scripts/coverage-fix.sh mode change 100755 => 100644 scripts/test-coveralls.sh mode change 100755 => 100644 scripts/test.sh mode change 100755 => 100644 src/styles/_sourcecast.scss mode change 100755 => 100644 src/styles/_sourcereel.scss mode change 100755 => 100644 src/styles/_workspace.scss mode change 100755 => 100644 src/styles/index.scss diff --git a/.husky/pre-push b/.husky/pre-push old mode 100755 new mode 100644 diff --git a/.tool-versions b/.tool-versions old mode 100755 new mode 100644 diff --git a/scripts/coverage-fix.sh b/scripts/coverage-fix.sh old mode 100755 new mode 100644 diff --git a/scripts/test-coveralls.sh b/scripts/test-coveralls.sh old mode 100755 new mode 100644 diff --git a/scripts/test.sh b/scripts/test.sh old mode 100755 new mode 100644 diff --git a/src/styles/_sourcecast.scss b/src/styles/_sourcecast.scss old mode 100755 new mode 100644 diff --git a/src/styles/_sourcereel.scss b/src/styles/_sourcereel.scss old mode 100755 new mode 100644 diff --git a/src/styles/_workspace.scss b/src/styles/_workspace.scss old mode 100755 new mode 100644 diff --git a/src/styles/index.scss b/src/styles/index.scss old mode 100755 new mode 100644 From 7ecb927c7af2600b88d30e474715cacbb378e427 Mon Sep 17 00:00:00 2001 From: desmondwong1215 Date: Sat, 22 Feb 2025 21:28:11 +0800 Subject: [PATCH 04/16] First commit --- .husky/pre-push | 0 .tool-versions | 0 scripts/coverage-fix.sh | 0 scripts/test-coveralls.sh | 0 scripts/test.sh | 0 src/styles/_sourcecast.scss | 0 src/styles/_sourcereel.scss | 0 src/styles/_workspace.scss | 0 src/styles/index.scss | 0 9 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .husky/pre-push mode change 100644 => 100755 .tool-versions mode change 100644 => 100755 scripts/coverage-fix.sh mode change 100644 => 100755 scripts/test-coveralls.sh mode change 100644 => 100755 scripts/test.sh mode change 100644 => 100755 src/styles/_sourcecast.scss mode change 100644 => 100755 src/styles/_sourcereel.scss mode change 100644 => 100755 src/styles/_workspace.scss mode change 100644 => 100755 src/styles/index.scss diff --git a/.husky/pre-push b/.husky/pre-push old mode 100644 new mode 100755 diff --git a/.tool-versions b/.tool-versions old mode 100644 new mode 100755 diff --git a/scripts/coverage-fix.sh b/scripts/coverage-fix.sh old mode 100644 new mode 100755 diff --git a/scripts/test-coveralls.sh b/scripts/test-coveralls.sh old mode 100644 new mode 100755 diff --git a/scripts/test.sh b/scripts/test.sh old mode 100644 new mode 100755 diff --git a/src/styles/_sourcecast.scss b/src/styles/_sourcecast.scss old mode 100644 new mode 100755 diff --git a/src/styles/_sourcereel.scss b/src/styles/_sourcereel.scss old mode 100644 new mode 100755 diff --git a/src/styles/_workspace.scss b/src/styles/_workspace.scss old mode 100644 new mode 100755 diff --git a/src/styles/index.scss b/src/styles/index.scss old mode 100644 new mode 100755 From e2a1f2c09fc1f95ce7d2b6714843073d34f95ee8 Mon Sep 17 00:00:00 2001 From: desmondwong1215 Date: Wed, 26 Feb 2025 21:39:07 +0800 Subject: [PATCH 05/16] enable editing story cell --- src/commons/sagas/StoriesSaga.ts | 7 +- src/commons/utils/StoriesHelper.ts | 72 ++++++++++++- src/features/stories/StoriesTypes.ts | 5 +- .../storiesComponents/BackendAccess.ts | 100 ++++++++++++++++-- .../storiesComponents/EditStoryCell.tsx | 67 ++++++++++++ .../stories/storiesComponents/SourceBlock.tsx | 34 ++++-- .../storiesComponents/UserBlogContent.tsx | 34 ++++-- .../storiesComponents/ViewStoryCell.tsx | 25 +++++ src/pages/stories/Stories.tsx | 3 - src/pages/stories/StoriesTable.tsx | 8 +- src/pages/stories/Story.tsx | 52 +++++---- 11 files changed, 355 insertions(+), 52 deletions(-) create mode 100644 src/features/stories/storiesComponents/EditStoryCell.tsx create mode 100644 src/features/stories/storiesComponents/ViewStoryCell.tsx diff --git a/src/commons/sagas/StoriesSaga.ts b/src/commons/sagas/StoriesSaga.ts index c071186724..48ba0f0c6e 100644 --- a/src/commons/sagas/StoriesSaga.ts +++ b/src/commons/sagas/StoriesSaga.ts @@ -21,7 +21,7 @@ import { combineSagaHandlers } from '../redux/utils'; import { resetSideContent } from '../sideContent/SideContentActions'; import { actions } from '../utils/ActionsHelper'; import { showSuccessMessage, showWarningMessage } from '../utils/notifications/NotificationsHelper'; -import { defaultStoryContent } from '../utils/StoriesHelper'; +import { defaultHeader, defaultContent } from '../utils/StoriesHelper'; import { selectTokens } from './BackendSaga'; import { evalCodeSaga } from './WorkspaceSaga/helpers/evalCode'; @@ -47,7 +47,8 @@ const StoriesSaga = combineSagaHandlers(sagaActions, { } else { const defaultStory: StoryData = { title: '', - content: defaultStoryContent, + header: defaultHeader, + content: defaultContent, pinOrder: null }; yield put(actions.setCurrentStory(defaultStory)); @@ -68,6 +69,7 @@ const StoriesSaga = combineSagaHandlers(sagaActions, { tokens, userId, story.title, + story.header, story.content, story.pinOrder ); @@ -87,6 +89,7 @@ const StoriesSaga = combineSagaHandlers(sagaActions, { tokens, id, story.title, + story.header, story.content, story.pinOrder ); diff --git a/src/commons/utils/StoriesHelper.ts b/src/commons/utils/StoriesHelper.ts index 3f74db64f1..e9b5c37b1e 100644 --- a/src/commons/utils/StoriesHelper.ts +++ b/src/commons/utils/StoriesHelper.ts @@ -10,6 +10,7 @@ import SourceBlock, { SourceBlockProps } from 'src/features/stories/storiesCompo import { unified } from 'unified'; import { ReplaceTypeAtIndex } from './TypeHelper'; +import { StoryCell } from 'src/features/stories/storiesComponents/BackendAccess'; export const defaultStoryContent = `--- config: @@ -131,6 +132,70 @@ show(heart); \`\`\` `; +export const defaultHeader: string = `--- +config: + chapter: 4 + variant: default + +env: + iterFib: + chapter: 4 + variant: default + recuFib: + chapter: 4 + variant: default + rune: + chapter: 4 + variant: default +---`; + +export const defaultContent: StoryCell[] = [ + { + id: 0, + isCode: true, + env: "", + content: + `function print(message) { +display(message); +draw_data(list(1, 2, 3, 4)); +} +display("hello world1"); +`, + }, + { + id: 1, + isCode: false, + env: "", + content: + `# Hello world! +## hello world!! +hello world!!! +hello world!!! +\`\`\`\` +\`\`\`{source} +print("hello world") +\`\`\` +\`\`\`\` +`, + }, + { + id: 2, + isCode: true, + env: "recuFib", + content: + `print("source academy stories"); +`, + }, + { + id: 3, + isCode: true, + env: "", + content: + `print("hello world"); +`, + } +]; + export const scrollSync = (editor: IEditorProps, preview: HTMLElement) => { const editorScrollTop = editor.session.getScrollTop(); const editorScrollHeight = editor.renderer.layerConfig.maxHeight; @@ -156,6 +221,7 @@ type HandlerType = { ) => ReturnType; }; +let currentIndex: number; const handleCustomComponents: HandlerType = { code: (state, node) => { const rawLang = node.lang ?? ''; @@ -167,7 +233,8 @@ const handleCustomComponents: HandlerType = { // const lang = rawLang.substring(1, rawLang.length - 1); const props: SourceBlockProps = { content: node.value, - commands: node.meta ?? '' + commands: node.meta ?? '', + index: currentIndex, }; // Disable typecheck as "source-block" is not a standard HTML tag const element = h('source-block', props) as any; @@ -175,7 +242,8 @@ const handleCustomComponents: HandlerType = { } }; -export const renderStoryMarkdown = (markdown: string): React.ReactNode => { +export const renderStoryMarkdown = (markdown: string, index: number): React.ReactNode => { + currentIndex = index; const mdast = fromMarkdown(markdown); const hast = toHast(mdast, { handlers: handleCustomComponents }) ?? h(); return ( diff --git a/src/features/stories/StoriesTypes.ts b/src/features/stories/StoriesTypes.ts index 3f524a6472..f30909e57f 100644 --- a/src/features/stories/StoriesTypes.ts +++ b/src/features/stories/StoriesTypes.ts @@ -1,6 +1,6 @@ import { Context } from 'js-slang'; import { DebuggerContext } from 'src/commons/workspace/WorkspaceTypes'; - +import { StoryCell } from './storiesComponents/BackendAccess'; import { InterpreterOutput, StoriesRole } from '../../commons/application/ApplicationTypes'; export type StoryMetadata = { @@ -10,7 +10,8 @@ export type StoryMetadata = { export type StoryData = { title: string; - content: string; + header: string; + content: StoryCell[]; pinOrder: number | null; }; diff --git a/src/features/stories/storiesComponents/BackendAccess.ts b/src/features/stories/storiesComponents/BackendAccess.ts index c494c13ed6..bb4a55b46d 100644 --- a/src/features/stories/storiesComponents/BackendAccess.ts +++ b/src/features/stories/storiesComponents/BackendAccess.ts @@ -12,6 +12,86 @@ import { store } from 'src/pages/createStore'; import { Tokens } from '../../../commons/application/types/SessionTypes'; import { NameUsernameRole } from '../../../pages/academy/adminPanel/subcomponents/AddStoriesUserPanel'; import { AdminPanelStoriesUser, StoryListView, StoryView } from '../StoriesTypes'; +import { defaultStoryContent } from 'src/commons/utils/StoriesHelper'; + +export type StoryCell = { + id: number; + isCode: boolean; + env: string; + content: string; +}; + +const tempHeader: String = `--- +config: + chapter: 4 + variant: default + +env: + iterFib: + chapter: 4 + variant: default + recuFib: + chapter: 4 + variant: default + rune: + chapter: 4 + variant: default +---`; + +const tempContent: StoryCell[] = [ +// { +// id: 0, +// isCode: true, +// env: "", +// content: +// `function print(message) { +// display(message); +// } +// draw_data(list(1, 2, 3, 4)); +// display("hello world1"); +// `, +// }, + { + id: 1, + isCode: false, + env: "", + content: + `# Hello world! +## hello world!! +hello world!!! +hello world!!! +\`\`\`\` +\`\`\`{source} +print("hello world") +\`\`\` +\`\`\`\` +`, + }, + { + id: 2, + isCode: true, + env: "recuFib", + content: + `print("source academy stories"); +`, + }, + { + id: 3, + isCode: true, + env: "", + content: + `print("hello world"); +`, + }, + { + id: 4, + isCode: true, + env: "", + content: + `print("hello world"); +`, + } +]; // Helpers @@ -83,7 +163,8 @@ export const getStories = async (tokens: Tokens): Promise ({...story, header: tempHeader, content: tempContent})); }; export const getStory = async (tokens: Tokens, storyId: number): Promise => { @@ -95,7 +176,12 @@ export const getStory = async (tokens: Tokens, storyId: number): Promise => { const resp = await requestStoryBackend(`/groups/${getStoriesGroupId()}/stories`, 'POST', { - body: { authorId, title, content, pinOrder }, + body: { authorId, title, defaultStoryContent, pinOrder }, ...tokens }); if (!resp) { @@ -123,11 +210,12 @@ export const updateStory = async ( tokens: Tokens, id: number, title: string, - content: string, + header: string, + content: StoryCell[], pinOrder: number | null ): Promise => { const resp = await requestStoryBackend(`/groups/${getStoriesGroupId()}/stories/${id}`, 'PUT', { - body: { title, content, pinOrder }, + body: { title, defaultStoryContent, pinOrder }, ...tokens }); if (!resp) { diff --git a/src/features/stories/storiesComponents/EditStoryCell.tsx b/src/features/stories/storiesComponents/EditStoryCell.tsx new file mode 100644 index 0000000000..ec6f66dbe4 --- /dev/null +++ b/src/features/stories/storiesComponents/EditStoryCell.tsx @@ -0,0 +1,67 @@ +import { useEffect, useState } from "react"; +import { renderStoryMarkdown } from "src/commons/utils/StoriesHelper"; +import AceEditor from 'react-ace'; +import { StoryCell } from "./BackendAccess"; +import { ControlButtonSaveButton } from "src/commons/controlBar/ControlBarSaveButton"; + +type Props = { + story: StoryCell; + editContent: (id: number, newContent: string) => void; +}; + +function EditStoryCell(props: Props) { + + const { id, isCode, env, content } = props.story; + const [isEditMode, setEditMode] = useState(false); + const [storyContent, setStoryContent] = useState(content); + const [isDirty, setIsDirty] = useState(false); + + const saveButClicked = () => { + setEditMode(false); + setIsDirty(false); + props.editContent(id, storyContent); + } + + const onEditorValueChange = (content: string) => { + setStoryContent(content); + setIsDirty(true); + } + + const handleDoubleClick = () => { + if (!isCode) { + setEditMode(true); + } + } + + useEffect(() => { + if (isCode) { + setStoryContent("\`\`\`{source} env:" + env + "\n" + content); + } + }, []); + + return
+ {isEditMode &&
+ +
} + {isEditMode ? + : renderStoryMarkdown(storyContent, id) + } +
+} + +export default EditStoryCell; \ No newline at end of file diff --git a/src/features/stories/storiesComponents/SourceBlock.tsx b/src/features/stories/storiesComponents/SourceBlock.tsx index 86ecd97114..cb6c1ec8d2 100644 --- a/src/features/stories/storiesComponents/SourceBlock.tsx +++ b/src/features/stories/storiesComponents/SourceBlock.tsx @@ -22,10 +22,13 @@ import { ExternalLibraryName } from '../../../commons/application/types/External import { Output } from '../../../commons/repl/Repl'; import { getModeString, selectMode } from '../../../commons/utils/AceHelper'; import { DEFAULT_ENV } from './UserBlogContent'; +import { ControlButtonSaveButton } from 'src/commons/controlBar/ControlBarSaveButton'; +import { StoryCell } from './BackendAccess'; export type SourceBlockProps = { content: string; commands: string; // env is in commands + index: number; }; /** @@ -50,7 +53,9 @@ const SourceBlock: React.FC = props => { const dispatch = useDispatch(); const [code, setCode] = useState(props.content); const [outputIndex, setOutputIndex] = useState(Infinity); + const [isDirty, setIsDirty] = useState(false); + const { currentStory: story } = useTypedSelector(store => store.stories); const envList = useTypedSelector(store => Object.keys(store.stories.envs)); // setting env @@ -203,6 +208,17 @@ const SourceBlock: React.FC = props => { dispatch(StoriesActions.clearStoryEnv(env)); }; + const editorOnChange = (code: string) => { + setCode(code); + setIsDirty(true); + } + + const saveButClicked = () => { + setIsDirty(false); + story!.content.filter((story: StoryCell) => story.id == props.index)[0].content = code; + dispatch(StoriesActions.setCurrentStory({...story!, content: [...story!.content]})); + } + selectMode(chapter, variant, ExternalLibraryName.NONE); return ( @@ -210,11 +226,17 @@ const SourceBlock: React.FC = props => {
- + {isDirty + ? + : } {envDisplayLabel} @@ -230,7 +252,7 @@ const SourceBlock: React.FC = props => { height="1px" width="100%" value={code} - onChange={code => setCode(code)} + onChange={editorOnChange} commands={[ { name: 'evaluate', diff --git a/src/features/stories/storiesComponents/UserBlogContent.tsx b/src/features/stories/storiesComponents/UserBlogContent.tsx index 30f373b2c0..b603b8f27d 100644 --- a/src/features/stories/storiesComponents/UserBlogContent.tsx +++ b/src/features/stories/storiesComponents/UserBlogContent.tsx @@ -1,13 +1,16 @@ import { Chapter, Variant } from 'js-slang/dist/types'; import yaml from 'js-yaml'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import debounceRender from 'react-debounce-render'; import Constants from 'src/commons/utils/Constants'; import { propsAreEqual } from 'src/commons/utils/MemoizeHelper'; -import { renderStoryMarkdown } from 'src/commons/utils/StoriesHelper'; +// import { renderStoryMarkdown } from 'src/commons/utils/StoriesHelper'; +import EditStoryCell from './EditStoryCell'; import StoriesActions from 'src/features/stories/StoriesActions'; import { store } from '../../../pages/createStore'; +import ViewStoryCell from './ViewStoryCell'; +import { StoryCell } from './BackendAccess'; export const DEFAULT_ENV = 'default'; @@ -92,23 +95,32 @@ export function getYamlHeader(content: string): { header: string; content: strin } type Props = { - fileContent: string; + header: string; + contents: StoryCell[]; + isViewOnly: boolean; + editContent: (id: number, newContent: string) => void; }; -const UserBlogContent: React.FC = ({ fileContent }) => { - const [content, setContent] = useState(''); +const UserBlogContent: React.FC = ({ header, contents, isViewOnly, editContent}) => { useEffect(() => { - const { header, content } = getYamlHeader(fileContent); - setContent(content); + // const header = getYamlHeader(fileContent).header; store.dispatch(StoriesActions.clearStoryEnv()); handleHeaders(header); - }, [fileContent]); + }, [header, contents]); - return content ? ( + return contents.length > 0 ? (
-
{renderStoryMarkdown(content)}
-
Hello world!
+ {isViewOnly + ? contents.map((story, key) => ) + : contents.map((story, key) => )}
) : (
diff --git a/src/features/stories/storiesComponents/ViewStoryCell.tsx b/src/features/stories/storiesComponents/ViewStoryCell.tsx new file mode 100644 index 0000000000..f7922bdfb6 --- /dev/null +++ b/src/features/stories/storiesComponents/ViewStoryCell.tsx @@ -0,0 +1,25 @@ +import { renderStoryMarkdown } from "src/commons/utils/StoriesHelper"; +import { StoryCell } from "./BackendAccess"; +import { useEffect, useState } from "react"; + +type Props = { + story: StoryCell; +}; + +function ViewStoryCell(props: Props) { + + const { id, isCode, env, content } = props.story; + const [storyContent, setStoryContent] = useState(content); + + useEffect(() => { + if (isCode) { + setStoryContent("\`\`\`{source} env:" + env + "\n" + content); + } + }, []); + + return
+ {renderStoryMarkdown(storyContent, id)} +
+} + +export default ViewStoryCell; \ No newline at end of file diff --git a/src/pages/stories/Stories.tsx b/src/pages/stories/Stories.tsx index db96feadb9..ef7859cfc2 100644 --- a/src/pages/stories/Stories.tsx +++ b/src/pages/stories/Stories.tsx @@ -10,7 +10,6 @@ import GradingText from 'src/commons/grading/GradingText'; import { showSimpleConfirmDialog } from 'src/commons/utils/DialogHelper'; import { useTypedSelector } from 'src/commons/utils/Hooks'; import StoriesActions from 'src/features/stories/StoriesActions'; -import { getYamlHeader } from 'src/features/stories/storiesComponents/UserBlogContent'; import StoriesTable from './StoriesTable'; import StoryActions from './StoryActions'; @@ -142,8 +141,6 @@ const Stories: React.FC = () => { ({ ...story, content: getYamlHeader(story.content).content })) .filter( story => // Always show pinned stories diff --git a/src/pages/stories/StoriesTable.tsx b/src/pages/stories/StoriesTable.tsx index d66d73379a..bf5816cc89 100644 --- a/src/pages/stories/StoriesTable.tsx +++ b/src/pages/stories/StoriesTable.tsx @@ -19,6 +19,12 @@ type Props = { const MAX_EXCERPT_LENGTH = 35; const truncate = (content: string) => { + console.log(content); + console.log(content.replaceAll(/\s+/g, ' ').length <= MAX_EXCERPT_LENGTH + ? content.replaceAll(/\s+/g, ' ') + : content.split(/\s+/).reduce((acc, cur) => { + return acc.length + cur.length <= MAX_EXCERPT_LENGTH ? acc + ' ' + cur : acc; + }, '') + '…'); return content.replaceAll(/\s+/g, ' ').length <= MAX_EXCERPT_LENGTH ? content.replaceAll(/\s+/g, ' ') : content.split(/\s+/).reduce((acc, cur) => { @@ -50,7 +56,7 @@ const StoriesTable: React.FC = ({ headers, stories, storyActions }) => { flex: 6, field: 'content', headerName: 'Content', - valueFormatter: ({ value }) => truncate(value), + valueFormatter: ({ value }) => truncate(value[0].content), cellStyle: { textAlign: 'left' } }, { diff --git a/src/pages/stories/Story.tsx b/src/pages/stories/Story.tsx index 676eb59cc1..79705aa0a0 100644 --- a/src/pages/stories/Story.tsx +++ b/src/pages/stories/Story.tsx @@ -1,18 +1,20 @@ import 'js-slang/dist/editors/ace/theme/source'; -import { Classes, InputGroup } from '@blueprintjs/core'; +import { Classes } from '@blueprintjs/core'; +import { TextInput } from '@tremor/react'; import classNames from 'classnames'; import { useEffect, useState } from 'react'; -import AceEditor, { IEditorProps } from 'react-ace'; +// import AceEditor, { IEditorProps } from 'react-ace'; import { useDispatch } from 'react-redux'; import { useParams } from 'react-router'; import ControlBar, { ControlBarProps } from 'src/commons/controlBar/ControlBar'; import { ControlButtonSaveButton } from 'src/commons/controlBar/ControlBarSaveButton'; import { useTypedSelector } from 'src/commons/utils/Hooks'; -import { scrollSync } from 'src/commons/utils/StoriesHelper'; +// import { scrollSync } from 'src/commons/utils/StoriesHelper'; import StoriesActions from 'src/features/stories/StoriesActions'; import UserBlogContent from '../../features/stories/storiesComponents/UserBlogContent'; +import { StoryCell } from 'src/features/stories/storiesComponents/BackendAccess'; type Props = { isViewOnly?: boolean; @@ -21,8 +23,11 @@ type Props = { const Story: React.FC = ({ isViewOnly = false }) => { const dispatch = useDispatch(); const [isDirty, setIsDirty] = useState(false); + // const [header, setHeader] = useState(tempHeader); const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); + // const header = story?.header; + // const [contents, setContents] = useState(tempContent); const { id: idToSet } = useParams<{ id: string }>(); useEffect(() => { // Clear screen on first load @@ -37,27 +42,35 @@ const Story: React.FC = ({ isViewOnly = false }) => { return <>; } - const onEditorScroll = (e: IEditorProps) => { - const userblogContainer = document.getElementById('userblogContainer'); - if (userblogContainer) { - scrollSync(e, userblogContainer); - } - }; + const { header: header , content: contents } = story!; - const onEditorValueChange = (val: string) => { - setIsDirty(true); - dispatch(StoriesActions.setCurrentStory({ ...story, content: val })); - }; + const editContent = (id: number, newContent: string) => { + contents.filter((story: StoryCell) => story.id == id)[0].content = newContent; + dispatch(StoriesActions.setCurrentStory({...story, content: [...contents]})); + } + + // const onEditorScroll = (e: IEditorProps) => { + // const userblogContainer = document.getElementById('userblogContainer'); + // if (userblogContainer) { + // scrollSync(e, userblogContainer); + // } + // }; + + // const onEditorValueChange = (val: string) => { + // setIsDirty(true); + // dispatch(StoriesActions.setCurrentStory({ ...story, content: val })); + // }; - const { title, content } = story; + // const { title, content } = story; + const { title } = story; const controlBarProps: ControlBarProps = { editorButtons: [ isViewOnly ? ( <>{title} ) : ( - { @@ -79,6 +92,7 @@ const Story: React.FC = ({ isViewOnly = false }) => { dispatch(StoriesActions.createStory(story)); } // TODO: Set isDirty to false + setIsDirty(false); }} hasUnsavedChanges={isDirty} /> @@ -90,7 +104,7 @@ const Story: React.FC = ({ isViewOnly = false }) => {
- {!isViewOnly && ( + {/* {!isViewOnly && ( = ({ isViewOnly = false }) => { wrapEnabled={true} setOptions={{ fontFamily: "'Inconsolata', 'Consolas', monospace" }} /> - )} + )} */}
- +
From 64b23a881af2be9118c60f4fc55a184eeae9f10e Mon Sep 17 00:00:00 2001 From: desmondwong1215 Date: Fri, 28 Feb 2025 01:20:03 +0800 Subject: [PATCH 06/16] enable adding story cell and new env --- src/commons/sagas/StoriesSaga.ts | 16 ++- src/commons/utils/StoriesHelper.ts | 70 +--------- src/features/stories/StoriesActions.ts | 1 + src/features/stories/StoriesTypes.ts | 8 +- .../storiesComponents/BackendAccess.ts | 62 +++++---- .../storiesComponents/CreateStoryCell.tsx | 96 ++++++++++++++ .../storiesComponents/EditStoryCell.tsx | 98 +++++++++++--- .../stories/storiesComponents/SourceBlock.tsx | 66 +++++++++- .../storiesComponents/UserBlogContent.tsx | 122 +++++++++++++++++- .../storiesComponents/ViewStoryCell.tsx | 6 +- src/pages/stories/StoriesTable.tsx | 6 - src/pages/stories/Story.tsx | 35 ++++- 12 files changed, 439 insertions(+), 147 deletions(-) create mode 100644 src/features/stories/storiesComponents/CreateStoryCell.tsx diff --git a/src/commons/sagas/StoriesSaga.ts b/src/commons/sagas/StoriesSaga.ts index 48ba0f0c6e..f1f14d7e11 100644 --- a/src/commons/sagas/StoriesSaga.ts +++ b/src/commons/sagas/StoriesSaga.ts @@ -10,7 +10,10 @@ import { getStory, postStory, putStoriesUserRole, - updateStory + updateStory, + // updateHeader, + tempHeader, + tempContent } from 'src/features/stories/storiesComponents/BackendAccess'; import { StoryData, StoryListView, StoryView } from 'src/features/stories/StoriesTypes'; @@ -21,7 +24,7 @@ import { combineSagaHandlers } from '../redux/utils'; import { resetSideContent } from '../sideContent/SideContentActions'; import { actions } from '../utils/ActionsHelper'; import { showSuccessMessage, showWarningMessage } from '../utils/notifications/NotificationsHelper'; -import { defaultHeader, defaultContent } from '../utils/StoriesHelper'; +// import { defaultHeader, defaultContent } from '../utils/StoriesHelper'; import { selectTokens } from './BackendSaga'; import { evalCodeSaga } from './WorkspaceSaga/helpers/evalCode'; @@ -47,8 +50,8 @@ const StoriesSaga = combineSagaHandlers(sagaActions, { } else { const defaultStory: StoryData = { title: '', - header: defaultHeader, - content: defaultContent, + header: tempHeader, + content: tempContent, pinOrder: null }; yield put(actions.setCurrentStory(defaultStory)); @@ -110,6 +113,11 @@ const StoriesSaga = combineSagaHandlers(sagaActions, { yield put(actions.getStoriesList()); }, + // updateHeader: function* (action) { + // const newHeader = action.payload; + // yield call(updateHeader, newHeader); + // }, + getStoriesUser: function* () { const tokens: Tokens = yield selectTokens(); const me: { diff --git a/src/commons/utils/StoriesHelper.ts b/src/commons/utils/StoriesHelper.ts index e9b5c37b1e..544e189751 100644 --- a/src/commons/utils/StoriesHelper.ts +++ b/src/commons/utils/StoriesHelper.ts @@ -10,7 +10,6 @@ import SourceBlock, { SourceBlockProps } from 'src/features/stories/storiesCompo import { unified } from 'unified'; import { ReplaceTypeAtIndex } from './TypeHelper'; -import { StoryCell } from 'src/features/stories/storiesComponents/BackendAccess'; export const defaultStoryContent = `--- config: @@ -132,70 +131,6 @@ show(heart); \`\`\` `; -export const defaultHeader: string = `--- -config: - chapter: 4 - variant: default - -env: - iterFib: - chapter: 4 - variant: default - recuFib: - chapter: 4 - variant: default - rune: - chapter: 4 - variant: default ----`; - -export const defaultContent: StoryCell[] = [ - { - id: 0, - isCode: true, - env: "", - content: - `function print(message) { -display(message); -draw_data(list(1, 2, 3, 4)); -} -display("hello world1"); -`, - }, - { - id: 1, - isCode: false, - env: "", - content: - `# Hello world! -## hello world!! -hello world!!! -hello world!!! -\`\`\`\` -\`\`\`{source} -print("hello world") -\`\`\` -\`\`\`\` -`, - }, - { - id: 2, - isCode: true, - env: "recuFib", - content: - `print("source academy stories"); -`, - }, - { - id: 3, - isCode: true, - env: "", - content: - `print("hello world"); -`, - } -]; - export const scrollSync = (editor: IEditorProps, preview: HTMLElement) => { const editorScrollTop = editor.session.getScrollTop(); const editorScrollHeight = editor.renderer.layerConfig.maxHeight; @@ -222,6 +157,7 @@ type HandlerType = { }; let currentIndex: number; +let view: boolean; const handleCustomComponents: HandlerType = { code: (state, node) => { const rawLang = node.lang ?? ''; @@ -235,6 +171,7 @@ const handleCustomComponents: HandlerType = { content: node.value, commands: node.meta ?? '', index: currentIndex, + isViewOnly: view, }; // Disable typecheck as "source-block" is not a standard HTML tag const element = h('source-block', props) as any; @@ -242,8 +179,9 @@ const handleCustomComponents: HandlerType = { } }; -export const renderStoryMarkdown = (markdown: string, index: number): React.ReactNode => { +export const renderStoryMarkdown = (markdown: string, index: number, isViewOnly: boolean): React.ReactNode => { currentIndex = index; + view = isViewOnly; const mdast = fromMarkdown(markdown); const hast = toHast(mdast, { handlers: handleCustomComponents }) ?? h(); return ( diff --git a/src/features/stories/StoriesActions.ts b/src/features/stories/StoriesActions.ts index ae90e48a81..3cf55e5753 100644 --- a/src/features/stories/StoriesActions.ts +++ b/src/features/stories/StoriesActions.ts @@ -28,6 +28,7 @@ const StoriesActions = createActions('stories', { createStory: (story: StoryParams) => story, saveStory: (story: StoryParams, id: number) => ({ story, id }), deleteStory: (id: number) => id, + updateHeader: (newHeader: string) => newHeader, // Auth-related actions getStoriesUser: () => ({}), diff --git a/src/features/stories/StoriesTypes.ts b/src/features/stories/StoriesTypes.ts index f30909e57f..c32e0affef 100644 --- a/src/features/stories/StoriesTypes.ts +++ b/src/features/stories/StoriesTypes.ts @@ -1,8 +1,14 @@ import { Context } from 'js-slang'; import { DebuggerContext } from 'src/commons/workspace/WorkspaceTypes'; -import { StoryCell } from './storiesComponents/BackendAccess'; import { InterpreterOutput, StoriesRole } from '../../commons/application/ApplicationTypes'; +export type StoryCell = { + index: number; + isCode: boolean; + env: string; + content: string; +}; + export type StoryMetadata = { authorId: number; authorName: string; diff --git a/src/features/stories/storiesComponents/BackendAccess.ts b/src/features/stories/storiesComponents/BackendAccess.ts index bb4a55b46d..6fa17cae0b 100644 --- a/src/features/stories/storiesComponents/BackendAccess.ts +++ b/src/features/stories/storiesComponents/BackendAccess.ts @@ -13,19 +13,13 @@ import { Tokens } from '../../../commons/application/types/SessionTypes'; import { NameUsernameRole } from '../../../pages/academy/adminPanel/subcomponents/AddStoriesUserPanel'; import { AdminPanelStoriesUser, StoryListView, StoryView } from '../StoriesTypes'; import { defaultStoryContent } from 'src/commons/utils/StoriesHelper'; +import { StoryCell } from '../StoriesTypes'; -export type StoryCell = { - id: number; - isCode: boolean; - env: string; - content: string; -}; - -const tempHeader: String = `--- -config: - chapter: 4 - variant: default +// config: +// chapter: 4 +// variant: default +export const tempHeader: string = `--- env: iterFib: chapter: 4 @@ -35,24 +29,23 @@ env: variant: default rune: chapter: 4 - variant: default ----`; + variant: default`; -const tempContent: StoryCell[] = [ -// { -// id: 0, -// isCode: true, -// env: "", -// content: -// `function print(message) { -// display(message); -// } -// draw_data(list(1, 2, 3, 4)); -// display("hello world1"); -// `, -// }, +export const tempContent: StoryCell[] = [ + { + index: 0, + isCode: true, + env: "iterFib", + content: + `function print(message) { + display(message); +} +draw_data(list(1, 2, 3, 4)); +display("hello world1"); +`, + }, { - id: 1, + index: 1, isCode: false, env: "", content: @@ -68,7 +61,7 @@ print("hello world") `, }, { - id: 2, + index: 2, isCode: true, env: "recuFib", content: @@ -76,17 +69,17 @@ print("hello world") `, }, { - id: 3, + index: 3, isCode: true, - env: "", + env: "iterFib", content: `print("hello world"); `, }, { - id: 4, + index: 4, isCode: true, - env: "", + env: "iterFib", content: `print("hello world"); `, @@ -293,3 +286,8 @@ export const deleteUserUserGroups = async ( const user = await resp.json(); return user; }; + +// export const updateHeader = (newHeader: string): string | null => { +// tempHeader = newHeader; +// return tempHeader; +// } diff --git a/src/features/stories/storiesComponents/CreateStoryCell.tsx b/src/features/stories/storiesComponents/CreateStoryCell.tsx new file mode 100644 index 0000000000..0fe05a0059 --- /dev/null +++ b/src/features/stories/storiesComponents/CreateStoryCell.tsx @@ -0,0 +1,96 @@ +import { Menu, MenuItem } from "@blueprintjs/core"; +import { useState } from "react"; +import AceEditor from 'react-ace'; +import { ControlButtonSaveButton } from "src/commons/controlBar/ControlBarSaveButton"; +import { showWarningMessage } from "src/commons/utils/notifications/NotificationsHelper"; + +type Props = { + index: number; + envs: string[]; + saveNewStoryCell: (index: number, isCode: boolean, env: string, content: string) => void; +}; + +const NewStoryCell: React.FC = ({ + index, + envs, + saveNewStoryCell +}) => { + + const [isCode, setIsCode] = useState(false); + const [env, setEnv] = useState(envs[0]); + const [code, setCode] = useState(""); + const [isDirty, setIsDirty] = useState(false); + + const editorOnChange = (code: string) => { + setCode(code); + setIsDirty(code.trim() !== ""); + } + + const reset = () => { + setCode(""); + setEnv(envs[0]); + setIsCode(false); + setIsDirty(false); + } + + const saveButClicked = () => { + if (!isDirty) { + showWarningMessage("Cannot save empty story cell!"); + return; + } + saveNewStoryCell(index, isCode, isCode ? env : "", code); + reset(); + } + + return
+
+ + + + setIsCode(false)} text="Markdown" /> + setIsCode(true)} text="Source" /> + + + {isCode &&
+ + + {envs.map((env: string, index: number) => {setEnv(env)}} + text={env} + />)} + + +
+ } +
+ +
+}; + +export default NewStoryCell; \ No newline at end of file diff --git a/src/features/stories/storiesComponents/EditStoryCell.tsx b/src/features/stories/storiesComponents/EditStoryCell.tsx index ec6f66dbe4..f351ff664b 100644 --- a/src/features/stories/storiesComponents/EditStoryCell.tsx +++ b/src/features/stories/storiesComponents/EditStoryCell.tsx @@ -1,25 +1,36 @@ import { useEffect, useState } from "react"; import { renderStoryMarkdown } from "src/commons/utils/StoriesHelper"; import AceEditor from 'react-ace'; -import { StoryCell } from "./BackendAccess"; +import { StoryCell } from '../StoriesTypes'; import { ControlButtonSaveButton } from "src/commons/controlBar/ControlBarSaveButton"; +import { Button } from "@blueprintjs/core"; +import NewStoryCell from "./CreateStoryCell"; type Props = { story: StoryCell; + envs: string[]; editContent: (id: number, newContent: string) => void; + saveNewStoryCell: (index: number, isCode: boolean, env: string, content: string) => void; }; function EditStoryCell(props: Props) { - const { id, isCode, env, content } = props.story; + const { index, isCode, env, content } = props.story; const [isEditMode, setEditMode] = useState(false); const [storyContent, setStoryContent] = useState(content); const [isDirty, setIsDirty] = useState(false); + const [showButs, setShowButs] = useState(false); + const [showNewCellUp, setShowNewCellUp] = useState(false); + const [showNewCellDown, setShowNewCellDown] = useState(false); const saveButClicked = () => { setEditMode(false); setIsDirty(false); - props.editContent(id, storyContent); + setStoryContent(storyContent.trim()); + setShowButs(false); + setShowNewCellUp(false); + setShowNewCellDown(false); + props.editContent(index, storyContent.trim()); } const onEditorValueChange = (content: string) => { @@ -39,28 +50,77 @@ function EditStoryCell(props: Props) { } }, []); - return
+ return
setShowButs(true)} + onMouseLeave={() => setShowButs(false)}> + {showNewCellUp && } {isEditMode &&
} - {isEditMode ? - : renderStoryMarkdown(storyContent, id) - } +
+ {showButs &&
+ + + +
} + {isEditMode ? + : renderStoryMarkdown(storyContent, index, false) + } +
+ {showNewCellDown && }
} diff --git a/src/features/stories/storiesComponents/SourceBlock.tsx b/src/features/stories/storiesComponents/SourceBlock.tsx index cb6c1ec8d2..61d0637205 100644 --- a/src/features/stories/storiesComponents/SourceBlock.tsx +++ b/src/features/stories/storiesComponents/SourceBlock.tsx @@ -1,4 +1,4 @@ -import { Card, Classes } from '@blueprintjs/core'; +import { Card, Classes, Menu, MenuItem } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import { Chapter, Variant } from 'js-slang/dist/types'; import React, { useEffect, useRef, useState } from 'react'; @@ -21,14 +21,14 @@ import { makeSubstVisualizerTabFrom } from 'src/pages/playground/PlaygroundTabs' import { ExternalLibraryName } from '../../../commons/application/types/ExternalTypes'; import { Output } from '../../../commons/repl/Repl'; import { getModeString, selectMode } from '../../../commons/utils/AceHelper'; -import { DEFAULT_ENV } from './UserBlogContent'; +import { DEFAULT_ENV, handleHeaders } from './UserBlogContent'; import { ControlButtonSaveButton } from 'src/commons/controlBar/ControlBarSaveButton'; -import { StoryCell } from './BackendAccess'; export type SourceBlockProps = { content: string; commands: string; // env is in commands index: number; + isViewOnly: boolean; }; /** @@ -74,6 +74,9 @@ const SourceBlock: React.FC = props => { store => store.stories.envs[env]?.context.variant || Constants.defaultSourceVariant ); + const [currentEnv, setCurrentEnv] = useState(env); + const [currentChapter, setCurrentChapter] = useState(chapter); + useEffect(() => { setCode(props.content); }, [props.content]); @@ -103,6 +106,7 @@ const SourceBlock: React.FC = props => { env === DEFAULT_ENV ? styliseSublanguage(chapter, variant) : env + ' | ' + styliseSublanguage(chapter, variant); + // TODO: Add CSE machine tabs and shift to language config @@ -215,8 +219,32 @@ const SourceBlock: React.FC = props => { const saveButClicked = () => { setIsDirty(false); - story!.content.filter((story: StoryCell) => story.id == props.index)[0].content = code; - dispatch(StoriesActions.setCurrentStory({...story!, content: [...story!.content]})); + setCode(code.trim()); + story!.content[props.index].content = code.trim(); + if (currentEnv !== env) { + story!.content[props.index].env = currentEnv; + } + let newHeader = story!.header.split('\n'); + if (currentChapter !== chapter) { + const index = envList.indexOf(env); + newHeader[index * 3 + 3] = ` chapter: ${chapter}`; + } + execResetEnv(); + handleHeaders(newHeader.join('\n')); + dispatch(StoriesActions.setCurrentStory({...story!, content: [...story!.content], header: newHeader.join('\n')})); + } + + const changeEnv = (env: string) => { + setCurrentEnv(env); + let header = story!.header.split('\n'); + const index = envList.indexOf(env); + setCurrentChapter(+header[3 + index * 3].substring(13)); + setIsDirty(true); + } + + const changeEnvChapter = (chapter: Chapter) => { + setCurrentChapter(chapter); + setIsDirty(true); } selectMode(chapter, variant, ExternalLibraryName.NONE); @@ -238,7 +266,33 @@ const SourceBlock: React.FC = props => { isEntrypointFileDefined />} - {envDisplayLabel} + {props.isViewOnly + ? envDisplayLabel + :
+ + + {envList.map((env: string, index: number) => changeEnv(env)} + />)} + + +

|

+ + + changeEnvChapter(1)} text={styliseSublanguage(Chapter.SOURCE_1, variant)}/> + changeEnvChapter(2)} text={styliseSublanguage(Chapter.SOURCE_2, variant)}/> + changeEnvChapter(3)} text={styliseSublanguage(Chapter.SOURCE_3, variant)}/> + changeEnvChapter(4)} text={styliseSublanguage(Chapter.SOURCE_4, variant)}/> + + +
}
diff --git a/src/features/stories/storiesComponents/UserBlogContent.tsx b/src/features/stories/storiesComponents/UserBlogContent.tsx index b603b8f27d..991d1cad3c 100644 --- a/src/features/stories/storiesComponents/UserBlogContent.tsx +++ b/src/features/stories/storiesComponents/UserBlogContent.tsx @@ -1,6 +1,6 @@ import { Chapter, Variant } from 'js-slang/dist/types'; import yaml from 'js-yaml'; -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import debounceRender from 'react-debounce-render'; import Constants from 'src/commons/utils/Constants'; import { propsAreEqual } from 'src/commons/utils/MemoizeHelper'; @@ -10,7 +10,14 @@ import StoriesActions from 'src/features/stories/StoriesActions'; import { store } from '../../../pages/createStore'; import ViewStoryCell from './ViewStoryCell'; -import { StoryCell } from './BackendAccess'; +import { StoryCell } from "../StoriesTypes"; +import NewStoryCell from './CreateStoryCell'; +import { TextInput } from '@tremor/react'; +import { Menu, MenuItem } from '@blueprintjs/core'; +import { styliseSublanguage } from 'src/commons/application/ApplicationTypes'; +import { showWarningMessage } from 'src/commons/utils/notifications/NotificationsHelper'; +import { ControlButtonSaveButton } from 'src/commons/controlBar/ControlBarSaveButton'; +import ControlBar, { ControlBarProps } from 'src/commons/controlBar/ControlBar'; export const DEFAULT_ENV = 'default'; @@ -36,7 +43,7 @@ function handleEnvironment(envConfig: Record): void { } } -function handleHeaders(headers: string): void { +export function handleHeaders(headers: string): void { if (headers === '') { store.dispatch( StoriesActions.addStoryEnv( @@ -64,7 +71,7 @@ function handleHeaders(headers: string): void { } } } catch (err) { - console.warn(err); + console.warn(err,); if (err instanceof yaml.YAMLException) { // default headers store.dispatch( @@ -94,23 +101,117 @@ export function getYamlHeader(content: string): { header: string; content: strin }; } +export function getEnvironments(header: string): string[] { + const environments: string[] = []; + const temp = header.split("\n"); + for (let i = 2; i < temp.length - 1; i += 3) { + environments.push(temp[i].substring(2, temp[i].length - 1)); + } + return environments; +} + +export function constructHeader(header: string, env: string, chapter: Chapter, variant: Variant): string { + const newHeader: string[] = header.split('\n'); + newHeader.push(` ${env}:`); + newHeader.push(` chapter: ${chapter}`); + newHeader.push(` variant: ${variant}`); + return newHeader.join('\n'); +} + type Props = { header: string; contents: StoryCell[]; isViewOnly: boolean; editContent: (id: number, newContent: string) => void; + editHeader: (newHeader: string) => void; + saveNewStoryCell: (index: number, isCode: boolean, env: string, content: string) => void; }; -const UserBlogContent: React.FC = ({ header, contents, isViewOnly, editContent}) => { +const UserBlogContent: React.FC = ({ + header, + contents, + isViewOnly, + editContent, + editHeader, + saveNewStoryCell + }) => { + + const [envs, setEnvs] = useState(getEnvironments(header)); + const [newEnv, setNewEnv] = useState(""); + // TODO: enable different variant + const variant: Variant = Variant.DEFAULT; + const [currentChapter, setEnvChapter] = useState(Chapter.SOURCE_1); + const [isDirty, setIsDirty] = useState(false); useEffect(() => { // const header = getYamlHeader(fileContent).header; store.dispatch(StoriesActions.clearStoryEnv()); handleHeaders(header); - }, [header, contents]); + setEnvs(getEnvironments(header)); + }, [header]); + + const saveButClicked = () => { + setNewEnv(""); + setIsDirty(false); + if (newEnv.trim() === "") { + showWarningMessage("environment name cannot be empty"); + return; + } else if (envs.includes(newEnv)) { + showWarningMessage(`${newEnv} already exists!`) + return; + } + header = header.concat(` + ${newEnv}: + chapter: ${currentChapter} + variant: default` + ); + editHeader(header); + } + + const controlBarProps: ControlBarProps = { + editorButtons: [ +
+ { + setNewEnv(e.target.value); + if (e.target.value.trim() !== "") { + setIsDirty(true); + } else { + setIsDirty(false); + } + }} + /> + + + setEnvChapter(1)} text={styliseSublanguage(Chapter.SOURCE_1, variant)}/> + setEnvChapter(2)} text={styliseSublanguage(Chapter.SOURCE_2, variant)}/> + setEnvChapter(3)} text={styliseSublanguage(Chapter.SOURCE_3, variant)}/> + setEnvChapter(4)} text={styliseSublanguage(Chapter.SOURCE_4, variant)}/> + + + +
+ ] + }; return contents.length > 0 ? (
+ {!isViewOnly && } {isViewOnly ? contents.map((story, key) => = ({ header, contents, isViewOnly, editCo />) : contents.map((story, key) => )} + {!isViewOnly &&
+ +
}
) : (
diff --git a/src/features/stories/storiesComponents/ViewStoryCell.tsx b/src/features/stories/storiesComponents/ViewStoryCell.tsx index f7922bdfb6..0be72d52d2 100644 --- a/src/features/stories/storiesComponents/ViewStoryCell.tsx +++ b/src/features/stories/storiesComponents/ViewStoryCell.tsx @@ -1,5 +1,5 @@ import { renderStoryMarkdown } from "src/commons/utils/StoriesHelper"; -import { StoryCell } from "./BackendAccess"; +import { StoryCell } from '../StoriesTypes'; import { useEffect, useState } from "react"; type Props = { @@ -8,7 +8,7 @@ type Props = { function ViewStoryCell(props: Props) { - const { id, isCode, env, content } = props.story; + const { index, isCode, env, content } = props.story; const [storyContent, setStoryContent] = useState(content); useEffect(() => { @@ -18,7 +18,7 @@ function ViewStoryCell(props: Props) { }, []); return
- {renderStoryMarkdown(storyContent, id)} + {renderStoryMarkdown(storyContent, index, true)}
} diff --git a/src/pages/stories/StoriesTable.tsx b/src/pages/stories/StoriesTable.tsx index bf5816cc89..c0a45d304a 100644 --- a/src/pages/stories/StoriesTable.tsx +++ b/src/pages/stories/StoriesTable.tsx @@ -19,12 +19,6 @@ type Props = { const MAX_EXCERPT_LENGTH = 35; const truncate = (content: string) => { - console.log(content); - console.log(content.replaceAll(/\s+/g, ' ').length <= MAX_EXCERPT_LENGTH - ? content.replaceAll(/\s+/g, ' ') - : content.split(/\s+/).reduce((acc, cur) => { - return acc.length + cur.length <= MAX_EXCERPT_LENGTH ? acc + ' ' + cur : acc; - }, '') + '…'); return content.replaceAll(/\s+/g, ' ').length <= MAX_EXCERPT_LENGTH ? content.replaceAll(/\s+/g, ' ') : content.split(/\s+/).reduce((acc, cur) => { diff --git a/src/pages/stories/Story.tsx b/src/pages/stories/Story.tsx index 79705aa0a0..0686e990e5 100644 --- a/src/pages/stories/Story.tsx +++ b/src/pages/stories/Story.tsx @@ -14,7 +14,7 @@ import { useTypedSelector } from 'src/commons/utils/Hooks'; import StoriesActions from 'src/features/stories/StoriesActions'; import UserBlogContent from '../../features/stories/storiesComponents/UserBlogContent'; -import { StoryCell } from 'src/features/stories/storiesComponents/BackendAccess'; +import { StoryCell } from 'src/features/stories/StoriesTypes'; type Props = { isViewOnly?: boolean; @@ -44,11 +44,31 @@ const Story: React.FC = ({ isViewOnly = false }) => { const { header: header , content: contents } = story!; - const editContent = (id: number, newContent: string) => { - contents.filter((story: StoryCell) => story.id == id)[0].content = newContent; + const editContent = (index: number, newContent: string) => { + contents.filter((story: StoryCell) => story.index == index)[0].content = newContent; dispatch(StoriesActions.setCurrentStory({...story, content: [...contents]})); } + const editHeader = (newHeader: string) => { + dispatch(StoriesActions.setCurrentStory({...story, header: newHeader})); + } + + const saveNewStoryCell = (index: number, isCode: boolean, env: string, content: string) => { + const contents = story.content; + for (let i = index; i < contents.length; i++) { + contents[i].index += 1; + } + const newContent: StoryCell = { + index: index, + isCode: isCode, + env: env, + content: content, + } + contents.push(newContent); + contents.sort((a, b) => a.index - b.index); + dispatch(StoriesActions.setCurrentStory({...story, content: [...contents]})); + } + // const onEditorScroll = (e: IEditorProps) => { // const userblogContainer = document.getElementById('userblogContainer'); // if (userblogContainer) { @@ -121,7 +141,14 @@ const Story: React.FC = ({ isViewOnly = false }) => { /> )} */}
- +
From b061fec7106b087c97a4438a82279a55b706d6d1 Mon Sep 17 00:00:00 2001 From: desmondwong1215 Date: Sat, 1 Mar 2025 09:29:05 +0800 Subject: [PATCH 07/16] enable dragging to change sequence --- package.json | 3 + .../storiesComponents/EditStoryCell.tsx | 15 +++- .../storiesComponents/UserBlogContent.tsx | 22 ++++-- src/pages/stories/Story.tsx | 73 ++++++++++++------- yarn.lock | 52 +++++++++++++ 5 files changed, 130 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index 99c6b36a06..bb99c9610d 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,9 @@ "@blueprintjs/datetime2": "^2.3.3", "@blueprintjs/icons": "^5.9.0", "@blueprintjs/select": "^5.1.3", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@mantine/hooks": "^7.11.2", "@octokit/rest": "^20.0.0", "@reduxjs/toolkit": "^1.9.7", diff --git a/src/features/stories/storiesComponents/EditStoryCell.tsx b/src/features/stories/storiesComponents/EditStoryCell.tsx index f351ff664b..a605d0f2e4 100644 --- a/src/features/stories/storiesComponents/EditStoryCell.tsx +++ b/src/features/stories/storiesComponents/EditStoryCell.tsx @@ -5,6 +5,8 @@ import { StoryCell } from '../StoriesTypes'; import { ControlButtonSaveButton } from "src/commons/controlBar/ControlBarSaveButton"; import { Button } from "@blueprintjs/core"; import NewStoryCell from "./CreateStoryCell"; +import { useSortable } from "@dnd-kit/sortable"; +import { CSS } from "@dnd-kit/utilities"; type Props = { story: StoryCell; @@ -16,12 +18,19 @@ type Props = { function EditStoryCell(props: Props) { const { index, isCode, env, content } = props.story; + const id = index; const [isEditMode, setEditMode] = useState(false); const [storyContent, setStoryContent] = useState(content); const [isDirty, setIsDirty] = useState(false); const [showButs, setShowButs] = useState(false); const [showNewCellUp, setShowNewCellUp] = useState(false); const [showNewCellDown, setShowNewCellDown] = useState(false); + const {attributes, listeners, setNodeRef, transform, transition} = useSortable({id}); + + const style = { + transition, + transform: CSS.Transform.toString(transform), + }; const saveButClicked = () => { setEditMode(false); @@ -53,7 +62,11 @@ function EditStoryCell(props: Props) { return
setShowButs(true)} - onMouseLeave={() => setShowButs(false)}> + onMouseLeave={() => setShowButs(false)} + ref={setNodeRef} + {...attributes} + {...listeners} + style={style}> {showNewCellUp && = ({ key={key} story={story} />) - : contents.map((story, key) => )} + : { + return { + ...content, + id: index + } + })} strategy={verticalListSortingStrategy}> + {contents.map((story, key) => )} + } {!isViewOnly &&
= ({ isViewOnly = false }) => { ] }; + const handleDragEnd = (event: DragEndEvent) => { + const {active, over} = event; + + // sequence does not change + if (active.id == over!.id) { + return; + } + const activeIndex: number = contents.findIndex((content) => content.index === active.id); + const overIndex: number = contents.findIndex((content) => content.index === over!.id); + const temp: StoryCell = contents[activeIndex]; + contents[activeIndex] = contents[overIndex]; + contents[overIndex] = temp; + console.log(contents); + dispatch(StoriesActions.setCurrentStory({...story, content: [...contents]})); + } + return (
-
- {/* {!isViewOnly && ( - - )} */} -
- + +
+ {/* {!isViewOnly && ( + + )} */} +
+ +
-
+
); }; diff --git a/yarn.lock b/yarn.lock index 3cf4a3c557..da11d8deb1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2054,6 +2054,55 @@ __metadata: languageName: node linkType: hard +"@dnd-kit/accessibility@npm:^3.1.1": + version: 3.1.1 + resolution: "@dnd-kit/accessibility@npm:3.1.1" + dependencies: + tslib: "npm:^2.0.0" + peerDependencies: + react: ">=16.8.0" + checksum: 10c0/be0bf41716dc58f9386bc36906ec1ce72b7b42b6d1d0e631d347afe9bd8714a829bd6f58a346dd089b1519e93918ae2f94497411a61a4f5e4d9247c6cfd1fef8 + languageName: node + linkType: hard + +"@dnd-kit/core@npm:^6.3.1": + version: 6.3.1 + resolution: "@dnd-kit/core@npm:6.3.1" + dependencies: + "@dnd-kit/accessibility": "npm:^3.1.1" + "@dnd-kit/utilities": "npm:^3.2.2" + tslib: "npm:^2.0.0" + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 10c0/196db95d81096d9dc248983533eab91ba83591770fa5c894b1ac776f42af0d99522b3fd5bb3923411470e4733fcfa103e6ee17adc17b9b7eb54c7fbec5ff7c52 + languageName: node + linkType: hard + +"@dnd-kit/sortable@npm:^10.0.0": + version: 10.0.0 + resolution: "@dnd-kit/sortable@npm:10.0.0" + dependencies: + "@dnd-kit/utilities": "npm:^3.2.2" + tslib: "npm:^2.0.0" + peerDependencies: + "@dnd-kit/core": ^6.3.0 + react: ">=16.8.0" + checksum: 10c0/37ee48bc6789fb512dc0e4c374a96d19abe5b2b76dc34856a5883aaa96c3297891b94cc77bbc409e074dcce70967ebcb9feb40cd9abadb8716fc280b4c7f99af + languageName: node + linkType: hard + +"@dnd-kit/utilities@npm:^3.2.2": + version: 3.2.2 + resolution: "@dnd-kit/utilities@npm:3.2.2" + dependencies: + tslib: "npm:^2.0.0" + peerDependencies: + react: ">=16.8.0" + checksum: 10c0/9aa90526f3e3fd567b5acc1b625a63177b9e8d00e7e50b2bd0e08fa2bf4dba7e19529777e001fdb8f89a7ce69f30b190c8364d390212634e0afdfa8c395e85a0 + languageName: node + linkType: hard + "@emotion/babel-plugin@npm:^11.11.0": version: 11.11.0 resolution: "@emotion/babel-plugin@npm:11.11.0" @@ -9589,6 +9638,9 @@ __metadata: "@blueprintjs/select": "npm:^5.1.3" "@convergencelabs/ace-collab-ext": "npm:^0.6.0" "@craco/craco": "npm:^7.1.0" + "@dnd-kit/core": "npm:^6.3.1" + "@dnd-kit/sortable": "npm:^10.0.0" + "@dnd-kit/utilities": "npm:^3.2.2" "@mantine/hooks": "npm:^7.11.2" "@octokit/rest": "npm:^20.0.0" "@reduxjs/toolkit": "npm:^1.9.7" From 513934cefe285259023f1e4d3d3ff0b9042b5bc2 Mon Sep 17 00:00:00 2001 From: desmondwong1215 Date: Sun, 9 Mar 2025 20:49:19 +0800 Subject: [PATCH 08/16] Remove Dnd and debug for inserting cell --- src/commons/sagas/StoriesSaga.ts | 10 +- src/features/stories/StoriesTypes.ts | 1 + .../storiesComponents/BackendAccess.ts | 31 ++- .../storiesComponents/CreateStoryCell.tsx | 45 +++- .../storiesComponents/EditStoryCell.tsx | 230 ++++++++++-------- .../stories/storiesComponents/SourceBlock.tsx | 41 +++- .../storiesComponents/UserBlogContent.tsx | 68 +++--- src/pages/stories/Story.tsx | 91 +++---- src/styles/_stories.scss | 1 + 9 files changed, 307 insertions(+), 211 deletions(-) diff --git a/src/commons/sagas/StoriesSaga.ts b/src/commons/sagas/StoriesSaga.ts index f1f14d7e11..a7cd9e200c 100644 --- a/src/commons/sagas/StoriesSaga.ts +++ b/src/commons/sagas/StoriesSaga.ts @@ -12,8 +12,8 @@ import { putStoriesUserRole, updateStory, // updateHeader, - tempHeader, - tempContent + defaultHeader, + defaultContent } from 'src/features/stories/storiesComponents/BackendAccess'; import { StoryData, StoryListView, StoryView } from 'src/features/stories/StoriesTypes'; @@ -50,8 +50,8 @@ const StoriesSaga = combineSagaHandlers(sagaActions, { } else { const defaultStory: StoryData = { title: '', - header: tempHeader, - content: tempContent, + header: defaultHeader, + content: defaultContent, pinOrder: null }; yield put(actions.setCurrentStory(defaultStory)); @@ -87,6 +87,8 @@ const StoriesSaga = combineSagaHandlers(sagaActions, { saveStory: function* (action) { const tokens: Tokens = yield selectTokens(); const { story, id } = action.payload; + console.log("In saveStory"); + console.log(story.header); const updatedStory: StoryView | null = yield call( updateStory, tokens, diff --git a/src/features/stories/StoriesTypes.ts b/src/features/stories/StoriesTypes.ts index c32e0affef..b371a2214a 100644 --- a/src/features/stories/StoriesTypes.ts +++ b/src/features/stories/StoriesTypes.ts @@ -3,6 +3,7 @@ import { DebuggerContext } from 'src/commons/workspace/WorkspaceTypes'; import { InterpreterOutput, StoriesRole } from '../../commons/application/ApplicationTypes'; export type StoryCell = { + // id: number; index: number; isCode: boolean; env: string; diff --git a/src/features/stories/storiesComponents/BackendAccess.ts b/src/features/stories/storiesComponents/BackendAccess.ts index 6fa17cae0b..e0fe77184d 100644 --- a/src/features/stories/storiesComponents/BackendAccess.ts +++ b/src/features/stories/storiesComponents/BackendAccess.ts @@ -19,7 +19,7 @@ import { StoryCell } from '../StoriesTypes'; // chapter: 4 // variant: default -export const tempHeader: string = `--- +export const defaultHeader: string = `--- env: iterFib: chapter: 4 @@ -31,8 +31,9 @@ env: chapter: 4 variant: default`; -export const tempContent: StoryCell[] = [ +export const defaultContent: StoryCell[] = [ { + // id: 0, index: 0, isCode: true, env: "iterFib", @@ -45,6 +46,7 @@ display("hello world1"); `, }, { + // id: 1, index: 1, isCode: false, env: "", @@ -61,6 +63,7 @@ print("hello world") `, }, { + // id: 2, index: 2, isCode: true, env: "recuFib", @@ -69,6 +72,7 @@ print("hello world") `, }, { + // id: 3, index: 3, isCode: true, env: "iterFib", @@ -77,15 +81,19 @@ print("hello world") `, }, { + // id: 4, index: 4, isCode: true, env: "iterFib", content: - `print("hello world"); + `print("why this cell?"); `, } ]; +let tempHeader = defaultHeader; +let tempContent = defaultContent; + // Helpers type StoryRequestHelperParams = RemoveLast>; @@ -157,7 +165,7 @@ export const getStories = async (tokens: Tokens): Promise ({...story, header: tempHeader, content: tempContent})); + return stories.map((story: any) => ({...story, header: tempContent, content: tempContent})); }; export const getStory = async (tokens: Tokens, storyId: number): Promise => { @@ -173,8 +181,9 @@ export const getStory = async (tokens: Tokens, storyId: number): Promise void; }; const NewStoryCell: React.FC = ({ index, - envs, - saveNewStoryCell }) => { + const dispatch = useDispatch(); + const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); + const envs = getEnvironments(story!.header); const [isCode, setIsCode] = useState(false); const [env, setEnv] = useState(envs[0]); const [code, setCode] = useState(""); const [isDirty, setIsDirty] = useState(false); + // const setIsTyping = useContext(SourceBlockContext); + if (!story) { + return
; + } + const editorOnChange = (code: string) => { setCode(code); setIsDirty(code.trim() !== ""); @@ -33,12 +42,33 @@ const NewStoryCell: React.FC = ({ setIsDirty(false); } + const saveNewStoryCell = () => { + const contents = story.content; + for (let i = index; i < contents.length; i++) { + contents[i].index += 1; + } + const newContent: StoryCell = { + // id: index, + index: index, + isCode: isCode, + env: isCode ? env : "", + content: code, + } + contents.push(newContent); + contents.sort((a, b) => a.index - b.index); + const newStory = {...story, content: [...contents]}; + console.log("a new cell is saved"); + console.log(newStory); + dispatch(StoriesActions.setCurrentStory({...newStory})); + dispatch(StoriesActions.saveStory(newStory, storyId!)); + } + const saveButClicked = () => { if (!isDirty) { showWarningMessage("Cannot save empty story cell!"); return; } - saveNewStoryCell(index, isCode, isCode ? env : "", code); + saveNewStoryCell(); reset(); } @@ -49,7 +79,8 @@ const NewStoryCell: React.FC = ({ gap: "5px", padding: "5px", backgroundColor: "#2c3e50" - }}> + }} + onPointerDown={(e) => e.stopPropagation()}> = ({ theme="source" value={code} onChange={editorOnChange} + // onFocus={() => setIsTyping(true)} + // onBlur={() => setIsTyping(false)} minLines={5} maxLines={20} fontSize={17} diff --git a/src/features/stories/storiesComponents/EditStoryCell.tsx b/src/features/stories/storiesComponents/EditStoryCell.tsx index a605d0f2e4..27a63aedee 100644 --- a/src/features/stories/storiesComponents/EditStoryCell.tsx +++ b/src/features/stories/storiesComponents/EditStoryCell.tsx @@ -1,36 +1,64 @@ -import { useEffect, useState } from "react"; +import { createContext, useEffect, useState } from "react"; import { renderStoryMarkdown } from "src/commons/utils/StoriesHelper"; import AceEditor from 'react-ace'; import { StoryCell } from '../StoriesTypes'; import { ControlButtonSaveButton } from "src/commons/controlBar/ControlBarSaveButton"; import { Button } from "@blueprintjs/core"; import NewStoryCell from "./CreateStoryCell"; -import { useSortable } from "@dnd-kit/sortable"; -import { CSS } from "@dnd-kit/utilities"; +import { useDispatch } from "react-redux"; +import { useTypedSelector } from "src/commons/utils/Hooks"; +import StoriesActions from "../StoriesActions"; +// import { useSortable } from "@dnd-kit/sortable"; +// import { CSS } from "@dnd-kit/utilities"; type Props = { - story: StoryCell; - envs: string[]; - editContent: (id: number, newContent: string) => void; - saveNewStoryCell: (index: number, isCode: boolean, env: string, content: string) => void; + index: number; }; +export const SourceBlockContext = createContext<(isTyping: boolean) => void>(() => {}); + function EditStoryCell(props: Props) { - const { index, isCode, env, content } = props.story; - const id = index; - const [isEditMode, setEditMode] = useState(false); - const [storyContent, setStoryContent] = useState(content); - const [isDirty, setIsDirty] = useState(false); - const [showButs, setShowButs] = useState(false); - const [showNewCellUp, setShowNewCellUp] = useState(false); - const [showNewCellDown, setShowNewCellDown] = useState(false); - const {attributes, listeners, setNodeRef, transform, transition} = useSortable({id}); + const dispatch = useDispatch(); + const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); + const [ isCode, setIsCode ] = useState(false); + const [ env, setEnv ] = useState(""); + // const [ content, setContent ] = useState(""); + const [ storyContent, setStoryContent ] = useState(""); + const [ isEditMode, setEditMode ] = useState(false); + const [ isDirty, setIsDirty ] = useState(false); + const [ showButs, setShowButs ] = useState(false); + const [ showNewCellUp, setShowNewCellUp ] = useState(false); + const [ showNewCellDown, setShowNewCellDown ] = useState(false); + // const {attributes, listeners, setNodeRef, transform, transition} = useSortable({id}); + + // const style = { + // transition, + // transform: CSS.Transform.toString(transform), + // }; + + useEffect(() => { + if (!story) return; + setStoryContent(story.content[props.index].content); + setEnv(story.content[props.index].env); + setIsCode(story.content[props.index].isCode); + console.log(story.content[props.index], props.index, isCode); + }, [story]); - const style = { - transition, - transform: CSS.Transform.toString(transform), - }; + if (!story) { + // will never reach here, as it has been checked in Story.tsx + return
; + } + + const editContent = () => { + console.log("content is editted"); + const contents = story.content; + const newContent = storyContent.trim(); + contents.filter((story: StoryCell) => story.index == props.index)[0].content = newContent; + const newStory = {...story, content: [...contents]}; + dispatch(StoriesActions.setCurrentStory(newStory)); + dispatch(StoriesActions.saveStory(newStory, storyId!)); + } const saveButClicked = () => { setEditMode(false); @@ -39,7 +67,7 @@ function EditStoryCell(props: Props) { setShowButs(false); setShowNewCellUp(false); setShowNewCellDown(false); - props.editContent(index, storyContent.trim()); + editContent(); } const onEditorValueChange = (content: string) => { @@ -53,87 +81,95 @@ function EditStoryCell(props: Props) { } } - useEffect(() => { - if (isCode) { - setStoryContent("\`\`\`{source} env:" + env + "\n" + content); - } - }, []); - return
setShowButs(true)} onMouseLeave={() => setShowButs(false)} - ref={setNodeRef} - {...attributes} - {...listeners} - style={style}> - {showNewCellUp && } - {isEditMode &&
- -
} -
- {showButs &&
- - - + // onPointerDown={(e) => e.stopPropagation()} + // ref={setNodeRef} + // {...(attributes)} + // {...(!isTyping && listeners)} + // style={style} + > + {/* */} + {showNewCellUp && } + {isEditMode &&
e.stopPropagation()} + > +
} - {isEditMode ? - : renderStoryMarkdown(storyContent, index, false) - } -
- {showNewCellDown && } +
+ {showButs &&
+ + + +
} + {isEditMode ? setIsTyping(true)} + // onBlur={() => setIsTyping(false)} + minLines={5} + maxLines={20} + fontSize={17} + highlightActiveLine={false} + showPrintMargin={false} + wrapEnabled={true} + setOptions={{ fontFamily: "'Inconsolata', 'Consolas', monospace" }} + style={{marginTop: "0px"}} + /> + : renderStoryMarkdown( + isCode + ? ("\`\`\`{source} env:" + env + "\n" + storyContent) + : storyContent, props.index, false) + } +
+ {showNewCellDown && } + {/* */}
} diff --git a/src/features/stories/storiesComponents/SourceBlock.tsx b/src/features/stories/storiesComponents/SourceBlock.tsx index 61d0637205..ea2c8deaf7 100644 --- a/src/features/stories/storiesComponents/SourceBlock.tsx +++ b/src/features/stories/storiesComponents/SourceBlock.tsx @@ -21,8 +21,11 @@ import { makeSubstVisualizerTabFrom } from 'src/pages/playground/PlaygroundTabs' import { ExternalLibraryName } from '../../../commons/application/types/ExternalTypes'; import { Output } from '../../../commons/repl/Repl'; import { getModeString, selectMode } from '../../../commons/utils/AceHelper'; -import { DEFAULT_ENV, handleHeaders } from './UserBlogContent'; +import { DEFAULT_ENV, getEnvironments, handleHeaders } from './UserBlogContent'; import { ControlButtonSaveButton } from 'src/commons/controlBar/ControlBarSaveButton'; +// import { SourceBlockContext } from './EditStoryCell'; +// import { useSortable } from '@dnd-kit/sortable'; +// import { CSS } from "@dnd-kit/utilities"; export type SourceBlockProps = { content: string; @@ -55,8 +58,10 @@ const SourceBlock: React.FC = props => { const [outputIndex, setOutputIndex] = useState(Infinity); const [isDirty, setIsDirty] = useState(false); - const { currentStory: story } = useTypedSelector(store => store.stories); + const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); const envList = useTypedSelector(store => Object.keys(store.stories.envs)); + const { header: header } = story!; + const envs = getEnvironments(header); // setting env const commandsEnv = parseMetadata('env', props.commands); @@ -74,6 +79,12 @@ const SourceBlock: React.FC = props => { store => store.stories.envs[env]?.context.variant || Constants.defaultSourceVariant ); + const getChapter = () => { + const envIndex = envs.indexOf(env); + // number indicating the chapter start from index 13 + return parseInt(header.split(`\n`)[envIndex * 3 + 3].substring(13)); + } + const [currentEnv, setCurrentEnv] = useState(env); const [currentChapter, setCurrentChapter] = useState(chapter); @@ -81,6 +92,10 @@ const SourceBlock: React.FC = props => { setCode(props.content); }, [props.content]); + useEffect(() => { + setCurrentChapter(getChapter()); + }, [story]); + const output = useTypedSelector(store => store.stories.envs[env]?.output || []); const { selectedTab, setSelectedTab } = useSideContent(`stories.${env}`); @@ -222,16 +237,20 @@ const SourceBlock: React.FC = props => { setCode(code.trim()); story!.content[props.index].content = code.trim(); if (currentEnv !== env) { + console.log("In source block: env is editted"); story!.content[props.index].env = currentEnv; } let newHeader = story!.header.split('\n'); if (currentChapter !== chapter) { + console.log("In source block: chapter is editted"); const index = envList.indexOf(env); - newHeader[index * 3 + 3] = ` chapter: ${chapter}`; + newHeader[index * 3 + 3] = ` chapter: ${currentChapter}`; } execResetEnv(); handleHeaders(newHeader.join('\n')); - dispatch(StoriesActions.setCurrentStory({...story!, content: [...story!.content], header: newHeader.join('\n')})); + const newStory = {...story!, content: [...story!.content], header: newHeader.join('\n')}; + dispatch(StoriesActions.setCurrentStory(newStory)); + dispatch(StoriesActions.saveStory(newStory, storyId!)); } const changeEnv = (env: string) => { @@ -250,7 +269,7 @@ const SourceBlock: React.FC = props => { selectMode(chapter, variant, ExternalLibraryName.NONE); return ( -
+
e.stopPropagation()}>
@@ -286,10 +305,10 @@ const SourceBlock: React.FC = props => {

|

- changeEnvChapter(1)} text={styliseSublanguage(Chapter.SOURCE_1, variant)}/> - changeEnvChapter(2)} text={styliseSublanguage(Chapter.SOURCE_2, variant)}/> - changeEnvChapter(3)} text={styliseSublanguage(Chapter.SOURCE_3, variant)}/> - changeEnvChapter(4)} text={styliseSublanguage(Chapter.SOURCE_4, variant)}/> + {[1, 2, 3, 4].map((chapter: Chapter, index: number) => changeEnvChapter(chapter)} text={styliseSublanguage(chapter, variant)}/> + )}
} @@ -297,7 +316,7 @@ const SourceBlock: React.FC = props => {
-
+
{e.stopPropagation()}}> = props => { width="100%" value={code} onChange={editorOnChange} + // onFocus={() => setIsTyping(true)} + // onBlur={() => setIsTyping(false)} commands={[ { name: 'evaluate', diff --git a/src/features/stories/storiesComponents/UserBlogContent.tsx b/src/features/stories/storiesComponents/UserBlogContent.tsx index f7e60e8547..d94515dd8a 100644 --- a/src/features/stories/storiesComponents/UserBlogContent.tsx +++ b/src/features/stories/storiesComponents/UserBlogContent.tsx @@ -10,7 +10,6 @@ import StoriesActions from 'src/features/stories/StoriesActions'; import { store } from '../../../pages/createStore'; import ViewStoryCell from './ViewStoryCell'; -import { StoryCell } from "../StoriesTypes"; import NewStoryCell from './CreateStoryCell'; import { TextInput } from '@tremor/react'; import { Menu, MenuItem } from '@blueprintjs/core'; @@ -18,7 +17,9 @@ import { styliseSublanguage } from 'src/commons/application/ApplicationTypes'; import { showWarningMessage } from 'src/commons/utils/notifications/NotificationsHelper'; import { ControlButtonSaveButton } from 'src/commons/controlBar/ControlBarSaveButton'; import ControlBar, { ControlBarProps } from 'src/commons/controlBar/ControlBar'; -import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'; +import { useDispatch } from 'react-redux'; +import { useTypedSelector } from 'src/commons/utils/Hooks'; +// import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'; export const DEFAULT_ENV = 'default'; @@ -72,7 +73,6 @@ export function handleHeaders(headers: string): void { } } } catch (err) { - console.warn(err,); if (err instanceof yaml.YAMLException) { // default headers store.dispatch( @@ -120,37 +120,43 @@ export function constructHeader(header: string, env: string, chapter: Chapter, v } type Props = { - header: string; - contents: StoryCell[]; isViewOnly: boolean; - editContent: (id: number, newContent: string) => void; - editHeader: (newHeader: string) => void; - saveNewStoryCell: (index: number, isCode: boolean, env: string, content: string) => void; }; const UserBlogContent: React.FC = ({ - header, - contents, - isViewOnly, - editContent, - editHeader, - saveNewStoryCell + isViewOnly, }) => { - const [envs, setEnvs] = useState(getEnvironments(header)); const [newEnv, setNewEnv] = useState(""); // TODO: enable different variant const variant: Variant = Variant.DEFAULT; const [currentChapter, setEnvChapter] = useState(Chapter.SOURCE_1); const [isDirty, setIsDirty] = useState(false); + const dispatch = useDispatch(); + const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); + const { content: contents, header: header } = story!; + const [envs, setEnvs] = useState(getEnvironments(header)); useEffect(() => { // const header = getYamlHeader(fileContent).header; store.dispatch(StoriesActions.clearStoryEnv()); handleHeaders(header); setEnvs(getEnvironments(header)); + console.log("header resets"); }, [header]); + if (!story) { + // will never reach here, as it has been check in Story.tsx + return
; + } + + const editHeader = (newHeader: string) => { + console.log("header is editted"); + const newStory = {...story, header: newHeader}; + dispatch(StoriesActions.setCurrentStory(newStory)); + dispatch(StoriesActions.saveStory(newStory, storyId!)); + } + const saveButClicked = () => { setNewEnv(""); setIsDirty(false); @@ -161,12 +167,12 @@ const UserBlogContent: React.FC = ({ showWarningMessage(`${newEnv} already exists!`) return; } - header = header.concat(` + const newHeader = header.concat(` ${newEnv}: chapter: ${currentChapter} variant: default` ); - editHeader(header); + editHeader(newHeader); } const controlBarProps: ControlBarProps = { @@ -218,25 +224,23 @@ const UserBlogContent: React.FC = ({ key={key} story={story} />) - : { - return { - ...content, - id: index - } - })} strategy={verticalListSortingStrategy}> - {contents.map((story, key) => { + // return { + // ...content, + // id: content.id, + // } + // })} strategy={verticalListSortingStrategy}> + : contents.map((_, key) => { + // console.log(story); + return )} - } + index={key} + // story={{...story}} + />})} + {/* } */} {!isViewOnly &&
}
diff --git a/src/pages/stories/Story.tsx b/src/pages/stories/Story.tsx index 3885bb8036..120bf0e174 100644 --- a/src/pages/stories/Story.tsx +++ b/src/pages/stories/Story.tsx @@ -14,8 +14,7 @@ import { useTypedSelector } from 'src/commons/utils/Hooks'; import StoriesActions from 'src/features/stories/StoriesActions'; import UserBlogContent from '../../features/stories/storiesComponents/UserBlogContent'; -import { StoryCell } from 'src/features/stories/StoriesTypes'; -import { DndContext, DragEndEvent, closestCorners } from "@dnd-kit/core"; +// import { DndContext, DragEndEvent, closestCorners } from "@dnd-kit/core"; type Props = { isViewOnly?: boolean; @@ -24,12 +23,10 @@ type Props = { const Story: React.FC = ({ isViewOnly = false }) => { const dispatch = useDispatch(); const [isDirty, setIsDirty] = useState(false); - // const [header, setHeader] = useState(tempHeader); const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); - // const header = story?.header; - // const [contents, setContents] = useState(tempContent); const { id: idToSet } = useParams<{ id: string }>(); + useEffect(() => { // Clear screen on first load dispatch(StoriesActions.setCurrentStory(null)); @@ -43,33 +40,6 @@ const Story: React.FC = ({ isViewOnly = false }) => { return <>; } - const { header: header , content: contents } = story!; - - const editContent = (index: number, newContent: string) => { - contents.filter((story: StoryCell) => story.index == index)[0].content = newContent; - dispatch(StoriesActions.setCurrentStory({...story, content: [...contents]})); - } - - const editHeader = (newHeader: string) => { - dispatch(StoriesActions.setCurrentStory({...story, header: newHeader})); - } - - const saveNewStoryCell = (index: number, isCode: boolean, env: string, content: string) => { - const contents = story.content; - for (let i = index; i < contents.length; i++) { - contents[i].index += 1; - } - const newContent: StoryCell = { - index: index, - isCode: isCode, - env: env, - content: content, - } - contents.push(newContent); - contents.sort((a, b) => a.index - b.index); - dispatch(StoriesActions.setCurrentStory({...story, content: [...contents]})); - } - // const onEditorScroll = (e: IEditorProps) => { // const userblogContainer = document.getElementById('userblogContainer'); // if (userblogContainer) { @@ -83,7 +53,7 @@ const Story: React.FC = ({ isViewOnly = false }) => { // }; // const { title, content } = story; - const { title } = story; + const { title: title } = story; const controlBarProps: ControlBarProps = { editorButtons: [ @@ -121,26 +91,42 @@ const Story: React.FC = ({ isViewOnly = false }) => { ] }; - const handleDragEnd = (event: DragEndEvent) => { - const {active, over} = event; - - // sequence does not change - if (active.id == over!.id) { - return; - } - const activeIndex: number = contents.findIndex((content) => content.index === active.id); - const overIndex: number = contents.findIndex((content) => content.index === over!.id); - const temp: StoryCell = contents[activeIndex]; - contents[activeIndex] = contents[overIndex]; - contents[overIndex] = temp; - console.log(contents); - dispatch(StoriesActions.setCurrentStory({...story, content: [...contents]})); - } + // const handleDragEnd = (event: DragEndEvent) => { + // const {active, over} = event; + + // // sequence does not change + // if (active.id == over!.id) { + // return; + // } + // let highIndex: number; + // let lowIndex: number; + // if (active.id > over!.id) { + // console.log("front") + // highIndex = contents.findIndex((content) => content.id === active.id); + // lowIndex = contents.findIndex((content) => content.id === over!.id); + // for (let i = lowIndex; i < highIndex; i++) { + // contents[i].index += 1; + // } + // contents[highIndex].index = lowIndex; + // } else { + // console.log("back"); + // highIndex = contents.findIndex((content) => content.id === over!.id); + // lowIndex = contents.findIndex((content) => content.id === active.id); + // for (let i = lowIndex + 1; i <= highIndex; i++) { + // contents[i].index -= 1; + // } + // contents[lowIndex].index = highIndex; + // } + // contents.sort((a, b) => a.index - b.index); + // console.log(contents); + // // setContents([...contents]); + // dispatch(StoriesActions.setCurrentStory({...story, content: [...contents]})); + // } return (
- + {/* */}
{/* {!isViewOnly && ( = ({ isViewOnly = false }) => { )} */}
-
+ {/*
*/}
); }; diff --git a/src/styles/_stories.scss b/src/styles/_stories.scss index deb55d57ad..c8037050af 100644 --- a/src/styles/_stories.scss +++ b/src/styles/_stories.scss @@ -51,6 +51,7 @@ padding: 16px 24px; font-size: 1rem; line-height: 1.5; + cursor: grab; & > * { margin: 0; From cf7f2fca45ec053e1ca43b7ab5b0af73493ab9ae Mon Sep 17 00:00:00 2001 From: desmondwong1215 Date: Mon, 10 Mar 2025 20:09:57 +0800 Subject: [PATCH 09/16] Enable deletion --- .../storiesComponents/EditStoryCell.tsx | 39 +++++++++++++-- .../stories/storiesComponents/SourceBlock.tsx | 47 ++++++++++++++----- .../storiesComponents/UserBlogContent.tsx | 6 +-- src/pages/stories/Story.tsx | 14 +++--- 4 files changed, 79 insertions(+), 27 deletions(-) diff --git a/src/features/stories/storiesComponents/EditStoryCell.tsx b/src/features/stories/storiesComponents/EditStoryCell.tsx index 27a63aedee..ab97ea0e65 100644 --- a/src/features/stories/storiesComponents/EditStoryCell.tsx +++ b/src/features/stories/storiesComponents/EditStoryCell.tsx @@ -23,13 +23,13 @@ function EditStoryCell(props: Props) { const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); const [ isCode, setIsCode ] = useState(false); const [ env, setEnv ] = useState(""); - // const [ content, setContent ] = useState(""); const [ storyContent, setStoryContent ] = useState(""); const [ isEditMode, setEditMode ] = useState(false); const [ isDirty, setIsDirty ] = useState(false); const [ showButs, setShowButs ] = useState(false); const [ showNewCellUp, setShowNewCellUp ] = useState(false); const [ showNewCellDown, setShowNewCellDown ] = useState(false); + // const id = props.index; // const {attributes, listeners, setNodeRef, transform, transition} = useSortable({id}); // const style = { @@ -63,11 +63,33 @@ function EditStoryCell(props: Props) { const saveButClicked = () => { setEditMode(false); setIsDirty(false); - setStoryContent(storyContent.trim()); setShowButs(false); setShowNewCellUp(false); setShowNewCellDown(false); - editContent(); + if (storyContent.trim().length > 0) { + setStoryContent(storyContent.trim()); + editContent(); + } else { + deleteStoryCell(); + } + } + + const deleteStoryCell = () => { + console.log(`story cell ${props.index} is deleted`); + const contents = story.content; + const newContents = []; + for (let i = 0; i < contents.length; i++) { + if (props.index === i) { + continue; + } else if (props.index < i) { + contents[i].index--; + } + newContents.push(contents[i]); + } + const newStory = {...story, content: newContents}; + console.log(newStory); + dispatch(StoriesActions.setCurrentStory(newStory)); + dispatch(StoriesActions.saveStory(newStory, storyId!)); } const onEditorValueChange = (content: string) => { @@ -85,10 +107,10 @@ function EditStoryCell(props: Props) { onDoubleClick={handleDoubleClick} onMouseEnter={() => setShowButs(true)} onMouseLeave={() => setShowButs(false)} - // onPointerDown={(e) => e.stopPropagation()} + onPointerDown={(e) => e.stopPropagation()} // ref={setNodeRef} // {...(attributes)} - // {...(!isTyping && listeners)} + // {...(listeners)} // style={style} > {/* */} @@ -141,6 +163,13 @@ function EditStoryCell(props: Props) { // setIsTyping(false); setEditMode(false); }}>Cancel +
} {isEditMode ? = props => { const [code, setCode] = useState(props.content); const [outputIndex, setOutputIndex] = useState(Infinity); const [isDirty, setIsDirty] = useState(false); - const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); const envList = useTypedSelector(store => Object.keys(store.stories.envs)); const { header: header } = story!; @@ -79,15 +79,15 @@ const SourceBlock: React.FC = props => { store => store.stories.envs[env]?.context.variant || Constants.defaultSourceVariant ); + const [currentEnv, setCurrentEnv] = useState(env); + const [currentChapter, setCurrentChapter] = useState(chapter); + const getChapter = () => { const envIndex = envs.indexOf(env); // number indicating the chapter start from index 13 return parseInt(header.split(`\n`)[envIndex * 3 + 3].substring(13)); } - const [currentEnv, setCurrentEnv] = useState(env); - const [currentChapter, setCurrentChapter] = useState(chapter); - useEffect(() => { setCode(props.content); }, [props.content]); @@ -232,23 +232,46 @@ const SourceBlock: React.FC = props => { setIsDirty(true); } + const deleteStoryCell = (contents: StoryCell[]) => { + console.log(`story cell ${props.index} is deleted`); + for (let i = props.index + 1; i < contents.length; i++) { + contents[i].index--; + } + contents.splice(props.index, 1); + } + + const editHeader = (header: string[]) => { + console.log("In source block: chapter is editted"); + const index = envList.indexOf(currentEnv); + header[index * 3 + 3] = ` chapter: ${currentChapter}`; + return header; + } + const saveButClicked = () => { setIsDirty(false); - setCode(code.trim()); - story!.content[props.index].content = code.trim(); + const trimmedCode = code.trim(); + setCode(trimmedCode); + const contents = [...story!.content]; + if (trimmedCode.length === 0) { + deleteStoryCell(contents); + } else { + contents[props.index].content = trimmedCode; + } if (currentEnv !== env) { + // set a new env console.log("In source block: env is editted"); + console.log(currentEnv, env); story!.content[props.index].env = currentEnv; } - let newHeader = story!.header.split('\n'); + const newHeader = story!.header.split('\n'); if (currentChapter !== chapter) { - console.log("In source block: chapter is editted"); - const index = envList.indexOf(env); - newHeader[index * 3 + 3] = ` chapter: ${currentChapter}`; + // set a new chapter for the corresponding env, all source block with the same env will change tgt + console.log(currentChapter, chapter); + editHeader(newHeader); } execResetEnv(); handleHeaders(newHeader.join('\n')); - const newStory = {...story!, content: [...story!.content], header: newHeader.join('\n')}; + const newStory = {...story!, content: contents, header: newHeader.join('\n')}; dispatch(StoriesActions.setCurrentStory(newStory)); dispatch(StoriesActions.saveStory(newStory, storyId!)); } @@ -269,7 +292,7 @@ const SourceBlock: React.FC = props => { selectMode(chapter, variant, ExternalLibraryName.NONE); return ( -
e.stopPropagation()}> +
diff --git a/src/features/stories/storiesComponents/UserBlogContent.tsx b/src/features/stories/storiesComponents/UserBlogContent.tsx index d94515dd8a..2a38947124 100644 --- a/src/features/stories/storiesComponents/UserBlogContent.tsx +++ b/src/features/stories/storiesComponents/UserBlogContent.tsx @@ -227,17 +227,15 @@ const UserBlogContent: React.FC = ({ // : { // return { // ...content, - // id: content.id, + // id: content.index, // } // })} strategy={verticalListSortingStrategy}> : contents.map((_, key) => { - // console.log(story); return })} - {/* } */} + {/* } */} {!isViewOnly &&
= ({ isViewOnly = false }) => { // } // let highIndex: number; // let lowIndex: number; + // const contents = story!.content; // if (active.id > over!.id) { // console.log("front") - // highIndex = contents.findIndex((content) => content.id === active.id); - // lowIndex = contents.findIndex((content) => content.id === over!.id); + // highIndex = contents.findIndex((content) => content.index === active.id); + // lowIndex = contents.findIndex((content) => content.index === over!.id); // for (let i = lowIndex; i < highIndex; i++) { // contents[i].index += 1; // } // contents[highIndex].index = lowIndex; // } else { // console.log("back"); - // highIndex = contents.findIndex((content) => content.id === over!.id); - // lowIndex = contents.findIndex((content) => content.id === active.id); + // highIndex = contents.findIndex((content) => content.index === over!.id); + // lowIndex = contents.findIndex((content) => content.index === active.id); // for (let i = lowIndex + 1; i <= highIndex; i++) { // contents[i].index -= 1; // } @@ -119,8 +120,9 @@ const Story: React.FC = ({ isViewOnly = false }) => { // } // contents.sort((a, b) => a.index - b.index); // console.log(contents); - // // setContents([...contents]); - // dispatch(StoriesActions.setCurrentStory({...story, content: [...contents]})); + // const newStory = {...story, content: [...contents]}; + // dispatch(StoriesActions.setCurrentStory(newStory)); + // dispatch(StoriesActions.saveStory(newStory, storyId!)); // } return ( From 450ed82bc4abefd79c29ce69ae17735d08c7e2ab Mon Sep 17 00:00:00 2001 From: desmondwong1215 Date: Tue, 11 Mar 2025 23:41:07 +0800 Subject: [PATCH 10/16] enable dnd --- src/commons/sideContent/SideContent.tsx | 2 +- src/features/stories/DragContext.ts | 18 +++ .../storiesComponents/BackendAccess.ts | 5 - .../storiesComponents/CreateStoryCell.tsx | 7 +- .../stories/storiesComponents/Draggable.tsx | 51 ++++++++ .../stories/storiesComponents/DropArea.tsx | 63 ++++++++++ .../storiesComponents/EditStoryCell.tsx | 112 +++++++++++------- .../stories/storiesComponents/SourceBlock.tsx | 2 +- .../storiesComponents/UserBlogContent.tsx | 26 ++-- src/pages/stories/Story.tsx | 68 ----------- src/styles/_stories.scss | 30 ++++- 11 files changed, 248 insertions(+), 136 deletions(-) create mode 100644 src/features/stories/DragContext.ts create mode 100644 src/features/stories/storiesComponents/Draggable.tsx create mode 100644 src/features/stories/storiesComponents/DropArea.tsx diff --git a/src/commons/sideContent/SideContent.tsx b/src/commons/sideContent/SideContent.tsx index 75cffad579..4b7a025292 100644 --- a/src/commons/sideContent/SideContent.tsx +++ b/src/commons/sideContent/SideContent.tsx @@ -70,7 +70,7 @@ const SideContent = ({ renderActiveTabPanelOnly, editorWidth, ...props }: SideCo {({ tabs: allTabs, alerts: tabAlerts, changeTabsCallback, selectedTab, height }) => (
- +
void; +} + +export const DragContext = createContext(null); + +export const getDragItem = () => { + const dragItem = useContext(DragContext); + + if (dragItem == null) { + throw Error("Drag Context cannot be null when in use"); + } + + return dragItem; +} \ No newline at end of file diff --git a/src/features/stories/storiesComponents/BackendAccess.ts b/src/features/stories/storiesComponents/BackendAccess.ts index e0fe77184d..359b0977c3 100644 --- a/src/features/stories/storiesComponents/BackendAccess.ts +++ b/src/features/stories/storiesComponents/BackendAccess.ts @@ -303,8 +303,3 @@ export const deleteUserUserGroups = async ( const user = await resp.json(); return user; }; - -// export const updateHeader = (newHeader: string): string | null => { -// tempHeader = newHeader; -// return tempHeader; -// } diff --git a/src/features/stories/storiesComponents/CreateStoryCell.tsx b/src/features/stories/storiesComponents/CreateStoryCell.tsx index 9ded0f449f..6484bcbaa6 100644 --- a/src/features/stories/storiesComponents/CreateStoryCell.tsx +++ b/src/features/stories/storiesComponents/CreateStoryCell.tsx @@ -24,7 +24,6 @@ const NewStoryCell: React.FC = ({ const [env, setEnv] = useState(envs[0]); const [code, setCode] = useState(""); const [isDirty, setIsDirty] = useState(false); - // const setIsTyping = useContext(SourceBlockContext); if (!story) { return
; @@ -48,7 +47,6 @@ const NewStoryCell: React.FC = ({ contents[i].index += 1; } const newContent: StoryCell = { - // id: index, index: index, isCode: isCode, env: isCode ? env : "", @@ -79,8 +77,7 @@ const NewStoryCell: React.FC = ({ gap: "5px", padding: "5px", backgroundColor: "#2c3e50" - }} - onPointerDown={(e) => e.stopPropagation()}> + }}> = ({ theme="source" value={code} onChange={editorOnChange} - // onFocus={() => setIsTyping(true)} - // onBlur={() => setIsTyping(false)} minLines={5} maxLines={20} fontSize={17} diff --git a/src/features/stories/storiesComponents/Draggable.tsx b/src/features/stories/storiesComponents/Draggable.tsx new file mode 100644 index 0000000000..709cfd0362 --- /dev/null +++ b/src/features/stories/storiesComponents/Draggable.tsx @@ -0,0 +1,51 @@ +import React, { useRef } from 'react'; +import { getDragItem } from '../DragContext'; + +interface DraggableProps { + children: React.ReactNode; + id: number; +} + +const Draggable: React.FC = ({ children, id }) => { + const elementRef = useRef(null); + const { setIndex } = getDragItem(); + + const handleDragStart = (e: React.DragEvent) => { + + if (elementRef.current) { + elementRef.current.style.opacity = '0.7'; + // Critical: Set the drag image to be just this element + e.dataTransfer.setDragImage(elementRef.current, 0, 0); + setIndex(id); + } + + // Set the effect + e.dataTransfer.effectAllowed = 'move'; + + // Prevent text selection during drag + document.body.style.userSelect = 'none'; + }; + + const handleDragEnd = () => { + // Re-enable text selection + if (elementRef.current) { + elementRef.current.style.opacity = '1'; + } + document.body.style.userSelect = ''; + }; + + return ( +
+ {children} +
+ ); +}; + +export default Draggable; \ No newline at end of file diff --git a/src/features/stories/storiesComponents/DropArea.tsx b/src/features/stories/storiesComponents/DropArea.tsx new file mode 100644 index 0000000000..e876fbd2a3 --- /dev/null +++ b/src/features/stories/storiesComponents/DropArea.tsx @@ -0,0 +1,63 @@ +import { useState } from "react"; +import { useTypedSelector } from "src/commons/utils/Hooks"; +import { getDragItem } from "../DragContext"; +import { useDispatch } from "react-redux"; +import StoriesActions from "../StoriesActions"; + +interface DropAreaProps { + dropIndex: number; +} + +const DropArea: React.FC = ({ dropIndex }) => { + + const [showDrop, setShowDrop] = useState(false); + const dragItem = getDragItem(); + const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); + const dispatch = useDispatch(); + + if (!story) { + // will never reached here, as story has been checked in Story.tsx + return
; + } + + const onDrop = () => { + const contents = story!.content; + let dragIndex = dragItem!.index!; + if (dragIndex > dropIndex) { + console.log("front"); + for (let i = dropIndex + 1; i < dragIndex; i++) { + contents[i].index += 1; + } + contents[dragIndex].index = dropIndex + 1; + } else if (dragIndex < dropIndex) { + console.log("back"); + console.log(dragIndex, dropIndex); + for (let i = dragIndex + 1; i <= dropIndex; i++) { + contents[i].index -= 1; + } + contents[dragIndex].index = dropIndex; + } else { + return; + } + contents.sort((a, b) => a.index - b.index); + console.log(contents); + const newStory = {...story, content: [...contents]}; + dispatch(StoriesActions.setCurrentStory(newStory)); + dispatch(StoriesActions.saveStory(newStory, storyId!)); + }; + + return
setShowDrop(true)} + onDragLeave={() => setShowDrop(false)} + onDrop={() => { + onDrop(); + setShowDrop(false); + }} + onDragOver={(e) => e.preventDefault()} + > + Drop Here +
+} + +export default DropArea; \ No newline at end of file diff --git a/src/features/stories/storiesComponents/EditStoryCell.tsx b/src/features/stories/storiesComponents/EditStoryCell.tsx index ab97ea0e65..928dfae6d9 100644 --- a/src/features/stories/storiesComponents/EditStoryCell.tsx +++ b/src/features/stories/storiesComponents/EditStoryCell.tsx @@ -8,8 +8,9 @@ import NewStoryCell from "./CreateStoryCell"; import { useDispatch } from "react-redux"; import { useTypedSelector } from "src/commons/utils/Hooks"; import StoriesActions from "../StoriesActions"; -// import { useSortable } from "@dnd-kit/sortable"; -// import { CSS } from "@dnd-kit/utilities"; +import { showSimpleConfirmDialog } from "src/commons/utils/DialogHelper"; +import Draggable from "./Draggable"; +import DropArea from "./DropArea"; type Props = { index: number; @@ -29,13 +30,6 @@ function EditStoryCell(props: Props) { const [ showButs, setShowButs ] = useState(false); const [ showNewCellUp, setShowNewCellUp ] = useState(false); const [ showNewCellDown, setShowNewCellDown ] = useState(false); - // const id = props.index; - // const {attributes, listeners, setNodeRef, transform, transition} = useSortable({id}); - - // const style = { - // transition, - // transform: CSS.Transform.toString(transform), - // }; useEffect(() => { if (!story) return; @@ -50,10 +44,9 @@ function EditStoryCell(props: Props) { return
; } - const editContent = () => { + const editContent = (newContent: string) => { console.log("content is editted"); const contents = story.content; - const newContent = storyContent.trim(); contents.filter((story: StoryCell) => story.index == props.index)[0].content = newContent; const newStory = {...story, content: [...contents]}; dispatch(StoriesActions.setCurrentStory(newStory)); @@ -67,14 +60,32 @@ function EditStoryCell(props: Props) { setShowNewCellUp(false); setShowNewCellDown(false); if (storyContent.trim().length > 0) { - setStoryContent(storyContent.trim()); - editContent(); + const trimmedContent = storyContent.trim(); + setStoryContent(trimmedContent); + editContent(trimmedContent); } else { - deleteStoryCell(); + deleteWithoutConfirmation(); + } + } + + const deleteWithConfirmation = async () => { + const confirm = await showSimpleConfirmDialog({ + contents: ( + <> +

Delete the story cell?

+

Note: This action is irreversible.

+ + ), + positiveIntent: 'danger', + positiveLabel: 'Delete' + }); + if (!confirm) { + return; } + deleteWithoutConfirmation(); } - const deleteStoryCell = () => { + const deleteWithoutConfirmation = () => { console.log(`story cell ${props.index} is deleted`); const contents = story.content; const newContents = []; @@ -87,7 +98,6 @@ function EditStoryCell(props: Props) { newContents.push(contents[i]); } const newStory = {...story, content: newContents}; - console.log(newStory); dispatch(StoriesActions.setCurrentStory(newStory)); dispatch(StoriesActions.saveStory(newStory, storyId!)); } @@ -103,22 +113,36 @@ function EditStoryCell(props: Props) { } } + const moveStoryCell = (moveUp: boolean) => { + const swapIndex = props.index + (moveUp ? -1 : 1); + // check if the user is moving the story cell out of the array bound + if (swapIndex < 0 || swapIndex >= story.content.length) { + return; + } + if (moveUp) { + story.content[swapIndex].index++; + story.content[props.index].index--; + } else { + story.content[swapIndex].index--; + story.content[props.index].index++; + } + const temp = story.content[props.index]; + story.content[props.index] = story.content[swapIndex]; + story.content[swapIndex] = temp; + const newStory = {...story, content: [...story.content]}; + dispatch(StoriesActions.setCurrentStory(newStory)); + dispatch(StoriesActions.saveStory(newStory, storyId!)); + } + return
setShowButs(true)} onMouseLeave={() => setShowButs(false)} - onPointerDown={(e) => e.stopPropagation()} - // ref={setNodeRef} - // {...(attributes)} - // {...(listeners)} - // style={style} > - {/* */} {showNewCellUp && } {isEditMode &&
e.stopPropagation()} > + +
} {isEditMode ? setIsTyping(true)} - // onBlur={() => setIsTyping(false)} minLines={5} maxLines={20} fontSize={17} @@ -189,16 +219,18 @@ function EditStoryCell(props: Props) { setOptions={{ fontFamily: "'Inconsolata', 'Consolas', monospace" }} style={{marginTop: "0px"}} /> - : renderStoryMarkdown( + : + {renderStoryMarkdown( isCode ? ("\`\`\`{source} env:" + env + "\n" + storyContent) - : storyContent, props.index, false) + : storyContent, props.index, false)} + }
{showNewCellDown && } - {/* */} +
} diff --git a/src/features/stories/storiesComponents/SourceBlock.tsx b/src/features/stories/storiesComponents/SourceBlock.tsx index 7ea1bb1c2e..f4aac8d751 100644 --- a/src/features/stories/storiesComponents/SourceBlock.tsx +++ b/src/features/stories/storiesComponents/SourceBlock.tsx @@ -339,7 +339,7 @@ const SourceBlock: React.FC = props => {
-
{e.stopPropagation()}}> +
= ({ const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); const { content: contents, header: header } = story!; const [envs, setEnvs] = useState(getEnvironments(header)); + const [activeIndex, setActiveIndex] = useState(null); useEffect(() => { - // const header = getYamlHeader(fileContent).header; store.dispatch(StoriesActions.clearStoryEnv()); handleHeaders(header); setEnvs(getEnvironments(header)); @@ -224,18 +224,16 @@ const UserBlogContent: React.FC = ({ key={key} story={story} />) - // : { - // return { - // ...content, - // id: content.index, - // } - // })} strategy={verticalListSortingStrategy}> - : contents.map((_, key) => { - return +
+ +
+ {contents.map((_, key) => { + return })} - {/*
} */} + } {!isViewOnly &&
= ({ isViewOnly = false }) => { return <>; } - // const onEditorScroll = (e: IEditorProps) => { - // const userblogContainer = document.getElementById('userblogContainer'); - // if (userblogContainer) { - // scrollSync(e, userblogContainer); - // } - // }; - - // const onEditorValueChange = (val: string) => { - // setIsDirty(true); - // dispatch(StoriesActions.setCurrentStory({ ...story, content: val })); - // }; - - // const { title, content } = story; const { title: title } = story; const controlBarProps: ControlBarProps = { @@ -91,68 +75,16 @@ const Story: React.FC = ({ isViewOnly = false }) => { ] }; - // const handleDragEnd = (event: DragEndEvent) => { - // const {active, over} = event; - - // // sequence does not change - // if (active.id == over!.id) { - // return; - // } - // let highIndex: number; - // let lowIndex: number; - // const contents = story!.content; - // if (active.id > over!.id) { - // console.log("front") - // highIndex = contents.findIndex((content) => content.index === active.id); - // lowIndex = contents.findIndex((content) => content.index === over!.id); - // for (let i = lowIndex; i < highIndex; i++) { - // contents[i].index += 1; - // } - // contents[highIndex].index = lowIndex; - // } else { - // console.log("back"); - // highIndex = contents.findIndex((content) => content.index === over!.id); - // lowIndex = contents.findIndex((content) => content.index === active.id); - // for (let i = lowIndex + 1; i <= highIndex; i++) { - // contents[i].index -= 1; - // } - // contents[lowIndex].index = highIndex; - // } - // contents.sort((a, b) => a.index - b.index); - // console.log(contents); - // const newStory = {...story, content: [...contents]}; - // dispatch(StoriesActions.setCurrentStory(newStory)); - // dispatch(StoriesActions.saveStory(newStory, storyId!)); - // } - return (
- {/* */}
- {/* {!isViewOnly && ( - - )} */}
- {/*
*/}
); }; diff --git a/src/styles/_stories.scss b/src/styles/_stories.scss index c8037050af..476d8d9184 100644 --- a/src/styles/_stories.scss +++ b/src/styles/_stories.scss @@ -51,7 +51,7 @@ padding: 16px 24px; font-size: 1rem; line-height: 1.5; - cursor: grab; + // cursor: grab; & > * { margin: 0; @@ -94,6 +94,34 @@ pre code { white-space: pre; } + + [draggable="true"] { + user-select: none; + -webkit-user-select: none; + cursor: grab; + position: relative; /* Ensure proper positioning */ + } + + [draggable="true"]:active { + cursor: grabbing; + } + + .drop-area { + width: 100%; + height: 100px; + columns: #dcdcdc; + border: 1px dashed #dcdcdc; + border-radius: 10px; + padding: 15px; + opacity: 1; + transition: all 0.2s ease-in-out; + } + + .hide-drop { + opacity: 0; + height: 30px; + margin: 0px; + } } } } From e9f72bbf2b17e5f6275071bdf48ac219fd991b9a Mon Sep 17 00:00:00 2001 From: desmondwong1215 Date: Wed, 2 Apr 2025 16:57:46 +0800 Subject: [PATCH 11/16] solve yarn run eslint error --- .coverage-fix.tmp | 3 + lcov.info | 1150 +++++++++++++++++ src/commons/sagas/StoriesSaga.ts | 9 +- .../{sharedb-ace.d.ts => sharedb-ace.ts} | 0 src/features/stories/DragContext.ts | 2 +- src/features/stories/StoriesTypes.ts | 1 + .../storiesComponents/BackendAccess.ts | 4 +- .../storiesComponents/CreateStoryCell.tsx | 3 +- .../stories/storiesComponents/Draggable.tsx | 5 +- .../stories/storiesComponents/DropArea.tsx | 9 +- .../storiesComponents/EditStoryCell.tsx | 14 +- .../stories/storiesComponents/SourceBlock.tsx | 30 +- .../storiesComponents/UserBlogContent.tsx | 22 +- .../storiesComponents/ViewStoryCell.tsx | 4 +- src/i18n/{i18next.d.ts => i18next.ts} | 0 src/pages/stories/Story.tsx | 5 + src/{react-app-env.d.ts => react-app-env.ts} | 0 17 files changed, 1220 insertions(+), 41 deletions(-) create mode 100644 .coverage-fix.tmp create mode 100644 lcov.info rename src/commons/workspace/{sharedb-ace.d.ts => sharedb-ace.ts} (100%) rename src/i18n/{i18next.d.ts => i18next.ts} (100%) rename src/{react-app-env.d.ts => react-app-env.ts} (100%) diff --git a/.coverage-fix.tmp b/.coverage-fix.tmp new file mode 100644 index 0000000000..7df2305e3a --- /dev/null +++ b/.coverage-fix.tmp @@ -0,0 +1,3 @@ +./src/i18n/i18next.ts +./src/commons/workspace/sharedb-ace.ts +./src/react-app-env.ts diff --git a/lcov.info b/lcov.info new file mode 100644 index 0000000000..a7819e63bd --- /dev/null +++ b/lcov.info @@ -0,0 +1,1150 @@ +TN: +SF:src/react-app-env.ts +FNF:0 +FNH:0 +LF:0 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/commons/sagas/StoriesSaga.ts +FN:34,(anonymous_0) +FN:36,(anonymous_1) +FN:43,(anonymous_2) +FN:59,(anonymous_3) +FN:62,(anonymous_4) +FN:86,(anonymous_5) +FN:109,(anonymous_6) +FN:122,(anonymous_7) +FN:140,(anonymous_8) +FN:144,(anonymous_9) +FN:146,(anonymous_10) +FN:163,(anonymous_11) +FN:172,(anonymous_12) +FN:183,(anonymous_13) +FNF:14 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +DA:31,15 +DA:32,15 +DA:35,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:41,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:50,0 +DA:56,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:69,0 +DA:80,0 +DA:81,0 +DA:84,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:102,0 +DA:103,0 +DA:106,0 +DA:110,0 +DA:111,0 +DA:112,0 +DA:114,0 +DA:123,0 +DA:130,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:135,0 +DA:137,0 +DA:138,0 +DA:141,0 +DA:142,0 +DA:143,0 +DA:144,0 +DA:146,0 +DA:147,0 +DA:148,0 +DA:151,0 +DA:152,0 +DA:164,0 +DA:166,0 +DA:168,0 +DA:169,0 +DA:173,0 +DA:174,0 +DA:176,0 +DA:178,0 +DA:179,0 +DA:180,0 +DA:184,0 +DA:185,0 +DA:187,0 +DA:188,0 +DA:189,0 +DA:190,0 +LF:69 +LH:2 +BRDA:38,0,0,0 +BRDA:38,0,1,0 +BRDA:46,1,0,0 +BRDA:46,1,1,0 +BRDA:64,2,0,0 +BRDA:64,2,1,0 +BRDA:80,3,0,0 +BRDA:80,3,1,0 +BRDA:102,4,0,0 +BRDA:102,4,1,0 +BRDA:132,5,0,0 +BRDA:132,5,1,0 +BRDA:168,6,0,0 +BRDA:168,6,1,0 +BRDA:178,7,0,0 +BRDA:178,7,1,0 +BRDA:188,8,0,0 +BRDA:188,8,1,0 +BRF:18 +BRH:0 +end_of_record +TN: +SF:src/commons/workspace/sharedb-ace.ts +FNF:0 +FNH:0 +LF:0 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/features/stories/DragContext.ts +FN:10,(anonymous_0) +FNF:1 +FNH:0 +FNDA:0,(anonymous_0) +DA:8,15 +DA:10,15 +DA:11,0 +DA:13,0 +DA:14,0 +DA:17,0 +LF:6 +LH:2 +BRDA:13,0,0,0 +BRDA:13,0,1,0 +BRF:2 +BRH:0 +end_of_record +TN: +SF:src/features/stories/StoriesTypes.ts +FNF:0 +FNH:0 +LF:0 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/features/stories/storiesComponents/BackendAccess.ts +FN:100,(anonymous_0) +FN:105,(anonymous_1) +FN:118,(anonymous_2) +FN:137,(anonymous_3) +FN:145,(anonymous_4) +FN:159,(anonymous_5) +FN:168,(anonymous_6) +FN:171,(anonymous_7) +FN:190,(anonymous_8) +FN:211,(anonymous_9) +FN:241,(anonymous_10) +FN:252,(anonymous_11) +FN:265,(anonymous_12) +FN:287,(anonymous_13) +FNF:14 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +DA:22,15 +DA:34,15 +DA:94,15 +DA:95,15 +DA:100,15 +DA:101,0 +DA:102,0 +DA:105,15 +DA:106,0 +DA:107,0 +DA:108,0 +DA:111,0 +DA:112,0 +DA:118,15 +DA:127,0 +DA:130,0 +DA:131,0 +DA:133,0 +DA:134,0 +DA:137,15 +DA:142,0 +DA:145,0 +DA:149,0 +DA:150,0 +DA:151,0 +DA:154,0 +DA:155,0 +DA:159,15 +DA:160,0 +DA:163,0 +DA:164,0 +DA:166,0 +DA:168,0 +DA:171,15 +DA:172,0 +DA:177,0 +DA:178,0 +DA:180,0 +DA:184,0 +DA:185,0 +DA:186,0 +DA:187,0 +DA:190,15 +DA:198,0 +DA:202,0 +DA:203,0 +DA:204,0 +DA:206,0 +DA:207,0 +DA:208,0 +DA:211,15 +DA:219,0 +DA:223,0 +DA:224,0 +DA:225,0 +DA:227,0 +DA:228,0 +DA:232,0 +DA:233,0 +DA:234,0 +DA:235,0 +DA:236,0 +DA:237,0 +DA:241,15 +DA:242,0 +DA:245,0 +DA:246,0 +DA:248,0 +DA:249,0 +DA:252,15 +DA:255,0 +DA:258,0 +DA:259,0 +DA:261,0 +DA:262,0 +DA:265,15 +DA:270,0 +DA:279,0 +DA:280,0 +DA:281,0 +DA:283,0 +DA:284,0 +DA:287,15 +DA:291,0 +DA:299,0 +DA:300,0 +DA:301,0 +DA:303,0 +DA:304,0 +LF:89 +LH:16 +BRDA:107,0,0,0 +BRDA:107,0,1,0 +BRDA:130,1,0,0 +BRDA:130,1,1,0 +BRDA:149,2,0,0 +BRDA:149,2,1,0 +BRDA:163,3,0,0 +BRDA:163,3,1,0 +BRDA:177,4,0,0 +BRDA:177,4,1,0 +BRDA:202,5,0,0 +BRDA:202,5,1,0 +BRDA:223,6,0,0 +BRDA:223,6,1,0 +BRDA:245,7,0,0 +BRDA:245,7,1,0 +BRDA:258,8,0,0 +BRDA:258,8,1,0 +BRDA:279,9,0,0 +BRDA:279,9,1,0 +BRDA:299,10,0,0 +BRDA:299,10,1,0 +BRF:22 +BRH:0 +end_of_record +TN: +SF:src/features/stories/storiesComponents/CreateStoryCell.tsx +FN:17,(anonymous_0) +FN:22,(anonymous_1) +FN:33,(anonymous_2) +FN:38,(anonymous_3) +FN:45,(anonymous_4) +FN:57,(anonymous_5) +FN:65,(anonymous_6) +FN:89,(anonymous_7) +FN:90,(anonymous_8) +FN:96,(anonymous_9) +FN:98,(anonymous_10) +FNF:11 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +DA:17,15 +DA:21,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:29,0 +DA:30,0 +DA:33,0 +DA:34,0 +DA:35,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:50,0 +DA:56,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:70,0 +DA:71,0 +DA:74,0 +DA:89,0 +DA:90,0 +DA:96,0 +DA:98,0 +LF:41 +LH:1 +BRDA:29,0,0,0 +BRDA:29,0,1,0 +BRDA:53,1,0,0 +BRDA:53,1,1,0 +BRDA:66,2,0,0 +BRDA:66,2,1,0 +BRDA:88,3,0,0 +BRDA:88,3,1,0 +BRDA:93,4,0,0 +BRDA:93,4,1,0 +BRF:10 +BRH:0 +end_of_record +TN: +SF:src/features/stories/storiesComponents/Draggable.tsx +FN:10,(anonymous_0) +FN:14,(anonymous_1) +FN:30,(anonymous_2) +FNF:3 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +DA:10,15 +DA:11,0 +DA:12,0 +DA:14,0 +DA:16,0 +DA:17,0 +DA:19,0 +DA:20,0 +DA:24,0 +DA:27,0 +DA:30,0 +DA:32,0 +DA:33,0 +DA:35,0 +DA:38,0 +LF:15 +LH:1 +BRDA:16,0,0,0 +BRDA:16,0,1,0 +BRDA:32,1,0,0 +BRDA:32,1,1,0 +BRF:4 +BRH:0 +end_of_record +TN: +SF:src/features/stories/storiesComponents/DropArea.tsx +FN:12,(anonymous_0) +FN:16,(anonymous_1) +FN:24,(anonymous_2) +FN:43,(anonymous_3) +FN:52,(anonymous_4) +FN:53,(anonymous_5) +FN:54,(anonymous_6) +FN:58,(anonymous_7) +FNF:8 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +DA:12,15 +DA:14,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:19,0 +DA:21,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:37,0 +DA:39,0 +DA:41,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:50,0 +DA:52,0 +DA:53,0 +DA:55,0 +DA:56,0 +DA:58,0 +LF:33 +LH:1 +BRDA:19,0,0,0 +BRDA:19,0,1,0 +BRDA:27,1,0,0 +BRDA:27,1,1,0 +BRDA:33,2,0,0 +BRDA:33,2,1,0 +BRDA:51,3,0,0 +BRDA:51,3,1,0 +BRF:8 +BRH:0 +end_of_record +TN: +SF:src/features/stories/storiesComponents/EditStoryCell.tsx +FN:20,(anonymous_0) +FN:22,EditStoryCell +FN:25,(anonymous_2) +FN:35,(anonymous_3) +FN:49,(anonymous_4) +FN:52,(anonymous_5) +FN:58,(anonymous_6) +FN:73,(anonymous_7) +FN:90,(anonymous_8) +FN:107,(anonymous_9) +FN:112,(anonymous_10) +FN:118,(anonymous_11) +FN:141,(anonymous_12) +FN:142,(anonymous_13) +FN:169,(anonymous_14) +FN:175,(anonymous_15) +FN:181,(anonymous_16) +FN:188,(anonymous_17) +FN:195,(anonymous_18) +FN:204,(anonymous_19) +FNF:20 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,EditStoryCell +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +DA:20,15 +DA:24,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:31,0 +DA:32,0 +DA:33,0 +DA:35,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:44,0 +DA:46,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:69,0 +DA:73,0 +DA:74,0 +DA:84,0 +DA:85,0 +DA:87,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:93,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:97,0 +DA:98,0 +DA:100,0 +DA:102,0 +DA:103,0 +DA:104,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:112,0 +DA:113,0 +DA:114,0 +DA:118,0 +DA:119,0 +DA:121,0 +DA:122,0 +DA:124,0 +DA:125,0 +DA:126,0 +DA:128,0 +DA:129,0 +DA:131,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:135,0 +DA:136,0 +DA:139,0 +DA:141,0 +DA:142,0 +DA:170,0 +DA:176,0 +DA:182,0 +DA:183,0 +DA:189,0 +DA:190,0 +DA:196,0 +DA:197,0 +DA:199,0 +DA:205,0 +LF:89 +LH:1 +BRDA:36,0,0,0 +BRDA:36,0,1,0 +BRDA:44,1,0,0 +BRDA:44,1,1,0 +BRDA:64,2,0,0 +BRDA:64,2,1,0 +BRDA:84,3,0,0 +BRDA:84,3,1,0 +BRDA:95,4,0,0 +BRDA:95,4,1,0 +BRDA:97,5,0,0 +BRDA:97,5,1,0 +BRDA:113,6,0,0 +BRDA:113,6,1,0 +BRDA:119,7,0,0 +BRDA:119,7,1,0 +BRDA:121,8,0,0 +BRDA:121,8,1,0 +BRDA:121,9,0,0 +BRDA:121,9,1,0 +BRDA:124,10,0,0 +BRDA:124,10,1,0 +BRDA:144,11,0,0 +BRDA:144,11,1,0 +BRDA:147,12,0,0 +BRDA:147,12,1,0 +BRDA:155,13,0,0 +BRDA:155,13,1,0 +BRDA:208,14,0,0 +BRDA:208,14,1,0 +BRDA:226,15,0,0 +BRDA:226,15,1,0 +BRDA:232,16,0,0 +BRDA:232,16,1,0 +BRF:34 +BRH:0 +end_of_record +TN: +SF:src/features/stories/storiesComponents/SourceBlock.tsx +FN:43,parseMetadata +FN:53,(anonymous_1) +FN:58,(anonymous_2) +FN:59,(anonymous_3) +FN:73,(anonymous_4) +FN:76,(anonymous_5) +FN:82,(anonymous_6) +FN:88,(anonymous_7) +FN:92,(anonymous_8) +FN:97,(anonymous_9) +FN:108,(anonymous_10) +FN:113,(anonymous_11) +FN:165,(anonymous_12) +FN:176,(anonymous_13) +FN:222,(anonymous_14) +FN:240,(anonymous_15) +FN:244,(anonymous_16) +FN:249,(anonymous_17) +FN:257,(anonymous_18) +FN:264,(anonymous_19) +FN:293,(anonymous_20) +FN:301,(anonymous_21) +FN:335,(anonymous_22) +FN:338,(anonymous_23) +FN:345,(anonymous_24) +FN:347,(anonymous_25) +FN:375,(anonymous_26) +FNF:27 +FNH:0 +FNDA:0,parseMetadata +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,(anonymous_21) +FNDA:0,(anonymous_22) +FNDA:0,(anonymous_23) +FNDA:0,(anonymous_24) +FNDA:0,(anonymous_25) +FNDA:0,(anonymous_26) +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:50,0 +DA:53,15 +DA:54,0 +DA:55,0 +DA:56,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:64,0 +DA:66,0 +DA:72,0 +DA:73,0 +DA:75,0 +DA:76,0 +DA:79,0 +DA:80,0 +DA:82,0 +DA:83,0 +DA:85,0 +DA:88,0 +DA:89,0 +DA:92,0 +DA:93,0 +DA:97,0 +DA:98,0 +DA:108,0 +DA:109,0 +DA:112,0 +DA:120,0 +DA:123,0 +DA:124,0 +DA:126,0 +DA:133,0 +DA:147,0 +DA:148,0 +DA:165,0 +DA:166,0 +DA:171,0 +DA:172,0 +DA:173,0 +DA:176,0 +DA:181,0 +DA:189,0 +DA:191,0 +DA:198,0 +DA:203,0 +DA:206,0 +DA:210,0 +DA:222,0 +DA:228,0 +DA:230,0 +DA:231,0 +DA:232,0 +DA:233,0 +DA:237,0 +DA:238,0 +DA:240,0 +DA:241,0 +DA:244,0 +DA:245,0 +DA:246,0 +DA:249,0 +DA:250,0 +DA:251,0 +DA:252,0 +DA:254,0 +DA:257,0 +DA:258,0 +DA:259,0 +DA:260,0 +DA:261,0 +DA:264,0 +DA:265,0 +DA:266,0 +DA:267,0 +DA:268,0 +DA:269,0 +DA:270,0 +DA:272,0 +DA:274,0 +DA:276,0 +DA:277,0 +DA:278,0 +DA:280,0 +DA:281,0 +DA:283,0 +DA:284,0 +DA:286,0 +DA:287,0 +DA:288,0 +DA:289,0 +DA:290,0 +DA:293,0 +DA:294,0 +DA:295,0 +DA:296,0 +DA:297,0 +DA:298,0 +DA:301,0 +DA:302,0 +DA:303,0 +DA:306,0 +DA:308,0 +DA:335,0 +DA:338,0 +DA:345,0 +DA:347,0 +DA:375,0 +LF:114 +LH:1 +BRDA:46,0,0,0 +BRDA:46,0,1,0 +BRDA:47,1,0,0 +BRDA:47,1,1,0 +BRDA:66,2,0,0 +BRDA:66,2,1,0 +BRDA:68,3,0,0 +BRDA:68,3,1,0 +BRDA:73,4,0,0 +BRDA:73,4,1,0 +BRDA:76,5,0,0 +BRDA:76,5,1,0 +BRDA:97,6,0,0 +BRDA:97,6,1,0 +BRDA:133,7,0,0 +BRDA:133,7,1,0 +BRDA:152,8,0,0 +BRDA:152,8,1,0 +BRDA:171,9,0,0 +BRDA:171,9,1,0 +BRDA:172,10,0,0 +BRDA:172,10,1,0 +BRDA:172,11,0,0 +BRDA:172,11,1,0 +BRDA:189,12,0,0 +BRDA:189,12,1,0 +BRDA:198,13,0,0 +BRDA:198,13,1,0 +BRDA:199,14,0,0 +BRDA:199,14,1,0 +BRDA:199,14,2,0 +BRDA:228,15,0,0 +BRDA:228,15,1,0 +BRDA:269,16,0,0 +BRDA:269,16,1,0 +BRDA:274,17,0,0 +BRDA:274,17,1,0 +BRDA:281,18,0,0 +BRDA:281,18,1,0 +BRDA:313,19,0,0 +BRDA:313,19,1,0 +BRDA:325,20,0,0 +BRDA:325,20,1,0 +BRF:43 +BRH:0 +end_of_record +TN: +SF:src/features/stories/storiesComponents/UserBlogContent.tsx +FN:30,handleEnvironment +FN:36,(anonymous_1) +FN:48,handleHeaders +FN:89,getYamlHeader +FN:105,getEnvironments +FN:114,constructHeader +FN:126,(anonymous_6) +FN:136,(anonymous_7) +FN:141,(anonymous_8) +FN:153,(anonymous_9) +FN:160,(anonymous_10) +FN:193,(anonymous_11) +FN:204,(anonymous_12) +FN:205,(anonymous_13) +FN:206,(anonymous_14) +FN:207,(anonymous_15) +FN:223,(anonymous_16) +FN:231,(anonymous_17) +FNF:18 +FNH:0 +FNDA:0,handleEnvironment +FNDA:0,(anonymous_1) +FNDA:0,handleHeaders +FNDA:0,getYamlHeader +FNDA:0,getEnvironments +FNDA:0,constructHeader +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +DA:24,15 +DA:26,15 +DA:27,15 +DA:28,15 +DA:31,0 +DA:32,0 +DA:35,0 +DA:36,0 +DA:40,0 +DA:44,0 +DA:49,0 +DA:50,0 +DA:57,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:68,0 +DA:69,0 +DA:72,0 +DA:76,0 +DA:78,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:99,0 +DA:106,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:111,0 +DA:115,0 +DA:116,0 +DA:117,0 +DA:118,0 +DA:119,0 +DA:126,15 +DA:130,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:135,0 +DA:136,0 +DA:137,0 +DA:138,0 +DA:139,0 +DA:141,0 +DA:142,0 +DA:143,0 +DA:144,0 +DA:145,0 +DA:148,0 +DA:150,0 +DA:153,0 +DA:154,0 +DA:155,0 +DA:156,0 +DA:157,0 +DA:160,0 +DA:161,0 +DA:162,0 +DA:163,0 +DA:164,0 +DA:165,0 +DA:166,0 +DA:167,0 +DA:168,0 +DA:170,0 +DA:175,0 +DA:178,0 +DA:194,0 +DA:195,0 +DA:196,0 +DA:198,0 +DA:204,0 +DA:205,0 +DA:206,0 +DA:207,0 +DA:219,0 +DA:223,0 +DA:232,0 +LF:87 +LH:5 +BRDA:35,0,0,0 +BRDA:35,0,1,0 +BRDA:40,1,0,0 +BRDA:40,1,1,0 +BRDA:49,2,0,0 +BRDA:49,2,1,0 +BRDA:62,3,0,0 +BRDA:62,3,1,0 +BRDA:62,3,2,0 +BRDA:76,4,0,0 +BRDA:76,4,1,0 +BRDA:91,5,0,0 +BRDA:91,5,1,0 +BRDA:95,6,0,0 +BRDA:95,6,1,0 +BRDA:148,7,0,0 +BRDA:148,7,1,0 +BRDA:163,8,0,0 +BRDA:163,8,1,0 +BRDA:166,9,0,0 +BRDA:166,9,1,0 +BRDA:195,10,0,0 +BRDA:195,10,1,0 +BRDA:219,11,0,0 +BRDA:219,11,1,0 +BRDA:221,12,0,0 +BRDA:221,12,1,0 +BRDA:222,13,0,0 +BRDA:222,13,1,0 +BRDA:237,14,0,0 +BRDA:237,14,1,0 +BRF:31 +BRH:0 +end_of_record +TN: +SF:src/features/stories/storiesComponents/ViewStoryCell.tsx +FN:10,ViewStoryCell +FN:15,(anonymous_1) +FNF:2 +FNH:0 +FNDA:0,ViewStoryCell +FNDA:0,(anonymous_1) +DA:12,0 +DA:13,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:22,0 +LF:6 +LH:0 +BRDA:16,0,0,0 +BRDA:16,0,1,0 +BRF:2 +BRH:0 +end_of_record +TN: +SF:src/i18n/i18next.ts +FNF:0 +FNH:0 +LF:0 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/pages/stories/Story.tsx +FN:21,(anonymous_0) +FN:25,(anonymous_1) +FN:28,(anonymous_2) +FN:52,(anonymous_3) +FN:62,(anonymous_4) +FN:99,(anonymous_5) +FN:102,(anonymous_6) +FNF:7 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +DA:21,0 +DA:22,0 +DA:23,0 +DA:25,0 +DA:26,0 +DA:28,0 +DA:30,0 +DA:33,0 +DA:37,0 +DA:38,0 +DA:41,0 +DA:43,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:67,0 +DA:69,0 +DA:72,0 +DA:75,0 +DA:83,0 +DA:99,0 +DA:100,0 +DA:102,0 +DA:103,0 +LF:27 +LH:0 +BRDA:21,0,0,0 +BRDA:33,1,0,0 +BRDA:33,1,1,0 +BRDA:37,2,0,0 +BRDA:37,2,1,0 +BRDA:45,3,0,0 +BRDA:45,3,1,0 +BRDA:59,4,0,0 +BRDA:59,4,1,0 +BRDA:63,5,0,0 +BRDA:63,5,1,0 +BRDA:67,6,0,0 +BRDA:67,6,1,0 +BRF:13 +BRH:0 +end_of_record + + diff --git a/src/commons/sagas/StoriesSaga.ts b/src/commons/sagas/StoriesSaga.ts index a7cd9e200c..8f1fef63c0 100644 --- a/src/commons/sagas/StoriesSaga.ts +++ b/src/commons/sagas/StoriesSaga.ts @@ -2,6 +2,9 @@ import { Context } from 'js-slang'; import { call, put, select } from 'redux-saga/effects'; import StoriesActions from 'src/features/stories/StoriesActions'; import { + defaultContent, + // updateHeader, + defaultHeader, deleteStory, deleteUserUserGroups, getAdminPanelStoriesUsers, @@ -10,11 +13,7 @@ import { getStory, postStory, putStoriesUserRole, - updateStory, - // updateHeader, - defaultHeader, - defaultContent -} from 'src/features/stories/storiesComponents/BackendAccess'; + updateStory} from 'src/features/stories/storiesComponents/BackendAccess'; import { StoryData, StoryListView, StoryView } from 'src/features/stories/StoriesTypes'; import SessionActions from '../application/actions/SessionActions'; diff --git a/src/commons/workspace/sharedb-ace.d.ts b/src/commons/workspace/sharedb-ace.ts similarity index 100% rename from src/commons/workspace/sharedb-ace.d.ts rename to src/commons/workspace/sharedb-ace.ts diff --git a/src/features/stories/DragContext.ts b/src/features/stories/DragContext.ts index de345b4f73..535f0663fc 100644 --- a/src/features/stories/DragContext.ts +++ b/src/features/stories/DragContext.ts @@ -7,7 +7,7 @@ type DragContextProps = { export const DragContext = createContext(null); -export const getDragItem = () => { +export const useDragItem = () => { const dragItem = useContext(DragContext); if (dragItem == null) { diff --git a/src/features/stories/StoriesTypes.ts b/src/features/stories/StoriesTypes.ts index b371a2214a..f4896c5d91 100644 --- a/src/features/stories/StoriesTypes.ts +++ b/src/features/stories/StoriesTypes.ts @@ -1,5 +1,6 @@ import { Context } from 'js-slang'; import { DebuggerContext } from 'src/commons/workspace/WorkspaceTypes'; + import { InterpreterOutput, StoriesRole } from '../../commons/application/ApplicationTypes'; export type StoryCell = { diff --git a/src/features/stories/storiesComponents/BackendAccess.ts b/src/features/stories/storiesComponents/BackendAccess.ts index 359b0977c3..4e3ccd1e7e 100644 --- a/src/features/stories/storiesComponents/BackendAccess.ts +++ b/src/features/stories/storiesComponents/BackendAccess.ts @@ -6,13 +6,13 @@ import { showWarningMessage } from 'src/commons/utils/notifications/NotificationsHelper'; import { request } from 'src/commons/utils/RequestHelper'; +import { defaultStoryContent } from 'src/commons/utils/StoriesHelper'; import { RemoveLast } from 'src/commons/utils/TypeHelper'; import { store } from 'src/pages/createStore'; import { Tokens } from '../../../commons/application/types/SessionTypes'; import { NameUsernameRole } from '../../../pages/academy/adminPanel/subcomponents/AddStoriesUserPanel'; import { AdminPanelStoriesUser, StoryListView, StoryView } from '../StoriesTypes'; -import { defaultStoryContent } from 'src/commons/utils/StoriesHelper'; import { StoryCell } from '../StoriesTypes'; // config: @@ -22,7 +22,7 @@ import { StoryCell } from '../StoriesTypes'; export const defaultHeader: string = `--- env: iterFib: - chapter: 4 + chapter: 2 variant: default recuFib: chapter: 4 diff --git a/src/features/stories/storiesComponents/CreateStoryCell.tsx b/src/features/stories/storiesComponents/CreateStoryCell.tsx index 6484bcbaa6..4d28361644 100644 --- a/src/features/stories/storiesComponents/CreateStoryCell.tsx +++ b/src/features/stories/storiesComponents/CreateStoryCell.tsx @@ -5,8 +5,9 @@ import { useDispatch } from "react-redux"; import { ControlButtonSaveButton } from "src/commons/controlBar/ControlBarSaveButton"; import { useTypedSelector } from "src/commons/utils/Hooks"; import { showWarningMessage } from "src/commons/utils/notifications/NotificationsHelper"; -import { StoryCell } from "../StoriesTypes"; + import StoriesActions from "../StoriesActions"; +import { StoryCell } from "../StoriesTypes"; import { getEnvironments } from "./UserBlogContent"; type Props = { diff --git a/src/features/stories/storiesComponents/Draggable.tsx b/src/features/stories/storiesComponents/Draggable.tsx index 709cfd0362..b4e50da12a 100644 --- a/src/features/stories/storiesComponents/Draggable.tsx +++ b/src/features/stories/storiesComponents/Draggable.tsx @@ -1,5 +1,6 @@ import React, { useRef } from 'react'; -import { getDragItem } from '../DragContext'; + +import { useDragItem } from '../DragContext'; interface DraggableProps { children: React.ReactNode; @@ -8,7 +9,7 @@ interface DraggableProps { const Draggable: React.FC = ({ children, id }) => { const elementRef = useRef(null); - const { setIndex } = getDragItem(); + const { setIndex } = useDragItem(); const handleDragStart = (e: React.DragEvent) => { diff --git a/src/features/stories/storiesComponents/DropArea.tsx b/src/features/stories/storiesComponents/DropArea.tsx index e876fbd2a3..590ee63151 100644 --- a/src/features/stories/storiesComponents/DropArea.tsx +++ b/src/features/stories/storiesComponents/DropArea.tsx @@ -1,7 +1,8 @@ import { useState } from "react"; -import { useTypedSelector } from "src/commons/utils/Hooks"; -import { getDragItem } from "../DragContext"; import { useDispatch } from "react-redux"; +import { useTypedSelector } from "src/commons/utils/Hooks"; + +import { useDragItem } from "../DragContext"; import StoriesActions from "../StoriesActions"; interface DropAreaProps { @@ -11,7 +12,7 @@ interface DropAreaProps { const DropArea: React.FC = ({ dropIndex }) => { const [showDrop, setShowDrop] = useState(false); - const dragItem = getDragItem(); + const dragItem = useDragItem(); const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); const dispatch = useDispatch(); @@ -22,7 +23,7 @@ const DropArea: React.FC = ({ dropIndex }) => { const onDrop = () => { const contents = story!.content; - let dragIndex = dragItem!.index!; + const dragIndex = dragItem!.index!; if (dragIndex > dropIndex) { console.log("front"); for (let i = dropIndex + 1; i < dragIndex; i++) { diff --git a/src/features/stories/storiesComponents/EditStoryCell.tsx b/src/features/stories/storiesComponents/EditStoryCell.tsx index 928dfae6d9..4c7060b770 100644 --- a/src/features/stories/storiesComponents/EditStoryCell.tsx +++ b/src/features/stories/storiesComponents/EditStoryCell.tsx @@ -1,14 +1,15 @@ +import { Button } from "@blueprintjs/core"; import { createContext, useEffect, useState } from "react"; -import { renderStoryMarkdown } from "src/commons/utils/StoriesHelper"; import AceEditor from 'react-ace'; -import { StoryCell } from '../StoriesTypes'; -import { ControlButtonSaveButton } from "src/commons/controlBar/ControlBarSaveButton"; -import { Button } from "@blueprintjs/core"; -import NewStoryCell from "./CreateStoryCell"; import { useDispatch } from "react-redux"; +import { ControlButtonSaveButton } from "src/commons/controlBar/ControlBarSaveButton"; +import { showSimpleConfirmDialog } from "src/commons/utils/DialogHelper"; import { useTypedSelector } from "src/commons/utils/Hooks"; +import { renderStoryMarkdown } from "src/commons/utils/StoriesHelper"; + import StoriesActions from "../StoriesActions"; -import { showSimpleConfirmDialog } from "src/commons/utils/DialogHelper"; +import { StoryCell } from '../StoriesTypes'; +import NewStoryCell from "./CreateStoryCell"; import Draggable from "./Draggable"; import DropArea from "./DropArea"; @@ -37,6 +38,7 @@ function EditStoryCell(props: Props) { setEnv(story.content[props.index].env); setIsCode(story.content[props.index].isCode); console.log(story.content[props.index], props.index, isCode); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [story]); if (!story) { diff --git a/src/features/stories/storiesComponents/SourceBlock.tsx b/src/features/stories/storiesComponents/SourceBlock.tsx index f4aac8d751..c739e788fd 100644 --- a/src/features/stories/storiesComponents/SourceBlock.tsx +++ b/src/features/stories/storiesComponents/SourceBlock.tsx @@ -6,6 +6,7 @@ import AceEditor from 'react-ace'; import { useDispatch } from 'react-redux'; import { ResultOutput, styliseSublanguage } from 'src/commons/application/ApplicationTypes'; import { ControlBarRunButton } from 'src/commons/controlBar/ControlBarRunButton'; +import { ControlButtonSaveButton } from 'src/commons/controlBar/ControlBarSaveButton'; import ControlButton from 'src/commons/ControlButton'; import makeDataVisualizerTabFrom from 'src/commons/sideContent/content/SideContentDataVisualizer'; import makeHtmlDisplayTabFrom from 'src/commons/sideContent/content/SideContentHtmlDisplay'; @@ -21,12 +22,8 @@ import { makeSubstVisualizerTabFrom } from 'src/pages/playground/PlaygroundTabs' import { ExternalLibraryName } from '../../../commons/application/types/ExternalTypes'; import { Output } from '../../../commons/repl/Repl'; import { getModeString, selectMode } from '../../../commons/utils/AceHelper'; -import { DEFAULT_ENV, getEnvironments, handleHeaders } from './UserBlogContent'; -import { ControlButtonSaveButton } from 'src/commons/controlBar/ControlBarSaveButton'; import { StoryCell } from '../StoriesTypes'; -// import { SourceBlockContext } from './EditStoryCell'; -// import { useSortable } from '@dnd-kit/sortable'; -// import { CSS } from "@dnd-kit/utilities"; +import { DEFAULT_ENV, getEnvironments, handleHeaders } from './UserBlogContent'; export type SourceBlockProps = { content: string; @@ -94,11 +91,24 @@ const SourceBlock: React.FC = props => { useEffect(() => { setCurrentChapter(getChapter()); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [story]); const output = useTypedSelector(store => store.stories.envs[env]?.output || []); const { selectedTab, setSelectedTab } = useSideContent(`stories.${env}`); + // useEffect(() => { + // if (!selectedTab) { + // console.log("hello"); + // console.log(setSelectedTab); + // setSelectedTab(SideContentType.storiesRun); + // } + // }, []); + + useEffect(() => { + console.log("selected tab is changed: ", selectedTab); + }, [selectedTab]); + const onChangeTabs = React.useCallback( ( newTabId: SideContentType, @@ -110,11 +120,13 @@ const SourceBlock: React.FC = props => { dispatch( StoriesActions.toggleStoriesUsingSubst(newTabId === SideContentType.substVisualizer, env) ); + console.log(selectedTab); + console.log("selected tab: ", newTabId); setSelectedTab(newTabId); }, // eslint-disable-next-line react-hooks/exhaustive-deps - [] + [selectedTab] ); const envDisplayLabel = @@ -215,7 +227,9 @@ const SourceBlock: React.FC = props => { // is handled by the component setting. if (selectedTab) onChangeTabs(selectedTab, selectedTab, {} as any); + console.log("Running on ", selectedTab); dispatch(StoriesActions.evalStory(env, code)); + console.log(selectedTab); setOutputIndex(output.length); }; @@ -278,7 +292,7 @@ const SourceBlock: React.FC = props => { const changeEnv = (env: string) => { setCurrentEnv(env); - let header = story!.header.split('\n'); + const header = story!.header.split('\n'); const index = envList.indexOf(env); setCurrentChapter(+header[3 + index * 3].substring(13)); setIsDirty(true); @@ -383,4 +397,4 @@ const SourceBlock: React.FC = props => { ); }; -export default SourceBlock; +export default SourceBlock; \ No newline at end of file diff --git a/src/features/stories/storiesComponents/UserBlogContent.tsx b/src/features/stories/storiesComponents/UserBlogContent.tsx index 6fe9a4a032..bc2430877b 100644 --- a/src/features/stories/storiesComponents/UserBlogContent.tsx +++ b/src/features/stories/storiesComponents/UserBlogContent.tsx @@ -1,25 +1,25 @@ +import { Menu, MenuItem } from '@blueprintjs/core'; +import { TextInput } from '@tremor/react'; import { Chapter, Variant } from 'js-slang/dist/types'; import yaml from 'js-yaml'; import React, { useEffect, useState } from 'react'; import debounceRender from 'react-debounce-render'; +import { useDispatch } from 'react-redux'; +import { styliseSublanguage } from 'src/commons/application/ApplicationTypes'; +import ControlBar, { ControlBarProps } from 'src/commons/controlBar/ControlBar'; +import { ControlButtonSaveButton } from 'src/commons/controlBar/ControlBarSaveButton'; import Constants from 'src/commons/utils/Constants'; +import { useTypedSelector } from 'src/commons/utils/Hooks'; import { propsAreEqual } from 'src/commons/utils/MemoizeHelper'; -import EditStoryCell from './EditStoryCell'; +import { showWarningMessage } from 'src/commons/utils/notifications/NotificationsHelper'; import StoriesActions from 'src/features/stories/StoriesActions'; import { store } from '../../../pages/createStore'; -import ViewStoryCell from './ViewStoryCell'; +import { DragContext } from '../DragContext'; import NewStoryCell from './CreateStoryCell'; -import { TextInput } from '@tremor/react'; -import { Menu, MenuItem } from '@blueprintjs/core'; -import { styliseSublanguage } from 'src/commons/application/ApplicationTypes'; -import { showWarningMessage } from 'src/commons/utils/notifications/NotificationsHelper'; -import { ControlButtonSaveButton } from 'src/commons/controlBar/ControlBarSaveButton'; -import ControlBar, { ControlBarProps } from 'src/commons/controlBar/ControlBar'; -import { useDispatch } from 'react-redux'; -import { useTypedSelector } from 'src/commons/utils/Hooks'; import DropArea from './DropArea'; -import { DragContext } from '../DragContext'; +import EditStoryCell from './EditStoryCell'; +import ViewStoryCell from './ViewStoryCell'; export const DEFAULT_ENV = 'default'; diff --git a/src/features/stories/storiesComponents/ViewStoryCell.tsx b/src/features/stories/storiesComponents/ViewStoryCell.tsx index 0be72d52d2..ae3c9e165e 100644 --- a/src/features/stories/storiesComponents/ViewStoryCell.tsx +++ b/src/features/stories/storiesComponents/ViewStoryCell.tsx @@ -1,6 +1,7 @@ +import { useEffect, useState } from "react"; import { renderStoryMarkdown } from "src/commons/utils/StoriesHelper"; + import { StoryCell } from '../StoriesTypes'; -import { useEffect, useState } from "react"; type Props = { story: StoryCell; @@ -15,6 +16,7 @@ function ViewStoryCell(props: Props) { if (isCode) { setStoryContent("\`\`\`{source} env:" + env + "\n" + content); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return
diff --git a/src/i18n/i18next.d.ts b/src/i18n/i18next.ts similarity index 100% rename from src/i18n/i18next.d.ts rename to src/i18n/i18next.ts diff --git a/src/pages/stories/Story.tsx b/src/pages/stories/Story.tsx index aa0da205ca..92547dff5d 100644 --- a/src/pages/stories/Story.tsx +++ b/src/pages/stories/Story.tsx @@ -9,6 +9,7 @@ import { useParams } from 'react-router'; import ControlBar, { ControlBarProps } from 'src/commons/controlBar/ControlBar'; import { ControlButtonSaveButton } from 'src/commons/controlBar/ControlBarSaveButton'; import { useTypedSelector } from 'src/commons/utils/Hooks'; +import { showWarningMessage } from 'src/commons/utils/notifications/NotificationsHelper'; import StoriesActions from 'src/features/stories/StoriesActions'; import UserBlogContent from '../../features/stories/storiesComponents/UserBlogContent'; @@ -59,6 +60,10 @@ const Story: React.FC = ({ isViewOnly = false }) => { { + if (story.title.trim() === "") { + showWarningMessage("story name cannot be empty"); + return; + } if (storyId) { // Update story dispatch(StoriesActions.saveStory(story, storyId)); diff --git a/src/react-app-env.d.ts b/src/react-app-env.ts similarity index 100% rename from src/react-app-env.d.ts rename to src/react-app-env.ts From 6b3a437ecd835bf2ce449dbe08d65cfa3ca5c262 Mon Sep 17 00:00:00 2001 From: desmondwong1215 Date: Wed, 2 Apr 2025 17:38:37 +0800 Subject: [PATCH 12/16] solve yarn run eslint error --- public/externalLibs/sound/soundToneMatrix.js | 6 +- src/commons/sagas/StoriesSaga.ts | 5 +- src/commons/sideContent/SideContent.tsx | 2 +- src/commons/utils/StoriesHelper.ts | 8 +- src/features/stories/DragContext.ts | 20 +- .../storiesComponents/BackendAccess.ts | 47 +- .../storiesComponents/CreateStoryCell.tsx | 256 ++++----- .../stories/storiesComponents/Draggable.tsx | 13 +- .../stories/storiesComponents/DropArea.tsx | 107 ++-- .../storiesComponents/EditStoryCell.tsx | 503 +++++++++--------- .../stories/storiesComponents/SourceBlock.tsx | 100 ++-- .../storiesComponents/UserBlogContent.tsx | 113 ++-- .../storiesComponents/ViewStoryCell.tsx | 51 +- src/i18n/i18next.ts | 3 +- src/pages/stories/Stories.tsx | 11 +- src/pages/stories/Story.tsx | 14 +- src/styles/_stories.scss | 22 +- 17 files changed, 661 insertions(+), 620 deletions(-) diff --git a/public/externalLibs/sound/soundToneMatrix.js b/public/externalLibs/sound/soundToneMatrix.js index 8638a90378..d0246757bf 100644 --- a/public/externalLibs/sound/soundToneMatrix.js +++ b/public/externalLibs/sound/soundToneMatrix.js @@ -36,7 +36,7 @@ var timeout_matrix; // for coloring the matrix accordingly while it's being played var timeout_color; -var timeout_objects = new Array(); +var timeout_objects = []; // vector_to_list returns a list that contains the elements of the argument vector // in the given order. @@ -54,7 +54,7 @@ function vector_to_list(vector) { function x_y_to_row_column(x, y) { var row = Math.floor((y - margin_length) / (square_side_length + distance_between_squares)); var column = Math.floor((x - margin_length) / (square_side_length + distance_between_squares)); - return Array(row, column); + return [row, column]; } // given the row number of a square, return the leftmost coordinate @@ -365,5 +365,5 @@ function clear_all_timeout() { clearTimeout(timeout_objects[i]); } - timeout_objects = new Array(); + timeout_objects = []; } diff --git a/src/commons/sagas/StoriesSaga.ts b/src/commons/sagas/StoriesSaga.ts index 8f1fef63c0..a03a345b35 100644 --- a/src/commons/sagas/StoriesSaga.ts +++ b/src/commons/sagas/StoriesSaga.ts @@ -13,7 +13,8 @@ import { getStory, postStory, putStoriesUserRole, - updateStory} from 'src/features/stories/storiesComponents/BackendAccess'; + updateStory +} from 'src/features/stories/storiesComponents/BackendAccess'; import { StoryData, StoryListView, StoryView } from 'src/features/stories/StoriesTypes'; import SessionActions from '../application/actions/SessionActions'; @@ -86,7 +87,7 @@ const StoriesSaga = combineSagaHandlers(sagaActions, { saveStory: function* (action) { const tokens: Tokens = yield selectTokens(); const { story, id } = action.payload; - console.log("In saveStory"); + console.log('In saveStory'); console.log(story.header); const updatedStory: StoryView | null = yield call( updateStory, diff --git a/src/commons/sideContent/SideContent.tsx b/src/commons/sideContent/SideContent.tsx index 4b7a025292..226beffe30 100644 --- a/src/commons/sideContent/SideContent.tsx +++ b/src/commons/sideContent/SideContent.tsx @@ -70,7 +70,7 @@ const SideContent = ({ renderActiveTabPanelOnly, editorWidth, ...props }: SideCo {({ tabs: allTabs, alerts: tabAlerts, changeTabsCallback, selectedTab, height }) => (
- +
{ +export const renderStoryMarkdown = ( + markdown: string, + index: number, + isViewOnly: boolean +): React.ReactNode => { currentIndex = index; view = isViewOnly; const mdast = fromMarkdown(markdown); diff --git a/src/features/stories/DragContext.ts b/src/features/stories/DragContext.ts index 535f0663fc..6ea99a3da6 100644 --- a/src/features/stories/DragContext.ts +++ b/src/features/stories/DragContext.ts @@ -1,18 +1,18 @@ -import { createContext, useContext } from "react"; +import { createContext, useContext } from 'react'; type DragContextProps = { - index: number | null; - setIndex: (index: number) => void; -} + index: number | null; + setIndex: (index: number) => void; +}; export const DragContext = createContext(null); export const useDragItem = () => { - const dragItem = useContext(DragContext); + const dragItem = useContext(DragContext); - if (dragItem == null) { - throw Error("Drag Context cannot be null when in use"); - } + if (dragItem == null) { + throw Error('Drag Context cannot be null when in use'); + } - return dragItem; -} \ No newline at end of file + return dragItem; +}; diff --git a/src/features/stories/storiesComponents/BackendAccess.ts b/src/features/stories/storiesComponents/BackendAccess.ts index 4e3ccd1e7e..e590d072b4 100644 --- a/src/features/stories/storiesComponents/BackendAccess.ts +++ b/src/features/stories/storiesComponents/BackendAccess.ts @@ -29,29 +29,27 @@ env: variant: default rune: chapter: 4 - variant: default`; + variant: default`; export const defaultContent: StoryCell[] = [ { // id: 0, index: 0, isCode: true, - env: "iterFib", - content: - `function print(message) { + env: 'iterFib', + content: `function print(message) { display(message); } draw_data(list(1, 2, 3, 4)); display("hello world1"); -`, +` }, { // id: 1, index: 1, isCode: false, - env: "", - content: - `# Hello world! + env: '', + content: `# Hello world! ## hello world!! hello world!!! hello world!!! @@ -60,34 +58,31 @@ hello world!!! print("hello world") \`\`\` \`\`\`\` -`, +` }, { // id: 2, index: 2, isCode: true, - env: "recuFib", - content: - `print("source academy stories"); -`, + env: 'recuFib', + content: `print("source academy stories"); +` }, { // id: 3, index: 3, isCode: true, - env: "iterFib", - content: - `print("hello world"); -`, + env: 'iterFib', + content: `print("hello world"); +` }, { // id: 4, index: 4, isCode: true, - env: "iterFib", - content: - `print("why this cell?"); -`, + env: 'iterFib', + content: `print("why this cell?"); +` } ]; @@ -165,7 +160,7 @@ export const getStories = async (tokens: Tokens): Promise ({...story, header: tempContent, content: tempContent})); + return stories.map((story: any) => ({ ...story, header: tempContent, content: tempContent })); }; export const getStory = async (tokens: Tokens, storyId: number): Promise => { @@ -181,8 +176,8 @@ export const getStory = async (tokens: Tokens, storyId: number): Promise = ({ - index, -}) => { - - const dispatch = useDispatch(); - const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); - const envs = getEnvironments(story!.header); - const [isCode, setIsCode] = useState(false); - const [env, setEnv] = useState(envs[0]); - const [code, setCode] = useState(""); - const [isDirty, setIsDirty] = useState(false); - - if (!story) { - return
; - } - - const editorOnChange = (code: string) => { - setCode(code); - setIsDirty(code.trim() !== ""); - } - - const reset = () => { - setCode(""); - setEnv(envs[0]); - setIsCode(false); - setIsDirty(false); - } - - const saveNewStoryCell = () => { - const contents = story.content; - for (let i = index; i < contents.length; i++) { - contents[i].index += 1; - } - const newContent: StoryCell = { - index: index, - isCode: isCode, - env: isCode ? env : "", - content: code, - } - contents.push(newContent); - contents.sort((a, b) => a.index - b.index); - const newStory = {...story, content: [...contents]}; - console.log("a new cell is saved"); - console.log(newStory); - dispatch(StoriesActions.setCurrentStory({...newStory})); - dispatch(StoriesActions.saveStory(newStory, storyId!)); - } - - const saveButClicked = () => { - if (!isDirty) { - showWarningMessage("Cannot save empty story cell!"); - return; - } - saveNewStoryCell(); - reset(); - } - - return
-
- - - - setIsCode(false)} text="Markdown" /> - setIsCode(true)} text="Source" /> - - - {isCode &&
- - - {envs.map((env: string, index: number) => {setEnv(env)}} - text={env} - />)} - - -
- } -
- -
-}; - -export default NewStoryCell; \ No newline at end of file +import { Menu, MenuItem } from '@blueprintjs/core'; +import { useState } from 'react'; +import AceEditor from 'react-ace'; +import { useDispatch } from 'react-redux'; +import { ControlButtonSaveButton } from 'src/commons/controlBar/ControlBarSaveButton'; +import { useTypedSelector } from 'src/commons/utils/Hooks'; +import { showWarningMessage } from 'src/commons/utils/notifications/NotificationsHelper'; + +import StoriesActions from '../StoriesActions'; +import { StoryCell } from '../StoriesTypes'; +import { getEnvironments } from './UserBlogContent'; + +type Props = { + index: number; +}; + +const NewStoryCell: React.FC = ({ index }) => { + const dispatch = useDispatch(); + const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); + const envs = getEnvironments(story!.header); + const [isCode, setIsCode] = useState(false); + const [env, setEnv] = useState(envs[0]); + const [code, setCode] = useState(''); + const [isDirty, setIsDirty] = useState(false); + + if (!story) { + return
; + } + + const editorOnChange = (code: string) => { + setCode(code); + setIsDirty(code.trim() !== ''); + }; + + const reset = () => { + setCode(''); + setEnv(envs[0]); + setIsCode(false); + setIsDirty(false); + }; + + const saveNewStoryCell = () => { + const contents = story.content; + for (let i = index; i < contents.length; i++) { + contents[i].index += 1; + } + const newContent: StoryCell = { + index: index, + isCode: isCode, + env: isCode ? env : '', + content: code + }; + contents.push(newContent); + contents.sort((a, b) => a.index - b.index); + const newStory = { ...story, content: [...contents] }; + console.log('a new cell is saved'); + console.log(newStory); + dispatch(StoriesActions.setCurrentStory({ ...newStory })); + dispatch(StoriesActions.saveStory(newStory, storyId!)); + }; + + const saveButClicked = () => { + if (!isDirty) { + showWarningMessage('Cannot save empty story cell!'); + return; + } + saveNewStoryCell(); + reset(); + }; + + return ( +
+
+ + + + setIsCode(false)} text="Markdown" /> + setIsCode(true)} text="Source" /> + + + {isCode && ( +
+ + + {envs.map((env: string, index: number) => ( + { + setEnv(env); + }} + text={env} + /> + ))} + + +
+ )} +
+ +
+ ); +}; + +export default NewStoryCell; diff --git a/src/features/stories/storiesComponents/Draggable.tsx b/src/features/stories/storiesComponents/Draggable.tsx index b4e50da12a..2e75888bd8 100644 --- a/src/features/stories/storiesComponents/Draggable.tsx +++ b/src/features/stories/storiesComponents/Draggable.tsx @@ -12,17 +12,16 @@ const Draggable: React.FC = ({ children, id }) => { const { setIndex } = useDragItem(); const handleDragStart = (e: React.DragEvent) => { - if (elementRef.current) { elementRef.current.style.opacity = '0.7'; - // Critical: Set the drag image to be just this element + // Critical: Set the drag image to be just this element e.dataTransfer.setDragImage(elementRef.current, 0, 0); setIndex(id); } - + // Set the effect e.dataTransfer.effectAllowed = 'move'; - + // Prevent text selection during drag document.body.style.userSelect = 'none'; }; @@ -30,7 +29,7 @@ const Draggable: React.FC = ({ children, id }) => { const handleDragEnd = () => { // Re-enable text selection if (elementRef.current) { - elementRef.current.style.opacity = '1'; + elementRef.current.style.opacity = '1'; } document.body.style.userSelect = ''; }; @@ -42,11 +41,11 @@ const Draggable: React.FC = ({ children, id }) => { onDragStart={handleDragStart} onDragEnd={handleDragEnd} className="draggable-element" - id={id + ""} + id={id + ''} > {children}
); }; -export default Draggable; \ No newline at end of file +export default Draggable; diff --git a/src/features/stories/storiesComponents/DropArea.tsx b/src/features/stories/storiesComponents/DropArea.tsx index 590ee63151..c3dee9a817 100644 --- a/src/features/stories/storiesComponents/DropArea.tsx +++ b/src/features/stories/storiesComponents/DropArea.tsx @@ -1,64 +1,65 @@ -import { useState } from "react"; -import { useDispatch } from "react-redux"; -import { useTypedSelector } from "src/commons/utils/Hooks"; +import { useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { useTypedSelector } from 'src/commons/utils/Hooks'; -import { useDragItem } from "../DragContext"; -import StoriesActions from "../StoriesActions"; +import { useDragItem } from '../DragContext'; +import StoriesActions from '../StoriesActions'; interface DropAreaProps { - dropIndex: number; + dropIndex: number; } const DropArea: React.FC = ({ dropIndex }) => { + const [showDrop, setShowDrop] = useState(false); + const dragItem = useDragItem(); + const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); + const dispatch = useDispatch(); - const [showDrop, setShowDrop] = useState(false); - const dragItem = useDragItem(); - const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); - const dispatch = useDispatch(); - - if (!story) { - // will never reached here, as story has been checked in Story.tsx - return
; - } + if (!story) { + // will never reached here, as story has been checked in Story.tsx + return
; + } - const onDrop = () => { - const contents = story!.content; - const dragIndex = dragItem!.index!; - if (dragIndex > dropIndex) { - console.log("front"); - for (let i = dropIndex + 1; i < dragIndex; i++) { - contents[i].index += 1; - } - contents[dragIndex].index = dropIndex + 1; - } else if (dragIndex < dropIndex) { - console.log("back"); - console.log(dragIndex, dropIndex); - for (let i = dragIndex + 1; i <= dropIndex; i++) { - contents[i].index -= 1; - } - contents[dragIndex].index = dropIndex; - } else { - return; - } - contents.sort((a, b) => a.index - b.index); - console.log(contents); - const newStory = {...story, content: [...contents]}; - dispatch(StoriesActions.setCurrentStory(newStory)); - dispatch(StoriesActions.saveStory(newStory, storyId!)); - }; + const onDrop = () => { + const contents = story!.content; + const dragIndex = dragItem!.index!; + if (dragIndex > dropIndex) { + console.log('front'); + for (let i = dropIndex + 1; i < dragIndex; i++) { + contents[i].index += 1; + } + contents[dragIndex].index = dropIndex + 1; + } else if (dragIndex < dropIndex) { + console.log('back'); + console.log(dragIndex, dropIndex); + for (let i = dragIndex + 1; i <= dropIndex; i++) { + contents[i].index -= 1; + } + contents[dragIndex].index = dropIndex; + } else { + return; + } + contents.sort((a, b) => a.index - b.index); + console.log(contents); + const newStory = { ...story, content: [...contents] }; + dispatch(StoriesActions.setCurrentStory(newStory)); + dispatch(StoriesActions.saveStory(newStory, storyId!)); + }; - return
setShowDrop(true)} - onDragLeave={() => setShowDrop(false)} - onDrop={() => { - onDrop(); - setShowDrop(false); - }} - onDragOver={(e) => e.preventDefault()} - > - Drop Here + return ( +
setShowDrop(true)} + onDragLeave={() => setShowDrop(false)} + onDrop={() => { + onDrop(); + setShowDrop(false); + }} + onDragOver={e => e.preventDefault()} + > + Drop Here
-} + ); +}; -export default DropArea; \ No newline at end of file +export default DropArea; diff --git a/src/features/stories/storiesComponents/EditStoryCell.tsx b/src/features/stories/storiesComponents/EditStoryCell.tsx index 4c7060b770..eca00ab70c 100644 --- a/src/features/stories/storiesComponents/EditStoryCell.tsx +++ b/src/features/stories/storiesComponents/EditStoryCell.tsx @@ -1,239 +1,264 @@ -import { Button } from "@blueprintjs/core"; -import { createContext, useEffect, useState } from "react"; -import AceEditor from 'react-ace'; -import { useDispatch } from "react-redux"; -import { ControlButtonSaveButton } from "src/commons/controlBar/ControlBarSaveButton"; -import { showSimpleConfirmDialog } from "src/commons/utils/DialogHelper"; -import { useTypedSelector } from "src/commons/utils/Hooks"; -import { renderStoryMarkdown } from "src/commons/utils/StoriesHelper"; - -import StoriesActions from "../StoriesActions"; -import { StoryCell } from '../StoriesTypes'; -import NewStoryCell from "./CreateStoryCell"; -import Draggable from "./Draggable"; -import DropArea from "./DropArea"; - -type Props = { - index: number; -}; - -export const SourceBlockContext = createContext<(isTyping: boolean) => void>(() => {}); - -function EditStoryCell(props: Props) { - - const dispatch = useDispatch(); - const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); - const [ isCode, setIsCode ] = useState(false); - const [ env, setEnv ] = useState(""); - const [ storyContent, setStoryContent ] = useState(""); - const [ isEditMode, setEditMode ] = useState(false); - const [ isDirty, setIsDirty ] = useState(false); - const [ showButs, setShowButs ] = useState(false); - const [ showNewCellUp, setShowNewCellUp ] = useState(false); - const [ showNewCellDown, setShowNewCellDown ] = useState(false); - - useEffect(() => { - if (!story) return; - setStoryContent(story.content[props.index].content); - setEnv(story.content[props.index].env); - setIsCode(story.content[props.index].isCode); - console.log(story.content[props.index], props.index, isCode); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [story]); - - if (!story) { - // will never reach here, as it has been checked in Story.tsx - return
; - } - - const editContent = (newContent: string) => { - console.log("content is editted"); - const contents = story.content; - contents.filter((story: StoryCell) => story.index == props.index)[0].content = newContent; - const newStory = {...story, content: [...contents]}; - dispatch(StoriesActions.setCurrentStory(newStory)); - dispatch(StoriesActions.saveStory(newStory, storyId!)); - } - - const saveButClicked = () => { - setEditMode(false); - setIsDirty(false); - setShowButs(false); - setShowNewCellUp(false); - setShowNewCellDown(false); - if (storyContent.trim().length > 0) { - const trimmedContent = storyContent.trim(); - setStoryContent(trimmedContent); - editContent(trimmedContent); - } else { - deleteWithoutConfirmation(); - } - } - - const deleteWithConfirmation = async () => { - const confirm = await showSimpleConfirmDialog({ - contents: ( - <> -

Delete the story cell?

-

Note: This action is irreversible.

- - ), - positiveIntent: 'danger', - positiveLabel: 'Delete' - }); - if (!confirm) { - return; - } - deleteWithoutConfirmation(); - } - - const deleteWithoutConfirmation = () => { - console.log(`story cell ${props.index} is deleted`); - const contents = story.content; - const newContents = []; - for (let i = 0; i < contents.length; i++) { - if (props.index === i) { - continue; - } else if (props.index < i) { - contents[i].index--; - } - newContents.push(contents[i]); - } - const newStory = {...story, content: newContents}; - dispatch(StoriesActions.setCurrentStory(newStory)); - dispatch(StoriesActions.saveStory(newStory, storyId!)); - } - - const onEditorValueChange = (content: string) => { - setStoryContent(content); - setIsDirty(true); - } - - const handleDoubleClick = () => { - if (!isCode) { - setEditMode(true); - } - } - - const moveStoryCell = (moveUp: boolean) => { - const swapIndex = props.index + (moveUp ? -1 : 1); - // check if the user is moving the story cell out of the array bound - if (swapIndex < 0 || swapIndex >= story.content.length) { - return; - } - if (moveUp) { - story.content[swapIndex].index++; - story.content[props.index].index--; - } else { - story.content[swapIndex].index--; - story.content[props.index].index++; - } - const temp = story.content[props.index]; - story.content[props.index] = story.content[swapIndex]; - story.content[swapIndex] = temp; - const newStory = {...story, content: [...story.content]}; - dispatch(StoriesActions.setCurrentStory(newStory)); - dispatch(StoriesActions.saveStory(newStory, storyId!)); - } - - return
setShowButs(true)} - onMouseLeave={() => setShowButs(false)} - > - {showNewCellUp && } - {isEditMode &&
- -
} -
- {showButs &&
- - - - - - -
} - {isEditMode ? - : - {renderStoryMarkdown( - isCode - ? ("\`\`\`{source} env:" + env + "\n" + storyContent) - : storyContent, props.index, false)} - - } -
- {showNewCellDown && } - -
-} - -export default EditStoryCell; \ No newline at end of file +import { Button } from '@blueprintjs/core'; +import { createContext, useEffect, useState } from 'react'; +import AceEditor from 'react-ace'; +import { useDispatch } from 'react-redux'; +import { ControlButtonSaveButton } from 'src/commons/controlBar/ControlBarSaveButton'; +import { showSimpleConfirmDialog } from 'src/commons/utils/DialogHelper'; +import { useTypedSelector } from 'src/commons/utils/Hooks'; +import { renderStoryMarkdown } from 'src/commons/utils/StoriesHelper'; + +import StoriesActions from '../StoriesActions'; +import { StoryCell } from '../StoriesTypes'; +import NewStoryCell from './CreateStoryCell'; +import Draggable from './Draggable'; +import DropArea from './DropArea'; + +type Props = { + index: number; +}; + +export const SourceBlockContext = createContext<(isTyping: boolean) => void>(() => {}); + +function EditStoryCell(props: Props) { + const dispatch = useDispatch(); + const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); + const [isCode, setIsCode] = useState(false); + const [env, setEnv] = useState(''); + const [storyContent, setStoryContent] = useState(''); + const [isEditMode, setEditMode] = useState(false); + const [isDirty, setIsDirty] = useState(false); + const [showButs, setShowButs] = useState(false); + const [showNewCellUp, setShowNewCellUp] = useState(false); + const [showNewCellDown, setShowNewCellDown] = useState(false); + + useEffect(() => { + if (!story) return; + setStoryContent(story.content[props.index].content); + setEnv(story.content[props.index].env); + setIsCode(story.content[props.index].isCode); + console.log(story.content[props.index], props.index, isCode); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [story]); + + if (!story) { + // will never reach here, as it has been checked in Story.tsx + return
; + } + + const editContent = (newContent: string) => { + console.log('content is editted'); + const contents = story.content; + contents.filter((story: StoryCell) => story.index == props.index)[0].content = newContent; + const newStory = { ...story, content: [...contents] }; + dispatch(StoriesActions.setCurrentStory(newStory)); + dispatch(StoriesActions.saveStory(newStory, storyId!)); + }; + + const saveButClicked = () => { + setEditMode(false); + setIsDirty(false); + setShowButs(false); + setShowNewCellUp(false); + setShowNewCellDown(false); + if (storyContent.trim().length > 0) { + const trimmedContent = storyContent.trim(); + setStoryContent(trimmedContent); + editContent(trimmedContent); + } else { + deleteWithoutConfirmation(); + } + }; + + const deleteWithConfirmation = async () => { + const confirm = await showSimpleConfirmDialog({ + contents: ( + <> +

Delete the story cell?

+

Note: This action is irreversible.

+ + ), + positiveIntent: 'danger', + positiveLabel: 'Delete' + }); + if (!confirm) { + return; + } + deleteWithoutConfirmation(); + }; + + const deleteWithoutConfirmation = () => { + console.log(`story cell ${props.index} is deleted`); + const contents = story.content; + const newContents = []; + for (let i = 0; i < contents.length; i++) { + if (props.index === i) { + continue; + } else if (props.index < i) { + contents[i].index--; + } + newContents.push(contents[i]); + } + const newStory = { ...story, content: newContents }; + dispatch(StoriesActions.setCurrentStory(newStory)); + dispatch(StoriesActions.saveStory(newStory, storyId!)); + }; + + const onEditorValueChange = (content: string) => { + setStoryContent(content); + setIsDirty(true); + }; + + const handleDoubleClick = () => { + if (!isCode) { + setEditMode(true); + } + }; + + const moveStoryCell = (moveUp: boolean) => { + const swapIndex = props.index + (moveUp ? -1 : 1); + // check if the user is moving the story cell out of the array bound + if (swapIndex < 0 || swapIndex >= story.content.length) { + return; + } + if (moveUp) { + story.content[swapIndex].index++; + story.content[props.index].index--; + } else { + story.content[swapIndex].index--; + story.content[props.index].index++; + } + const temp = story.content[props.index]; + story.content[props.index] = story.content[swapIndex]; + story.content[swapIndex] = temp; + const newStory = { ...story, content: [...story.content] }; + dispatch(StoriesActions.setCurrentStory(newStory)); + dispatch(StoriesActions.saveStory(newStory, storyId!)); + }; + + return ( +
setShowButs(true)} + onMouseLeave={() => setShowButs(false)} + > + {showNewCellUp && } + {isEditMode && ( +
+ +
+ )} +
+ {showButs && ( +
+ + + + + + +
+ )} + {isEditMode ? ( + + ) : ( + + {renderStoryMarkdown( + isCode ? '```{source} env:' + env + '\n' + storyContent : storyContent, + props.index, + false + )} + + )} +
+ {showNewCellDown && } + +
+ ); +} + +export default EditStoryCell; diff --git a/src/features/stories/storiesComponents/SourceBlock.tsx b/src/features/stories/storiesComponents/SourceBlock.tsx index c739e788fd..b4d7af73d8 100644 --- a/src/features/stories/storiesComponents/SourceBlock.tsx +++ b/src/features/stories/storiesComponents/SourceBlock.tsx @@ -83,7 +83,7 @@ const SourceBlock: React.FC = props => { const envIndex = envs.indexOf(env); // number indicating the chapter start from index 13 return parseInt(header.split(`\n`)[envIndex * 3 + 3].substring(13)); - } + }; useEffect(() => { setCode(props.content); @@ -106,7 +106,7 @@ const SourceBlock: React.FC = props => { // }, []); useEffect(() => { - console.log("selected tab is changed: ", selectedTab); + console.log('selected tab is changed: ', selectedTab); }, [selectedTab]); const onChangeTabs = React.useCallback( @@ -121,7 +121,7 @@ const SourceBlock: React.FC = props => { StoriesActions.toggleStoriesUsingSubst(newTabId === SideContentType.substVisualizer, env) ); console.log(selectedTab); - console.log("selected tab: ", newTabId); + console.log('selected tab: ', newTabId); setSelectedTab(newTabId); }, @@ -133,7 +133,6 @@ const SourceBlock: React.FC = props => { env === DEFAULT_ENV ? styliseSublanguage(chapter, variant) : env + ' | ' + styliseSublanguage(chapter, variant); - // TODO: Add CSE machine tabs and shift to language config @@ -227,7 +226,7 @@ const SourceBlock: React.FC = props => { // is handled by the component setting. if (selectedTab) onChangeTabs(selectedTab, selectedTab, {} as any); - console.log("Running on ", selectedTab); + console.log('Running on ', selectedTab); dispatch(StoriesActions.evalStory(env, code)); console.log(selectedTab); setOutputIndex(output.length); @@ -244,22 +243,22 @@ const SourceBlock: React.FC = props => { const editorOnChange = (code: string) => { setCode(code); setIsDirty(true); - } + }; const deleteStoryCell = (contents: StoryCell[]) => { console.log(`story cell ${props.index} is deleted`); for (let i = props.index + 1; i < contents.length; i++) { - contents[i].index--; + contents[i].index--; } contents.splice(props.index, 1); - } + }; const editHeader = (header: string[]) => { - console.log("In source block: chapter is editted"); + console.log('In source block: chapter is editted'); const index = envList.indexOf(currentEnv); header[index * 3 + 3] = ` chapter: ${currentChapter}`; return header; - } + }; const saveButClicked = () => { setIsDirty(false); @@ -273,7 +272,7 @@ const SourceBlock: React.FC = props => { } if (currentEnv !== env) { // set a new env - console.log("In source block: env is editted"); + console.log('In source block: env is editted'); console.log(currentEnv, env); story!.content[props.index].env = currentEnv; } @@ -285,10 +284,10 @@ const SourceBlock: React.FC = props => { } execResetEnv(); handleHeaders(newHeader.join('\n')); - const newStory = {...story!, content: contents, header: newHeader.join('\n')}; + const newStory = { ...story!, content: contents, header: newHeader.join('\n') }; dispatch(StoriesActions.setCurrentStory(newStory)); dispatch(StoriesActions.saveStory(newStory, storyId!)); - } + }; const changeEnv = (env: string) => { setCurrentEnv(env); @@ -296,12 +295,12 @@ const SourceBlock: React.FC = props => { const index = envList.indexOf(env); setCurrentChapter(+header[3 + index * 3].substring(13)); setIsDirty(true); - } + }; const changeEnvChapter = (chapter: Chapter) => { setCurrentChapter(chapter); setIsDirty(true); - } + }; selectMode(chapter, variant, ExternalLibraryName.NONE); @@ -310,45 +309,52 @@ const SourceBlock: React.FC = props => {
- {isDirty - ? - : } + /> + )} - {props.isViewOnly - ? envDisplayLabel - :
- - - {envList.map((env: string, index: number) => changeEnv(env)} - />)} - - -

|

- - - {[1, 2, 3, 4].map((chapter: Chapter, index: number) => changeEnvChapter(chapter)} text={styliseSublanguage(chapter, variant)}/> - )} - - -
} + {props.isViewOnly ? ( + envDisplayLabel + ) : ( +
+ + + {envList.map((env: string, index: number) => ( + changeEnv(env)} /> + ))} + + +

|

+ + + {[1, 2, 3, 4].map((chapter: Chapter, index: number) => ( + changeEnvChapter(chapter)} + text={styliseSublanguage(chapter, variant)} + /> + ))} + + +
+ )}
@@ -397,4 +403,4 @@ const SourceBlock: React.FC = props => { ); }; -export default SourceBlock; \ No newline at end of file +export default SourceBlock; diff --git a/src/features/stories/storiesComponents/UserBlogContent.tsx b/src/features/stories/storiesComponents/UserBlogContent.tsx index bc2430877b..1c83284787 100644 --- a/src/features/stories/storiesComponents/UserBlogContent.tsx +++ b/src/features/stories/storiesComponents/UserBlogContent.tsx @@ -104,14 +104,19 @@ export function getYamlHeader(content: string): { header: string; content: strin export function getEnvironments(header: string): string[] { const environments: string[] = []; - const temp = header.split("\n"); + const temp = header.split('\n'); for (let i = 2; i < temp.length - 1; i += 3) { environments.push(temp[i].substring(2, temp[i].length - 1)); } return environments; } -export function constructHeader(header: string, env: string, chapter: Chapter, variant: Variant): string { +export function constructHeader( + header: string, + env: string, + chapter: Chapter, + variant: Variant +): string { const newHeader: string[] = header.split('\n'); newHeader.push(` ${env}:`); newHeader.push(` chapter: ${chapter}`); @@ -123,18 +128,15 @@ type Props = { isViewOnly: boolean; }; -const UserBlogContent: React.FC = ({ - isViewOnly, - }) => { - - const [newEnv, setNewEnv] = useState(""); +const UserBlogContent: React.FC = ({ isViewOnly }) => { + const [newEnv, setNewEnv] = useState(''); // TODO: enable different variant const variant: Variant = Variant.DEFAULT; const [currentChapter, setEnvChapter] = useState(Chapter.SOURCE_1); const [isDirty, setIsDirty] = useState(false); const dispatch = useDispatch(); const { currentStory: story, currentStoryId: storyId } = useTypedSelector(store => store.stories); - const { content: contents, header: header } = story!; + const { content: contents, header: header } = story!; const [envs, setEnvs] = useState(getEnvironments(header)); const [activeIndex, setActiveIndex] = useState(null); @@ -142,7 +144,7 @@ const UserBlogContent: React.FC = ({ store.dispatch(StoriesActions.clearStoryEnv()); handleHeaders(header); setEnvs(getEnvironments(header)); - console.log("header resets"); + console.log('header resets'); }, [header]); if (!story) { @@ -151,60 +153,72 @@ const UserBlogContent: React.FC = ({ } const editHeader = (newHeader: string) => { - console.log("header is editted"); - const newStory = {...story, header: newHeader}; + console.log('header is editted'); + const newStory = { ...story, header: newHeader }; dispatch(StoriesActions.setCurrentStory(newStory)); dispatch(StoriesActions.saveStory(newStory, storyId!)); - } + }; const saveButClicked = () => { - setNewEnv(""); + setNewEnv(''); setIsDirty(false); - if (newEnv.trim() === "") { - showWarningMessage("environment name cannot be empty"); + if (newEnv.trim() === '') { + showWarningMessage('environment name cannot be empty'); return; } else if (envs.includes(newEnv)) { - showWarningMessage(`${newEnv} already exists!`) + showWarningMessage(`${newEnv} already exists!`); return; } const newHeader = header.concat(` ${newEnv}: chapter: ${currentChapter} - variant: default` - ); + variant: default`); editHeader(newHeader); - } + }; const controlBarProps: ControlBarProps = { editorButtons: [
+ justifyContent: 'space-between', + width: '100%' + }} + > { setNewEnv(e.target.value); - if (e.target.value.trim() !== "") { + if (e.target.value.trim() !== '') { setIsDirty(true); } else { setIsDirty(false); } }} /> - + - setEnvChapter(1)} text={styliseSublanguage(Chapter.SOURCE_1, variant)}/> - setEnvChapter(2)} text={styliseSublanguage(Chapter.SOURCE_2, variant)}/> - setEnvChapter(3)} text={styliseSublanguage(Chapter.SOURCE_3, variant)}/> - setEnvChapter(4)} text={styliseSublanguage(Chapter.SOURCE_4, variant)}/> + setEnvChapter(1)} + text={styliseSublanguage(Chapter.SOURCE_1, variant)} + /> + setEnvChapter(2)} + text={styliseSublanguage(Chapter.SOURCE_2, variant)} + /> + setEnvChapter(3)} + text={styliseSublanguage(Chapter.SOURCE_3, variant)} + /> + setEnvChapter(4)} + text={styliseSublanguage(Chapter.SOURCE_4, variant)} + /> = ({ return contents.length > 0 ? (
- {!isViewOnly && } - {isViewOnly - ? contents.map((story, key) => ) - : -
- + {!isViewOnly && } + {isViewOnly ? ( + contents.map((story, key) => ) + ) : ( + +
+ +
+ {contents.map((_, key) => { + return ; + })} +
+ )} + {!isViewOnly && ( +
+
- {contents.map((_, key) => { - return })} - } - {!isViewOnly &&
- -
} + )}
) : (
diff --git a/src/features/stories/storiesComponents/ViewStoryCell.tsx b/src/features/stories/storiesComponents/ViewStoryCell.tsx index ae3c9e165e..d13072bdcf 100644 --- a/src/features/stories/storiesComponents/ViewStoryCell.tsx +++ b/src/features/stories/storiesComponents/ViewStoryCell.tsx @@ -1,27 +1,24 @@ -import { useEffect, useState } from "react"; -import { renderStoryMarkdown } from "src/commons/utils/StoriesHelper"; - -import { StoryCell } from '../StoriesTypes'; - -type Props = { - story: StoryCell; -}; - -function ViewStoryCell(props: Props) { - - const { index, isCode, env, content } = props.story; - const [storyContent, setStoryContent] = useState(content); - - useEffect(() => { - if (isCode) { - setStoryContent("\`\`\`{source} env:" + env + "\n" + content); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return
- {renderStoryMarkdown(storyContent, index, true)} -
-} - -export default ViewStoryCell; \ No newline at end of file +import { useEffect, useState } from 'react'; +import { renderStoryMarkdown } from 'src/commons/utils/StoriesHelper'; + +import { StoryCell } from '../StoriesTypes'; + +type Props = { + story: StoryCell; +}; + +function ViewStoryCell(props: Props) { + const { index, isCode, env, content } = props.story; + const [storyContent, setStoryContent] = useState(content); + + useEffect(() => { + if (isCode) { + setStoryContent('```{source} env:' + env + '\n' + content); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return
{renderStoryMarkdown(storyContent, index, true)}
; +} + +export default ViewStoryCell; diff --git a/src/i18n/i18next.ts b/src/i18n/i18next.ts index dbf6283bcb..7f5741615e 100644 --- a/src/i18n/i18next.ts +++ b/src/i18n/i18next.ts @@ -1,8 +1,7 @@ import 'i18next'; // import all namespaces (for the default language, only) -import commons from 'locales/en/commons.json'; - +// import commons from 'locales/en/commons.json'; import { defaultLanguage, i18nLanguageCode } from './locales'; export type i18nDefaultLangKeys = (typeof defaultLanguage)[i18nLanguageCode.DEFAULT]; diff --git a/src/pages/stories/Stories.tsx b/src/pages/stories/Stories.tsx index ef7859cfc2..cbeae44b56 100644 --- a/src/pages/stories/Stories.tsx +++ b/src/pages/stories/Stories.tsx @@ -140,12 +140,11 @@ const Stories: React.FC = () => { - // Always show pinned stories - story.isPinned || story.authorName.toLowerCase().includes(query.toLowerCase()) - )} + stories={storyList.filter( + story => + // Always show pinned stories + story.isPinned || story.authorName.toLowerCase().includes(query.toLowerCase()) + )} storyActions={story => { const isAuthor = storiesUserId === story.authorId; const hasWritePermissions = diff --git a/src/pages/stories/Story.tsx b/src/pages/stories/Story.tsx index 92547dff5d..23e72bdbfb 100644 --- a/src/pages/stories/Story.tsx +++ b/src/pages/stories/Story.tsx @@ -60,8 +60,8 @@ const Story: React.FC = ({ isViewOnly = false }) => { { - if (story.title.trim() === "") { - showWarningMessage("story name cannot be empty"); + if (story.title.trim() === '') { + showWarningMessage('story name cannot be empty'); return; } if (storyId) { @@ -83,13 +83,11 @@ const Story: React.FC = ({ isViewOnly = false }) => { return (
-
-
- -
+
+
+
+
); }; diff --git a/src/styles/_stories.scss b/src/styles/_stories.scss index 476d8d9184..3bdcfd5de8 100644 --- a/src/styles/_stories.scss +++ b/src/styles/_stories.scss @@ -95,26 +95,26 @@ white-space: pre; } - [draggable="true"] { + [draggable='true'] { user-select: none; -webkit-user-select: none; cursor: grab; position: relative; /* Ensure proper positioning */ } - - [draggable="true"]:active { + + [draggable='true']:active { cursor: grabbing; } .drop-area { - width: 100%; - height: 100px; - columns: #dcdcdc; - border: 1px dashed #dcdcdc; - border-radius: 10px; - padding: 15px; - opacity: 1; - transition: all 0.2s ease-in-out; + width: 100%; + height: 100px; + columns: #dcdcdc; + border: 1px dashed #dcdcdc; + border-radius: 10px; + padding: 15px; + opacity: 1; + transition: all 0.2s ease-in-out; } .hide-drop { From 87b8770c7e3514068d5979b57d3a485f6577b833 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Fri, 18 Apr 2025 03:55:32 +0800 Subject: [PATCH 13/16] Remove files that shouldn't be committed --- .coverage-fix.tmp | 3 - lcov.info | 1150 --------------------------------------------- 2 files changed, 1153 deletions(-) delete mode 100644 .coverage-fix.tmp delete mode 100644 lcov.info diff --git a/.coverage-fix.tmp b/.coverage-fix.tmp deleted file mode 100644 index 7df2305e3a..0000000000 --- a/.coverage-fix.tmp +++ /dev/null @@ -1,3 +0,0 @@ -./src/i18n/i18next.ts -./src/commons/workspace/sharedb-ace.ts -./src/react-app-env.ts diff --git a/lcov.info b/lcov.info deleted file mode 100644 index a7819e63bd..0000000000 --- a/lcov.info +++ /dev/null @@ -1,1150 +0,0 @@ -TN: -SF:src/react-app-env.ts -FNF:0 -FNH:0 -LF:0 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/commons/sagas/StoriesSaga.ts -FN:34,(anonymous_0) -FN:36,(anonymous_1) -FN:43,(anonymous_2) -FN:59,(anonymous_3) -FN:62,(anonymous_4) -FN:86,(anonymous_5) -FN:109,(anonymous_6) -FN:122,(anonymous_7) -FN:140,(anonymous_8) -FN:144,(anonymous_9) -FN:146,(anonymous_10) -FN:163,(anonymous_11) -FN:172,(anonymous_12) -FN:183,(anonymous_13) -FNF:14 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -DA:31,15 -DA:32,15 -DA:35,0 -DA:36,0 -DA:37,0 -DA:38,0 -DA:41,0 -DA:44,0 -DA:45,0 -DA:46,0 -DA:47,0 -DA:48,0 -DA:50,0 -DA:56,0 -DA:60,0 -DA:61,0 -DA:62,0 -DA:64,0 -DA:65,0 -DA:66,0 -DA:69,0 -DA:80,0 -DA:81,0 -DA:84,0 -DA:87,0 -DA:88,0 -DA:89,0 -DA:90,0 -DA:91,0 -DA:102,0 -DA:103,0 -DA:106,0 -DA:110,0 -DA:111,0 -DA:112,0 -DA:114,0 -DA:123,0 -DA:130,0 -DA:132,0 -DA:133,0 -DA:134,0 -DA:135,0 -DA:137,0 -DA:138,0 -DA:141,0 -DA:142,0 -DA:143,0 -DA:144,0 -DA:146,0 -DA:147,0 -DA:148,0 -DA:151,0 -DA:152,0 -DA:164,0 -DA:166,0 -DA:168,0 -DA:169,0 -DA:173,0 -DA:174,0 -DA:176,0 -DA:178,0 -DA:179,0 -DA:180,0 -DA:184,0 -DA:185,0 -DA:187,0 -DA:188,0 -DA:189,0 -DA:190,0 -LF:69 -LH:2 -BRDA:38,0,0,0 -BRDA:38,0,1,0 -BRDA:46,1,0,0 -BRDA:46,1,1,0 -BRDA:64,2,0,0 -BRDA:64,2,1,0 -BRDA:80,3,0,0 -BRDA:80,3,1,0 -BRDA:102,4,0,0 -BRDA:102,4,1,0 -BRDA:132,5,0,0 -BRDA:132,5,1,0 -BRDA:168,6,0,0 -BRDA:168,6,1,0 -BRDA:178,7,0,0 -BRDA:178,7,1,0 -BRDA:188,8,0,0 -BRDA:188,8,1,0 -BRF:18 -BRH:0 -end_of_record -TN: -SF:src/commons/workspace/sharedb-ace.ts -FNF:0 -FNH:0 -LF:0 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/features/stories/DragContext.ts -FN:10,(anonymous_0) -FNF:1 -FNH:0 -FNDA:0,(anonymous_0) -DA:8,15 -DA:10,15 -DA:11,0 -DA:13,0 -DA:14,0 -DA:17,0 -LF:6 -LH:2 -BRDA:13,0,0,0 -BRDA:13,0,1,0 -BRF:2 -BRH:0 -end_of_record -TN: -SF:src/features/stories/StoriesTypes.ts -FNF:0 -FNH:0 -LF:0 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/features/stories/storiesComponents/BackendAccess.ts -FN:100,(anonymous_0) -FN:105,(anonymous_1) -FN:118,(anonymous_2) -FN:137,(anonymous_3) -FN:145,(anonymous_4) -FN:159,(anonymous_5) -FN:168,(anonymous_6) -FN:171,(anonymous_7) -FN:190,(anonymous_8) -FN:211,(anonymous_9) -FN:241,(anonymous_10) -FN:252,(anonymous_11) -FN:265,(anonymous_12) -FN:287,(anonymous_13) -FNF:14 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -DA:22,15 -DA:34,15 -DA:94,15 -DA:95,15 -DA:100,15 -DA:101,0 -DA:102,0 -DA:105,15 -DA:106,0 -DA:107,0 -DA:108,0 -DA:111,0 -DA:112,0 -DA:118,15 -DA:127,0 -DA:130,0 -DA:131,0 -DA:133,0 -DA:134,0 -DA:137,15 -DA:142,0 -DA:145,0 -DA:149,0 -DA:150,0 -DA:151,0 -DA:154,0 -DA:155,0 -DA:159,15 -DA:160,0 -DA:163,0 -DA:164,0 -DA:166,0 -DA:168,0 -DA:171,15 -DA:172,0 -DA:177,0 -DA:178,0 -DA:180,0 -DA:184,0 -DA:185,0 -DA:186,0 -DA:187,0 -DA:190,15 -DA:198,0 -DA:202,0 -DA:203,0 -DA:204,0 -DA:206,0 -DA:207,0 -DA:208,0 -DA:211,15 -DA:219,0 -DA:223,0 -DA:224,0 -DA:225,0 -DA:227,0 -DA:228,0 -DA:232,0 -DA:233,0 -DA:234,0 -DA:235,0 -DA:236,0 -DA:237,0 -DA:241,15 -DA:242,0 -DA:245,0 -DA:246,0 -DA:248,0 -DA:249,0 -DA:252,15 -DA:255,0 -DA:258,0 -DA:259,0 -DA:261,0 -DA:262,0 -DA:265,15 -DA:270,0 -DA:279,0 -DA:280,0 -DA:281,0 -DA:283,0 -DA:284,0 -DA:287,15 -DA:291,0 -DA:299,0 -DA:300,0 -DA:301,0 -DA:303,0 -DA:304,0 -LF:89 -LH:16 -BRDA:107,0,0,0 -BRDA:107,0,1,0 -BRDA:130,1,0,0 -BRDA:130,1,1,0 -BRDA:149,2,0,0 -BRDA:149,2,1,0 -BRDA:163,3,0,0 -BRDA:163,3,1,0 -BRDA:177,4,0,0 -BRDA:177,4,1,0 -BRDA:202,5,0,0 -BRDA:202,5,1,0 -BRDA:223,6,0,0 -BRDA:223,6,1,0 -BRDA:245,7,0,0 -BRDA:245,7,1,0 -BRDA:258,8,0,0 -BRDA:258,8,1,0 -BRDA:279,9,0,0 -BRDA:279,9,1,0 -BRDA:299,10,0,0 -BRDA:299,10,1,0 -BRF:22 -BRH:0 -end_of_record -TN: -SF:src/features/stories/storiesComponents/CreateStoryCell.tsx -FN:17,(anonymous_0) -FN:22,(anonymous_1) -FN:33,(anonymous_2) -FN:38,(anonymous_3) -FN:45,(anonymous_4) -FN:57,(anonymous_5) -FN:65,(anonymous_6) -FN:89,(anonymous_7) -FN:90,(anonymous_8) -FN:96,(anonymous_9) -FN:98,(anonymous_10) -FNF:11 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -DA:17,15 -DA:21,0 -DA:22,0 -DA:23,0 -DA:24,0 -DA:25,0 -DA:26,0 -DA:27,0 -DA:29,0 -DA:30,0 -DA:33,0 -DA:34,0 -DA:35,0 -DA:38,0 -DA:39,0 -DA:40,0 -DA:41,0 -DA:42,0 -DA:45,0 -DA:46,0 -DA:47,0 -DA:48,0 -DA:50,0 -DA:56,0 -DA:57,0 -DA:58,0 -DA:59,0 -DA:60,0 -DA:61,0 -DA:62,0 -DA:65,0 -DA:66,0 -DA:67,0 -DA:68,0 -DA:70,0 -DA:71,0 -DA:74,0 -DA:89,0 -DA:90,0 -DA:96,0 -DA:98,0 -LF:41 -LH:1 -BRDA:29,0,0,0 -BRDA:29,0,1,0 -BRDA:53,1,0,0 -BRDA:53,1,1,0 -BRDA:66,2,0,0 -BRDA:66,2,1,0 -BRDA:88,3,0,0 -BRDA:88,3,1,0 -BRDA:93,4,0,0 -BRDA:93,4,1,0 -BRF:10 -BRH:0 -end_of_record -TN: -SF:src/features/stories/storiesComponents/Draggable.tsx -FN:10,(anonymous_0) -FN:14,(anonymous_1) -FN:30,(anonymous_2) -FNF:3 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -DA:10,15 -DA:11,0 -DA:12,0 -DA:14,0 -DA:16,0 -DA:17,0 -DA:19,0 -DA:20,0 -DA:24,0 -DA:27,0 -DA:30,0 -DA:32,0 -DA:33,0 -DA:35,0 -DA:38,0 -LF:15 -LH:1 -BRDA:16,0,0,0 -BRDA:16,0,1,0 -BRDA:32,1,0,0 -BRDA:32,1,1,0 -BRF:4 -BRH:0 -end_of_record -TN: -SF:src/features/stories/storiesComponents/DropArea.tsx -FN:12,(anonymous_0) -FN:16,(anonymous_1) -FN:24,(anonymous_2) -FN:43,(anonymous_3) -FN:52,(anonymous_4) -FN:53,(anonymous_5) -FN:54,(anonymous_6) -FN:58,(anonymous_7) -FNF:8 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -DA:12,15 -DA:14,0 -DA:15,0 -DA:16,0 -DA:17,0 -DA:19,0 -DA:21,0 -DA:24,0 -DA:25,0 -DA:26,0 -DA:27,0 -DA:28,0 -DA:29,0 -DA:30,0 -DA:32,0 -DA:33,0 -DA:34,0 -DA:35,0 -DA:36,0 -DA:37,0 -DA:39,0 -DA:41,0 -DA:43,0 -DA:44,0 -DA:45,0 -DA:46,0 -DA:47,0 -DA:50,0 -DA:52,0 -DA:53,0 -DA:55,0 -DA:56,0 -DA:58,0 -LF:33 -LH:1 -BRDA:19,0,0,0 -BRDA:19,0,1,0 -BRDA:27,1,0,0 -BRDA:27,1,1,0 -BRDA:33,2,0,0 -BRDA:33,2,1,0 -BRDA:51,3,0,0 -BRDA:51,3,1,0 -BRF:8 -BRH:0 -end_of_record -TN: -SF:src/features/stories/storiesComponents/EditStoryCell.tsx -FN:20,(anonymous_0) -FN:22,EditStoryCell -FN:25,(anonymous_2) -FN:35,(anonymous_3) -FN:49,(anonymous_4) -FN:52,(anonymous_5) -FN:58,(anonymous_6) -FN:73,(anonymous_7) -FN:90,(anonymous_8) -FN:107,(anonymous_9) -FN:112,(anonymous_10) -FN:118,(anonymous_11) -FN:141,(anonymous_12) -FN:142,(anonymous_13) -FN:169,(anonymous_14) -FN:175,(anonymous_15) -FN:181,(anonymous_16) -FN:188,(anonymous_17) -FN:195,(anonymous_18) -FN:204,(anonymous_19) -FNF:20 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,EditStoryCell -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -FNDA:0,(anonymous_18) -FNDA:0,(anonymous_19) -DA:20,15 -DA:24,0 -DA:25,0 -DA:26,0 -DA:27,0 -DA:28,0 -DA:29,0 -DA:30,0 -DA:31,0 -DA:32,0 -DA:33,0 -DA:35,0 -DA:36,0 -DA:37,0 -DA:38,0 -DA:39,0 -DA:40,0 -DA:44,0 -DA:46,0 -DA:49,0 -DA:50,0 -DA:51,0 -DA:52,0 -DA:53,0 -DA:54,0 -DA:55,0 -DA:58,0 -DA:59,0 -DA:60,0 -DA:61,0 -DA:62,0 -DA:63,0 -DA:64,0 -DA:65,0 -DA:66,0 -DA:67,0 -DA:69,0 -DA:73,0 -DA:74,0 -DA:84,0 -DA:85,0 -DA:87,0 -DA:90,0 -DA:91,0 -DA:92,0 -DA:93,0 -DA:94,0 -DA:95,0 -DA:96,0 -DA:97,0 -DA:98,0 -DA:100,0 -DA:102,0 -DA:103,0 -DA:104,0 -DA:107,0 -DA:108,0 -DA:109,0 -DA:112,0 -DA:113,0 -DA:114,0 -DA:118,0 -DA:119,0 -DA:121,0 -DA:122,0 -DA:124,0 -DA:125,0 -DA:126,0 -DA:128,0 -DA:129,0 -DA:131,0 -DA:132,0 -DA:133,0 -DA:134,0 -DA:135,0 -DA:136,0 -DA:139,0 -DA:141,0 -DA:142,0 -DA:170,0 -DA:176,0 -DA:182,0 -DA:183,0 -DA:189,0 -DA:190,0 -DA:196,0 -DA:197,0 -DA:199,0 -DA:205,0 -LF:89 -LH:1 -BRDA:36,0,0,0 -BRDA:36,0,1,0 -BRDA:44,1,0,0 -BRDA:44,1,1,0 -BRDA:64,2,0,0 -BRDA:64,2,1,0 -BRDA:84,3,0,0 -BRDA:84,3,1,0 -BRDA:95,4,0,0 -BRDA:95,4,1,0 -BRDA:97,5,0,0 -BRDA:97,5,1,0 -BRDA:113,6,0,0 -BRDA:113,6,1,0 -BRDA:119,7,0,0 -BRDA:119,7,1,0 -BRDA:121,8,0,0 -BRDA:121,8,1,0 -BRDA:121,9,0,0 -BRDA:121,9,1,0 -BRDA:124,10,0,0 -BRDA:124,10,1,0 -BRDA:144,11,0,0 -BRDA:144,11,1,0 -BRDA:147,12,0,0 -BRDA:147,12,1,0 -BRDA:155,13,0,0 -BRDA:155,13,1,0 -BRDA:208,14,0,0 -BRDA:208,14,1,0 -BRDA:226,15,0,0 -BRDA:226,15,1,0 -BRDA:232,16,0,0 -BRDA:232,16,1,0 -BRF:34 -BRH:0 -end_of_record -TN: -SF:src/features/stories/storiesComponents/SourceBlock.tsx -FN:43,parseMetadata -FN:53,(anonymous_1) -FN:58,(anonymous_2) -FN:59,(anonymous_3) -FN:73,(anonymous_4) -FN:76,(anonymous_5) -FN:82,(anonymous_6) -FN:88,(anonymous_7) -FN:92,(anonymous_8) -FN:97,(anonymous_9) -FN:108,(anonymous_10) -FN:113,(anonymous_11) -FN:165,(anonymous_12) -FN:176,(anonymous_13) -FN:222,(anonymous_14) -FN:240,(anonymous_15) -FN:244,(anonymous_16) -FN:249,(anonymous_17) -FN:257,(anonymous_18) -FN:264,(anonymous_19) -FN:293,(anonymous_20) -FN:301,(anonymous_21) -FN:335,(anonymous_22) -FN:338,(anonymous_23) -FN:345,(anonymous_24) -FN:347,(anonymous_25) -FN:375,(anonymous_26) -FNF:27 -FNH:0 -FNDA:0,parseMetadata -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -FNDA:0,(anonymous_18) -FNDA:0,(anonymous_19) -FNDA:0,(anonymous_20) -FNDA:0,(anonymous_21) -FNDA:0,(anonymous_22) -FNDA:0,(anonymous_23) -FNDA:0,(anonymous_24) -FNDA:0,(anonymous_25) -FNDA:0,(anonymous_26) -DA:44,0 -DA:45,0 -DA:46,0 -DA:47,0 -DA:50,0 -DA:53,15 -DA:54,0 -DA:55,0 -DA:56,0 -DA:57,0 -DA:58,0 -DA:59,0 -DA:60,0 -DA:61,0 -DA:64,0 -DA:66,0 -DA:72,0 -DA:73,0 -DA:75,0 -DA:76,0 -DA:79,0 -DA:80,0 -DA:82,0 -DA:83,0 -DA:85,0 -DA:88,0 -DA:89,0 -DA:92,0 -DA:93,0 -DA:97,0 -DA:98,0 -DA:108,0 -DA:109,0 -DA:112,0 -DA:120,0 -DA:123,0 -DA:124,0 -DA:126,0 -DA:133,0 -DA:147,0 -DA:148,0 -DA:165,0 -DA:166,0 -DA:171,0 -DA:172,0 -DA:173,0 -DA:176,0 -DA:181,0 -DA:189,0 -DA:191,0 -DA:198,0 -DA:203,0 -DA:206,0 -DA:210,0 -DA:222,0 -DA:228,0 -DA:230,0 -DA:231,0 -DA:232,0 -DA:233,0 -DA:237,0 -DA:238,0 -DA:240,0 -DA:241,0 -DA:244,0 -DA:245,0 -DA:246,0 -DA:249,0 -DA:250,0 -DA:251,0 -DA:252,0 -DA:254,0 -DA:257,0 -DA:258,0 -DA:259,0 -DA:260,0 -DA:261,0 -DA:264,0 -DA:265,0 -DA:266,0 -DA:267,0 -DA:268,0 -DA:269,0 -DA:270,0 -DA:272,0 -DA:274,0 -DA:276,0 -DA:277,0 -DA:278,0 -DA:280,0 -DA:281,0 -DA:283,0 -DA:284,0 -DA:286,0 -DA:287,0 -DA:288,0 -DA:289,0 -DA:290,0 -DA:293,0 -DA:294,0 -DA:295,0 -DA:296,0 -DA:297,0 -DA:298,0 -DA:301,0 -DA:302,0 -DA:303,0 -DA:306,0 -DA:308,0 -DA:335,0 -DA:338,0 -DA:345,0 -DA:347,0 -DA:375,0 -LF:114 -LH:1 -BRDA:46,0,0,0 -BRDA:46,0,1,0 -BRDA:47,1,0,0 -BRDA:47,1,1,0 -BRDA:66,2,0,0 -BRDA:66,2,1,0 -BRDA:68,3,0,0 -BRDA:68,3,1,0 -BRDA:73,4,0,0 -BRDA:73,4,1,0 -BRDA:76,5,0,0 -BRDA:76,5,1,0 -BRDA:97,6,0,0 -BRDA:97,6,1,0 -BRDA:133,7,0,0 -BRDA:133,7,1,0 -BRDA:152,8,0,0 -BRDA:152,8,1,0 -BRDA:171,9,0,0 -BRDA:171,9,1,0 -BRDA:172,10,0,0 -BRDA:172,10,1,0 -BRDA:172,11,0,0 -BRDA:172,11,1,0 -BRDA:189,12,0,0 -BRDA:189,12,1,0 -BRDA:198,13,0,0 -BRDA:198,13,1,0 -BRDA:199,14,0,0 -BRDA:199,14,1,0 -BRDA:199,14,2,0 -BRDA:228,15,0,0 -BRDA:228,15,1,0 -BRDA:269,16,0,0 -BRDA:269,16,1,0 -BRDA:274,17,0,0 -BRDA:274,17,1,0 -BRDA:281,18,0,0 -BRDA:281,18,1,0 -BRDA:313,19,0,0 -BRDA:313,19,1,0 -BRDA:325,20,0,0 -BRDA:325,20,1,0 -BRF:43 -BRH:0 -end_of_record -TN: -SF:src/features/stories/storiesComponents/UserBlogContent.tsx -FN:30,handleEnvironment -FN:36,(anonymous_1) -FN:48,handleHeaders -FN:89,getYamlHeader -FN:105,getEnvironments -FN:114,constructHeader -FN:126,(anonymous_6) -FN:136,(anonymous_7) -FN:141,(anonymous_8) -FN:153,(anonymous_9) -FN:160,(anonymous_10) -FN:193,(anonymous_11) -FN:204,(anonymous_12) -FN:205,(anonymous_13) -FN:206,(anonymous_14) -FN:207,(anonymous_15) -FN:223,(anonymous_16) -FN:231,(anonymous_17) -FNF:18 -FNH:0 -FNDA:0,handleEnvironment -FNDA:0,(anonymous_1) -FNDA:0,handleHeaders -FNDA:0,getYamlHeader -FNDA:0,getEnvironments -FNDA:0,constructHeader -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -DA:24,15 -DA:26,15 -DA:27,15 -DA:28,15 -DA:31,0 -DA:32,0 -DA:35,0 -DA:36,0 -DA:40,0 -DA:44,0 -DA:49,0 -DA:50,0 -DA:57,0 -DA:59,0 -DA:60,0 -DA:61,0 -DA:62,0 -DA:64,0 -DA:65,0 -DA:66,0 -DA:68,0 -DA:69,0 -DA:72,0 -DA:76,0 -DA:78,0 -DA:90,0 -DA:91,0 -DA:92,0 -DA:94,0 -DA:95,0 -DA:96,0 -DA:99,0 -DA:106,0 -DA:107,0 -DA:108,0 -DA:109,0 -DA:111,0 -DA:115,0 -DA:116,0 -DA:117,0 -DA:118,0 -DA:119,0 -DA:126,15 -DA:130,0 -DA:132,0 -DA:133,0 -DA:134,0 -DA:135,0 -DA:136,0 -DA:137,0 -DA:138,0 -DA:139,0 -DA:141,0 -DA:142,0 -DA:143,0 -DA:144,0 -DA:145,0 -DA:148,0 -DA:150,0 -DA:153,0 -DA:154,0 -DA:155,0 -DA:156,0 -DA:157,0 -DA:160,0 -DA:161,0 -DA:162,0 -DA:163,0 -DA:164,0 -DA:165,0 -DA:166,0 -DA:167,0 -DA:168,0 -DA:170,0 -DA:175,0 -DA:178,0 -DA:194,0 -DA:195,0 -DA:196,0 -DA:198,0 -DA:204,0 -DA:205,0 -DA:206,0 -DA:207,0 -DA:219,0 -DA:223,0 -DA:232,0 -LF:87 -LH:5 -BRDA:35,0,0,0 -BRDA:35,0,1,0 -BRDA:40,1,0,0 -BRDA:40,1,1,0 -BRDA:49,2,0,0 -BRDA:49,2,1,0 -BRDA:62,3,0,0 -BRDA:62,3,1,0 -BRDA:62,3,2,0 -BRDA:76,4,0,0 -BRDA:76,4,1,0 -BRDA:91,5,0,0 -BRDA:91,5,1,0 -BRDA:95,6,0,0 -BRDA:95,6,1,0 -BRDA:148,7,0,0 -BRDA:148,7,1,0 -BRDA:163,8,0,0 -BRDA:163,8,1,0 -BRDA:166,9,0,0 -BRDA:166,9,1,0 -BRDA:195,10,0,0 -BRDA:195,10,1,0 -BRDA:219,11,0,0 -BRDA:219,11,1,0 -BRDA:221,12,0,0 -BRDA:221,12,1,0 -BRDA:222,13,0,0 -BRDA:222,13,1,0 -BRDA:237,14,0,0 -BRDA:237,14,1,0 -BRF:31 -BRH:0 -end_of_record -TN: -SF:src/features/stories/storiesComponents/ViewStoryCell.tsx -FN:10,ViewStoryCell -FN:15,(anonymous_1) -FNF:2 -FNH:0 -FNDA:0,ViewStoryCell -FNDA:0,(anonymous_1) -DA:12,0 -DA:13,0 -DA:15,0 -DA:16,0 -DA:17,0 -DA:22,0 -LF:6 -LH:0 -BRDA:16,0,0,0 -BRDA:16,0,1,0 -BRF:2 -BRH:0 -end_of_record -TN: -SF:src/i18n/i18next.ts -FNF:0 -FNH:0 -LF:0 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/pages/stories/Story.tsx -FN:21,(anonymous_0) -FN:25,(anonymous_1) -FN:28,(anonymous_2) -FN:52,(anonymous_3) -FN:62,(anonymous_4) -FN:99,(anonymous_5) -FN:102,(anonymous_6) -FNF:7 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -DA:21,0 -DA:22,0 -DA:23,0 -DA:25,0 -DA:26,0 -DA:28,0 -DA:30,0 -DA:33,0 -DA:37,0 -DA:38,0 -DA:41,0 -DA:43,0 -DA:53,0 -DA:54,0 -DA:55,0 -DA:63,0 -DA:64,0 -DA:65,0 -DA:67,0 -DA:69,0 -DA:72,0 -DA:75,0 -DA:83,0 -DA:99,0 -DA:100,0 -DA:102,0 -DA:103,0 -LF:27 -LH:0 -BRDA:21,0,0,0 -BRDA:33,1,0,0 -BRDA:33,1,1,0 -BRDA:37,2,0,0 -BRDA:37,2,1,0 -BRDA:45,3,0,0 -BRDA:45,3,1,0 -BRDA:59,4,0,0 -BRDA:59,4,1,0 -BRDA:63,5,0,0 -BRDA:63,5,1,0 -BRDA:67,6,0,0 -BRDA:67,6,1,0 -BRF:13 -BRH:0 -end_of_record - - From 2f18c115ab26907b0690361db34cab7b2534dec9 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Fri, 18 Apr 2025 03:57:33 +0800 Subject: [PATCH 14/16] Revert some unnecessary changes --- public/externalLibs/sound/soundToneMatrix.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/externalLibs/sound/soundToneMatrix.js b/public/externalLibs/sound/soundToneMatrix.js index d0246757bf..8638a90378 100644 --- a/public/externalLibs/sound/soundToneMatrix.js +++ b/public/externalLibs/sound/soundToneMatrix.js @@ -36,7 +36,7 @@ var timeout_matrix; // for coloring the matrix accordingly while it's being played var timeout_color; -var timeout_objects = []; +var timeout_objects = new Array(); // vector_to_list returns a list that contains the elements of the argument vector // in the given order. @@ -54,7 +54,7 @@ function vector_to_list(vector) { function x_y_to_row_column(x, y) { var row = Math.floor((y - margin_length) / (square_side_length + distance_between_squares)); var column = Math.floor((x - margin_length) / (square_side_length + distance_between_squares)); - return [row, column]; + return Array(row, column); } // given the row number of a square, return the leftmost coordinate @@ -365,5 +365,5 @@ function clear_all_timeout() { clearTimeout(timeout_objects[i]); } - timeout_objects = []; + timeout_objects = new Array(); } From d54129042c931965b16c3fad8c84d7ab10319df7 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Fri, 18 Apr 2025 03:58:39 +0800 Subject: [PATCH 15/16] Revert DTS renaming --- src/commons/workspace/{sharedb-ace.ts => sharedb-ace.d.ts} | 0 src/i18n/{i18next.ts => i18next.d.ts} | 0 src/{react-app-env.ts => react-app-env.d.ts} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/commons/workspace/{sharedb-ace.ts => sharedb-ace.d.ts} (100%) rename src/i18n/{i18next.ts => i18next.d.ts} (100%) rename src/{react-app-env.ts => react-app-env.d.ts} (100%) diff --git a/src/commons/workspace/sharedb-ace.ts b/src/commons/workspace/sharedb-ace.d.ts similarity index 100% rename from src/commons/workspace/sharedb-ace.ts rename to src/commons/workspace/sharedb-ace.d.ts diff --git a/src/i18n/i18next.ts b/src/i18n/i18next.d.ts similarity index 100% rename from src/i18n/i18next.ts rename to src/i18n/i18next.d.ts diff --git a/src/react-app-env.ts b/src/react-app-env.d.ts similarity index 100% rename from src/react-app-env.ts rename to src/react-app-env.d.ts From 7c8ac89a18a28081af66885afb9a3ea16396f36d Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Fri, 18 Apr 2025 03:59:48 +0800 Subject: [PATCH 16/16] Revert i18n change --- src/i18n/i18next.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/i18next.d.ts b/src/i18n/i18next.d.ts index 7f5741615e..dbf6283bcb 100644 --- a/src/i18n/i18next.d.ts +++ b/src/i18n/i18next.d.ts @@ -1,7 +1,8 @@ import 'i18next'; // import all namespaces (for the default language, only) -// import commons from 'locales/en/commons.json'; +import commons from 'locales/en/commons.json'; + import { defaultLanguage, i18nLanguageCode } from './locales'; export type i18nDefaultLangKeys = (typeof defaultLanguage)[i18nLanguageCode.DEFAULT];