refactor: remove turborepo
This commit is contained in:
275
src/commons.ts
Normal file
275
src/commons.ts
Normal file
@@ -0,0 +1,275 @@
|
||||
import type { Contact } from "@/types";
|
||||
import { ask, message } from "@tauri-apps/plugin-dialog";
|
||||
import { relaunch } from "@tauri-apps/plugin-process";
|
||||
import { check } from "@tauri-apps/plugin-updater";
|
||||
import { BitcoinUnit } from "bitcoin-units";
|
||||
import { type ClassValue, clsx } from "clsx";
|
||||
import dayjs from "dayjs";
|
||||
import relativeTime from "dayjs/plugin/relativeTime";
|
||||
import updateLocale from "dayjs/plugin/updateLocale";
|
||||
import { decode } from "light-bolt11-decoder";
|
||||
import type { ReactNode } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { type BaseEditor, Transforms } from "slate";
|
||||
import { ReactEditor } from "slate-react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { AUDIOS, IMAGES, VIDEOS } from "./constants";
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
dayjs.extend(updateLocale);
|
||||
|
||||
dayjs.updateLocale("en", {
|
||||
relativeTime: {
|
||||
past: "%s ago",
|
||||
s: "just now",
|
||||
m: "1m",
|
||||
mm: "%dm",
|
||||
h: "1h",
|
||||
hh: "%dh",
|
||||
d: "1d",
|
||||
dd: "%dd",
|
||||
},
|
||||
});
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export const Portal = ({ children }: { children?: ReactNode }) => {
|
||||
return typeof document === "object"
|
||||
? ReactDOM.createPortal(children, document.body)
|
||||
: null;
|
||||
};
|
||||
|
||||
export const isImagePath = (path: string) => {
|
||||
for (const suffix of ["jpg", "jpeg", "gif", "png", "webp", "avif", "tiff"]) {
|
||||
if (path.endsWith(suffix)) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const isImageUrl = (url: string) => {
|
||||
try {
|
||||
if (!url) return false;
|
||||
const ext = new URL(url).pathname.split(".").pop();
|
||||
return ["jpg", "jpeg", "gif", "png", "webp", "avif", "tiff"].includes(ext);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const insertImage = (editor: ReactEditor | BaseEditor, url: string) => {
|
||||
const text = { text: "" };
|
||||
const image = [
|
||||
{
|
||||
type: "image",
|
||||
url,
|
||||
children: [text],
|
||||
},
|
||||
];
|
||||
const extraText = [
|
||||
{
|
||||
type: "paragraph",
|
||||
children: [text],
|
||||
},
|
||||
];
|
||||
|
||||
// @ts-ignore, idk
|
||||
ReactEditor.focus(editor);
|
||||
Transforms.insertNodes(editor, image);
|
||||
Transforms.insertNodes(editor, extraText);
|
||||
};
|
||||
|
||||
export const insertNostrEvent = (
|
||||
editor: ReactEditor | BaseEditor,
|
||||
eventId: string,
|
||||
) => {
|
||||
const text = { text: "" };
|
||||
const event = [
|
||||
{
|
||||
type: "event",
|
||||
eventId: `nostr:${eventId}`,
|
||||
children: [text],
|
||||
},
|
||||
];
|
||||
const extraText = [
|
||||
{
|
||||
type: "paragraph",
|
||||
children: [text],
|
||||
},
|
||||
];
|
||||
|
||||
Transforms.insertNodes(editor, event);
|
||||
Transforms.insertNodes(editor, extraText);
|
||||
};
|
||||
|
||||
export function formatCreatedAt(time: number, message = false) {
|
||||
let formated: string;
|
||||
|
||||
const now = dayjs();
|
||||
const inputTime = dayjs.unix(time);
|
||||
const diff = now.diff(inputTime, "hour");
|
||||
|
||||
if (message) {
|
||||
if (diff < 12) {
|
||||
formated = inputTime.format("HH:mm A");
|
||||
} else {
|
||||
formated = inputTime.format("MMM DD");
|
||||
}
|
||||
} else {
|
||||
if (diff < 24) {
|
||||
formated = inputTime.from(now, true);
|
||||
} else {
|
||||
formated = inputTime.format("MMM DD");
|
||||
}
|
||||
}
|
||||
|
||||
return formated;
|
||||
}
|
||||
|
||||
export function displayNsec(key: string, len: number) {
|
||||
if (key.length <= len) return key;
|
||||
|
||||
const separator = " ... ";
|
||||
|
||||
const sepLen = separator.length;
|
||||
const charsToShow = len - sepLen;
|
||||
const frontChars = Math.ceil(charsToShow / 2);
|
||||
const backChars = Math.floor(charsToShow / 2);
|
||||
|
||||
return (
|
||||
key.substr(0, frontChars) + separator + key.substr(key.length - backChars)
|
||||
);
|
||||
}
|
||||
|
||||
export function displayNpub(pubkey: string, len: number) {
|
||||
if (pubkey.length <= len) return pubkey;
|
||||
|
||||
const separator = " ... ";
|
||||
|
||||
const sepLen = separator.length;
|
||||
const charsToShow = len - sepLen;
|
||||
const frontChars = Math.ceil(charsToShow / 2);
|
||||
const backChars = Math.floor(charsToShow / 2);
|
||||
|
||||
return (
|
||||
pubkey.substr(0, frontChars) +
|
||||
separator +
|
||||
pubkey.substr(pubkey.length - backChars)
|
||||
);
|
||||
}
|
||||
|
||||
export function displayLongHandle(str: string) {
|
||||
const split = str.split("@");
|
||||
const handle = split[0];
|
||||
const service = split[1];
|
||||
|
||||
return `${handle.substring(0, 16)}...@${service}`;
|
||||
}
|
||||
|
||||
// convert number to K, M, B, T, etc.
|
||||
export const compactNumber = Intl.NumberFormat("en", { notation: "compact" });
|
||||
|
||||
// country name
|
||||
export const regionNames = new Intl.DisplayNames(["en"], { type: "language" });
|
||||
|
||||
// verify link can be preview
|
||||
export function canPreview(text: string) {
|
||||
const url = new URL(text);
|
||||
const ext = url.pathname.split(".").pop();
|
||||
const hostname = url.hostname;
|
||||
|
||||
if (VIDEOS.includes(ext)) return false;
|
||||
if (IMAGES.includes(ext)) return false;
|
||||
if (AUDIOS.includes(ext)) return false;
|
||||
|
||||
if (hostname === "youtube.com") return false;
|
||||
if (hostname === "youtu.be") return false;
|
||||
if (hostname === "x.com") return false;
|
||||
if (hostname === "twitter.com") return false;
|
||||
if (hostname === "facebook.com") return false;
|
||||
if (hostname === "vimeo.com") return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// source: https://github.com/synonymdev/bitkit/blob/master/src/utils/displayValues/index.ts
|
||||
export function getBitcoinDisplayValues(satoshis: number) {
|
||||
let bitcoinFormatted = new BitcoinUnit(satoshis, "satoshis")
|
||||
.getValue()
|
||||
.toFixed(10)
|
||||
.replace(/\.?0+$/, "");
|
||||
|
||||
const [bitcoinWhole, bitcoinDecimal] = bitcoinFormatted.split(".");
|
||||
|
||||
// format sats to group thousands
|
||||
// 4000000 -> 4 000 000
|
||||
let res = "";
|
||||
bitcoinFormatted
|
||||
.split("")
|
||||
.reverse()
|
||||
.forEach((c, index) => {
|
||||
if (index > 0 && index % 3 === 0) {
|
||||
res = ` ${res}`;
|
||||
}
|
||||
res = c + res;
|
||||
});
|
||||
|
||||
bitcoinFormatted = res;
|
||||
|
||||
return {
|
||||
bitcoinFormatted,
|
||||
bitcoinWhole,
|
||||
bitcoinDecimal,
|
||||
};
|
||||
}
|
||||
|
||||
export function decodeZapInvoice(tags?: string[][]) {
|
||||
const invoice = tags.find((tag) => tag[0] === "bolt11")?.[1];
|
||||
if (!invoice) return;
|
||||
|
||||
const decodedInvoice = decode(invoice);
|
||||
const amountSection = decodedInvoice.sections.find(
|
||||
(s: { name: string }) => s.name === "amount",
|
||||
);
|
||||
|
||||
const amount = Number.parseInt(amountSection.value);
|
||||
const displayValue = getBitcoinDisplayValues(amount);
|
||||
|
||||
return displayValue;
|
||||
}
|
||||
|
||||
export async function checkForAppUpdates(silent: boolean) {
|
||||
const update = await check();
|
||||
|
||||
if (!update) {
|
||||
if (silent) return;
|
||||
|
||||
await message("You are on the latest version. Stay awesome!", {
|
||||
title: "No Update Available",
|
||||
kind: "info",
|
||||
okLabel: "OK",
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (update?.available) {
|
||||
const yes = await ask(
|
||||
`Update to ${update.version} is available!\n\nRelease notes: ${update.body}`,
|
||||
{
|
||||
title: "Update Available",
|
||||
kind: "info",
|
||||
okLabel: "Update",
|
||||
cancelLabel: "Cancel",
|
||||
},
|
||||
);
|
||||
|
||||
if (yes) {
|
||||
await update.downloadAndInstall();
|
||||
await relaunch();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user