From e9d5dbeef2bf1dab9dfb863d970b70b3074e3d0a Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 2 Nov 2022 14:56:20 -0400 Subject: add basic heartbeat functinality througha ping/pong api cycle --- src/server/ApiManagers/UploadManager.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/server/ApiManagers') diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index fe4c475c9..0a16bd8ec 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -41,6 +41,16 @@ export function clientPathToFile(directory: Directory, filename: string) { 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', -- cgit v1.2.3-70-g09d2 From 27f518632c24f69fff360bef36eb0e5426167b83 Mon Sep 17 00:00:00 2001 From: James Hu <51237606+jameshu111@users.noreply.github.com> Date: Wed, 7 Jun 2023 12:30:53 -0400 Subject: Update other uses --- src/client/documents/Documents.ts | 4 ++-- src/client/util/Import & Export/DirectoryImportBox.tsx | 2 +- src/client/util/ReportManager.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/RecordingBox/RecordingView.tsx | 2 +- src/client/views/nodes/ScreenshotBox.tsx | 4 ++-- src/mobile/ImageUpload.tsx | 2 +- src/server/ApiManagers/UploadManager.ts | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) (limited to 'src/server/ApiManagers') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 77f0e1e5e..0030af982 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1844,7 +1844,7 @@ export namespace DocUtils { export async function uploadFilesToDocs(files: File[], options: DocumentOptions) { const generatedDocuments: Doc[] = []; const fileNoGuidPairs: Networking.FileGuidPair[] = []; - files.map(file => fileNoGuidPairs.push({file : file})); + files.map(file => fileNoGuidPairs.push({file})); const upfiles = await Networking.UploadFilesToServer(fileNoGuidPairs); for (const { source: { name, type }, @@ -1857,7 +1857,7 @@ export namespace DocUtils { export function uploadFileToDoc(file: File, options: DocumentOptions, overwriteDoc: Doc) { const generatedDocuments: Doc[] = []; - Networking.UploadFilesToServer([{file: file, guid: overwriteDoc[Id]}]).then(upfiles => { + Networking.UploadFilesToServer([{file, guid: overwriteDoc[Id]}]).then(upfiles => { const { source: { name, type }, result, diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index b9bb22564..1a4c2450e 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -112,7 +112,7 @@ export class DirectoryImportBox extends React.Component { sizes.push(file.size); modifiedDates.push(file.lastModified); }); - collector.push(...(await Networking.UploadFilesToServer(batch))); + collector.push(...(await Networking.UploadFilesToServer(batch.map(file =>({file}))))); runInAction(() => (this.completed += batch.length)); }); diff --git a/src/client/util/ReportManager.tsx b/src/client/util/ReportManager.tsx index 51742d455..4c1020455 100644 --- a/src/client/util/ReportManager.tsx +++ b/src/client/util/ReportManager.tsx @@ -173,7 +173,7 @@ export class ReportManager extends React.Component<{}> { // upload the files to the server if (input.files && input.files.length !== 0) { const fileArray: File[] = Array.from(input.files); - (Networking.UploadFilesToServer(fileArray)).then(links => { + (Networking.UploadFilesToServer(fileArray.map(file =>({file})))).then(links => { console.log('finshed uploading', links.map(this.getServerPath)); this.setFileLinks((links ?? []).map(this.getServerPath)); }) diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 0cb849923..6558d215a 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -233,7 +233,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent { - const [{ result }] = await Networking.UploadFilesToServer(e.data); + const [{ result }] = await Networking.UploadFilesToServer({file: e.data}); if (!(result instanceof Error)) { this.props.Document[this.fieldKey] = new AudioField(result.accessPaths.agnostic.client); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 0769e26d0..687683e6e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1013,7 +1013,7 @@ export class DocumentViewInternal extends DocComponent { - const [{ result }] = await Networking.UploadFilesToServer(e.data); + const [{ result }] = await Networking.UploadFilesToServer({file: e.data}); if (!(result instanceof Error)) { const audioField = new AudioField(result.accessPaths.agnostic.client); const audioAnnos = Cast(dataDoc[field + '-audioAnnotations'], listSpec(AudioField), null); diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index 424ebc384..51eb774e2 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -67,7 +67,7 @@ export function RecordingView(props: IRecordingViewProps) { const videoFiles = videos.map((vid, i) => new File(vid.videoChunks, `segvideo${i}.mkv`, { type: vid.videoChunks[0].type, lastModified: Date.now() })); // upload the segments to the server and get their server access paths - const serverPaths: string[] = (await Networking.UploadFilesToServer(videoFiles)).map(res => (res.result instanceof Error ? '' : res.result.accessPaths.agnostic.server)); + const serverPaths: string[] = (await Networking.UploadFilesToServer(videoFiles.map(file => ({file})))).map(res => (res.result instanceof Error ? '' : res.result.accessPaths.agnostic.server)); // concat the segments together using post call const result: Upload.AccessPathInfo | Error = await Networking.PostToServer('/concatVideos', serverPaths); diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 1e178b123..312b3c619 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -224,7 +224,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent aud_chunks.push(e.data); this._audioRec.onstop = async (e: any) => { - const [{ result }] = await Networking.UploadFilesToServer(aud_chunks); + const [{ result }] = await Networking.UploadFilesToServer(aud_chunks.map((file: any) => ({file}))); if (!(result instanceof Error)) { this.dataDoc[this.props.fieldKey + '-audio'] = new AudioField(result.accessPaths.agnostic.client); } @@ -237,7 +237,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent { console.log('screenshotbox: upload'); const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() }); - const [{ result }] = await Networking.UploadFilesToServer(file); + const [{ result }] = await Networking.UploadFilesToServer({file}); this.dataDoc[this.fieldKey + '_duration'] = (new Date().getTime() - this.recordingStart!) / 1000; if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index f910d765e..da38fcaee 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -42,7 +42,7 @@ export class Uploader extends React.Component { this.process = "Uploading Files"; for (let index = 0; index < files.length; ++index) { const file = files[index]; - const res = await Networking.UploadFilesToServer(file); + const res = await Networking.UploadFilesToServer({file}); this.setOpacity(3, "1"); // Slab 3 // For each item that the user has selected res.map(async ({ result }) => { diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index 74c06b4a6..94f744848 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -75,7 +75,7 @@ export default class UploadManager extends ApiManager { for (const key in files) { const f = files[key]; if (!Array.isArray(f)) { - const result = await DashUploadUtils.upload(f); + const result = await DashUploadUtils.upload(f, key); result && !(result.result instanceof Error) && results.push(result); } } -- cgit v1.2.3-70-g09d2 From b2c0855d6d701bd80666e0693bd193dc69efb4a0 Mon Sep 17 00:00:00 2001 From: James Hu <51237606+jameshu111@users.noreply.github.com> Date: Wed, 7 Jun 2023 12:45:08 -0400 Subject: Comments --- src/client/Network.ts | 16 +++++++++++++--- src/client/documents/Documents.ts | 18 ++++++++++++++++-- src/server/ApiManagers/UploadManager.ts | 2 +- src/server/DashUploadUtils.ts | 2 +- 4 files changed, 31 insertions(+), 7 deletions(-) (limited to 'src/server/ApiManagers') diff --git a/src/client/Network.ts b/src/client/Network.ts index 28825823d..9c293f9af 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -17,19 +17,26 @@ export namespace Networking { return requestPromise.post(options); } + /** + * FileGuidPair attaches a guid to a file that is being uploaded, + * allowing the client to track the upload progress. + * + * When files are dragged to the canvas, the overWriteDoc's ID is + * used as the guid. Otherwise, a new guid is generated. + */ export interface FileGuidPair { file: File; guid?: string; } /** * Handles uploading basic file types to server and makes the API call to "/uploadFormData" endpoint - * with the mapping of GUID to filem as parameters. + * with the mapping of guid to filem as parameters. * - * @param fileguidpairs the files to be uploaded to the server + * @param fileguidpairs the files and corresponding guids to be uploaded to the server * @returns the response as a json from the server */ export async function UploadFilesToServer(fileguidpairs: FileGuidPair | FileGuidPair[]): Promise[]> { - const formData = new FormData(); + const formData = new FormData(); if (Array.isArray(fileguidpairs)) { if (!fileguidpairs.length) { return []; @@ -45,8 +52,11 @@ export namespace Networking { ]) ); } + // If the fileguidpair has a guid to use (From the overwriteDoc) use that guid. Otherwise, + // generate a new guid. fileguidpairs.forEach(fileguidpair => formData.append(fileguidpair.guid ?? Utils.GenerateGuid(), fileguidpair.file)); } else { + // Handle the case where fileguidpairs is a single file. formData.append(fileguidpairs.guid ?? Utils.GenerateGuid(), fileguidpairs.file); } const parameters = { diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0030af982..06b48fe96 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1841,10 +1841,22 @@ export namespace DocUtils { }); } + /** + * uploadFilesToDocs will take in an array of Files, and creates documents for the + * new files. + * + * @param files an array of files that will be uploaded + * @param options options to use while uploading + * @returns + */ export async function uploadFilesToDocs(files: File[], options: DocumentOptions) { const generatedDocuments: Doc[] = []; - const fileNoGuidPairs: Networking.FileGuidPair[] = []; - files.map(file => fileNoGuidPairs.push({file})); + + // UploadFilesToServer takes an array of FileGuidPairs, + // but these files do not have overwriteDocs, so + // we do not set guid, allowing the client to generate one. + const fileNoGuidPairs: Networking.FileGuidPair[] = files.map(file => ({file})); + const upfiles = await Networking.UploadFilesToServer(fileNoGuidPairs); for (const { source: { name, type }, @@ -1857,6 +1869,8 @@ export namespace DocUtils { export function uploadFileToDoc(file: File, options: DocumentOptions, overwriteDoc: Doc) { const generatedDocuments: Doc[] = []; + // Since this file has an overwriteDoc, we can set the client tracking guid + // to the overwriteDoc's guid. Networking.UploadFilesToServer([{file, guid: overwriteDoc[Id]}]).then(upfiles => { const { source: { name, type }, diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index 94f744848..ba6d7acfe 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -75,7 +75,7 @@ export default class UploadManager extends ApiManager { for (const key in files) { const f = files[key]; if (!Array.isArray(f)) { - const result = await DashUploadUtils.upload(f, key); + const result = await DashUploadUtils.upload(f, key); // key is the guid used by the client to track upload progress. result && !(result.result instanceof Error) && results.push(result); } } diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index 971fefb5a..eaaac4e6d 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -184,7 +184,7 @@ export namespace DashUploadUtils { export async function upload(file: File, overwriteGuid?: string): Promise { const { type, path, name } = file; const types = type?.split('/') ?? []; - uploadProgress.set(overwriteGuid ?? name, 'uploading'); + uploadProgress.set(overwriteGuid ?? name, 'uploading'); // If the client sent a guid it uses to track upload progress, use that guid. Otherwise, use the file's name. const category = types[0]; let format = `.${types[1]}`; -- cgit v1.2.3-70-g09d2 From 6344439d62e5450f69ca7c0492976e55241d250f Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 24 Jun 2023 11:34:15 -0400 Subject: changed server ping to generate client alerts --- src/client/util/PingManager.ts | 15 +++++++++++---- src/client/views/topbar/TopBar.scss | 5 ++++- src/client/views/topbar/TopBar.tsx | 19 +++++++++++++++---- src/server/ApiManagers/UploadManager.ts | 2 -- 4 files changed, 30 insertions(+), 11 deletions(-) (limited to 'src/server/ApiManagers') diff --git a/src/client/util/PingManager.ts b/src/client/util/PingManager.ts index 9f4bd3642..0c41a1ea7 100644 --- a/src/client/util/PingManager.ts +++ b/src/client/util/PingManager.ts @@ -7,16 +7,23 @@ export class PingManager { return PingManager._instance; } - @observable IsBeating: boolean = true; - private setIsBeating = action((status: boolean) => (this.IsBeating = status)); + @observable IsBeating = true; + private setIsBeating = action((status: boolean) => { + this.IsBeating = status; + setTimeout(this.showAlert, 100); + }); + showAlert = () => { + alert(PingManager.Instance.IsBeating ? 'The server connection is active' : 'The server connection has been interrupted.NOTE: Any changes made will appear to persist but will be lost after a browser refreshes.'); + }; sendPing = async (): Promise => { try { await Networking.PostToServer('/ping', { date: new Date() }); !this.IsBeating && this.setIsBeating(true); } catch { - console.error('ping error'); - this.IsBeating && this.setIsBeating(false); + if (this.IsBeating) { + this.setIsBeating(false); + } } }; diff --git a/src/client/views/topbar/TopBar.scss b/src/client/views/topbar/TopBar.scss index a0f803da5..ede59a910 100644 --- a/src/client/views/topbar/TopBar.scss +++ b/src/client/views/topbar/TopBar.scss @@ -9,8 +9,11 @@ } .topbarHeart-red { .iconButton-container.primary { + .iconButton-content { + color: red; + } .iconButton-background { - background: red; + background: black; } } } diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 157d7c04a..20cf563c1 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -1,4 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Tooltip } from '@mui/material'; import { Button, FontSize, IconButton, Size } from 'browndash-components'; import { action, computed, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; @@ -36,7 +37,9 @@ export class TopBar extends React.Component { }; @observable textColor: string = Colors.LIGHT_GRAY; - @observable backgroundColor: string = Colors.DARK_GRAY; + @computed get backgroundColor() { + return PingManager.Instance.IsBeating ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY; + } @observable happyHeart: boolean = PingManager.Instance.IsBeating; setHappyHeart = action((status: boolean) => (this.happyHeart = status)); @@ -146,9 +149,17 @@ export class TopBar extends React.Component { } /> window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')} icon={} /> } /> -
- } /> -
+ {'Server connection ' + (PingManager.Instance.IsBeating ? 'active' : 'broken')}}> +
+ } + /> +
+
{/*