Files
outline/server/presenters/unfurl.ts

212 lines
5.7 KiB
TypeScript

import { differenceInMinutes, formatDistanceToNowStrict } from "date-fns";
import { t } from "i18next";
import { UnfurlResourceType, UnfurlResponse } from "@shared/types";
import { dateLocale } from "@shared/utils/date";
import { Document, User, View } from "@server/models";
import { opts } from "@server/utils/i18n";
import { GitHubUtils } from "plugins/github/shared/GitHubUtils";
async function presentUnfurl(data: Record<string, any>) {
switch (data.type) {
case UnfurlResourceType.Mention:
return presentMention(data);
case UnfurlResourceType.Document:
return presentDocument(data);
case UnfurlResourceType.PR:
return presentPR(data);
case UnfurlResourceType.Issue:
return presentIssue(data);
default:
return presentOEmbed(data);
}
}
const presentOEmbed = (
data: Record<string, any>
): UnfurlResponse[UnfurlResourceType.OEmbed] => ({
type: UnfurlResourceType.OEmbed,
url: data.url,
title: data.title,
description: data.description,
thumbnailUrl: data.thumbnail_url,
});
const presentMention = async (
data: Record<string, any>
): Promise<UnfurlResponse[UnfurlResourceType.Mention]> => {
const user: User = data.user;
const document: Document = data.document;
const lastOnlineInfo = presentLastOnlineInfoFor(user);
const lastViewedInfo = await presentLastViewedInfoFor(user, document);
return {
type: UnfurlResourceType.Mention,
name: user.name,
avatarUrl: user.avatarUrl,
color: user.color,
lastActive: `${lastOnlineInfo}${lastViewedInfo}`,
};
};
const presentDocument = (
data: Record<string, any>
): UnfurlResponse[UnfurlResourceType.Document] => {
const document: Document = data.document;
const viewer: User = data.viewer;
return {
url: document.url,
type: UnfurlResourceType.Document,
id: document.id,
title: document.titleWithDefault,
summary: document.getSummary(),
lastActivityByViewer: presentLastActivityInfoFor(document, viewer),
};
};
const presentPR = (
data: Record<string, any>
): UnfurlResponse[UnfurlResourceType.PR] => ({
url: data.html_url,
type: UnfurlResourceType.PR,
id: `#${data.number}`,
title: data.title,
description: data.body,
author: {
name: data.user.login,
avatarUrl: data.user.avatar_url,
},
state: {
name: data.merged ? "merged" : data.state,
color: GitHubUtils.getColorForStatus(data.merged ? "merged" : data.state),
},
createdAt: data.created_at,
});
const presentIssue = (
data: Record<string, any>
): UnfurlResponse[UnfurlResourceType.Issue] => ({
url: data.html_url,
type: UnfurlResourceType.Issue,
id: `#${data.number}`,
title: data.title,
description: data.body_text,
author: {
name: data.user.login,
avatarUrl: data.user.avatar_url,
},
labels: data.labels.map((label: { name: string; color: string }) => ({
name: label.name,
color: `#${label.color}`,
})),
state: {
name: data.state,
color: GitHubUtils.getColorForStatus(
data.state === "closed" ? "done" : data.state
),
},
createdAt: data.created_at,
});
const presentLastOnlineInfoFor = (user: User) => {
const locale = dateLocale(user.language);
let info: string;
if (!user.lastActiveAt) {
info = t("Never logged in", { ...opts(user) });
} else if (differenceInMinutes(new Date(), user.lastActiveAt) < 5) {
info = t("Online now", { ...opts(user) });
} else {
info = t("Online {{ timeAgo }}", {
timeAgo: formatDistanceToNowStrict(user.lastActiveAt, {
addSuffix: true,
locale,
}),
...opts(user),
});
}
return info;
};
const presentLastViewedInfoFor = async (user: User, document: Document) => {
const lastView = await View.findOne({
where: {
userId: user.id,
documentId: document.id,
},
order: [["updatedAt", "DESC"]],
});
const lastViewedAt = lastView ? lastView.updatedAt : undefined;
const locale = dateLocale(user.language);
let info: string;
if (!lastViewedAt) {
info = t("Never viewed", { ...opts(user) });
} else if (differenceInMinutes(new Date(), lastViewedAt) < 5) {
info = t("Viewed just now", { ...opts(user) });
} else {
info = t("Viewed {{ timeAgo }}", {
timeAgo: formatDistanceToNowStrict(lastViewedAt, {
addSuffix: true,
locale,
}),
...opts(user),
});
}
return info;
};
const presentLastActivityInfoFor = (document: Document, viewer: User) => {
const locale = dateLocale(viewer.language);
const wasUpdated = document.createdAt !== document.updatedAt;
let info: string;
if (wasUpdated) {
const lastUpdatedByViewer = document.updatedBy.id === viewer.id;
if (lastUpdatedByViewer) {
info = t("You updated {{ timeAgo }}", {
timeAgo: formatDistanceToNowStrict(document.updatedAt, {
addSuffix: true,
locale,
}),
...opts(viewer),
});
} else {
info = t("{{ user }} updated {{ timeAgo }}", {
user: document.updatedBy.name,
timeAgo: formatDistanceToNowStrict(document.updatedAt, {
addSuffix: true,
locale,
}),
...opts(viewer),
});
}
} else {
const lastCreatedByViewer = document.createdById === viewer.id;
if (lastCreatedByViewer) {
info = t("You created {{ timeAgo }}", {
timeAgo: formatDistanceToNowStrict(document.createdAt, {
addSuffix: true,
locale,
}),
...opts(viewer),
});
} else {
info = t("{{ user }} created {{ timeAgo }}", {
user: document.createdBy.name,
timeAgo: formatDistanceToNowStrict(document.createdAt, {
addSuffix: true,
locale,
}),
...opts(viewer),
});
}
}
return info;
};
export default presentUnfurl;