Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from 'shared/hooks/useAsync';
import {
BitmapFrame,
BrowserFileSystem,
ClientScreenSpec,
TdpClient,
TdpClientEvent,
Expand Down Expand Up @@ -54,7 +55,7 @@ const meta: Meta = {
export default meta;

const fakeClient = () => {
const client = new TdpClient(() => null);
const client = new TdpClient(() => null, new BrowserFileSystem());
// Don't try to connect to a websocket.
client.connect = async spec => {
emitFrame(client, spec);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { screen } from '@testing-library/react';

import { render } from 'design/utils/testing';
import { makeSuccessAttempt } from 'shared/hooks/useAsync';
import { TdpClient } from 'shared/libs/tdp';
import { BrowserFileSystem, TdpClient } from 'shared/libs/tdp';
import { wait } from 'shared/utils/wait';

import { DesktopSession } from './DesktopSession';
Expand Down Expand Up @@ -72,7 +72,10 @@ const getMockTransport = () => {

test('reconnect button reinitializes the connection', async () => {
const transport = getMockTransport();
const tpdClient = new TdpClient(transport.getTransport);
const tpdClient = new TdpClient(
transport.getTransport,
new BrowserFileSystem()
);
jest.spyOn(tpdClient, 'connect');
jest.spyOn(tpdClient, 'shutdown');
const { unmount } = render(
Expand Down
16 changes: 8 additions & 8 deletions web/packages/shared/components/DesktopSession/Withholder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { ButtonState, TdpClient } from 'shared/libs/tdp';
import { BrowserFileSystem, ButtonState, TdpClient } from 'shared/libs/tdp';

import { Withholder } from './Withholder';

Expand Down Expand Up @@ -53,7 +53,7 @@ describe('withholder', () => {
const params = {
e: { key: 'Enter' } as KeyboardEvent as KeyboardEvent,
state: ButtonState.DOWN,
cli: new TdpClient(() => null),
cli: new TdpClient(() => null, new BrowserFileSystem()),
};
withholder.handleKeyboardEvent(params, mockHandleKeyboardEvent);
expect(mockHandleKeyboardEvent).toHaveBeenCalledWith(params);
Expand All @@ -63,19 +63,19 @@ describe('withholder', () => {
const metaDown = {
e: { key: 'Meta' } as KeyboardEvent,
state: ButtonState.DOWN,
cli: new TdpClient(() => null),
cli: new TdpClient(() => null, new BrowserFileSystem()),
};

const metaUp = {
e: { key: 'Meta' } as KeyboardEvent,
state: ButtonState.UP,
cli: new TdpClient(() => null),
cli: new TdpClient(() => null, new BrowserFileSystem()),
};

const enterDown = {
e: { key: 'Enter' } as KeyboardEvent as KeyboardEvent,
state: ButtonState.DOWN,
cli: new TdpClient(() => null),
cli: new TdpClient(() => null, new BrowserFileSystem()),
};

withholder.handleKeyboardEvent(metaDown, mockHandleKeyboardEvent);
Expand All @@ -95,12 +95,12 @@ describe('withholder', () => {
const metaParams = {
e: { key: 'Meta' } as KeyboardEvent,
state: ButtonState.UP,
cli: new TdpClient(() => null),
cli: new TdpClient(() => null, new BrowserFileSystem()),
};
const altParams = {
e: { key: 'Alt' } as KeyboardEvent,
state: ButtonState.UP,
cli: new TdpClient(() => null),
cli: new TdpClient(() => null, new BrowserFileSystem()),
};

withholder.handleKeyboardEvent(metaParams, mockHandleKeyboardEvent);
Expand All @@ -119,7 +119,7 @@ describe('withholder', () => {
const metaParams = {
e: { key: 'Meta' } as KeyboardEvent,
state: ButtonState.UP,
cli: new TdpClient(() => null),
cli: new TdpClient(() => null, new BrowserFileSystem()),
};
withholder.handleKeyboardEvent(metaParams, mockHandleKeyboardEvent);
expect((withholder as any).withheldKeys).toHaveLength(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,52 +121,23 @@ export default function useDesktopSession(
}
}

const onShareDirectory = () => {
const onShareDirectory = async () => {
try {
window
.showDirectoryPicker()
.then(sharedDirHandle => {
// Permissions granted and/or directory selected
setDirectorySharingState(prevState => ({
...prevState,
directorySelected: true,
}));
tdpClient.addSharedDirectory(sharedDirHandle);
tdpClient.sendSharedDirectoryAnnounce();
})
.catch(e => {
setDirectorySharingState(prevState => ({
...prevState,
directorySelected: false,
}));
addAlert({
severity: 'warn',
content: {
title: 'Failed to open the directory picker',
description: e.message,
},
});
});
await tdpClient.shareDirectory();
setDirectorySharingState(prevState => ({
...prevState,
directorySelected: true,
}));
} catch (e) {
setDirectorySharingState(prevState => ({
...prevState,
directorySelected: false,
}));
addAlert({
severity: 'warn',
// This is a gross error message, but should be infrequent enough that its worth just telling
// the user the likely problem, while also displaying the error message just in case that's not it.
// In a perfect world, we could check for which error message this is and display
// context appropriate directions.
content: {
title: 'Encountered an error while attempting to share a directory',
description:
e.message +
'. \n\nYour user role supports directory sharing over desktop access, \
however this feature is only available by default on some Chromium \
based browsers like Google Chrome or Microsoft Edge. Brave users can \
use the feature by navigating to brave://flags/#file-system-access-api \
and selecting "Enable". If you\'re not already, please switch to a supported browser.',
title: 'Could not share a directory',
description: e.message,
},
});
}
Expand Down
39 changes: 21 additions & 18 deletions web/packages/shared/libs/tdp/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ import Codec, {
} from './codec';
import {
PathDoesNotExistError,
SharedDirectoryManager,
SharedDirectoryAccess,
type FileOrDirInfo,
} from './sharedDirectoryManager';
} from './sharedDirectoryAccess';

export enum TdpClientEvent {
TDP_CLIENT_SCREEN_SPEC = 'tdp client screen spec',
Expand Down Expand Up @@ -110,18 +110,17 @@ export class TdpClient extends EventEmitter {
protected codec: Codec;
protected transport: TdpTransport | undefined;
private transportAbortController: AbortController | undefined;
private sdManager: SharedDirectoryManager;
private fastPathProcessor: FastPathProcessor | undefined;
private wasmReady: Promise<void> | undefined;

private logger = Logger.create('TDPClient');

constructor(
private getTransport: (signal: AbortSignal) => Promise<TdpTransport>
private getTransport: (signal: AbortSignal) => Promise<TdpTransport>,
private sharedDirectoryAccess: SharedDirectoryAccess
) {
super();
this.codec = new Codec();
this.sdManager = new SharedDirectoryManager();
}

/**
Expand Down Expand Up @@ -479,20 +478,22 @@ export class TdpClient extends EventEmitter {
// Since this is not a fatal error, we emit a warning but otherwise
// keep the sesion alive.
this.handleWarning(
`Failed to share directory '${this.sdManager.getName()}', drive redirection may be disabled on the RDP server.`,
`Failed to share directory '${this.sharedDirectoryAccess.getDirectoryName()}', drive redirection may be disabled on the RDP server.`,
TdpClientEvent.TDP_WARNING
);
return;
}

this.logger.info('Started sharing directory: ' + this.sdManager.getName());
this.logger.info(
`Started sharing directory: ${this.sharedDirectoryAccess.getDirectoryName()}`
);
}

async handleSharedDirectoryInfoRequest(buffer: ArrayBuffer) {
const req = this.codec.decodeSharedDirectoryInfoRequest(buffer);
const path = req.path;
try {
const info = await this.sdManager.getInfo(path);
const info = await this.sharedDirectoryAccess.stat(path);
this.sendSharedDirectoryInfoResponse({
completionId: req.completionId,
errCode: SharedDirectoryErrCode.Nil,
Expand Down Expand Up @@ -521,8 +522,8 @@ export class TdpClient extends EventEmitter {
const req = this.codec.decodeSharedDirectoryCreateRequest(buffer);

try {
await this.sdManager.create(req.path, req.fileType);
const info = await this.sdManager.getInfo(req.path);
await this.sharedDirectoryAccess.create(req.path, req.fileType);
const info = await this.sharedDirectoryAccess.stat(req.path);
this.sendSharedDirectoryCreateResponse({
completionId: req.completionId,
errCode: SharedDirectoryErrCode.Nil,
Expand All @@ -548,7 +549,7 @@ export class TdpClient extends EventEmitter {
const req = this.codec.decodeSharedDirectoryDeleteRequest(buffer);

try {
await this.sdManager.delete(req.path);
await this.sharedDirectoryAccess.delete(req.path);
this.sendSharedDirectoryDeleteResponse({
completionId: req.completionId,
errCode: SharedDirectoryErrCode.Nil,
Expand All @@ -564,7 +565,7 @@ export class TdpClient extends EventEmitter {

async handleSharedDirectoryReadRequest(buffer: ArrayBuffer) {
const req = this.codec.decodeSharedDirectoryReadRequest(buffer);
const readData = await this.sdManager.readFile(
const readData = await this.sharedDirectoryAccess.read(
req.path,
req.offset,
req.length
Expand All @@ -579,7 +580,7 @@ export class TdpClient extends EventEmitter {

async handleSharedDirectoryWriteRequest(buffer: ArrayBuffer) {
const req = this.codec.decodeSharedDirectoryWriteRequest(buffer);
const bytesWritten = await this.sdManager.writeFile(
const bytesWritten = await this.sharedDirectoryAccess.write(
req.path,
req.offset,
req.writeData
Expand Down Expand Up @@ -610,7 +611,8 @@ export class TdpClient extends EventEmitter {
const req = this.codec.decodeSharedDirectoryListRequest(buffer);
const path = req.path;

const infoList: FileOrDirInfo[] = await this.sdManager.listContents(path);
const infoList: FileOrDirInfo[] =
await this.sharedDirectoryAccess.readDir(path);
const fsoList: FileSystemObject[] = infoList.map(info => this.toFso(info));

this.sendSharedDirectoryListResponse({
Expand All @@ -622,7 +624,7 @@ export class TdpClient extends EventEmitter {

async handleSharedDirectoryTruncateRequest(buffer: ArrayBuffer) {
const req = this.codec.decodeSharedDirectoryTruncateRequest(buffer);
await this.sdManager.truncateFile(req.path, req.endOfFile);
await this.sharedDirectoryAccess.truncate(req.path, req.endOfFile);
this.sendSharedDirectoryTruncateResponse({
completionId: req.completionId,
errCode: SharedDirectoryErrCode.Nil,
Expand Down Expand Up @@ -706,12 +708,13 @@ export class TdpClient extends EventEmitter {
this.send(msg);
}

addSharedDirectory(sharedDirectory: FileSystemDirectoryHandle) {
this.sdManager.add(sharedDirectory);
async shareDirectory() {
await this.sharedDirectoryAccess.selectDirectory();
this.sendSharedDirectoryAnnounce();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is this line needed now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was previously called in useDesktopSession after selecting the directory

window
.showDirectoryPicker()
.then(sharedDirHandle => {
// Permissions granted and/or directory selected
setDirectorySharingState(prevState => ({
...prevState,
directorySelected: true,
}));
tdpClient.addSharedDirectory(sharedDirHandle);
tdpClient.sendSharedDirectoryAnnounce();

I moved it here to make this API a more high level (so that selecting a directory and actually sharing it is a single thing).

}

sendSharedDirectoryAnnounce() {
const name = this.sdManager.getName();
const name = this.sharedDirectoryAccess.getDirectoryName();
this.send(
this.codec.encodeSharedDirectoryAnnounce({
discard: 0, // This is always the first request.
Expand Down
1 change: 1 addition & 0 deletions web/packages/shared/libs/tdp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ export {
type BitmapFrame,
} from './client';
export * from './codec';
export * from './sharedDirectoryAccess';
Loading
Loading