Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

User/mahimashukla/3 p drag and drop #1903

Merged
merged 87 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from 86 commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
178b3c0
Initial Changes
Aug 9, 2023
a6b1bd7
Initial SDK changes to suppor 3P app drag and drop
Aug 30, 2023
cf0d497
Change files
Aug 30, 2023
d85f61f
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Aug 30, 2023
9638bd5
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Sep 6, 2023
b0874bb
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Sep 11, 2023
1c98ed9
Sending File instead of Blob
Sep 12, 2023
f5a1b39
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
Sep 12, 2023
e218e83
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Oct 4, 2023
2c229c0
Changed name of File to FilesFor3PApps
mahimaMSFT Oct 19, 2023
8205f33
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Oct 19, 2023
6f83ef7
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
shrshindeMSFT Oct 25, 2023
1e15144
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Oct 27, 2023
0fdd232
Review Comments
mahimaMSFT Oct 31, 2023
e2cba72
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Oct 31, 2023
c66f78d
Review Comments
mahimaMSFT Nov 7, 2023
95631f8
Review Comments
mahimaMSFT Nov 7, 2023
59a4899
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Nov 7, 2023
c7527c2
Build issue
mahimaMSFT Nov 7, 2023
097547a
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Nov 7, 2023
069ae59
Build Issue
mahimaMSFT Nov 7, 2023
3b570ee
Build Issue
mahimaMSFT Nov 7, 2023
6aac963
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
shrshindeMSFT Nov 7, 2023
ec57158
Review Comments
mahimaMSFT Nov 8, 2023
9da0286
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Nov 8, 2023
c09cbe7
Review Comments
mahimaMSFT Nov 8, 2023
af4f399
Adding E2E test case
mahimaMSFT Nov 10, 2023
2783d0b
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Nov 10, 2023
819cb73
Build Issues
mahimaMSFT Nov 10, 2023
4eaadae
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Nov 10, 2023
f17d76c
Build Issues
mahimaMSFT Nov 10, 2023
5d93472
E2E test case
mahimaMSFT Nov 13, 2023
0c1db79
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Nov 13, 2023
894db1a
Build Issues
mahimaMSFT Nov 14, 2023
49abdfc
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Nov 14, 2023
52c332a
Build Issue
mahimaMSFT Nov 14, 2023
e2abce0
Build Issues
mahimaMSFT Nov 14, 2023
785e8db
E2E Test
mahimaMSFT Nov 14, 2023
87abcd3
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
shrshindeMSFT Nov 14, 2023
d06571f
Build Issues
mahimaMSFT Nov 15, 2023
7f8fa4e
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Nov 15, 2023
3d70e0c
Build Issue
mahimaMSFT Nov 15, 2023
f8018a1
Review Comments
mahimaMSFT Nov 15, 2023
c9b1492
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Nov 16, 2023
aa88abb
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Nov 20, 2023
450ec18
Review Comment: Changed name
mahimaMSFT Nov 22, 2023
07c055b
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Nov 22, 2023
6d130e0
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Nov 22, 2023
16b5dc7
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
shrshindeMSFT Nov 28, 2023
2161b8e
Removed file
mahimaMSFT Nov 28, 2023
ba13361
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Nov 28, 2023
eced619
Delete microsoft-teams-library-js.sln
mahimaMSFT Nov 28, 2023
a5e3ca1
Update @microsoft-teams-js-e156a031-732a-4f22-8c0c-51d49568bff1.json
mahimaMSFT Nov 28, 2023
21a5c71
Review comments
mahimaMSFT Nov 29, 2023
636598c
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Nov 29, 2023
81d0de6
Review Comment
mahimaMSFT Nov 29, 2023
0003e13
Removed Media
mahimaMSFT Nov 29, 2023
f9deb1d
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
AE-MS Nov 29, 2023
cbb5939
Review Comments
mahimaMSFT Nov 29, 2023
67ab6fb
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Nov 29, 2023
8278d05
Review Comments
mahimaMSFT Nov 29, 2023
902d11d
Review Comments
mahimaMSFT Nov 29, 2023
e88ffc0
Review Comments
mahimaMSFT Nov 30, 2023
73ee2ff
Review Comments
mahimaMSFT Nov 30, 2023
0a78299
Review Comments
mahimaMSFT Nov 30, 2023
13139a3
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Nov 30, 2023
932cd57
Review Comments
mahimaMSFT Nov 30, 2023
ed75c9a
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Nov 30, 2023
639adf4
Review Comments
mahimaMSFT Nov 30, 2023
00cdddd
Review Comments
mahimaMSFT Dec 2, 2023
65b7a44
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Dec 2, 2023
4a53f88
Review Comments
mahimaMSFT Dec 2, 2023
af21452
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Dec 2, 2023
b9430db
Review Comments
mahimaMSFT Dec 4, 2023
007f00d
Review Comments
mahimaMSFT Dec 4, 2023
a212000
Review Comments
mahimaMSFT Dec 4, 2023
2e41fe3
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Dec 5, 2023
7e2ac78
Review Comments
mahimaMSFT Dec 6, 2023
ba636cd
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Dec 6, 2023
fb80d8c
Review Comment
mahimaMSFT Dec 6, 2023
352479c
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Dec 6, 2023
05b8c80
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
shrshindeMSFT Dec 7, 2023
1cd4916
Review Comments
mahimaMSFT Dec 7, 2023
3f34b8f
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Dec 7, 2023
1d20f16
Merge branch 'main' into user/mahimashukla/3PDragAndDrop
mahimaMSFT Dec 7, 2023
bb91143
Merge branch 'user/mahimashukla/3PDragAndDrop' of https://github.com/…
mahimaMSFT Dec 7, 2023
087ffb5
Review Comments
mahimaMSFT Dec 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions apps/teams-test-app/e2e-test-data/thirdPartyCloudStorage.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "ThirdPartyCloudStorage",
"platforms": "Web",
"version": ">2.18.0",
"checkIsSupported": {
"domElementName": "thirdPartyCloudStorageCapability",
"expectedOutput": "ThirdPartyCloudStorage is not supported"
},
"testCases": [
{
"title": "thirdPartyCloudStorage API Call - Success",
"type": "callResponse",
"boxSelector": "#box_thirdPartyCloudStorage",
"inputValue": "testThreadId",
"expectedAlertValue": "thirdPartyCloudStorage called with ##JSON_INPUT_VALUE##",
"expectedTestAppValue": "Received files in callback"
}
]
}
2 changes: 2 additions & 0 deletions apps/teams-test-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import SecondaryBrowserAPIs from './components/SecondaryBrowserAPIs';
import SharingAPIs from './components/SharingAPIs';
import StageViewAPIs from './components/StageViewAPIs';
import TeamsCoreAPIs from './components/TeamsCoreAPIs';
import ThirdPartyCloudStorageAPIs from './components/ThirdPartyCloudStorageAPIs';
import { isTestBackCompat } from './components/utils/isTestBackCompat';
import Version from './components/Version';
import VideoAPIs from './components/VideoEffectsApis';
Expand Down Expand Up @@ -175,6 +176,7 @@ const App = (): ReactElement => {
<StageViewAPIs />
<TeamsCoreAPIs />
<TeamsAPIs />
<ThirdPartyCloudStorageAPIs />
<VideoAPIs />
<VideoExAPIs />
<VisualMediaAPIs />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { SdkError, thirdPartyCloudStorage } from '@microsoft/teams-js';
import React, { ReactElement } from 'react';

import { ApiWithTextInput } from './utils';
import { ModuleWrapper } from './utils/ModuleWrapper';

const ThirdPartyCloudStorage = (): React.ReactElement =>
ApiWithTextInput<string>({
name: 'checkCapabilityThirdPartyCloudStorage',
title: 'thirdPartyCloudStorage',
onClick: {
// eslint-disable-next-line @typescript-eslint/no-empty-function
validateInput: (input) => {
if (!input) {
return; // API allows for no input to be provided
}
return;
},
submit: async (input, setResult) => {
// input will be the thread id
const filesCallback = (
attachments: thirdPartyCloudStorage.FilesFor3PStorage[],
error?: SdkError | undefined,
): void => {
if (error) {
setResult('error received in callback' + JSON.stringify(error));
} else {
console.log(attachments);
setResult('Received files in callback');
}
};
const result = thirdPartyCloudStorage.getDragAndDropFiles(input, filesCallback);
setResult(JSON.stringify(result));
return 'thirdPartyCloudStorage.getDragAndDropFiles() was called';
},
},
});

const ThirdPartyCloudStorageAPIs = (): ReactElement => (
<ModuleWrapper title="ThirdPartyCloudStorage">
<ThirdPartyCloudStorage />
</ModuleWrapper>
);

export default ThirdPartyCloudStorageAPIs;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Added support for drag and dropping files from third party storage providers using the `thirdPartyCloudStorage` capability",
"packageName": "@microsoft/teams-js",
"email": "mahimashukla@microsoft.com",
"dependentChangeType": "patch"
}
1 change: 1 addition & 0 deletions packages/teams-js/src/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export { visualMedia } from './visualMedia';
export { webStorage } from './webStorage';
export { call } from './call';
export { appInitialization } from './appInitialization';
export { thirdPartyCloudStorage } from './thirdPartyCloudStorage';
export {
callbackFunctionType,
enablePrintCapability,
Expand Down
1 change: 1 addition & 0 deletions packages/teams-js/src/public/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ interface IRuntimeV3 extends IBaseRuntime {
readonly joinedTeams?: {};
};
};
readonly thirdPartyCloudStorage?: {};
readonly teamsCore?: {};
readonly video?: {
readonly mediaStream?: {};
Expand Down
261 changes: 261 additions & 0 deletions packages/teams-js/src/public/thirdPartyCloudStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
import { sendMessageToParent } from '../internal/communication';
import { ensureInitialized } from '../internal/internalAPIs';
import { createFile, decodeAttachment } from '../internal/mediaUtil';
import { getLogger } from '../internal/telemetry';
import { errorNotSupportedOnPlatform, FrameContexts } from './constants';
import { ErrorCode, SdkError } from './interfaces';
import { runtime } from './runtime';

const Files3PLogger = getLogger('thirdPartyCloudStorage');

/**
* Extended files API 3P storage providers, features like sending Blob from Teams to 3P app on user
* actions like drag and drop to compose
* @beta
*/
export namespace thirdPartyCloudStorage {
/** Get context callback function type */
let files: FilesFor3PStorage[] = [];
let helper: AttachmentListHelper = {
fileType: '',
mahimaMSFT marked this conversation as resolved.
Show resolved Hide resolved
assembleAttachment: [],
};
let lastChunkVal = true; // setting it to true so that the very first file and first chunk does not fail

/**
* Object used to represent a file
* @beta
*
*/
export interface FilesFor3PStorage extends Blob {
/**
* A number that represents the number of milliseconds since the Unix epoch
*/
lastModified: number;
/**
* Name of the file
*/
name: string;
/**
* file type
*/
type: string;
/**
* A string containing the path of the file relative to the ancestor directory the user selected
*/
webkitRelativePath?: string;
}

/**
mahimaMSFT marked this conversation as resolved.
Show resolved Hide resolved
* File chunks an output of getDragAndDropFiles API from platform
* @beta
*/
export interface FileChunk {
/**
* Base 64 data for the requested uri
*/
chunk: string;
/**
* chunk sequence number
*/
chunkSequence: number;
/**
* whether or not this is the final chunk in a file
mahimaMSFT marked this conversation as resolved.
Show resolved Hide resolved
*/
endOfFile: boolean;
}

/**
* Output of getDragAndDropFiles API from platform
* @beta
*/
export interface FileResult {
/**
* Error encountered in getDragAndDropFiles API
*/
error?: SdkError;
/**
* File chunk which will be assemebled and converted into a blob
*/
fileChunk: FileChunk;
/**
* File index of the file for which chunk data is getting recieved
*/
fileIndex: number;
/**
* File type/MIME type which is getting recieved
*/
fileType: string;
/**
* Indicates whether this file is the last one in a sequence.
*/
isLastFile: boolean;
/**
* The name of the file.
*/
fileName: string;
}

/**
* Helper object to assembled file chunks
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please re-write this comment.

* @beta
*/
export interface AssembleAttachment {
/** A number representing the sequence of the attachment in the file chunks. */
sequence: number;
/** A Blob object representing the data of the file chunks. */
file: Blob;
}

/**
* Helper class for assembling files
mahimaMSFT marked this conversation as resolved.
Show resolved Hide resolved
* @beta
*/
export interface AttachmentListHelper {
/** A string representing the MIME type of the file */
fileType: string;
/** An array of {@link AssembleAttachment | AssembleAttachment} objects representing files to be sent as attachment */
assembleAttachment: AssembleAttachment[];
}

/**
* Defines the Callback function received from Third Party App
mahimaMSFT marked this conversation as resolved.
Show resolved Hide resolved
* @beta
*/
export interface DragAndDropFileCallback {
/** Callback from third party app */
(files: FilesFor3PStorage[], error?: SdkError): void;
}

let callback: DragAndDropFileCallback | null = null;

/**
* Get drag-and-drop files using a callback.
*
* @param {string} dragAndDropInput - Teams thread id or Teams conversation id from a Teams chat/channel
* @param {DragAndDropFileCallback} dragAndDropFileCallback - callback
* A callback function to handle the result of the operation
* @beta
*/
export function getDragAndDropFiles(
TrevorJoelHarris marked this conversation as resolved.
Show resolved Hide resolved
dragAndDropInput: string,
dragAndDropFileCallback: DragAndDropFileCallback,
): void {
if (!dragAndDropFileCallback) {
throw new Error('[getDragAndDropFiles] Callback cannot be null');
}
if (!dragAndDropInput || dragAndDropInput === '') {
const invalidInput: SdkError = { errorCode: ErrorCode.INVALID_ARGUMENTS };
dragAndDropFileCallback([], invalidInput);
return;
}

ensureInitialized(runtime, FrameContexts.content, FrameContexts.task);
if (!isSupported()) {
throw errorNotSupportedOnPlatform;
}

if (callback) {
callback = null;
throw new Error('getDragAndDropFiles cannot be called twice');
} else {
callback = dragAndDropFileCallback;
}
lastChunkVal = true;

sendMessageToParent(
'thirdPartyCloudStorage.getDragAndDropFiles',
[dragAndDropInput],
handleGetDragAndDropFilesCallbackRequest,
);
}

function handleGetDragAndDropFilesCallbackRequest(fileResult: FileResult): void {
if (callback) {
if (fileResult && fileResult.error) {
callback([], fileResult.error);
callback = null;
} else {
if (fileResult && fileResult.fileChunk) {
try {
if (!lastChunkVal && fileResult.fileChunk.chunkSequence === 0) {
// last chunk value was false
Files3PLogger("Last chunk is not received or 'endOfFile' value for previous chunk was not set to true");
lastChunkVal = true; // for next iteration

callback([], {
errorCode: ErrorCode.INTERNAL_ERROR,
message: 'error occurred while receiving data',
});
files = [];
callback = null;
}
const assemble: AssembleAttachment | null = decodeAttachment(fileResult.fileChunk, fileResult.fileType);
if (assemble) {
helper.assembleAttachment.push(assemble);
} else {
Files3PLogger(
mahimaMSFT marked this conversation as resolved.
Show resolved Hide resolved
`Received a null assemble attachment for when decoding chunk sequence ${fileResult.fileChunk.chunkSequence}; not including the chunk in the assembled file.`,
);
callback
? callback([], { errorCode: ErrorCode.INTERNAL_ERROR, message: 'error occurred while receiving data' })
: (callback = null);
files = [];
callback = null;
lastChunkVal = true;
}

// we will store this value to determine whether we received the last chunk of the previous file
lastChunkVal = fileResult.fileChunk.endOfFile;
if (fileResult.fileChunk.endOfFile) {
const fileBlob = createFile(helper.assembleAttachment, helper.fileType);

if (fileBlob) {
// Convert blob to File
const receivedFile = new File([fileBlob], fileResult.fileName, {
type: fileBlob.type,
});
files.push(receivedFile);
TrevorJoelHarris marked this conversation as resolved.
Show resolved Hide resolved
}

if (fileResult.isLastFile && callback) {
callback(files, fileResult.error);
files = [];
callback = null;
lastChunkVal = true;
}

helper = {
fileType: '',
assembleAttachment: [],
};
}
} catch (e) {
if (callback) {
callback([], { errorCode: ErrorCode.INTERNAL_ERROR, message: e });
files = [];
callback = null;
lastChunkVal = true;
}
}
} else {
callback([], { errorCode: ErrorCode.INTERNAL_ERROR, message: 'data received is null' });
files = [];
callback = null;
lastChunkVal = true;
}
}
}
}

/**
* Checks if the thirdPartyCloudStorage capability is supported by the host
* @returns boolean to represent whether the thirdPartyCloudStorage capability is supported
*
* @throws Error if {@linkcode app.initialize} has not successfully completed
* @beta
*/
export function isSupported(): boolean {
TrevorJoelHarris marked this conversation as resolved.
Show resolved Hide resolved
return ensureInitialized(runtime) && runtime.supports.thirdPartyCloudStorage ? true : false;
}
}
Loading
Loading