mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-02 23:06:36 +08:00
[优化] 格式化
This commit is contained in:
+13
-13
@@ -1,5 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
const publicEnvSchema = z.object({
|
||||
NEXT_PUBLIC_API_BASE_URL: z.string().default('/api'),
|
||||
NEXT_PUBLIC_APP_VERSION: z.string().default('dev'),
|
||||
@@ -9,17 +9,17 @@ const rawEnv = {
|
||||
NEXT_PUBLIC_API_BASE_URL: process.env.NEXT_PUBLIC_API_BASE_URL,
|
||||
NEXT_PUBLIC_APP_VERSION: process.env.NEXT_PUBLIC_APP_VERSION,
|
||||
};
|
||||
|
||||
const parsedEnv = publicEnvSchema.parse(rawEnv);
|
||||
|
||||
function normalizeBaseUrl(value: string) {
|
||||
if (value === '/') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return value.replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
|
||||
const parsedEnv = publicEnvSchema.parse(rawEnv);
|
||||
|
||||
function normalizeBaseUrl(value: string) {
|
||||
if (value === '/') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return value.replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
export const publicEnv = {
|
||||
apiBaseUrl: normalizeBaseUrl(parsedEnv.NEXT_PUBLIC_API_BASE_URL),
|
||||
appVersion: parsedEnv.NEXT_PUBLIC_APP_VERSION,
|
||||
|
||||
@@ -1,40 +1,42 @@
|
||||
export const THEME_STORAGE_KEY = 'openflare-theme-mode';
|
||||
export const THEME_MEDIA_QUERY = '(prefers-color-scheme: dark)';
|
||||
|
||||
export const themeModes = ['light', 'dark', 'system'] as const;
|
||||
|
||||
export type ThemeMode = (typeof themeModes)[number];
|
||||
export type ResolvedTheme = Exclude<ThemeMode, 'system'>;
|
||||
|
||||
export function isThemeMode(value: string | null | undefined): value is ThemeMode {
|
||||
return themeModes.includes(value as ThemeMode);
|
||||
}
|
||||
|
||||
export function resolveTheme(
|
||||
mode: ThemeMode,
|
||||
systemPrefersDark: boolean,
|
||||
): ResolvedTheme {
|
||||
if (mode === 'system') {
|
||||
return systemPrefersDark ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
export function getThemeInitScript() {
|
||||
return `(() => {
|
||||
const storageKey = '${THEME_STORAGE_KEY}';
|
||||
const mediaQuery = '${THEME_MEDIA_QUERY}';
|
||||
const isThemeMode = (value) => value === 'light' || value === 'dark' || value === 'system';
|
||||
const storedValue = window.localStorage.getItem(storageKey);
|
||||
const themeMode = isThemeMode(storedValue) ? storedValue : 'system';
|
||||
const resolvedTheme = themeMode === 'system'
|
||||
? (window.matchMedia(mediaQuery).matches ? 'dark' : 'light')
|
||||
: themeMode;
|
||||
const root = document.documentElement;
|
||||
|
||||
root.dataset.themeMode = themeMode;
|
||||
root.dataset.theme = resolvedTheme;
|
||||
root.style.colorScheme = resolvedTheme;
|
||||
})();`;
|
||||
}
|
||||
export const THEME_STORAGE_KEY = 'openflare-theme-mode';
|
||||
export const THEME_MEDIA_QUERY = '(prefers-color-scheme: dark)';
|
||||
|
||||
export const themeModes = ['light', 'dark', 'system'] as const;
|
||||
|
||||
export type ThemeMode = (typeof themeModes)[number];
|
||||
export type ResolvedTheme = Exclude<ThemeMode, 'system'>;
|
||||
|
||||
export function isThemeMode(
|
||||
value: string | null | undefined,
|
||||
): value is ThemeMode {
|
||||
return themeModes.includes(value as ThemeMode);
|
||||
}
|
||||
|
||||
export function resolveTheme(
|
||||
mode: ThemeMode,
|
||||
systemPrefersDark: boolean,
|
||||
): ResolvedTheme {
|
||||
if (mode === 'system') {
|
||||
return systemPrefersDark ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
export function getThemeInitScript() {
|
||||
return `(() => {
|
||||
const storageKey = '${THEME_STORAGE_KEY}';
|
||||
const mediaQuery = '${THEME_MEDIA_QUERY}';
|
||||
const isThemeMode = (value) => value === 'light' || value === 'dark' || value === 'system';
|
||||
const storedValue = window.localStorage.getItem(storageKey);
|
||||
const themeMode = isThemeMode(storedValue) ? storedValue : 'system';
|
||||
const resolvedTheme = themeMode === 'system'
|
||||
? (window.matchMedia(mediaQuery).matches ? 'dark' : 'light')
|
||||
: themeMode;
|
||||
const root = document.documentElement;
|
||||
|
||||
root.dataset.themeMode = themeMode;
|
||||
root.dataset.theme = resolvedTheme;
|
||||
root.style.colorScheme = resolvedTheme;
|
||||
})();`;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { clsx, type ClassValue } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type ClassValue, clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
import { type ClassValue, clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
@@ -1,72 +1,76 @@
|
||||
const dateTimeFormatter = new Intl.DateTimeFormat('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
});
|
||||
|
||||
const relativeTimeFormatter = new Intl.RelativeTimeFormat('zh-CN', {
|
||||
numeric: 'auto',
|
||||
});
|
||||
|
||||
function toDate(value: string | Date | null | undefined) {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalizedValue = typeof value === 'string' ? value.replace(' ', 'T') : value;
|
||||
const date = normalizedValue instanceof Date ? normalizedValue : new Date(normalizedValue);
|
||||
return Number.isNaN(date.getTime()) ? null : date;
|
||||
}
|
||||
|
||||
export function formatDateTime(value: string | Date | null | undefined) {
|
||||
const date = toDate(value);
|
||||
|
||||
if (!date) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
return dateTimeFormatter.format(date).replace(/\//g, '-');
|
||||
}
|
||||
|
||||
export function formatRelativeTime(value: string | Date | null | undefined) {
|
||||
const date = toDate(value);
|
||||
|
||||
if (!date) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
const diffMs = date.getTime() - Date.now();
|
||||
const diffSeconds = Math.round(diffMs / 1000);
|
||||
const absSeconds = Math.abs(diffSeconds);
|
||||
|
||||
if (absSeconds < 60) {
|
||||
return relativeTimeFormatter.format(diffSeconds, 'second');
|
||||
}
|
||||
|
||||
const diffMinutes = Math.round(diffSeconds / 60);
|
||||
if (Math.abs(diffMinutes) < 60) {
|
||||
return relativeTimeFormatter.format(diffMinutes, 'minute');
|
||||
}
|
||||
|
||||
const diffHours = Math.round(diffMinutes / 60);
|
||||
if (Math.abs(diffHours) < 24) {
|
||||
return relativeTimeFormatter.format(diffHours, 'hour');
|
||||
}
|
||||
|
||||
const diffDays = Math.round(diffHours / 24);
|
||||
if (Math.abs(diffDays) < 30) {
|
||||
return relativeTimeFormatter.format(diffDays, 'day');
|
||||
}
|
||||
|
||||
const diffMonths = Math.round(diffDays / 30);
|
||||
if (Math.abs(diffMonths) < 12) {
|
||||
return relativeTimeFormatter.format(diffMonths, 'month');
|
||||
}
|
||||
|
||||
const diffYears = Math.round(diffMonths / 12);
|
||||
return relativeTimeFormatter.format(diffYears, 'year');
|
||||
}
|
||||
const dateTimeFormatter = new Intl.DateTimeFormat('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
});
|
||||
|
||||
const relativeTimeFormatter = new Intl.RelativeTimeFormat('zh-CN', {
|
||||
numeric: 'auto',
|
||||
});
|
||||
|
||||
function toDate(value: string | Date | null | undefined) {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalizedValue =
|
||||
typeof value === 'string' ? value.replace(' ', 'T') : value;
|
||||
const date =
|
||||
normalizedValue instanceof Date
|
||||
? normalizedValue
|
||||
: new Date(normalizedValue);
|
||||
return Number.isNaN(date.getTime()) ? null : date;
|
||||
}
|
||||
|
||||
export function formatDateTime(value: string | Date | null | undefined) {
|
||||
const date = toDate(value);
|
||||
|
||||
if (!date) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
return dateTimeFormatter.format(date).replace(/\//g, '-');
|
||||
}
|
||||
|
||||
export function formatRelativeTime(value: string | Date | null | undefined) {
|
||||
const date = toDate(value);
|
||||
|
||||
if (!date) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
const diffMs = date.getTime() - Date.now();
|
||||
const diffSeconds = Math.round(diffMs / 1000);
|
||||
const absSeconds = Math.abs(diffSeconds);
|
||||
|
||||
if (absSeconds < 60) {
|
||||
return relativeTimeFormatter.format(diffSeconds, 'second');
|
||||
}
|
||||
|
||||
const diffMinutes = Math.round(diffSeconds / 60);
|
||||
if (Math.abs(diffMinutes) < 60) {
|
||||
return relativeTimeFormatter.format(diffMinutes, 'minute');
|
||||
}
|
||||
|
||||
const diffHours = Math.round(diffMinutes / 60);
|
||||
if (Math.abs(diffHours) < 24) {
|
||||
return relativeTimeFormatter.format(diffHours, 'hour');
|
||||
}
|
||||
|
||||
const diffDays = Math.round(diffHours / 24);
|
||||
if (Math.abs(diffDays) < 30) {
|
||||
return relativeTimeFormatter.format(diffDays, 'day');
|
||||
}
|
||||
|
||||
const diffMonths = Math.round(diffDays / 30);
|
||||
if (Math.abs(diffMonths) < 12) {
|
||||
return relativeTimeFormatter.format(diffMonths, 'month');
|
||||
}
|
||||
|
||||
const diffYears = Math.round(diffMonths / 12);
|
||||
return relativeTimeFormatter.format(diffYears, 'year');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user