0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00
ghost/apps/comments-ui/src/utils/helpers.ts
Kevin Ansfield f982bbe9fa Extracted comment member name and initial helper functions
no issue

- extracted functions for easier re-use and testing
- removed duplicate definition of comment member initials function
2024-10-15 07:53:57 +01:00

210 lines
6.7 KiB
TypeScript

import {Comment, Member, TranslationFunction} from '../AppContext';
export function formatNumber(number: number): string {
if (number !== 0 && !number) {
return '';
}
// Adds in commas for separators
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
export function formatRelativeTime(dateString: string, t: TranslationFunction): string {
const date = new Date(dateString);
const now = new Date();
// Diff is in seconds
let diff = Math.round((now.getTime() - date.getTime()) / 1000);
if (diff < 5) {
return t('Just now');
}
if (diff < 60) {
return t('{{amount}} seconds ago', {amount: diff});
}
// Diff in minutes
diff = diff / 60;
if (diff < 60) {
if (Math.floor(diff) === 1) {
return t(`One min ago`);
}
return t('{{amount}} mins ago', {amount: Math.floor(diff)});
}
// First check for yesterday
// (we ignore setting 'yesterday' if close to midnight and keep using minutes until 1 hour difference)
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
if (date.getFullYear() === yesterday.getFullYear() && date.getMonth() === yesterday.getMonth() && date.getDate() === yesterday.getDate()) {
return t('Yesterday');
}
// Diff in hours
diff = diff / 60;
if (diff < 24) {
if (Math.floor(diff) === 1) {
return t(`One hour ago`);
}
return t('{{amount}} hrs ago', {amount: Math.floor(diff)});
}
// Diff in days
diff = diff / 24;
if (diff < 7) {
if (Math.floor(diff) === 1) {
// Special case, we should compare based on dates in the future instead
return t(`One day ago`);
}
return t('{{amount}} days ago', {amount: Math.floor(diff)});
}
// Diff in weeks
diff = diff / 7;
if (diff < 4) {
if (Math.floor(diff) === 1) {
// Special case, we should compare based on dates in the future instead
return t(`One week ago`);
}
return t('{{amount}} weeks ago', {amount: Math.floor(diff)});
}
// Diff in months
diff = diff * 7 / 30;
if (diff < 12) {
if (Math.floor(diff) === 1) {
// Special case, we should compare based on dates in the future instead
return t(`One month ago`);
}
return t('{{amount}} months ago', {amount: Math.floor(diff)});
}
// Diff in years
diff = diff * 30 / 365;
if (Math.floor(diff) === 1) {
// Special case, we should compare based on dates in the future instead
return t(`One year ago`);
}
return t('{{amount}} years ago', {amount: Math.floor(diff)});
}
export function formatExplicitTime(dateString: string): string {
const date = new Date(dateString);
const day = date.toLocaleDateString('en-us', {day: '2-digit'}); // eg. 01
const month = date.toLocaleString('en-us', {month: 'short'}); // eg. Jan
const year = date.getFullYear(); // eg. 2022
const hour = (date.getHours() < 10 ? '0' : '') + date.getHours(); // eg. 02
const minute = (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(); // eg. 09
return `${day} ${month} ${year} ${hour}:${minute}`;
}
export function getInitials(name: string): string {
if (!name) {
return '';
}
const parts = name.split(' ');
if (parts.length === 0) {
return '';
}
if (parts.length === 1) {
return parts[0].substring(0, 1).toLocaleUpperCase();
}
return parts[0].substring(0, 1).toLocaleUpperCase() + parts[parts.length - 1].substring(0, 1).toLocaleUpperCase();
}
export function getMemberName(member: Member | null, t: TranslationFunction) {
if (!member) {
return t('Deleted member');
}
if (!member.name) {
return t('Anonymous');
}
return member.name;
}
export function getMemberNameFromComment(comment: Comment, t: TranslationFunction) {
return getMemberName(comment.member, t);
}
export function getMemberInitialsFromComment(comment: Comment, t: TranslationFunction) {
return getInitials(getMemberName(comment.member, t));
}
// Rudimentary check for screen width
// Note, this should be the same as breakpoint defined in Tailwind config
export function isMobile() {
return (Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0) < 480);
}
export function isCommentPublished(comment: Comment) {
return comment.status === 'published';
}
/**
* Returns the y scroll position (top) of the main window of a given element that is in one or multiple stacked iframes
*/
export const getScrollToPosition = (element: HTMLElement) => {
let yOffset = 0;
// Because we are working in an iframe, we need to resolve the position inside this iframe to the position in the top window
// Get the window of the element, not the window (which is the top window)
let currentWindow: Window | null = element.ownerDocument.defaultView;
// Loop all iframe parents (if we have multiple)
while (currentWindow && currentWindow !== window) {
const currentParentWindow = currentWindow.parent;
for (let idx = 0; idx < currentParentWindow.frames.length; idx++) {
if (currentParentWindow.frames[idx] === currentWindow) {
for (const frameElement of currentParentWindow.document.getElementsByTagName('iframe')) {
if (frameElement.contentWindow === currentWindow) {
const rect = frameElement.getBoundingClientRect();
yOffset += rect.top + currentWindow.pageYOffset;
}
}
currentWindow = currentParentWindow;
break;
}
}
}
const y = element.getBoundingClientRect().top + window.pageYOffset + yOffset;
return y;
};
/**
* Scroll to an element that is in an iframe, only if it is outside the current viewport
*/
export const scrollToElement = (element: HTMLElement) => {
// Is the form already in view?
const elementHeight = element.offsetHeight;
// Start y position of the form
const yMin = getScrollToPosition(element);
// Y position of the end of the form
const yMax = yMin + elementHeight;
// Trigger scrolling when yMin and yMax is closer than this to the border of the viewport
const offset = 64;
const viewportHeight = window.innerHeight;
const viewPortYMin = window.scrollY;
const viewPortYMax = viewPortYMin + viewportHeight;
if (yMin - offset < viewPortYMin || yMax + offset > viewPortYMax) {
// Center the form in the viewport
const yCenter = (yMin + yMax) / 2;
window.scrollTo({
top: yCenter - viewportHeight / 2,
left: 0,
behavior: 'smooth'
});
}
};