mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 10:02:59 +08:00
client-ts: Port services/utils
This commit is contained in:
parent
0c8092b8f4
commit
ba7035a346
20
package-lock.json
generated
20
package-lock.json
generated
@ -94,6 +94,7 @@
|
||||
"@electron-forge/plugin-auto-unpack-natives": "^6.4.2",
|
||||
"@types/archiver": "^6.0.2",
|
||||
"@types/better-sqlite3": "^7.6.9",
|
||||
"@types/bootstrap": "^5.2.10",
|
||||
"@types/cls-hooked": "^4.3.8",
|
||||
"@types/compression": "^1.7.5",
|
||||
"@types/cookie-parser": "^1.4.7",
|
||||
@ -2060,6 +2061,16 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@sindresorhus/is": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
|
||||
@ -2153,6 +2164,15 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/bootstrap": {
|
||||
"version": "5.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.10.tgz",
|
||||
"integrity": "sha512-F2X+cd6551tep0MvVZ6nM8v7XgGN/twpdNDjqS1TUM7YFNEtQYWk+dKAnH+T1gr6QgCoGMPl487xw/9hXooa2g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/cacheable-request": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
|
||||
|
@ -128,6 +128,7 @@
|
||||
"@electron-forge/plugin-auto-unpack-natives": "^6.4.2",
|
||||
"@types/archiver": "^6.0.2",
|
||||
"@types/better-sqlite3": "^7.6.9",
|
||||
"@types/bootstrap": "^5.2.10",
|
||||
"@types/cls-hooked": "^4.3.8",
|
||||
"@types/compression": "^1.7.5",
|
||||
"@types/cookie-parser": "^1.4.7",
|
||||
|
@ -1,4 +1,6 @@
|
||||
function reloadFrontendApp(reason) {
|
||||
import dayjs from "dayjs";
|
||||
|
||||
function reloadFrontendApp(reason: string) {
|
||||
if (reason) {
|
||||
logInfo(`Frontend app reload: ${reason}`);
|
||||
}
|
||||
@ -6,33 +8,33 @@ function reloadFrontendApp(reason) {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
function parseDate(str) {
|
||||
function parseDate(str: string) {
|
||||
try {
|
||||
return new Date(Date.parse(str));
|
||||
}
|
||||
catch (e) {
|
||||
catch (e: any) {
|
||||
throw new Error(`Can't parse date from '${str}': ${e.message} ${e.stack}`);
|
||||
}
|
||||
}
|
||||
|
||||
function padNum(num) {
|
||||
function padNum(num: number) {
|
||||
return `${num <= 9 ? "0" : ""}${num}`;
|
||||
}
|
||||
|
||||
function formatTime(date) {
|
||||
function formatTime(date: Date) {
|
||||
return `${padNum(date.getHours())}:${padNum(date.getMinutes())}`;
|
||||
}
|
||||
|
||||
function formatTimeWithSeconds(date) {
|
||||
function formatTimeWithSeconds(date: Date) {
|
||||
return `${padNum(date.getHours())}:${padNum(date.getMinutes())}:${padNum(date.getSeconds())}`;
|
||||
}
|
||||
|
||||
function formatTimeInterval(ms) {
|
||||
function formatTimeInterval(ms: number) {
|
||||
const seconds = Math.round(ms / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
const plural = (count, name) => `${count} ${name}${count > 1 ? 's' : ''}`;
|
||||
const plural = (count: number, name: string) => `${count} ${name}${count > 1 ? 's' : ''}`;
|
||||
const segments = [];
|
||||
|
||||
if (days > 0) {
|
||||
@ -60,20 +62,20 @@ function formatTimeInterval(ms) {
|
||||
return segments.join(", ");
|
||||
}
|
||||
|
||||
// this is producing local time!
|
||||
function formatDate(date) {
|
||||
/** this is producing local time! **/
|
||||
function formatDate(date: Date) {
|
||||
// return padNum(date.getDate()) + ". " + padNum(date.getMonth() + 1) + ". " + date.getFullYear();
|
||||
// instead of european format we'll just use ISO as that's pretty unambiguous
|
||||
|
||||
return formatDateISO(date);
|
||||
}
|
||||
|
||||
// this is producing local time!
|
||||
function formatDateISO(date) {
|
||||
/** this is producing local time! **/
|
||||
function formatDateISO(date: Date) {
|
||||
return `${date.getFullYear()}-${padNum(date.getMonth() + 1)}-${padNum(date.getDate())}`;
|
||||
}
|
||||
|
||||
function formatDateTime(date) {
|
||||
function formatDateTime(date: Date) {
|
||||
return `${formatDate(date)} ${formatTime(date)}`;
|
||||
}
|
||||
|
||||
@ -93,7 +95,7 @@ function isMac() {
|
||||
return navigator.platform.indexOf('Mac') > -1;
|
||||
}
|
||||
|
||||
function isCtrlKey(evt) {
|
||||
function isCtrlKey(evt: KeyboardEvent) {
|
||||
return (!isMac() && evt.ctrlKey)
|
||||
|| (isMac() && evt.metaKey);
|
||||
}
|
||||
@ -106,7 +108,7 @@ function assertArguments() {
|
||||
}
|
||||
}
|
||||
|
||||
const entityMap = {
|
||||
const entityMap: Record<string, string> = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
@ -117,11 +119,11 @@ const entityMap = {
|
||||
'=': '='
|
||||
};
|
||||
|
||||
function escapeHtml(str) {
|
||||
function escapeHtml(str: string) {
|
||||
return str.replace(/[&<>"'`=\/]/g, s => entityMap[s]);
|
||||
}
|
||||
|
||||
function formatSize(size) {
|
||||
function formatSize(size: number) {
|
||||
size = Math.max(Math.round(size / 1024), 1);
|
||||
|
||||
if (size < 1024) {
|
||||
@ -132,8 +134,8 @@ function formatSize(size) {
|
||||
}
|
||||
}
|
||||
|
||||
function toObject(array, fn) {
|
||||
const obj = {};
|
||||
function toObject<T>(array: T[], fn: (arg0: T) => [key: string, value: T]) {
|
||||
const obj: Record<string, T> = {};
|
||||
|
||||
for (const item of array) {
|
||||
const [key, value] = fn(item);
|
||||
@ -144,7 +146,7 @@ function toObject(array, fn) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
function randomString(len) {
|
||||
function randomString(len: number) {
|
||||
let text = "";
|
||||
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
@ -167,21 +169,22 @@ function isDesktop() {
|
||||
|| (!window.glob?.device && !/Mobi/.test(navigator.userAgent));
|
||||
}
|
||||
|
||||
// the cookie code below works for simple use cases only - ASCII only
|
||||
// not setting a path so that cookies do not leak into other websites if multiplexed with reverse proxy
|
||||
|
||||
function setCookie(name, value) {
|
||||
/**
|
||||
* the cookie code below works for simple use cases only - ASCII only
|
||||
* not setting a path so that cookies do not leak into other websites if multiplexed with reverse proxy
|
||||
*/
|
||||
function setCookie(name: string, value: string) {
|
||||
const date = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000);
|
||||
const expires = `; expires=${date.toUTCString()}`;
|
||||
|
||||
document.cookie = `${name}=${value || ""}${expires};`;
|
||||
}
|
||||
|
||||
function getNoteTypeClass(type) {
|
||||
function getNoteTypeClass(type: string) {
|
||||
return `type-${type}`;
|
||||
}
|
||||
|
||||
function getMimeTypeClass(mime) {
|
||||
function getMimeTypeClass(mime: string) {
|
||||
if (!mime) {
|
||||
return "";
|
||||
}
|
||||
@ -203,7 +206,7 @@ function closeActiveDialog() {
|
||||
}
|
||||
}
|
||||
|
||||
let $lastFocusedElement = null;
|
||||
let $lastFocusedElement: JQuery<HTMLElement> | null;
|
||||
|
||||
// perhaps there should be saved focused element per tab?
|
||||
function saveFocusedElement() {
|
||||
@ -235,7 +238,7 @@ function focusSavedElement() {
|
||||
$lastFocusedElement = null;
|
||||
}
|
||||
|
||||
async function openDialog($dialog, closeActDialog = true) {
|
||||
async function openDialog($dialog: JQuery<HTMLElement>, closeActDialog = true) {
|
||||
if (closeActDialog) {
|
||||
closeActiveDialog();
|
||||
glob.activeDialog = $dialog;
|
||||
@ -253,11 +256,13 @@ async function openDialog($dialog, closeActDialog = true) {
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Fix once keyboard_actions is ported.
|
||||
// @ts-ignore
|
||||
const keyboardActionsService = (await import("./keyboard_actions.js")).default;
|
||||
keyboardActionsService.updateDisplayedShortcuts($dialog);
|
||||
}
|
||||
|
||||
function isHtmlEmpty(html) {
|
||||
function isHtmlEmpty(html: string) {
|
||||
if (!html) {
|
||||
return true;
|
||||
} else if (typeof html !== 'string') {
|
||||
@ -281,13 +286,13 @@ async function clearBrowserCache() {
|
||||
}
|
||||
|
||||
function copySelectionToClipboard() {
|
||||
const text = window.getSelection().toString();
|
||||
if (navigator.clipboard) {
|
||||
const text = window?.getSelection()?.toString();
|
||||
if (text && navigator.clipboard) {
|
||||
navigator.clipboard.writeText(text);
|
||||
}
|
||||
}
|
||||
|
||||
function dynamicRequire(moduleName) {
|
||||
function dynamicRequire(moduleName: string) {
|
||||
if (typeof __non_webpack_require__ !== 'undefined') {
|
||||
return __non_webpack_require__(moduleName);
|
||||
}
|
||||
@ -296,7 +301,7 @@ function dynamicRequire(moduleName) {
|
||||
}
|
||||
}
|
||||
|
||||
function timeLimit(promise, limitMs, errorMessage) {
|
||||
function timeLimit(promise: Promise<void>, limitMs: number, errorMessage: string) {
|
||||
if (!promise || !promise.then) { // it's not actually a promise
|
||||
return promise;
|
||||
}
|
||||
@ -321,7 +326,7 @@ function timeLimit(promise, limitMs, errorMessage) {
|
||||
});
|
||||
}
|
||||
|
||||
function initHelpDropdown($el) {
|
||||
function initHelpDropdown($el: JQuery<HTMLElement>) {
|
||||
// stop inside clicks from closing the menu
|
||||
const $dropdownMenu = $el.find('.help-dropdown .dropdown-menu');
|
||||
$dropdownMenu.on('click', e => e.stopPropagation());
|
||||
@ -332,7 +337,7 @@ function initHelpDropdown($el) {
|
||||
|
||||
const wikiBaseUrl = "https://github.com/zadam/trilium/wiki/";
|
||||
|
||||
function openHelp($button) {
|
||||
function openHelp($button: JQuery<HTMLElement>) {
|
||||
const helpPage = $button.attr("data-help-page");
|
||||
|
||||
if (helpPage) {
|
||||
@ -342,7 +347,7 @@ function openHelp($button) {
|
||||
}
|
||||
}
|
||||
|
||||
function initHelpButtons($el) {
|
||||
function initHelpButtons($el: JQuery<HTMLElement>) {
|
||||
// for some reason, the .on(event, listener, handler) does not work here (e.g. Options -> Sync -> Help button)
|
||||
// so we do it manually
|
||||
$el.on("click", e => {
|
||||
@ -351,35 +356,38 @@ function initHelpButtons($el) {
|
||||
});
|
||||
}
|
||||
|
||||
function filterAttributeName(name) {
|
||||
function filterAttributeName(name: string) {
|
||||
return name.replace(/[^\p{L}\p{N}_:]/ug, "");
|
||||
}
|
||||
|
||||
const ATTR_NAME_MATCHER = new RegExp("^[\\p{L}\\p{N}_:]+$", "u");
|
||||
|
||||
function isValidAttributeName(name) {
|
||||
function isValidAttributeName(name: string) {
|
||||
return ATTR_NAME_MATCHER.test(name);
|
||||
}
|
||||
|
||||
function sleep(time_ms) {
|
||||
function sleep(time_ms: number) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, time_ms);
|
||||
});
|
||||
}
|
||||
|
||||
function escapeRegExp(str) {
|
||||
function escapeRegExp(str: string) {
|
||||
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
|
||||
}
|
||||
|
||||
function areObjectsEqual() {
|
||||
let i, l, leftChain, rightChain;
|
||||
function areObjectsEqual () {
|
||||
let i;
|
||||
let l;
|
||||
let leftChain: Object[];
|
||||
let rightChain: Object[];
|
||||
|
||||
function compare2Objects(x, y) {
|
||||
function compare2Objects (x: unknown, y: unknown) {
|
||||
let p;
|
||||
|
||||
// remember that NaN === NaN returns false
|
||||
// and isNaN(undefined) returns true
|
||||
if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
|
||||
if (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -414,7 +422,7 @@ function areObjectsEqual() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (x.prototype !== y.prototype) {
|
||||
if ((x as any).prototype !== (y as any).prototype) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -429,7 +437,7 @@ function areObjectsEqual() {
|
||||
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
|
||||
return false;
|
||||
}
|
||||
else if (typeof y[p] !== typeof x[p]) {
|
||||
else if (typeof (y as any)[p] !== typeof (x as any)[p]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -438,18 +446,18 @@ function areObjectsEqual() {
|
||||
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
|
||||
return false;
|
||||
}
|
||||
else if (typeof y[p] !== typeof x[p]) {
|
||||
else if (typeof (y as any)[p] !== typeof (x as any)[p]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (typeof (x[p])) {
|
||||
switch (typeof ((x as any)[p])) {
|
||||
case 'object':
|
||||
case 'function':
|
||||
|
||||
leftChain.push(x);
|
||||
rightChain.push(y);
|
||||
|
||||
if (!compare2Objects(x[p], y[p])) {
|
||||
if (!compare2Objects((x as any)[p], (y as any)[p])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -458,7 +466,7 @@ function areObjectsEqual() {
|
||||
break;
|
||||
|
||||
default:
|
||||
if (x[p] !== y[p]) {
|
||||
if ((x as any)[p] !== (y as any)[p]) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@ -486,10 +494,12 @@ function areObjectsEqual() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function copyHtmlToClipboard(content) {
|
||||
function listener(e) {
|
||||
e.clipboardData.setData("text/html", content);
|
||||
e.clipboardData.setData("text/plain", content);
|
||||
function copyHtmlToClipboard(content: string) {
|
||||
function listener(e: ClipboardEvent) {
|
||||
if (e.clipboardData) {
|
||||
e.clipboardData.setData("text/html", content);
|
||||
e.clipboardData.setData("text/plain", content);
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
document.addEventListener("copy", listener);
|
||||
@ -497,11 +507,8 @@ function copyHtmlToClipboard(content) {
|
||||
document.removeEventListener("copy", listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FNote} note
|
||||
* @return {string}
|
||||
*/
|
||||
function createImageSrcUrl(note) {
|
||||
// TODO: Set to FNote once the file is ported.
|
||||
function createImageSrcUrl(note: { noteId: string; title: string }) {
|
||||
return `api/images/${note.noteId}/${encodeURIComponent(note.title)}?timestamp=${Date.now()}`;
|
||||
}
|
||||
|
46
src/public/app/types.d.ts
vendored
Normal file
46
src/public/app/types.d.ts
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
import FNote from "./entities/fnote";
|
||||
|
||||
interface ElectronProcess {
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface CustomGlobals {
|
||||
isDesktop: boolean;
|
||||
isMobile: boolean;
|
||||
device: "mobile" | "desktop";
|
||||
getComponentsByEl: (el: unknown) => unknown;
|
||||
getHeaders: Promise<Record<string, string>>;
|
||||
getReferenceLinkTitle: (href: string) => Promise<string>;
|
||||
getReferenceLinkTitleSync: (href: string) => string;
|
||||
getActiveContextNote: FNote;
|
||||
requireLibrary: (library: string) => Promise<void>;
|
||||
ESLINT: { js: string[]; };
|
||||
appContext: AppContext;
|
||||
froca: Froca;
|
||||
treeCache: Froca;
|
||||
importMarkdownInline: () => Promise<unknown>;
|
||||
SEARCH_HELP_TEXT: string;
|
||||
activeDialog: JQuery<HTMLElement> | null;
|
||||
}
|
||||
|
||||
type RequireMethod = (moduleName: string) => any;
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
logError(message: string);
|
||||
logInfo(message: string);
|
||||
|
||||
process?: ElectronProcess;
|
||||
glob?: CustomGlobals;
|
||||
}
|
||||
|
||||
interface JQuery {
|
||||
autocomplete: (action: "close") => void;
|
||||
}
|
||||
|
||||
declare var logError: (message: string) => void;
|
||||
declare var logInfo: (message: string) => void;
|
||||
declare var glob: CustomGlobals;
|
||||
declare var require: RequireMethod;
|
||||
declare var __non_webpack_require__: RequireMethod | undefined;
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
],
|
||||
"exclude": ["./node_modules/**/*"],
|
||||
"files": [
|
||||
"src/types.d.ts"
|
||||
"src/types.d.ts",
|
||||
"src/public/app/types.d.ts"
|
||||
]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user