289 lines
8.0 KiB
TypeScript
289 lines
8.0 KiB
TypeScript
import { MantineColor } from "@mantine/core";
|
||
import { MESSAGE_MAX_TIME_TO_DELEVERED_S } from "../constants";
|
||
import { decode, encode } from "blurhash";
|
||
|
||
export function generateRandomKey(length: number): string {
|
||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||
let result = '';
|
||
const charactersLength = characters.length;
|
||
for (let i = 0; i < length; i++) {
|
||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||
}
|
||
return result;
|
||
}
|
||
|
||
export function murmurHash3_32_gc(key: string, seed: number = 0): number {
|
||
let remainder = key.length & 3;
|
||
let bytes = key.length - remainder;
|
||
let h1 = seed;
|
||
let c1 = 0xcc9e2d51;
|
||
let c2 = 0x1b873593;
|
||
let i = 0;
|
||
|
||
while (i < bytes) {
|
||
let k1 =
|
||
((key.charCodeAt(i) & 0xff)) |
|
||
((key.charCodeAt(++i) & 0xff) << 8) |
|
||
((key.charCodeAt(++i) & 0xff) << 16) |
|
||
((key.charCodeAt(++i) & 0xff) << 24);
|
||
++i;
|
||
|
||
k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
|
||
k1 = (k1 << 15) | (k1 >>> 17);
|
||
k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
|
||
|
||
h1 ^= k1;
|
||
h1 = (h1 << 13) | (h1 >>> 19);
|
||
let h1b = (((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16)) & 0xffffffff;
|
||
h1 = ((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16);
|
||
}
|
||
|
||
let k1 = 0;
|
||
|
||
switch (remainder) {
|
||
case 3:
|
||
k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
|
||
case 2:
|
||
k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
|
||
case 1:
|
||
k1 ^= (key.charCodeAt(i) & 0xff);
|
||
|
||
k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
|
||
k1 = (k1 << 15) | (k1 >>> 17);
|
||
k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
|
||
h1 ^= k1;
|
||
}
|
||
|
||
h1 ^= key.length;
|
||
|
||
h1 ^= h1 >>> 16;
|
||
h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
|
||
h1 ^= h1 >>> 13;
|
||
h1 = (((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16)) & 0xffffffff;
|
||
h1 ^= h1 >>> 16;
|
||
|
||
return h1 >>> 0;
|
||
}
|
||
|
||
function hashCode(input: string) {
|
||
let hash = 0;
|
||
for (let i = 0; i < input.length; i += 1) {
|
||
const char = input.charCodeAt(i);
|
||
hash = (hash << 5) - hash + char;
|
||
hash |= 0;
|
||
}
|
||
return hash;
|
||
}
|
||
|
||
const defaultColors: MantineColor[] = [
|
||
'blue',
|
||
'cyan',
|
||
'grape',
|
||
'green',
|
||
'indigo',
|
||
'lime',
|
||
'orange',
|
||
'pink',
|
||
'red',
|
||
'teal',
|
||
'violet',
|
||
];
|
||
|
||
export function getInitialsColor(name: string, colors: MantineColor[] = defaultColors) {
|
||
const hash = hashCode(name);
|
||
const index = Math.abs(hash) % colors.length;
|
||
return colors[index];
|
||
}
|
||
|
||
export function isMessageDeliveredByTime(messageTime: number, attachmentsCount: number): boolean {
|
||
let maxMessageDeliveredTime = MESSAGE_MAX_TIME_TO_DELEVERED_S * 1000;;
|
||
//((props.time && (Date.now() - props.time * 1000 > ((MESSAGE_MAX_TIME_TO_DELEVERED * 1000) * props.attachmentsCount || 1))
|
||
if (attachmentsCount == 0) {
|
||
let t = Date.now() - messageTime < maxMessageDeliveredTime;
|
||
return t;
|
||
}
|
||
return Date.now() - messageTime < (maxMessageDeliveredTime * attachmentsCount);
|
||
}
|
||
|
||
export function filePrapareForNetworkTransfer(file : File){
|
||
return new Promise<string>((resolve, reject) => {
|
||
const reader = new FileReader();
|
||
reader.onload = () => {
|
||
if(!reader.result){
|
||
reject();
|
||
return;
|
||
}
|
||
resolve(reader.result.toString());
|
||
}
|
||
reader.readAsDataURL(file);
|
||
})
|
||
}
|
||
|
||
export function humanFilesize(size: number) {
|
||
const suffixes = ['B', 'KB', 'MB', 'GB'];
|
||
if(size >= (1024 * 1024 * 1024)){
|
||
return Math.floor(size / (1024 * 1024 * 1024)) + suffixes[3];
|
||
}
|
||
if(size >= (1024 * 1024)){
|
||
return Math.floor(size / (1024 * 1024)) + suffixes[2];
|
||
}
|
||
if(size >= 1024){
|
||
return Math.floor(size / 1024) + suffixes[1];
|
||
}
|
||
return size + suffixes[0];
|
||
}
|
||
|
||
export function imagePrepareForNetworkTransfer(file: File) {
|
||
/**
|
||
* Нужно приводить все изображения к одному формату (png) чтобы избежать проблем с отображением
|
||
* в разных платформах (ios, android, web)
|
||
*/
|
||
return new Promise<string>((resolve, reject) => {
|
||
const reader = new FileReader();
|
||
reader.onload = async () => {
|
||
if (!reader.result) {
|
||
reject();
|
||
return;
|
||
}
|
||
let base64 = reader.result.toString();
|
||
if (file.type === 'image/png') {
|
||
resolve(base64);
|
||
return;
|
||
}
|
||
try {
|
||
const jpegBlob = file;
|
||
const pngBlob = await convertJpegBlobToPngBlob(jpegBlob);
|
||
const pngBase64 = await blobToBase64(pngBlob);
|
||
resolve(pngBase64);
|
||
} catch (error) {
|
||
reject(error);
|
||
}
|
||
};
|
||
reader.readAsDataURL(file);
|
||
});
|
||
}
|
||
|
||
export function blobToBase64(blob: Blob): Promise<string> {
|
||
return new Promise((resolve, reject) => {
|
||
const reader = new FileReader();
|
||
reader.onloadend = () => {
|
||
if (reader.result && typeof reader.result === "string") {
|
||
resolve(reader.result);
|
||
} else {
|
||
reject("Failed to convert blob to base64");
|
||
}
|
||
};
|
||
reader.onerror = reject;
|
||
reader.readAsDataURL(blob);
|
||
});
|
||
}
|
||
|
||
export function createBlobFromBase64Image(base64Image: string): Blob {
|
||
// Split the data URL to get the MIME type and the base64 data
|
||
const parts = base64Image.split(';');
|
||
const mimeType = parts[0].split(':')[1];
|
||
const base64 = parts[1].split(',')[1];
|
||
|
||
// Decode the base64 string
|
||
const byteCharacters = atob(base64);
|
||
const byteNumbers = new Array(byteCharacters.length);
|
||
|
||
// Convert to a Uint8Array
|
||
for (let i = 0; i < byteCharacters.length; i++) {
|
||
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
||
}
|
||
const byteArray = new Uint8Array(byteNumbers);
|
||
|
||
// Create the Blob
|
||
const blob = new Blob([byteArray], { type: mimeType });
|
||
return blob;
|
||
}
|
||
|
||
export function convertJpegBlobToPngBlob(jpegBlob: Blob): Promise<Blob> {
|
||
return new Promise((resolve, reject) => {
|
||
const img = new Image();
|
||
img.onload = () => {
|
||
const canvas = document.createElement('canvas');
|
||
canvas.width = img.width;
|
||
canvas.height = img.height;
|
||
const ctx = canvas.getContext('2d');
|
||
ctx?.drawImage(img, 0, 0);
|
||
|
||
// Convert the canvas content to a PNG Blob
|
||
canvas.toBlob((pngBlob) => {
|
||
if (pngBlob) {
|
||
resolve(pngBlob);
|
||
} else {
|
||
reject(new Error('Failed to create PNG blob.'));
|
||
}
|
||
}, 'image/png', 1);
|
||
};
|
||
img.onerror = (error) => reject(error);
|
||
img.src = URL.createObjectURL(jpegBlob);
|
||
});
|
||
}
|
||
|
||
export function dotMessageIfNeeded(message: string, maxLength: number): string {
|
||
if (message.length <= maxLength) {
|
||
return message;
|
||
}
|
||
return message.substring(0, maxLength).trim() + '...';
|
||
}
|
||
|
||
export function dotCenterIfNeeded(message: string, maxLength : number, viewSymbols : number = 3) {
|
||
if(message.length <= maxLength){
|
||
return message;
|
||
}
|
||
return message.slice(0, viewSymbols) + "..." + message.slice(-viewSymbols)
|
||
}
|
||
|
||
export function isImage(blob : string) : boolean {
|
||
if(!blob){
|
||
return false;
|
||
}
|
||
return blob.startsWith('data:image/');
|
||
}
|
||
|
||
export function blurhashToBase64Image(blurhash: string, width: number, height: number): string {
|
||
const pixels = decode(blurhash, width, height);
|
||
const canvas = document.createElement('canvas');
|
||
canvas.width = width;
|
||
canvas.height = height;
|
||
const ctx = canvas.getContext('2d');
|
||
const imageData = ctx?.createImageData(width, height);
|
||
if (imageData) {
|
||
imageData.data.set(pixels);
|
||
ctx?.putImageData(imageData, 0, 0);
|
||
return canvas.toDataURL();
|
||
}
|
||
return '';
|
||
}
|
||
|
||
export function base64ImageToBlurhash(base64Image: string): Promise<string> {
|
||
const img = new Image();
|
||
const canvas = document.createElement('canvas');
|
||
const ctx = canvas.getContext('2d');
|
||
|
||
return new Promise<string>((resolve, reject) => {
|
||
img.onload = () => {
|
||
canvas.width = img.width;
|
||
canvas.height = img.height;
|
||
ctx?.drawImage(img, 0, 0);
|
||
const imageData = ctx?.getImageData(0, 0, canvas.width, canvas.height);
|
||
if (imageData) {
|
||
const blurhash = encode(
|
||
imageData.data,
|
||
imageData.width,
|
||
imageData.height,
|
||
4,
|
||
4
|
||
);
|
||
resolve(blurhash);
|
||
} else {
|
||
reject('Failed to get image data from canvas.');
|
||
}
|
||
};
|
||
img.onerror = (error) => reject(error);
|
||
img.src = base64Image;
|
||
});
|
||
} |