blob: e598e2ba173a611fb7c9dd2c092a0d273b7fd55c [file] [log] [blame]
/**
* @license
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
CommentBasics,
CommentInfo,
PatchSetNum,
RobotCommentInfo,
Timestamp,
UrlEncodedCommentId,
} from '../types/common';
import {CommentSide, Side} from '../constants/constants';
import {parseDate} from './date-util';
export interface DraftCommentProps {
__draft?: boolean;
__draftID?: string;
__date?: Date;
}
export type DraftInfo = CommentBasics & DraftCommentProps;
/**
* Each of the type implements or extends CommentBasics.
*/
export type Comment = DraftInfo | CommentInfo | RobotCommentInfo;
export interface UIStateCommentProps {
// The `side` of the comment is PARENT or REVISION, but this is LEFT or RIGHT.
// TODO(TS): Remove the naming confusion of commentSide being of type of Side,
// but side being of type CommentSide. :-)
__commentSide?: Side;
// TODO(TS): Remove this. Seems to be exactly the same as `path`??
__path?: string;
collapsed?: boolean;
// TODO(TS): Consider allowing this only for drafts.
__editing?: boolean;
__otherEditing?: boolean;
}
export type UIDraft = DraftInfo & UIStateCommentProps;
export type UIHuman = CommentInfo & UIStateCommentProps;
export type UIRobot = RobotCommentInfo & UIStateCommentProps;
export type UIComment = UIHuman | UIRobot | UIDraft;
export type CommentMap = {[path: string]: boolean};
export function isRobot<T extends CommentInfo>(
x: T | DraftInfo | RobotCommentInfo | undefined
): x is RobotCommentInfo {
return !!x && !!(x as RobotCommentInfo).robot_id;
}
export function isDraft<T extends CommentInfo>(
x: T | UIDraft | undefined
): x is UIDraft {
return !!x && !!(x as UIDraft).__draft;
}
export interface PatchSetFile {
path: string;
basePath?: string;
patchNum?: PatchSetNum;
}
export interface PatchNumOnly {
patchNum: PatchSetNum;
}
export function isPatchSetFile(
x: PatchSetFile | PatchNumOnly
): x is PatchSetFile {
return !!(x as PatchSetFile).path;
}
interface SortableComment {
__draft?: boolean;
__date?: Date;
updated?: Timestamp;
id?: UrlEncodedCommentId;
}
export function sortComments<T extends SortableComment>(comments: T[]): T[] {
return comments.slice(0).sort((c1, c2) => {
const d1 = !!c1.__draft;
const d2 = !!c2.__draft;
if (d1 !== d2) return d1 ? 1 : -1;
const date1 = (c1.updated && parseDate(c1.updated)) || c1.__date;
const date2 = (c2.updated && parseDate(c2.updated)) || c2.__date;
const dateDiff = date1!.valueOf() - date2!.valueOf();
if (dateDiff !== 0) return dateDiff;
const id1 = c1.id ?? '';
const id2 = c2.id ?? '';
return id1.localeCompare(id2);
});
}
export interface CommentThread {
comments: UIComment[];
patchNum?: PatchSetNum;
path: string;
// TODO(TS): It would be nice to use LineNumber here, but the comment thread
// element actually relies on line to be undefined for file comments. Be
// aware of element attribute getters and setters, if you try to refactor
// this. :-) Still worthwhile to do ...
line?: number;
rootId: UrlEncodedCommentId;
commentSide?: CommentSide;
}
export function getLastComment(thread?: CommentThread): UIComment | undefined {
const len = thread?.comments.length;
return thread && len ? thread.comments[len - 1] : undefined;
}
export function isUnresolved(thread?: CommentThread): boolean {
return !!getLastComment(thread)?.unresolved;
}
export function isDraftThread(thread?: CommentThread): boolean {
return isDraft(getLastComment(thread));
}