Files
tools/ts_interfaces/collaboration.ts

366 lines
7.6 KiB
TypeScript

/**
* @file collaboration.ts
* @description Collaboration interfaces
* Comments, suggestions, user presence, and collaborative editing support
*/
import * as plugins from './plugins.js';
import type {
TPresenceStatus,
TCommentThreadStatus,
TSuggestionStatus,
TSuggestionChangeType,
TConflictResolutionStrategy,
TEditingMode,
TCollaborationPermission,
} from './types.js';
import type { IVersionChange } from './versioning.js';
// ============================================================================
// USER PRESENCE
// ============================================================================
/**
* User's current location in document
*/
export interface IUserLocation {
paragraphId?: string;
characterPosition?: number;
selectionRange?: {
start: number;
end: number;
};
}
/**
* Device information for presence
*/
export interface IDeviceInfo {
type: 'desktop' | 'tablet' | 'mobile';
browser?: string;
}
/**
* User presence information
*/
export interface IUserPresence {
userId: string;
displayName: string;
avatarUrl?: string;
color: string;
status: TPresenceStatus;
currentLocation?: IUserLocation;
lastActivity: number;
sessionStarted: number;
deviceInfo?: IDeviceInfo;
}
/**
* Real-time cursor position for collaborative editing
*/
export interface ICursorPosition {
userId: string;
paragraphId: string;
offset: number;
timestamp: number;
}
// ============================================================================
// COMMENTS
// ============================================================================
/**
* Anchor point for a comment
*/
export interface ICommentAnchor {
type: 'paragraph' | 'text_range' | 'document' | 'party' | 'attachment';
paragraphId?: string;
textRange?: {
start: number;
end: number;
quotedText: string;
};
elementPath?: string;
}
/**
* User mention in comment
*/
export interface IMention {
userId: string;
displayName: string;
notified: boolean;
notifiedAt?: number;
}
/**
* Reaction on a comment
*/
export interface IReaction {
emoji: string;
userId: string;
timestamp: number;
}
/**
* Individual comment within a thread
*/
export interface IComment {
id: string;
threadId: string;
authorId: string;
authorDisplayName: string;
authorAvatarUrl?: string;
content: string;
createdAt: number;
editedAt?: number;
mentions: IMention[];
reactions: IReaction[];
parentCommentId?: string;
isDeleted: boolean;
}
/**
* Comment thread on a specific location
*/
export interface ICommentThread {
id: string;
contractId: string;
versionId: string;
anchor: ICommentAnchor;
status: TCommentThreadStatus;
resolvedBy?: string;
resolvedAt?: number;
comments: IComment[];
createdAt: number;
createdBy: string;
lastActivityAt: number;
}
// ============================================================================
// SUGGESTIONS
// ============================================================================
/**
* Suggestion for proposed changes (track changes mode)
*/
export interface ISuggestion {
id: string;
contractId: string;
versionId: string;
targetParagraphId: string;
targetTextRange?: {
start: number;
end: number;
};
changeType: TSuggestionChangeType;
originalContent: string;
suggestedContent: string;
status: TSuggestionStatus;
suggestedBy: string;
suggestedByDisplayName: string;
suggestedAt: number;
reviewedBy?: string;
reviewedAt?: number;
rejectionReason?: string;
discussionThreadId?: string;
}
// ============================================================================
// CONFLICTS
// ============================================================================
/**
* Conflicting change from a user
*/
export interface IConflictingChange {
userId: string;
userDisplayName: string;
content: string;
timestamp: number;
}
/**
* Conflict marker for simultaneous edits
*/
export interface IEditConflict {
id: string;
contractId: string;
paragraphId: string;
baseContent: string;
changes: IConflictingChange[];
status: 'unresolved' | 'resolved' | 'auto_merged';
resolvedContent?: string;
resolvedBy?: string;
resolvedAt?: number;
resolutionStrategy?: TConflictResolutionStrategy;
}
// ============================================================================
// COLLABORATION SESSION
// ============================================================================
/**
* Collaborative editing session
*/
export interface ICollaborationSession {
id: string;
contractId: string;
versionId: string;
participants: IUserPresence[];
editingMode: TEditingMode;
activeCursors: ICursorPosition[];
pendingChanges: IVersionChange[];
startedAt: number;
lastActivityAt: number;
autoSaveInterval: number;
}
// ============================================================================
// COLLABORATOR
// ============================================================================
/**
* Notification preferences for a collaborator
*/
export interface INotificationPreferences {
onComment: boolean;
onSuggestion: boolean;
onVersionPublish: boolean;
onSignatureRequest: boolean;
}
/**
* Collaborator with permission
*/
export interface ICollaborator {
userId: string;
contact: plugins.tsclass.business.TContact;
permission: TCollaborationPermission;
invitedBy: string;
invitedAt: number;
acceptedAt?: number;
expiresAt?: number;
notificationPreferences: INotificationPreferences;
}
// ============================================================================
// FACTORY FUNCTIONS
// ============================================================================
/**
* Create a new comment thread
*/
export function createCommentThread(
contractId: string,
versionId: string,
anchor: ICommentAnchor,
userId: string
): ICommentThread {
const now = Date.now();
return {
id: crypto.randomUUID(),
contractId,
versionId,
anchor,
status: 'open',
comments: [],
createdAt: now,
createdBy: userId,
lastActivityAt: now,
};
}
/**
* Create a new comment
*/
export function createComment(
threadId: string,
authorId: string,
authorDisplayName: string,
content: string
): IComment {
return {
id: crypto.randomUUID(),
threadId,
authorId,
authorDisplayName,
content,
createdAt: Date.now(),
mentions: [],
reactions: [],
isDeleted: false,
};
}
/**
* Create a new suggestion
*/
export function createSuggestion(
contractId: string,
versionId: string,
targetParagraphId: string,
changeType: TSuggestionChangeType,
originalContent: string,
suggestedContent: string,
suggestedBy: string,
suggestedByDisplayName: string
): ISuggestion {
return {
id: crypto.randomUUID(),
contractId,
versionId,
targetParagraphId,
changeType,
originalContent,
suggestedContent,
status: 'pending',
suggestedBy,
suggestedByDisplayName,
suggestedAt: Date.now(),
};
}
/**
* Create a new collaborator
*/
export function createCollaborator(
userId: string,
contact: plugins.tsclass.business.TContact,
permission: TCollaborationPermission,
invitedBy: string
): ICollaborator {
return {
userId,
contact,
permission,
invitedBy,
invitedAt: Date.now(),
notificationPreferences: {
onComment: true,
onSuggestion: true,
onVersionPublish: true,
onSignatureRequest: true,
},
};
}
/**
* Create default user presence
*/
export function createUserPresence(
userId: string,
displayName: string,
color: string
): IUserPresence {
const now = Date.now();
return {
userId,
displayName,
color,
status: 'viewing',
lastActivity: now,
sessionStarted: now,
};
}