diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/client/util/PingManager.ts | 31 | ||||
| -rw-r--r-- | src/client/views/Main.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/MainView.tsx | 160 | ||||
| -rw-r--r-- | src/client/views/topbar/TopBar.tsx | 17 | ||||
| -rw-r--r-- | src/server/ApiManagers/UploadManager.ts | 10 |
5 files changed, 149 insertions, 71 deletions
diff --git a/src/client/util/PingManager.ts b/src/client/util/PingManager.ts new file mode 100644 index 000000000..7562faf03 --- /dev/null +++ b/src/client/util/PingManager.ts @@ -0,0 +1,31 @@ +import { action, IReactionDisposer, observable, observe, reaction } from "mobx"; +import { Networking } from "../Network"; +export class PingManager { + // create static instance and getter for global use + @observable static _instance: PingManager; + static get Instance(): PingManager { + return PingManager._instance; + } + + @observable isBeating: boolean = true; + private setIsBeating = action((status: boolean) => this.isBeating = status); + + ping = async (): Promise<void> => { + try { + const response = await Networking.PostToServer('/ping', { date: new Date() }); + // console.log('ping response', response, this.interval); + !this.isBeating && this.setIsBeating(true); + } catch { + console.error('ping error'); + this.isBeating && this.setIsBeating(false); + } + } + + // not used now, but may need to clear interval + private interval: NodeJS.Timeout | null = null; + INTERVAL_SECONDS = 1; + constructor() { + PingManager._instance = this; + this.interval = setInterval(this.ping, this.INTERVAL_SECONDS * 1000); + } +} diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 6b18caed0..b0b757388 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -15,6 +15,7 @@ import { TrackMovements } from '../util/TrackMovements'; import { CollectionView } from './collections/CollectionView'; import { MainView } from './MainView'; import * as dotenv from 'dotenv'; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import +import { PingManager } from '../util/PingManager'; dotenv.config(); AssignAllExtensions(); @@ -48,6 +49,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0 }; // bcz: not sure new LinkManager(); new TrackMovements(); new ReplayMovements(); + new PingManager(); root.render(<MainView />); }, 0); })(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index dbe8fb608..fbe44cf7a 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -66,6 +66,8 @@ import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider'; import { TopBar } from './topbar/TopBar'; +import 'browndash-components/dist/styles/global.min.css'; +import { PingManager } from '../util/PingManager'; const _global = (window /* browser */ || global) /* node */ as any; @observer @@ -143,75 +145,95 @@ export class MainView extends React.Component { componentDidMount() { document.getElementById('root')?.addEventListener('scroll', e => (ele => (ele.scrollLeft = ele.scrollTop = 0))(document.getElementById('root')!)); const ele = document.getElementById('loader'); - const prog = document.getElementById('dash-progress'); - if (ele && prog) { - // remove from DOM - setTimeout(() => { - prog.style.transition = '1s'; - prog.style.width = '100%'; - }, 0); - setTimeout(() => (ele.outerHTML = ''), 1000); - } - this._sidebarContent.proto = undefined; - if (!MainView.Live) { - DocServer.setPlaygroundFields([ - 'dataTransition', - 'viewTransition', - 'treeViewOpen', - 'layout_showSidebar', - 'carousel_index', - 'itemIndex', // for changing slides in presentations - 'layout_sidebarWidthPercent', - 'layout_currentTimecode', - 'layout_timelineHeightPercent', - 'presStatus', - 'freeform_panX', - 'freeform_panY', - 'overlayX', - 'overlayY', - 'layout_fitWidth', - 'nativeWidth', - 'nativeHeight', - 'text_scrollHeight', - 'text_height', - 'layout_hideMinimap', - 'freeform_scale', - 'layout_scrollTop', - 'hidden', - 'layout_curPage', - 'type_collection', - 'chromeHidden', - 'currentFrame', - 'width', - 'height', - 'nativeWidth', - ]); // can play with these fields on someone else's - } - DocServer.GetRefField('rtfProto').then( - proto => - proto instanceof Doc && - reaction( - () => StrCast(proto.BROADCAST_MESSAGE), - msg => msg && alert(msg) - ) - ); + console.log(PingManager.Instance); - const tag = document.createElement('script'); - tag.src = 'https://www.youtube.com/iframe_api'; - const firstScriptTag = document.getElementsByTagName('script')[0]; - firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag); - window.removeEventListener('keydown', KeyManager.Instance.handle); - window.addEventListener('keydown', KeyManager.Instance.handle); - window.removeEventListener('keyup', KeyManager.Instance.unhandle); - window.addEventListener('keyup', KeyManager.Instance.unhandle); - window.addEventListener('paste', KeyManager.Instance.paste as any); - document.addEventListener('dash', (e: any) => { - // event used by chrome plugin to tell Dash which document to focus on - const id = FormattedTextBox.GetDocFromUrl(e.detail); - DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentManager.Instance.showDocument(doc, { willPan: false }) : null)); - }); - document.addEventListener('linkAnnotationToDash', Hypothesis.linkListener); - this.initEventListeners(); + const wrapper = () => { + const prog = document.getElementById('dash-progress'); + if (ele && prog) { + // remove from DOM + setTimeout(() => { + //clearTimeout(); + prog.style.transition = '1s'; + prog.style.width = '100%'; + }, 0); + setTimeout(() => (ele.outerHTML = ''), 1000); + } + this._sidebarContent.proto = undefined; + if (!MainView.Live) { + DocServer.setPlaygroundFields([ + 'dataTransition', + 'viewTransition', + 'treeViewOpen', + 'showSidebar', + 'itemIndex', // for changing slides in presentations + 'sidebarWidthPercent', + 'currentTimecode', + 'timelineHeightPercent', + 'presStatus', + 'panX', + 'panY', + 'overlayX', + 'overlayY', + 'fitWidth', + 'nativeWidth', + 'nativeHeight', + 'text-scrollHeight', + 'text-height', + 'hideMinimap', + 'viewScale', + 'scrollTop', + 'hidden', + 'curPage', + 'viewType', + 'chromeHidden', + 'currentFrame', + 'width', + 'height', + 'nativeWidth', + ]); // can play with these fields on someone else's + } + DocServer.GetRefField('rtfProto').then( + proto => + proto instanceof Doc && + reaction( + () => StrCast(proto.BROADCAST_MESSAGE), + msg => msg && alert(msg) + ) + ); + + const tag = document.createElement('script'); + tag.src = 'https://www.youtube.com/iframe_api'; + const firstScriptTag = document.getElementsByTagName('script')[0]; + firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag); + window.removeEventListener('keydown', KeyManager.Instance.handle); + window.addEventListener('keydown', KeyManager.Instance.handle); + window.removeEventListener('keyup', KeyManager.Instance.unhandle); + window.addEventListener('keyup', KeyManager.Instance.unhandle); + window.addEventListener('paste', KeyManager.Instance.paste as any); + document.addEventListener('dash', (e: any) => { + // event used by chrome plugin to tell Dash which document to focus on + const id = FormattedTextBox.GetDocFromUrl(e.detail); + DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentManager.Instance.showDocument(doc, { willPan: false }) : null)); + }); + document.addEventListener('linkAnnotationToDash', Hypothesis.linkListener); + this.initEventListeners(); + }; + + if (PingManager.Instance.isBeating) { + wrapper(); + } else { + console.error('PingManager is not beating', new Date()); + const dispose = reaction( + () => PingManager.Instance.isBeating, + isBeating => { + if (isBeating) { + console.log('PingManager is beating', new Date()); + wrapper(); + dispose(); + } + } + ); + } } componentWillUnMount() { @@ -489,6 +511,8 @@ export class MainView extends React.Component { fa.faSquareRootAlt, fa.faVolumeMute, fa.faUserCircle, + fa.faHeart, + fa.faHeartBroken, fa.faHighlighter, fa.faRemoveFormat, fa.faHandPointUp, diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 71daad1a9..68d652cc4 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -1,14 +1,15 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, IconButton, Size } from 'browndash-components'; -import { action, computed, observable } from 'mobx'; +import { Button, FontSize, IconButton, Size } from 'browndash-components'; +import { action, computed, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { FaBug, FaCamera, FaStamp } from 'react-icons/fa'; -import { Doc } from '../../../fields/Doc'; +import { Doc } from '../../../fields/Doc'; import { AclAdmin } from '../../../fields/DocSymbols'; import { StrCast } from '../../../fields/Types'; import { GetEffectiveAcl } from '../../../fields/util'; import { DocumentManager } from '../../util/DocumentManager'; +import { PingManager } from '../../util/PingManager'; import { ReportManager } from '../../util/ReportManager'; import { ServerStats } from '../../util/ServerStats'; import { SettingsManager } from '../../util/SettingsManager'; @@ -37,6 +38,13 @@ export class TopBar extends React.Component { @observable textColor: string = Colors.LIGHT_GRAY; @observable backgroundColor: string = Colors.DARK_GRAY; + @observable happyHeart: boolean = PingManager.Instance.isBeating; + setHappyHeart = action((status: boolean) => (this.happyHeart = status)); + dispose = reaction( + () => PingManager.Instance.isBeating, + isBeating => this.setHappyHeart(isBeating) + ); + /** * Returns the left hand side of the topbar. * This side of the topbar contains the different modes. @@ -138,6 +146,9 @@ export class TopBar extends React.Component { <IconButton size={Size.SMALL} color={Colors.LIGHT_GRAY} onClick={ReportManager.Instance.open} icon={<FaBug />} /> <IconButton size={Size.SMALL} color={Colors.LIGHT_GRAY} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')} icon={<FontAwesomeIcon icon="question-circle" />} /> <IconButton size={Size.SMALL} color={Colors.LIGHT_GRAY} onClick={SettingsManager.Instance.open} icon={<FontAwesomeIcon icon="cog" />} /> + <div className={'topbarHeart' + (this.happyHeart ? '' : '-red')}> + <IconButton size={Size.SMALL} color={this.happyHeart ? Colors.LIGHT_BLUE : Colors.ERROR_RED} icon={<FontAwesomeIcon icon={this.happyHeart ? 'heart' : 'heart-broken'} />} /> + </div> {/* <Button text={'Logout'} borderRadius={5} hoverStyle={'gray'} backgroundColor={Colors.DARK_GRAY} color={this.textColor} fontSize={FontSize.SECONDARY} onClick={() => window.location.assign(Utils.prepend('/logout'))} /> */} </div> ); diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index ba6d7acfe..de1661ed6 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -43,6 +43,16 @@ export default class UploadManager extends ApiManager { protected initialize(register: Registration): void { register({ method: Method.POST, + subscription: '/ping', + secureHandler: async ({ req, res }) => { + // req.body contains the array of server paths to the videos + // console.log('ping', req.body); + _success(res, { message: 'pong', date: new Date() }); + }, + }); + + register({ + method: Method.POST, subscription: '/concatVideos', secureHandler: async ({ req, res }) => { // req.body contains the array of server paths to the videos |
