mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
195 lines
4.9 KiB
TypeScript
195 lines
4.9 KiB
TypeScript
import differenceInMinutes from 'date-fns/differenceInMinutes';
|
|
import differenceInMonths from 'date-fns/differenceInMonths';
|
|
import format from 'date-fns/format';
|
|
import formatDistanceToNow from 'date-fns/formatDistanceToNow';
|
|
import isAfter from 'date-fns/isAfter';
|
|
import ru from 'date-fns/locale/ru';
|
|
|
|
import { COMMENT_BLOCK_DETECTORS, COMMENT_BLOCK_TYPES, ICommentBlock } from '~/constants/comment';
|
|
import { ImagePresets } from '~/constants/urls';
|
|
import { IFile, ValueOf } from '~/types';
|
|
import { CONFIG } from '~/utils/config';
|
|
import {
|
|
formatExclamations,
|
|
formatTextClickableUsernames,
|
|
formatTextComments,
|
|
formatTextDash,
|
|
formatTextMarkdown,
|
|
formatTextSanitizeTags,
|
|
formatTextSanitizeYoutube,
|
|
formatTextTodos,
|
|
} from '~/utils/formatText';
|
|
import { pipe } from '~/utils/ramda';
|
|
import { splitTextByYoutube, splitTextOmitEmpty } from '~/utils/splitText';
|
|
|
|
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
|
|
const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0;
|
|
|
|
return {
|
|
x: centerX + radius * Math.cos(angleInRadians),
|
|
y: centerY + radius * Math.sin(angleInRadians),
|
|
};
|
|
}
|
|
|
|
export const describeArc = (
|
|
x: number,
|
|
y: number,
|
|
radius: number,
|
|
startAngle: number = 0,
|
|
endAngle: number = 360
|
|
): string => {
|
|
const start = polarToCartesian(x, y, radius, endAngle);
|
|
const end = polarToCartesian(x, y, radius, startAngle);
|
|
|
|
const largeArcFlag = endAngle - startAngle <= 180 ? 0 : 1;
|
|
|
|
return [
|
|
'M',
|
|
start.x,
|
|
start.y,
|
|
'A',
|
|
radius,
|
|
radius,
|
|
0,
|
|
largeArcFlag,
|
|
0,
|
|
end.x,
|
|
end.y,
|
|
'L',
|
|
x,
|
|
y,
|
|
'L',
|
|
start.x,
|
|
start.y,
|
|
].join(' ');
|
|
};
|
|
|
|
export const getURLFromString = (
|
|
url?: string,
|
|
size?: typeof ImagePresets[keyof typeof ImagePresets]
|
|
): string => {
|
|
if (size) {
|
|
return (url || '').replace('REMOTE_CURRENT://', `${CONFIG.remoteCurrent}cache/${size}/`);
|
|
}
|
|
|
|
return (url || '').replace('REMOTE_CURRENT://', CONFIG.remoteCurrent);
|
|
};
|
|
|
|
export const getURL = (
|
|
file: Partial<IFile> | undefined,
|
|
size?: typeof ImagePresets[keyof typeof ImagePresets]
|
|
) => {
|
|
return file?.url ? getURLFromString(file.url, size) : '';
|
|
};
|
|
|
|
export const formatText = pipe(
|
|
formatTextSanitizeYoutube,
|
|
formatTextComments,
|
|
formatTextTodos,
|
|
formatExclamations,
|
|
formatTextDash,
|
|
formatTextMarkdown,
|
|
formatTextSanitizeTags,
|
|
formatTextClickableUsernames
|
|
);
|
|
|
|
export const formatTextParagraphs = (text: string): string => (text && formatText(text)) || '';
|
|
|
|
export const findBlockType = (line: string): ValueOf<typeof COMMENT_BLOCK_TYPES> => {
|
|
const match = Object.values(COMMENT_BLOCK_DETECTORS).find(detector => line.match(detector.test));
|
|
return (match && match.type) || COMMENT_BLOCK_TYPES.TEXT;
|
|
};
|
|
|
|
export const splitCommentByBlocks = (text: string): ICommentBlock[] =>
|
|
pipe(
|
|
splitTextByYoutube,
|
|
splitTextOmitEmpty
|
|
)([text]).map(line => ({
|
|
type: findBlockType(line),
|
|
content: line,
|
|
}));
|
|
|
|
export const formatCommentText = (author?: string, text?: string): ICommentBlock[] =>
|
|
author && text ? splitCommentByBlocks(text) : [];
|
|
|
|
export const getPrettyDate = (date?: string): string => {
|
|
if (!date) {
|
|
return '';
|
|
}
|
|
|
|
if (differenceInMonths(new Date(), new Date(date)) >= 3) {
|
|
return format(new Date(date), 'd MMMM yyyy', { locale: ru });
|
|
}
|
|
|
|
return isAfter(new Date(date), new Date()) ||
|
|
differenceInMinutes(new Date(), new Date(date)) <= 30
|
|
? 'Только что'
|
|
: formatDistanceToNow(new Date(date), {
|
|
locale: ru,
|
|
includeSeconds: true,
|
|
addSuffix: true,
|
|
});
|
|
};
|
|
|
|
export const getYoutubeThumb = (url: string) => {
|
|
const match =
|
|
url &&
|
|
url.match(
|
|
/http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-_]*)(&(amp;)?[\w?=]*)?/
|
|
);
|
|
|
|
return match && match[1] ? `https://i.ytimg.com/vi/${match[1]}/hqdefault.jpg` : null;
|
|
};
|
|
|
|
export const stringToColour = (str: string) => {
|
|
let hash = 0;
|
|
|
|
for (let i = 0; i < str.length; i++) {
|
|
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
|
}
|
|
|
|
let colour = '#';
|
|
|
|
for (let i = 0; i < 3; i++) {
|
|
let value = (hash >> (i * 8)) & 0xff;
|
|
colour += ('00' + value.toString(16)).substr(-2);
|
|
}
|
|
|
|
return colour;
|
|
};
|
|
|
|
export const darken = (col: string, amt: number) => {
|
|
let usePound = false;
|
|
|
|
if (col[0] === '#') {
|
|
col = col.slice(1);
|
|
usePound = true;
|
|
}
|
|
|
|
const num = parseInt(col, 16);
|
|
|
|
let r = (num >> 16) + amt;
|
|
|
|
if (r > 255) r = 255;
|
|
else if (r < 0) r = 0;
|
|
|
|
let b = ((num >> 8) & 0x00ff) + amt;
|
|
|
|
if (b > 255) b = 255;
|
|
else if (b < 0) b = 0;
|
|
|
|
let g = (num & 0x0000ff) + amt;
|
|
|
|
if (g > 255) g = 255;
|
|
else if (g < 0) g = 0;
|
|
|
|
return (usePound ? '#' : '') + (g | (b << 8) | (r << 16)).toString(16);
|
|
};
|
|
|
|
export const sizeOf = (bytes: number): string => {
|
|
if (bytes === 0) {
|
|
return '0.00 B';
|
|
}
|
|
let e = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
return (bytes / Math.pow(1024, e)).toFixed(2) + ' ' + ' KMGTP'.charAt(e) + 'B';
|
|
};
|