diff --git a/packages/open-collaboration-vscode/src/collaboration-connection-provider.ts b/packages/open-collaboration-vscode/src/collaboration-connection-provider.ts index 317c247..d4a2127 100644 --- a/packages/open-collaboration-vscode/src/collaboration-connection-provider.ts +++ b/packages/open-collaboration-vscode/src/collaboration-connection-provider.ts @@ -30,7 +30,7 @@ export class CollaborationConnectionProvider { if (serverUrl) { return new ConnectionProvider({ url: serverUrl, - client: 'OCT-VSCode@' + version, + client: 'OCT-VSCode@' + packageVersion, opener: (url) => vscode.env.openExternal(vscode.Uri.parse(url)), transports: [SocketIoTransportProvider], userToken, diff --git a/packages/open-collaboration-vscode/src/collaboration-instance.ts b/packages/open-collaboration-vscode/src/collaboration-instance.ts index cd68be9..7c52432 100644 --- a/packages/open-collaboration-vscode/src/collaboration-instance.ts +++ b/packages/open-collaboration-vscode/src/collaboration-instance.ts @@ -17,6 +17,11 @@ import { inject, injectable, postConstruct } from 'inversify'; import { removeWorkspaceFolders } from './utils/workspace'; import { Mutex } from 'async-mutex'; import { CollaborationUri } from './utils/uri'; +import { userColors } from "./utils/package"; + +export interface PeerWithColor extends types.Peer { + color?: [number, number, number] | string; +} export class DisposablePeer implements vscode.Disposable { @@ -111,12 +116,7 @@ export class DisposablePeer implements vscode.Disposable { let colorIndex = 0; const defaultColors: ([number, number, number] | string)[] = [ - 'oct.user.yellow', // Yellow - 'oct.user.green', // Green - 'oct.user.magenta', // Magenta - 'oct.user.lightGreen', // Light green - 'oct.user.lightOrange', // Light orange - 'oct.user.lightMagenta', // Light magenta + ...userColors, [92, 45, 145], // Purple [0, 178, 148], // Light teal [255, 241, 0], // Light yellow @@ -205,8 +205,15 @@ export class CollaborationInstance implements vscode.Disposable { private readonly onDidDisposeEmitter: vscode.EventEmitter = new vscode.EventEmitter(); readonly onDidDispose: vscode.Event = this.onDidDisposeEmitter.event; - get connectedUsers(): DisposablePeer[] { - return Array.from(this.peers.values()); + get connectedUsers(): Promise { + return this.ownUserData.then(own => { + const all = Array.from(this.peers.values()).map(e => ({ + ...e.peer, + color: e.decoration.color + }) as PeerWithColor); + all.push(own); + return Array.from(all); + }); } get ownUserData(): Promise { @@ -277,7 +284,6 @@ export class CollaborationInstance implements vscode.Disposable { await this.initialize(initData); }); connection.room.onJoin(async (_, peer) => { - this.peers.set(peer.id, new DisposablePeer(this.yjsAwareness, peer)); if (this.host) { // Only initialize the user if we are the host const roots = vscode.workspace.workspaceFolders ?? []; @@ -294,6 +300,7 @@ export class CollaborationInstance implements vscode.Disposable { }; connection.peer.init(peer.id, initData); } + this.peers.set(peer.id, new DisposablePeer(this.yjsAwareness, peer)); this.onDidUsersChangeEmitter.fire(); }); connection.room.onLeave(async (_, peer) => { @@ -315,6 +322,7 @@ export class CollaborationInstance implements vscode.Disposable { connection.peer.onInfo((_, peer) => { this.yjsAwareness.setLocalStateField('peer', peer.id); this.identity.resolve(peer); + this.onDidUsersChangeEmitter.fire(); }); this.registerFileEvents(); @@ -863,5 +871,6 @@ export class CollaborationInstance implements vscode.Disposable { } this.fileSystem = new CollaborationFileSystemProvider(this.options.connection, this.yjs, data.host); this.toDispose.push(vscode.workspace.registerFileSystemProvider('oct', this.fileSystem)); + this.onDidUsersChangeEmitter.fire(); } } diff --git a/packages/open-collaboration-vscode/src/collaboration-status-view.ts b/packages/open-collaboration-vscode/src/collaboration-status-view.ts index 6d6622c..9a1559c 100644 --- a/packages/open-collaboration-vscode/src/collaboration-status-view.ts +++ b/packages/open-collaboration-vscode/src/collaboration-status-view.ts @@ -5,13 +5,13 @@ // ****************************************************************************** import * as vscode from 'vscode'; -import { CollaborationInstance, DisposablePeer } from './collaboration-instance'; +import { CollaborationInstance, PeerWithColor } from './collaboration-instance'; import { injectable } from 'inversify'; @injectable() -export class CollaborationStatusViewDataProvider implements vscode.TreeDataProvider { +export class CollaborationStatusViewDataProvider implements vscode.TreeDataProvider { - private onDidChangeTreeDataEmitter = new vscode.EventEmitter(); + private onDidChangeTreeDataEmitter = new vscode.EventEmitter(); onDidChangeTreeData = this.onDidChangeTreeDataEmitter.event; private instance: CollaborationInstance | undefined; @@ -19,29 +19,37 @@ export class CollaborationStatusViewDataProvider implements vscode.TreeDataProvi onConnection(instance: CollaborationInstance) { this.instance = instance; instance.onDidUsersChange(() => { - this.onDidChangeTreeDataEmitter.fire(undefined); + this.onDidChangeTreeDataEmitter.fire(); }); instance.onDidDispose(() => { this.instance = undefined; - this.onDidChangeTreeDataEmitter.fire(undefined); + this.onDidChangeTreeDataEmitter.fire(); }); - this.onDidChangeTreeDataEmitter.fire(undefined); + this.onDidChangeTreeDataEmitter.fire(); } - async getTreeItem(element: DisposablePeer): Promise { + async getTreeItem(peer: PeerWithColor): Promise { const self = await this.instance?.ownUserData; - const you = vscode.l10n.t('You'); - const treeItem = new vscode.TreeItem(element.peer.id === self?.id ? `${element.peer.name} (${you})` : element.peer.name); - treeItem.id = element.peer.id; + const treeItem = new vscode.TreeItem(peer.name); + const tags: string[] = []; + if (peer.id === self?.id) { + tags.push(vscode.l10n.t('You')); + } + if (peer.host) { + tags.push(vscode.l10n.t('Host')); + } + treeItem.description = tags.length ? ('(' + tags.join(' • ') + ')') : undefined; + treeItem.id = peer.id; treeItem.contextValue = 'self'; - if (self?.id !== element.peer.id) { - treeItem.iconPath = new vscode.ThemeIcon('circle-filled', element.decoration.getThemeColor()); - treeItem.contextValue = this.instance?.following === treeItem.id ? 'followedPeer' : 'peer'; + if (self?.id !== peer.id) { + const themeColor = typeof peer.color === 'string' ? new vscode.ThemeColor(peer.color) : undefined; + treeItem.iconPath = new vscode.ThemeIcon('circle-filled', themeColor); + treeItem.contextValue = this.instance?.following === peer.id ? 'followedPeer' : 'peer'; } return treeItem; } - getChildren(element?: DisposablePeer): vscode.ProviderResult { + getChildren(element?: PeerWithColor): vscode.ProviderResult { if (!element && this.instance) { return this.instance.connectedUsers; } @@ -49,7 +57,7 @@ export class CollaborationStatusViewDataProvider implements vscode.TreeDataProvi } update() { - this.onDidChangeTreeDataEmitter.fire(undefined); + this.onDidChangeTreeDataEmitter.fire(); } } diff --git a/packages/open-collaboration-vscode/src/commands.ts b/packages/open-collaboration-vscode/src/commands.ts index 54de51d..8bdfe2c 100644 --- a/packages/open-collaboration-vscode/src/commands.ts +++ b/packages/open-collaboration-vscode/src/commands.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { inject, injectable } from 'inversify'; import { FollowService } from './follow-service'; -import { CollaborationInstance, DisposablePeer } from './collaboration-instance'; +import { CollaborationInstance, PeerWithColor } from './collaboration-instance'; import { ExtensionContext } from './inversify'; import { CollaborationConnectionProvider, OCT_USER_TOKEN } from './collaboration-connection-provider'; import { QuickPickItem, showQuickPick } from './utils/quick-pick'; @@ -40,7 +40,7 @@ export class Commands { initialize(): void { this.context.subscriptions.push( - vscode.commands.registerCommand('oct.followPeer', (peer?: DisposablePeer) => this.followService.followPeer(peer)), + vscode.commands.registerCommand('oct.followPeer', (peer?: PeerWithColor) => this.followService.followPeer(peer?.id)), vscode.commands.registerCommand('oct.stopFollowPeer', () => this.followService.unfollowPeer()), vscode.commands.registerCommand('oct.enter', async () => { this.withConnectionProvider(async connectionProvider => { diff --git a/packages/open-collaboration-vscode/src/follow-service.ts b/packages/open-collaboration-vscode/src/follow-service.ts index 127103a..1b236ab 100644 --- a/packages/open-collaboration-vscode/src/follow-service.ts +++ b/packages/open-collaboration-vscode/src/follow-service.ts @@ -4,6 +4,7 @@ // terms of the MIT License, which is available in the project root. // ****************************************************************************** +import * as vscode from 'vscode'; import { inject, injectable } from 'inversify'; import { CollaborationInstance, DisposablePeer } from './collaboration-instance'; import { showQuickPick } from './utils/quick-pick'; @@ -19,22 +20,23 @@ export class FollowService { @inject(ContextKeyService) private contextKeyService: ContextKeyService; - async followPeer(peer?: DisposablePeer): Promise { + async followPeer(peer?: string): Promise { if (!CollaborationInstance.Current) { return; } if (!peer) { - const users = CollaborationInstance.Current.connectedUsers; - const items = users.map(user => ({ key: user, label: user.peer.name, detail: user.peer.id })); - peer = await showQuickPick(items); + const quickPick = vscode.window.createQuickPick(); + const users = await CollaborationInstance.Current.connectedUsers; + quickPick.items = users.map(user => ({ label: user.name, detail: user.id })); + peer = users[(await showQuickPick(quickPick))]?.id; } if (!peer) { return; } - CollaborationInstance.Current.followUser(peer.peer.id); + CollaborationInstance.Current.followUser(peer); this.viewDataProvider.update(); this.contextKeyService.setFollowing(true); } diff --git a/packages/open-collaboration-vscode/src/utils/package.ts b/packages/open-collaboration-vscode/src/utils/package.ts new file mode 100644 index 0000000..ee91a7c --- /dev/null +++ b/packages/open-collaboration-vscode/src/utils/package.ts @@ -0,0 +1,5 @@ +export const packageJson = require('../../package.json'); +export const packageVersion: string = packageJson.version; +export const userColors: string[] = packageJson.contributes.colors + .map((color: any) => color.id) + .filter((id: string) => id.startsWith('oct.user.'));