Files
everyone-can-use-english/enjoy/src/renderer/lib/utils.ts
an-lee 728bfae82f Feat: interactive courses (#736)
* add courses page

* add api for courses

* add course page

* update course type

* update client

* update course page

* refactor courses pages

* render chapter content

* shadow in course

* fix video handler

* update style

* mark finished examples

* fix media player

* update locale

* finish chapter

* refactor

* auto update chapter status

* audo finish chapter

* fix media provider

* fix wavesurfer player

* update continue btn

* refactor chapters & page

* minor fix

* fix undefined

* refactor

* refactor

* disable sentry in dev

* clean markdown format before alignment

* refactor

* fix regenerate

* fix transcription pre-process for `-` connector

* upgrade deps

* handle no chapters

* add llm chat api

* create llm chat

* display llm message

* create message

* handle error

* generate llm message

* display llm datetime

* scroll to message

* tts for llm message

* add course provider

* refactor

* translate llm message

* fix llm chat introduction

* refacotr

* upgrade deps

* refactor style

* handle undefined

* fix posts

* update locales

* update courses api

* add enrollments count

* upgrade yarn

* upgrade deps

* restore dep to fix package in mac

* upgrade deps
2024-07-11 19:14:40 +08:00

141 lines
3.5 KiB
TypeScript

import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import dayjs from "@renderer/lib/dayjs";
import { type DurationUnitType } from "dayjs/plugin/duration";
import i18next, { t } from "i18next";
import Chart from "chart.js/auto";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function secondsToTimestamp(seconds: number) {
const h = Math.floor(seconds / 3600).toString();
const m = Math.floor((seconds % 3600) / 60).toString();
const s = Math.floor((seconds % 3600) % 60).toString();
return `${h.padStart(2, "0")}:${m.padStart(2, "0")}:${s.padStart(2, "0")}`;
}
export function humanizeDuration(
duration: number,
unit: DurationUnitType = "second"
) {
dayjs.locale(i18next.resolvedLanguage?.toLowerCase() || "en");
return dayjs.duration(duration, unit).humanize();
}
export function formatDuration(
duration: number,
unit: DurationUnitType = "second",
format = "HH:mm:ss"
) {
dayjs.locale(i18next.resolvedLanguage?.toLowerCase() || "en");
const display = dayjs.duration(duration, unit).format(format);
return display.replace(/^00:/, "");
}
export function bytesToSize(bytes: number) {
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
if (bytes === 0) {
return "0 Byte";
}
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round(bytes / Math.pow(1024, i)) + " " + sizes[i];
}
export function formatDateTime(date: Date | string) {
dayjs.locale(i18next.resolvedLanguage?.toLowerCase() || "en");
const now = dayjs();
const then = dayjs(date);
if (now.diff(then, "hour") === 0) {
return then.fromNow();
} else if (now.isSame(then, "day")) {
return then.format("HH:mm");
} else if (now.diff(then, "year") === 0) {
return then.format("MM/DD HH:mm");
} else {
return then.format("YYYY/MM/DD HH:mm");
}
}
export function formatDate(date: string | Date) {
dayjs.locale(i18next.resolvedLanguage?.toLowerCase() || "en");
const now = dayjs();
const then = dayjs(date);
if (now.diff(then, "day") === 0) {
return t("today");
} else if (now.diff(then, "day") === 1) {
return t("yesterday");
} else {
return then.fromNow();
}
}
export function renderPitchContour(options: {
wrapper: HTMLElement;
canvasId: string;
labels: string[];
datasets: Chart["data"]["datasets"];
}) {
const { wrapper, datasets, labels, canvasId } = options;
const width = wrapper.getBoundingClientRect().width;
const height = wrapper.getBoundingClientRect().height;
const canvas = document.createElement("canvas");
canvas.id = canvasId;
canvas.style.position = "absolute";
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
canvas.style.top = "0";
canvas.style.left = "0";
wrapper.appendChild(canvas);
new Chart(canvas, {
type: "line",
data: {
labels,
datasets,
},
options: {
plugins: {
legend: {
display: false,
},
title: {
display: false,
},
},
scales: {
x: {
beginAtZero: true,
ticks: {
autoSkip: false,
},
display: false,
grid: {
display: false,
},
border: {
display: false,
},
},
y: {
display: false,
},
},
},
});
}
export function imgErrorToDefalut(
e: React.SyntheticEvent<HTMLImageElement, Event>
) {
const target = e.target as HTMLImageElement;
target.onerror = null;
target.src = "assets/default-img.jpg";
}