diff options
-rw-r--r-- | package-lock.json | 130 | ||||
-rw-r--r-- | src/client/util/BranchingTrailManager.tsx | 137 | ||||
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 26 | ||||
-rw-r--r-- | src/client/util/DragManager.ts | 58 | ||||
-rw-r--r-- | src/client/views/Main.tsx | 2 | ||||
-rw-r--r-- | src/client/views/OverlayView.tsx | 1 | ||||
-rw-r--r-- | src/client/views/StyleProvider.tsx | 4 | ||||
-rw-r--r-- | src/client/views/UndoStack.tsx | 6 | ||||
-rw-r--r-- | src/client/views/collections/TreeView.scss | 19 | ||||
-rw-r--r-- | src/client/views/collections/TreeView.tsx | 65 | ||||
-rw-r--r-- | src/client/views/collections/collectionLinear/CollectionLinearView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/FontIconBox/FontIconBox.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/RecordingBox/RecordingBox.tsx | 205 | ||||
-rw-r--r-- | src/client/views/nodes/RecordingBox/RecordingView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 5 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 45 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresElementBox.scss | 4 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresElementBox.tsx | 85 |
18 files changed, 702 insertions, 102 deletions
diff --git a/package-lock.json b/package-lock.json index 0199d3d31..c3190ddb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7094,12 +7094,97 @@ "strip-ansi": "^7.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "strip-ansi": { "version": "7.1.0", "bundled": true, "requires": { "ansi-regex": "^6.0.1" } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + } + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } } } }, @@ -8604,7 +8689,7 @@ } }, "string-width-cjs": { - "version": "npm:string-width@4.2.3", + "version": "npm:string-width-cjs@4.2.3", "bundled": true, "requires": { "emoji-regex": "^8.0.0", @@ -8627,7 +8712,7 @@ } }, "strip-ansi-cjs": { - "version": "npm:strip-ansi@6.0.1", + "version": "npm:strip-ansi-cjs@6.0.1", "bundled": true, "requires": { "ansi-regex": "^5.0.1" @@ -8786,7 +8871,7 @@ } }, "wrap-ansi-cjs": { - "version": "npm:wrap-ansi@7.0.0", + "version": "npm:wrap-ansi-cjs@7.0.0", "bundled": true, "requires": { "ansi-styles": "^4.0.0", @@ -10360,16 +10445,6 @@ "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", "dev": true }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "d3": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz", @@ -11750,28 +11825,6 @@ "is-symbol": "^1.0.2" } }, - "es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "dev": true, - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, "es6-promise": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", @@ -11783,7 +11836,6 @@ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "dev": true, "requires": { - "d": "^1.0.1", "ext": "^1.1.2" } }, @@ -27596,12 +27648,6 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", - "dev": true - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/src/client/util/BranchingTrailManager.tsx b/src/client/util/BranchingTrailManager.tsx new file mode 100644 index 000000000..a224b84f4 --- /dev/null +++ b/src/client/util/BranchingTrailManager.tsx @@ -0,0 +1,137 @@ +import { action, computed, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { Doc } from '../../fields/Doc'; +import { Id } from '../../fields/FieldSymbols'; +import { PresBox } from '../views/nodes/trails'; +import { OverlayView } from '../views/OverlayView'; +import { DocumentManager } from './DocumentManager'; +import { Docs } from '../documents/Documents'; +import { nullAudio } from '../../fields/URLField'; + +@observer +export class BranchingTrailManager extends React.Component { + public static Instance: BranchingTrailManager; + + constructor(props: any) { + super(props); + if (!BranchingTrailManager.Instance) { + BranchingTrailManager.Instance = this; + } + } + + setupUi = () => { + OverlayView.Instance.addWindow(<BranchingTrailManager></BranchingTrailManager>, { x: 100, y: 150, width: 1000, title: 'Branching Trail'}); + // OverlayView.Instance.forceUpdate(); + console.log(OverlayView.Instance); + // let hi = Docs.Create.TextDocument("beee", { + // x: 100, + // y: 100, + // }) + // hi.overlayX = 100; + // hi.overlayY = 100; + + // Doc.AddToMyOverlay(hi); + console.log(DocumentManager._overlayViews); + }; + + + // stack of the history + @observable private slideHistoryStack: String[] = []; + @action setSlideHistoryStack = action((newArr: String[]) => { + this.slideHistoryStack = newArr; + }); + + @observable private containsSet: Set<String> = new Set<String>(); + + // prev pres to copmare with + @observable private prevPresId: String | null = null; + @action setPrevPres = action((newId: String | null) => { + this.prevPresId = newId; + }); + + // docId to Doc map + @observable private docIdToDocMap: Map<String, Doc> = new Map<String, Doc>(); + + observeDocumentChange = (targetDoc: Doc, pres: PresBox) => { + const presId = pres.props.Document[Id]; + if (this.prevPresId === presId) { + return; + } + + const targetDocId = targetDoc[Id]; + this.docIdToDocMap.set(targetDocId, targetDoc); + + if (this.prevPresId === null) { + this.setupUi(); + } + + if (this.prevPresId === null || this.prevPresId !== presId) { + Doc.UserDoc().isBranchingMode = true; + this.setPrevPres(presId); + + // REVERT THE SET + const stringified = [presId, targetDocId].toString(); + if (this.containsSet.has([presId, targetDocId].toString())) { + // remove all the elements after the targetDocId + const newStack = this.slideHistoryStack.slice(0, this.slideHistoryStack.indexOf(stringified)); + const removed = this.slideHistoryStack.slice(this.slideHistoryStack.indexOf(stringified)); + this.setSlideHistoryStack(newStack); + + removed.forEach(info => this.containsSet.delete(info.toString())); + } else { + this.setSlideHistoryStack([...this.slideHistoryStack, stringified]); + this.containsSet.add(stringified); + } + } + console.log(this.slideHistoryStack.length); + if (this.slideHistoryStack.length === 0) { + Doc.UserDoc().isBranchingMode = false; + } + }; + + clickHandler = (e: React.PointerEvent<HTMLButtonElement>, targetDocId: string, removeIndex: number) => { + const targetDoc = this.docIdToDocMap.get(targetDocId); + if (!targetDoc) { + return; + } + + const newStack = this.slideHistoryStack.slice(0, removeIndex); + const removed = this.slideHistoryStack.slice(removeIndex); + + this.setSlideHistoryStack(newStack); + + removed.forEach(info => this.containsSet.delete(info.toString())); + DocumentManager.Instance.showDocument(targetDoc, { willZoomCentered: true }); + if (this.slideHistoryStack.length === 0) { + Doc.UserDoc().isBranchingMode = false; + } + //PresBox.NavigateToTarget(targetDoc, targetDoc); + }; + + @computed get trailBreadcrumbs() { + return ( + <div style={{ border: '.5rem solid green', padding: '5px', backgroundColor: 'white', minHeight: '50px', minWidth: '1000px' }}> + {this.slideHistoryStack.map((info, index) => { + const [presId, targetDocId] = info.split(','); + const doc = this.docIdToDocMap.get(targetDocId); + if (!doc) { + return <></>; + } + return ( + <span key={targetDocId}> + <button key={index} onPointerDown={e => this.clickHandler(e, targetDocId, index)}> + {presId.slice(0, 3) + ':' + doc.title} + </button> + -{'>'} + </span> + ); + })} + </div> + ); + } + + render() { + return <div>{BranchingTrailManager.Instance.trailBreadcrumbs}</div>; + } +} diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index d52e389d6..873361587 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -20,7 +20,6 @@ import { DashboardView } from "../views/DashboardView"; import { Colors } from "../views/global/globalEnums"; import { OpenWhere } from "../views/nodes/DocumentView"; import { ButtonType } from "../views/nodes/FontIconBox/FontIconBox"; -import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox"; import { OverlayView } from "../views/OverlayView"; import { DragManager, dropActionType } from "./DragManager"; import { MakeTemplate } from "./DropConverter"; @@ -29,6 +28,7 @@ import { LinkManager } from "./LinkManager"; import { ScriptingGlobals } from "./ScriptingGlobals"; import { ColorScheme, SettingsManager } from "./SettingsManager"; import { UndoManager } from "./UndoManager"; +import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox"; interface Button { // DocumentOptions fields a button can set @@ -607,6 +607,7 @@ export class CurrentUserUtils { { scripts: { }, opts: { title: "undoStack", layout: "<UndoStack>", toolTip: "Undo/Redo Stack"}}, // note: layout fields are hacks -- they don't actually run through the JSX parser (yet) { scripts: { }, opts: { title: "linker", layout: "<LinkingUI>", toolTip: "link started"}}, { scripts: { }, opts: { title: "currently playing", layout: "<CurrentlyPlayingUI>", toolTip: "currently playing media"}}, + { scripts: { }, opts: { title: "Branching", layout: "<Branching>", toolTip: "Branch, baby!"}} ]; const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', undoIgnoreFields: new List<string>(['opacity']), _dragOnlyWithinContainer: true, ...desc.opts}, desc.scripts)); const dockBtnsReqdOpts:DocumentOptions = { @@ -714,7 +715,14 @@ export class CurrentUserUtils { { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected { title: "Schema", icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected - { title: "Audio", icon: "microphone", toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'}, funcs: { }} + { title: "Video", icon: 'video', toolTip: "Dictate", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `getIsRecording()`}, ignoreClick: true, scripts: { onClick: 'return toggleRecording(_readOnly_)'}}, + { title: "StopRec", icon: "stop", toolTip: "Stop", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecording()`}, ignoreClick: true, scripts: { onClick: `return toggleRecording(_readOnly_)`}}, + { title: "Dropdown", toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList, expertMode: false, funcs: {hidden: `renderDropdown()`, btnList: `getWorkspaceRecordings()`}, ignoreClick: true, scripts: { script: `toggleRecPlayback(value)`}}, + { title: "Play Rec",icon: "play", toolTip: "Play recording", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsWorkspaceRecPlaying()`}, ignoreClick: true, scripts: { onClick: `return playWorkspaceRec(getCurrentRecording())`}}, + { title: "Pause Rec",icon: "pause", toolTip: "Pause recording", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsWorkspaceRecPaused()`}, ignoreClick: true, scripts: { onClick: `return pauseWorkspaceRec(getCurrentRecording())`}}, + { title: "Stop Rec", icon: "stop", toolTip: "Stop recording", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecPlayback()`}, ignoreClick: true, scripts: { onClick: `return closeWorkspaceRec(getCurrentRecording())`}}, + { title: "Add doc", icon: "down", toolTip: "add to doc", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecPlayback()`}, ignoreClick: true, scripts: { onClick: `addRectoWorkspace(getCurrentRecording())`}}, + { title: "Delete Rec", icon: "trash", toolTip: "delete selected recording", btnType: ButtonType.ToggleButton, expertMode: false, funcs: {hidden: `!getIsRecPlayback()`}, ignoreClick: true, scripts: { onClick: `removeWorkspaceRec(getCurrentRecording())`}} ]; } @@ -734,9 +742,21 @@ export class CurrentUserUtils { return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdOpts) ?? Docs.Create.FontIconDocument(reqdOpts), params.scripts, reqdFuncs); } + static setupContextMenuBtn(params:Button, menuDoc:Doc):Doc { const menuBtnDoc = DocListCast(menuDoc?.data).find(doc => doc.title === params.title); const subMenu = params.subMenu; + // Doc.UserDoc().workspaceRecordings = new List<Doc>; + if (Doc.UserDoc().currentRecording) { + //@ts-ignore + Doc.RemFromMyOverlay(Doc.UserDoc().currentRecording); + } + Doc.UserDoc().isRecording = false; + Doc.UserDoc().isRecPlayback = false; + Doc.UserDoc().currentRecording = undefined; + Doc.UserDoc().isPlaybackPlaying = false; + Doc.UserDoc().isWorkspaceRecPlaying = false; + Doc.UserDoc().isWorkspaceRecPaused = false; if (!subMenu) { // button does not have a sub menu return this.setupContextMenuButton(params, menuBtnDoc); } @@ -995,4 +1015,4 @@ ScriptingGlobals.add(function MySharedDocs() { return Doc.MySharedDocs; }, "docu ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode"); ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); -ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; });
\ No newline at end of file +ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 831a22866..5a11f2dca 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -4,7 +4,7 @@ import { Doc, Field, Opt, StrListCast } from '../../fields/Doc'; import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; import { ScriptField } from '../../fields/ScriptField'; -import { BoolCast, ScriptCast, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, ScriptCast, StrCast } from '../../fields/Types'; import { emptyFunction, Utils } from '../../Utils'; import { Docs, DocUtils } from '../documents/Documents'; import * as globalCssVariables from '../views/global/globalCssVariables.scss'; @@ -14,6 +14,7 @@ import { ScriptingGlobals } from './ScriptingGlobals'; import { SelectionManager } from './SelectionManager'; import { SnappingManager } from './SnappingManager'; import { UndoManager } from './UndoManager'; +import { listSpec } from '../../fields/Schema'; export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove @@ -235,6 +236,61 @@ export namespace DragManager { return true; } + export function StartDropdownDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, recordingIndex: number, options?: DragOptions, dropEvent?: () => any,) { + + const addAudioTag = (dropDoc: any) => { + dropDoc && !dropDoc.author_date && (dropDoc.author_date = new DateField()); + dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc); + return dropDoc; + }; + const finishDrag = async (e: DragCompleteEvent) => { + Doc.UserDoc().isAddRecToDocMode = false; + Doc.RemFromMyOverlay(Doc.UserDoc().currentRecording); + Doc.UserDoc().currentRecording = undefined; + Doc.UserDoc().isRecPlayback = false; + Doc.UserDoc().isWorkspaceRecPlaying = false; + Doc.UserDoc().isWorkspaceRecPaused = false; + Doc.UserDoc().isAddRecToDocMode = false; + Cast(Doc.UserDoc().workspaceRecordings, listSpec(Doc), null)?.splice(recordingIndex, 1); + const docDragData = e.docDragData; + dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails + if (docDragData && !docDragData.droppedDocuments.length) { + docDragData.dropAction = dragData.userDropAction || dragData.dropAction; + docDragData.droppedDocuments = ( + await Promise.all( + dragData.draggedDocuments.map(async d => + !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart) + ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) + : docDragData.dropAction === 'embed' + ? Doc.BestEmbedding(d) + : docDragData.dropAction === 'add' + ? d + : docDragData.dropAction === 'proto' + ? Doc.GetProto(d) + : docDragData.dropAction === 'copy' + ? ( + await Doc.MakeClone(d) + ).clone + : d + ) + ) + ).filter(d => d); + !['same', 'proto'].includes(docDragData.dropAction as any) && + docDragData.droppedDocuments + // .filter(drop => !drop.dragOnlyWithinContainer || ['embed', 'copy'].includes(docDragData.dropAction as any)) + .forEach((drop: Doc, i: number) => { + const dragProps = StrListCast(dragData.draggedDocuments[i].dropPropertiesToRemove); + const remProps = (dragData?.dropPropertiesToRemove || []).concat(Array.from(dragProps)); + [...remProps, 'dropPropertiesToRemove'].map(prop => (drop[prop] = undefined)); + }); + } + return e; + }; + dragData.draggedDocuments.map(d => d.dragFactory); // does this help? trying to make sure the dragFactory Doc is loaded + StartDrag(eles, dragData, downX, downY, options, finishDrag); + return true; + } + // drag a button template and drop a new button export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) { const finishDrag = (e: DragCompleteEvent) => { diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index a04309d0c..96bd52d39 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -14,6 +14,7 @@ import { TrackMovements } from '../util/TrackMovements'; import { CollectionView } from './collections/CollectionView'; import './global/globalScripts'; import { MainView } from './MainView'; +import { BranchingTrailManager } from '../util/BranchingTrailManager'; dotenv.config(); AssignAllExtensions(); @@ -50,6 +51,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' }; document.cookie = `loadtime=${loading};${expires};path=/`; new TrackMovements(); new ReplayMovements(); + new BranchingTrailManager({}); new PingManager(); root.render(<MainView />); }, 0); diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 62aa4d1d4..7d65914b3 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -145,6 +145,7 @@ export class OverlayView extends React.Component { @action addWindow(contents: JSX.Element, options: OverlayElementOptions): OverlayDisposer { + console.log("adding window"); const remove = action(() => { const index = this._elements.indexOf(contents); if (index !== -1) this._elements.splice(index, 1); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 12935400b..a9e4ac17d 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -237,12 +237,12 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps switch (doc?.type) { case DocumentType.COL: return StrCast( - doc?.layout_borderRounding, + doc?.layout_boxShadow, doc?._type_collection === CollectionViewType.Pile ? '4px 4px 10px 2px' : lockedPosition() || doc?._isGroup || docProps?.LayoutTemplateString ? undefined // groups have no drop shadow -- they're supposed to be "invisible". LayoutString's imply collection is being rendered as something else (e.g., title of a Slide) - : `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.layout_borderRounding, '0.2vw 0.2vw 0.8vw')}` + : `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.layout_boxShadow, '0.2vw 0.2vw 0.8vw')}` ); case DocumentType.LABEL: diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx index 8f3ae8e87..cdc389efe 100644 --- a/src/client/views/UndoStack.tsx +++ b/src/client/views/UndoStack.tsx @@ -39,15 +39,15 @@ export class UndoStack extends React.Component<UndoStackProps> { }}> {UndoManager.undoStackNames.map((name, i) => ( <div className="undoStack-resultContainer" key={i}> - <div className="undoStack-commandString">{name.replace(/[^\.]*\./, '')}</div> + <div className="undoStack-commandString">{StrCast(name).replace(/[^\.]*\./, '')}</div> </div> ))} {Array.from(UndoManager.redoStackNames) .reverse() .map((name, i) => ( <div className="undoStack-resultContainer" key={i}> - <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: SettingsManager.userBackgroundColor }}> - {name.replace(/[^\.]*\./, '')} + <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: 'red' }}> + {StrCast(name).replace(/[^\.]*\./, '')} </div> </div> ))} diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss index d22e85880..d3ba23b4e 100644 --- a/src/client/views/collections/TreeView.scss +++ b/src/client/views/collections/TreeView.scss @@ -23,6 +23,25 @@ .treeView-bulletIcons { width: 100%; height: 100%; + // position: absolute; + + .treeView-expandIcon { + display: none; + left: -8px; + position: absolute; + } + + .treeView-checkIcon { + left: 3.5px; + top: 2px; + position: absolute; + } + + &:hover { + .treeView-expandIcon { + display: unset; + } + } position: relative; display: flex; flex-direction: row; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 701150769..4f9310434 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc'; +import { Doc, DocListCast, Field, FieldResult, Opt, StrListCast } from '../../../fields/Doc'; import { DocData, Height, Width } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; @@ -222,6 +222,39 @@ export class TreeView extends React.Component<TreeViewProps> { } }; + @undoBatch + @action + recurToggle = (childList: Doc[]) => { + if (childList.length > 0) { + childList.forEach(child => { + console.log(child); + child.runProcess = !!!child.runProcess; + TreeView.ToggleChildrenRun.get(child)?.(); + }); + } + }; + + @undoBatch + @action + getRunningChildren = (childList: Doc[]) => { + if (childList.length === 0) { + return []; + } + + const runningChildren: FieldResult[] = []; + childList.forEach(child => { + if (child.runProcess && TreeView.GetRunningChildren.get(child)) { + if (child.runProcess) { + runningChildren.push(child); + } + runningChildren.push(...(TreeView.GetRunningChildren.get(child)?.() ?? [])); + } + }); + return runningChildren; + }; + + static GetRunningChildren = new Map<Doc, any>(); + static ToggleChildrenRun = new Map<Doc, () => void>(); constructor(props: any) { super(props); if (!TreeView._openLevelScript) { @@ -230,6 +263,19 @@ export class TreeView extends React.Component<TreeViewProps> { } this._openScript = Doc.IsSystem(this.props.document) ? undefined : () => TreeView._openLevelScript!; this._editTitleScript = Doc.IsSystem(this.props.document) ? () => TreeView._openLevelScript! : () => TreeView._openTitleScript!; + + // set for child processing highligting + // this.dataDoc.testing = 'testing'; + console.log(this.doc, this.dataDoc, this.childDocs); + this.dataDoc.hasChildren = this.childDocs.length > 0; + // this.dataDoc.children = this.childDocs; + TreeView.ToggleChildrenRun.set(this.doc, () => { + this.recurToggle(this.childDocs); + }); + + TreeView.GetRunningChildren.set(this.doc, () => { + return this.getRunningChildren(this.childDocs); + }); } _treeEle: any; @@ -718,15 +764,14 @@ export class TreeView extends React.Component<TreeViewProps> { ) ) : ( <div className="treeView-bulletIcons" style={{ color: Doc.IsSystem(DocCast(this.doc.proto)) ? 'red' : undefined }}> - {this.onCheckedClick ? ( - <IconButton - color={color} - icon={<FontAwesomeIcon size="sm" icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} />} - size={Size.XSMALL} + <div className={`treeView-${this.onCheckedClick ? 'checkIcon' : 'expandIcon'}`}> + <FontAwesomeIcon + size="sm" + style={{ display: this.childDocs?.length >= 1 ? 'block' : 'none' }} + icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} /> - ) : ( - <IconButton color={color} icon={<FontAwesomeIcon icon={iconType as IconProp} />} size={Size.XSMALL} /> - )} + </div> + {this.onCheckedClick ? null : typeof iconType === 'string' ? <FontAwesomeIcon icon={iconType as IconProp} /> : iconType} </div> )} </div> @@ -924,8 +969,8 @@ export class TreeView extends React.Component<TreeViewProps> { } })} Document={this.doc} + DataDoc={undefined} // or this.dataDoc? layout_fitWidth={returnTrue} - DataDoc={undefined} scriptContext={this} hideDecorationTitle={this.props.treeView.outlineMode} hideResizeHandles={this.props.treeView.outlineMode} diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 4bb5c5adf..0854bc611 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -22,6 +22,7 @@ import { CollectionSubView } from '../CollectionSubView'; import './CollectionLinearView.scss'; import { Button, Toggle, ToggleType, Type } from 'browndash-components'; import { Colors } from '../../global/globalEnums'; +import { BranchingTrailManager } from '../../../util/BranchingTrailManager'; import { SettingsManager } from '../../../util/SettingsManager'; /** @@ -146,6 +147,7 @@ export class CollectionLinearView extends CollectionSubView() { case '<LinkingUI>': return this.getLinkUI(); case '<CurrentlyPlayingUI>': return this.getCurrentlyPlayingUI(); case '<UndoStack>': return <UndoStack key={doc[Id]} width={200} height={40} inline={true} />; + case '<Branching>': return Doc.UserDoc().isBranchingMode ? <BranchingTrailManager /> : null; } const nested = doc._type_collection === CollectionViewType.Linear; @@ -208,7 +210,7 @@ export class CollectionLinearView extends CollectionSubView() { text={Cast(this.props.Document.icon, 'string', null)} icon={Cast(this.props.Document.icon, 'string', null) ? undefined : <FontAwesomeIcon color={SettingsManager.userColor} icon={isExpanded ? 'minus' : 'plus'} />} color={SettingsManager.userColor} - background={SettingsManager.userVariantColor} + // background={SettingsManager.userVariantColor} type={Type.TERT} onPointerDown={e => e.stopPropagation()} toggleType={ToggleType.BUTTON} diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index f286e5a42..1cf66fbdc 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -6,7 +6,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; import { ScriptField } from '../../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { Utils } from '../../../../Utils'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { SelectionManager } from '../../../util/SelectionManager'; @@ -235,7 +235,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { const list: IListItemProps[] = this.buttonList .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value)) .map(value => ({ - text: value.charAt(0).toUpperCase() + value.slice(1), + text: value === "string" ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title), val: value, style: getStyle(value), onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value), diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index 8fa2861b6..8a91b8102 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -1,4 +1,4 @@ -import { action, observable } from 'mobx'; +import { action, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { VideoField } from '../../../../fields/URLField'; @@ -9,12 +9,24 @@ import { VideoBox } from '../VideoBox'; import { RecordingView } from './RecordingView'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Presentation } from '../../../util/TrackMovements'; -import { Doc } from '../../../../fields/Doc'; +import { Doc, DocListCast } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; -import { BoolCast, DocCast } from '../../../../fields/Types'; +import { BoolCast, Cast, DocCast } from '../../../../fields/Types'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { DocumentManager } from '../../../util/DocumentManager'; import { Docs } from '../../../documents/Documents'; +import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; +import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; +import { DocumentView } from '../DocumentView'; +import { SettingsManager } from '../../../util/SettingsManager'; +import { PropertiesView } from '../../PropertiesView'; +import { PropertiesSection } from '../../PropertiesSection'; +import { PropertiesDocContextSelector } from '../../PropertiesDocContextSelector'; +import { listSpec } from '../../../../fields/Schema'; +import { DragManager } from '../../../util/DragManager'; +import { SelectionManager } from '../../../util/SelectionManager'; +import { AudioBox } from '../AudioBox'; +import { UndoManager, undoBatch } from '../../../util/UndoManager'; @observer export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() { @@ -55,6 +67,146 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() { } }; + /** + * This method toggles whether or not we are currently using the RecordingBox to record with the topbar button + * @param _readOnly_ + * @returns + */ + @undoBatch + @action + public static toggleWorkspaceRecording(_readOnly_: boolean) { + if (_readOnly_) return RecordingBox.screengrabber ? true : false; + if (RecordingBox.screengrabber) { + //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening + console.log('grabbing screen!') + RecordingBox.screengrabber.Pause?.(); + const remDoc = RecordingBox.screengrabber.rootDoc; + setTimeout(() => { + RecordingBox.screengrabber?.Finish?.(); + RecordingBox.screengrabber!.rootDoc.overlayX = 70; //was 100 + RecordingBox.screengrabber!.rootDoc.overlayY = 590; + console.log(RecordingBox.screengrabber) + RecordingBox.screengrabber = undefined; + }, 100); + //could break if recording takes too long to turn into videobox. If so, either increase time on setTimeout below or find diff place to do this + setTimeout(() => { + Doc.RemFromMyOverlay(remDoc); + + }, 1000) + Doc.UserDoc().isRecording = false; + Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.rootDoc); + } else { + //when we first press mic + const screengrabber = Docs.Create.WebCamDocument('', { + _width: 205, + _height: 115, + }); + screengrabber.overlayX = 70; //was -400 + screengrabber.overlayY = 590; //was 0 + screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; + Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay + DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => { + RecordingBox.screengrabber = docView.ComponentView as RecordingBox; + RecordingBox.screengrabber.Record?.(); + }); + Doc.UserDoc().isRecording = true; + } + } + + /** + * This method changes the menu depending on whether or not we are in playback mode + * @param value RecordingBox rootdoc + */ + @undoBatch + @action + public static toggleWorkspaceRecPlayback(value: Doc) { + let docval = undefined; + Doc.UserDoc().currentRecording = value; + console.log(value) + value.overlayX =70; + value.overlayY = 590; + if (!Doc.UserDoc().isAddRecToDocMode) { + Doc.UserDoc().isRecPlayback = true; + Doc.UserDoc().isWorkspaceRecPlaying = true; + Doc.AddToMyOverlay(value); + DocumentManager.Instance.AddViewRenderedCb(value, docView => { + Doc.UserDoc().currentRecording = docView.ComponentView as RecordingBox; + SelectionManager.SelectSchemaViewDoc(value); + })} else { + let recordingIndex = Array.from(Doc.UserDoc().workspaceRecordings).indexOf(value); + DragManager.StartDropdownDrag([document.createElement('div')],new DragManager.DocumentDragData([value]), 1, 1, recordingIndex); + } + } + + /** + * Adds the recording box to the canvas + * @param value current recordingbox + */ + @undoBatch + @action + public static addRecToWorkspace(value: RecordingBox) { + console.log("adding rec to doc"); + console.log(value); + let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView); + (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value.rootDoc); + let recordingIndex = Array.from(Doc.UserDoc().workspaceRecordings).indexOf(value.rootDoc); + console.log(recordingIndex); + Cast(Doc.UserDoc().workspaceRecordings, listSpec(Doc), null)?.splice(recordingIndex, 1); + Doc.UserDoc().isAddRecToDocMode = false; + Doc.RemFromMyOverlay(value.rootDoc); + Doc.UserDoc().currentRecording = undefined; + Doc.UserDoc().isRecPlayback = false; + Doc.UserDoc().isAddRecToDocMode = false; + Doc.UserDoc().isWorkspaceRecPlaying = false; + Doc.UserDoc().isWorkspaceRecPaused = false; + } + + @undoBatch + @action + public static playWorkspaceRec(value: VideoBox) { + value.Play(); + Doc.UserDoc().isWorkspaceRecPlaying = false; + Doc.UserDoc().isWorkspaceRecPaused = true; + } + + @undoBatch + @action + public static pauseWorkspaceRec(value: VideoBox) { + value.Pause(); + Doc.UserDoc().isWorkspaceRecPlaying = true; + Doc.UserDoc().isWorkspaceRecPaused = false; + } + + @undoBatch + @action + public static closeWorkspaceRec(value: VideoBox) { + value.Pause(); + Doc.RemFromMyOverlay(value.rootDoc); + Doc.UserDoc().currentRecording = undefined; + Doc.UserDoc().isRecPlayback = false; + Doc.UserDoc().isWorkspaceRecPlaying = false; + Doc.UserDoc().isWorkspaceRecPaused = false; + Doc.RemFromMyOverlay(value.rootDoc); + + } + + @undoBatch + @action + public static removeWorkspaceRec(value: VideoBox) { + let recordingIndex = Array.from(Doc.UserDoc().workspaceRecordings).indexOf(value.rootDoc); + Cast(Doc.UserDoc().workspaceRecordings, listSpec(Doc), null)?.splice(recordingIndex, 1); + Doc.UserDoc().isAddRecToDocMode = false; + Doc.RemFromMyOverlay(value.rootDoc); + Doc.UserDoc().currentRecording = undefined; + Doc.UserDoc().isRecPlayback = false; + Doc.UserDoc().isAddRecToDocMode = false; + Doc.UserDoc().isWorkspaceRecPlaying = false; + Doc.UserDoc().isWorkspaceRecPaused = false; + + } + + + Record: undefined | (() => void); Pause: undefined | (() => void); Finish: undefined | (() => void); @@ -81,28 +233,27 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() { } static screengrabber: RecordingBox | undefined; } -ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { - if (_readOnly_) return RecordingBox.screengrabber ? true : false; - if (RecordingBox.screengrabber) { - RecordingBox.screengrabber.Pause?.(); - setTimeout(() => { - RecordingBox.screengrabber?.Finish?.(); - RecordingBox.screengrabber!.rootDoc.overlayX = 100; - RecordingBox.screengrabber!.rootDoc.overlayY = 100; - RecordingBox.screengrabber = undefined; - }, 100); - } else { - const screengrabber = Docs.Create.WebCamDocument('', { - _width: 384, - _height: 216, - }); - screengrabber.overlayX = -400; - screengrabber.overlayY = 0; - screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; - Doc.AddToMyOverlay(screengrabber); - DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => { - RecordingBox.screengrabber = docView.ComponentView as RecordingBox; - RecordingBox.screengrabber.Record?.(); - }); + +ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { RecordingBox.toggleWorkspaceRecording(_readOnly_); }); +ScriptingGlobals.add(function toggleRecPlayback(value: Doc) { RecordingBox.toggleWorkspaceRecPlayback(value); }); +ScriptingGlobals.add(function addRectoWorkspace(value: RecordingBox) { RecordingBox.addRecToWorkspace(value); }); + +ScriptingGlobals.add(function playWorkspaceRec(value: VideoBox) { RecordingBox.playWorkspaceRec(value); }); +ScriptingGlobals.add(function pauseWorkspaceRec(value: VideoBox) { RecordingBox.pauseWorkspaceRec(value); }); +ScriptingGlobals.add(function closeWorkspaceRec(value: VideoBox) { RecordingBox.closeWorkspaceRec(value); }); +ScriptingGlobals.add(function removeWorkspaceRec(value: VideoBox) { RecordingBox.removeWorkspaceRec(value) }); + +ScriptingGlobals.add(function getWorkspaceRecordings() { return Doc.UserDoc().workspaceRecordings }); +ScriptingGlobals.add(function getIsRecording() { return Doc.UserDoc().isRecording; }) +ScriptingGlobals.add(function getIsRecPlayback() { return Doc.UserDoc().isRecPlayback; }) +ScriptingGlobals.add(function getCurrentRecording() { return Doc.UserDoc().currentRecording; }) +ScriptingGlobals.add(function getIsWorkspaceRecPlaying() { return Doc.UserDoc().isWorkspaceRecPlaying; }) +ScriptingGlobals.add(function getIsWorkspaceRecPaused() { return Doc.UserDoc().isWorkspaceRecPaused; }) +ScriptingGlobals.add(function renderDropdown() { + if (!Doc.UserDoc().workspaceRecordings || Array.from(Doc.UserDoc().workspaceRecordings).length === 0) { + return true; } -}, 'toggle recording'); + return false; +}) + + diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index 0e386b093..755f1adc0 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -9,6 +9,7 @@ import { Networking } from '../../../Network'; import { Presentation, TrackMovements } from '../../../util/TrackMovements'; import { ProgressBar } from './ProgressBar'; import './RecordingView.scss'; +import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; export interface MediaSegment { videoChunks: any[]; @@ -163,8 +164,9 @@ export function RecordingView(props: IRecordingViewProps) { // if this is called, then we're done recording all the segments const finish = () => { // call stop on the video recorder if active + console.log(videoRecorder.current?.state); videoRecorder.current?.state !== 'inactive' && videoRecorder.current?.stop(); - + console.log("this it") // end the streams (audio/video) to remove recording icon const stream = videoElementRef.current!.srcObject; stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop()); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 48716b867..5c2d09b0c 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -191,7 +191,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp this._finished = false; start = this.timeline.trimStart; } - + if (this.dataDoc._data_presentation) { + console.log("ninja") + } + console.log(this.dataDoc.data_presentation) try { this._audioPlayer && this.player && (this._audioPlayer.currentTime = this.player?.currentTime); update && this.player && this.playFrom(start, undefined, true); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index f750392f5..b0101d9a7 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -36,6 +36,9 @@ import { FieldView, FieldViewProps } from '../FieldView'; import { ScriptingBox } from '../ScriptingBox'; import './PresBox.scss'; import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums'; +import { BranchingTrailManager } from '../../../util/BranchingTrailManager'; +import { TreeView } from '../../collections/TreeView'; +import { OverlayView } from '../../OverlayView'; const { Howl } = require('howler'); export interface pinDataTypes { @@ -280,9 +283,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { return listItems.filter(doc => !doc.layout_unrendered); } }; + + // go to documents chain + runSubroutines = (childrenToRun: Doc[], normallyNextSlide: Doc) => { + console.log(childrenToRun, normallyNextSlide, 'runSUBFUNC'); + if (childrenToRun === undefined) { + console.log('children undefined'); + return; + } + if (childrenToRun[0] === normallyNextSlide) { + return; + } + + childrenToRun.forEach(child => { + DocumentManager.Instance.showDocument(child, {}); + }); + }; + // Called when the user activates 'next' - to move to the next part of the pres. trail @action next = () => { + console.log("next"); const progressiveReveal = (first: boolean) => { const presIndexed = Cast(this.activeItem?.presentation_indexed, 'number', null); if (presIndexed !== undefined) { @@ -320,6 +341,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { // Case 1: No more frames in current doc and next slide is defined, therefore move to next slide const slides = DocListCast(this.rootDoc[StrCast(this.presFieldKey, 'data')]); const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex; + + // before moving onto next slide, run the subroutines :) + const currentDoc = this.childDocs[this.itemIndex]; + //could i do this.childDocs[this.itemIndex] for first arg? + this.runSubroutines(TreeView.GetRunningChildren.get(currentDoc)?.() , this.childDocs[this.itemIndex + 1]); + this.nextSlide(curLast + 1 === this.childDocs.length ? (this.layoutDoc.presLoop ? 0 : curLast) : curLast + 1); progressiveReveal(true); // shows first progressive document, but without a transition effect } else { @@ -359,6 +386,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { //it'll also execute the necessary actions if presentation is playing. @undoBatch public gotoDocument = action((index: number, from?: Doc, group?: boolean, finished?: () => void) => { + console.log("going to document"); Doc.UnBrushAllDocs(); if (index >= 0 && index < this.childDocs.length) { this.rootDoc._itemIndex = index; @@ -711,6 +739,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { pinDoc.config_viewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]); } } + + @action + static reversePin(pinDoc: Doc, targetDoc: Doc) { + // const fkey = Doc.LayoutFieldKey(targetDoc); + pinDoc.config_data = targetDoc.data; + + console.log(pinDoc.presData); + } + /** * This method makes sure that cursor navigates to the element that * has the option open and last in the group. @@ -722,6 +759,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { navigateToActiveItem = (afterNav?: () => void) => { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; + // BranchingTrailManager.Instance.observeDocumentChange(targetDoc, this); const finished = () => { afterNav?.(); console.log('Finish Slide Nav: ' + targetDoc.title); @@ -1111,13 +1149,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { //Regular click @action selectElement = (doc: Doc, noNav = false) => { + // BranchingTrailManager.Instance.observeDocumentChange(doc, this); CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => clip?.ComponentView?.Pause?.()); if (noNav) { const index = this.childDocs.indexOf(doc); if (index >= 0 && index < this.childDocs.length) { this.rootDoc._itemIndex = index; } - } else this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem); + console.log("no nav") + } else { + this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem); + console.log('e bitch') + } this.updateCurrentPresentation(DocCast(doc.embedContainer)); }; diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss index 4f95f0c1f..9ac2b5a94 100644 --- a/src/client/views/nodes/trails/PresElementBox.scss +++ b/src/client/views/nodes/trails/PresElementBox.scss @@ -4,6 +4,10 @@ $light-background: #ececec; $slide-background: #d5dce2; $slide-active: #5b9fdd; +.testingv2 { + background-color: red; +} + .presItem-container { cursor: grab; display: flex; diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 529a5024f..d90f96249 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -25,6 +25,9 @@ import { PresBox } from './PresBox'; import './PresElementBox.scss'; import { PresMovement } from './PresEnums'; import React = require('react'); +import { TreeView } from '../../collections/TreeView'; +import { BranchingTrailManager } from '../../../util/BranchingTrailManager'; +import { MultiToggle, Type } from 'browndash-components'; /** * This class models the view a document added to presentation will have in the presentation. * It involves some functionality for its buttons and options. @@ -301,8 +304,28 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { activeItem.config_rotation = NumCast(targetDoc.rotation); activeItem.config_width = NumCast(targetDoc.width); activeItem.config_height = NumCast(targetDoc.height); - activeItem.config_pinLayout = true; + activeItem.config_pinLayout = !activeItem.config_pinLayout; + // activeItem.config_pinLayout = true; }; + + //wait i dont think i have to do anything here since by default it'll revert to the previously saved if I don't save + //so basically, don't have an onClick for this, just let it do nada for now + @undoBatch + @action + revertToPreviouslySaved = (presTargetDoc: Doc, activeItem: Doc) => { + console.log('reverting'); + // console.log("reverting to previosly saved\n"); + // console.log(this.prevTarget); + console.log("Content continuously updating"); + const target = DocCast(activeItem.annotationOn) ?? activeItem; + console.log(presTargetDoc.pinData) + PresBox.reversePin(activeItem, target); + // console.log("new target\n"); + // console.log(target); + // PresBox.pinDocView(activeItem, { pinData: PresBox.pinDataTypes(this.prevTarget) }, this.prevTarget ? this.prevTarget : target); + //figure out how to make it go back to the previously saved one + } + /** * Method called for updating the view of the currently selected document * @@ -365,7 +388,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { @undoBatch @action - startRecording = (e: React.MouseEvent, activeItem: Doc) => { + startRecording = (e: React.MouseEvent, activeItem: Doc) => { e.stopPropagation(); if (PresElementBox.videoIsRecorded(activeItem)) { // if we already have an existing recording @@ -399,6 +422,19 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { } }; + @undoBatch + @action + lfg = (e: React.MouseEvent) => { + e.stopPropagation(); + console.log('lfg called'); + // TODO: fix this bug + const { toggleChildrenRun } = this.rootDoc; + TreeView.ToggleChildrenRun.get(this.rootDoc)?.(); + + // call this.rootDoc.recurChildren() to get all the children + // if (iconClick) PresElementBox.showVideo = false; + }; + @computed get toolbarWidth(): number { const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox); @@ -414,8 +450,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false; const targetDoc: Doc = this.targetDoc; const activeItem: Doc = this.rootDoc; + const hasChildren: boolean = Cast(this.rootDoc?.hasChildren, 'boolean') || false; const items: JSX.Element[] = []; + items.push( <Tooltip key="slide" title={<div className="dash-tooltip">Update captured doc layout</div>}> <div @@ -426,16 +464,31 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { </div> </Tooltip> ); + // items.push( + // <Tooltip key="flex" title={<div className="dash-tooltip">Update captured doc content</div>}> + // <div + // className="slideButton" + // onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))} + // style={{ opacity: activeItem.config_pinData || activeItem.config_pinView ? 1 : 0.5, fontWeight: 700, display: 'flex' }}> + // C + // </div> + // </Tooltip> + // ); items.push( <Tooltip key="flex" title={<div className="dash-tooltip">Update captured doc content</div>}> <div - className="slideButton" - onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))} - style={{ opacity: activeItem.config_pinData || activeItem.config_pinView ? 1 : 0.5, fontWeight: 700, display: 'flex' }}> - C + className="slideButton" + style={{fontWeight: 700, display: 'flex'}} + > + <MultiToggle type={Type.PRIM} items={[ + {icon: <FontAwesomeIcon icon="rotate-left" color="white" size='sm'/>, tooltip: "Save data to presentation", val: 'revert', + onPointerDown: e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))}, + {icon: <FontAwesomeIcon icon="floppy-disk" color="white" size='sm'/>, tooltip: "Continously update content", val: "floppy-disk", + onPointerDown: e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.revertToPreviouslySaved(targetDoc, activeItem))}, + ]} /> </div> </Tooltip> - ); + ) items.push( <Tooltip key="slash" title={<div className="dash-tooltip">{this.recordingIsInOverlay ? 'Hide Recording' : `${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>}> <div className="slideButton" onClick={e => (this.recordingIsInOverlay ? this.hideRecording(e, true) : this.startRecording(e, activeItem))} style={{ fontWeight: 700 }}> @@ -486,6 +539,22 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { </div> </Tooltip> ); + if (!Doc.noviceMode && hasChildren) { + // TODO: replace with if treeveiw, has childrenDocs + items.push( + <Tooltip key="children" title={<div className="dash-tooltip">Run child processes (tree only)</div>}> + <div + className="slideButton" + onClick={e => { + e.stopPropagation(); + this.lfg(e); + }} + style={{ fontWeight: 700 }}> + <FontAwesomeIcon icon={'circle-play'} onPointerDown={e => e.stopPropagation()} /> + </div> + </Tooltip> + ); + } items.push( <Tooltip key="trash" title={<div className="dash-tooltip">Remove from presentation</div>}> <div className={'slideButton'} onClick={this.removePresentationItem}> @@ -532,7 +601,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { ) : ( <div ref={this._dragRef} - className={`presItem-slide ${isCurrent ? 'active' : ''}`} + className={`presItem-slide ${isCurrent ? 'active' : ''}${this.rootDoc.runProcess ? ' testingv2' : ''}`} style={{ display: 'infline-block', backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor), |