chore(client/ts): port some more files

This commit is contained in:
Elian Doran 2025-03-16 00:45:46 +02:00
parent ee5eba193a
commit 2828b39f48
No known key found for this signature in database
13 changed files with 125 additions and 50 deletions

View File

@ -13,7 +13,7 @@ import { t } from "../services/i18n.js";
import type FNote from "../entities/fnote.js";
// TODO: Move somewhere else nicer.
export type SqlExecuteResults = unknown[];
export type SqlExecuteResults = string[][][];
// TODO: Deduplicate with server.
interface SqlExecuteResponse {

View File

@ -252,7 +252,7 @@ async function cloneNoteToBranch(childNoteId: string, parentBranchId: string, pr
}
}
async function cloneNoteToParentNote(childNoteId: string, parentNoteId: string, prefix: string) {
async function cloneNoteToParentNote(childNoteId: string, parentNoteId: string, prefix?: string) {
const resp = await server.put<Response>(`notes/${childNoteId}/clone-to-note/${parentNoteId}`, {
prefix: prefix
});

View File

@ -1,6 +1,8 @@
import { t } from "../services/i18n.js";
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import NoteListRenderer from "../services/note_list_renderer.js";
import type FNote from "../entities/fnote.js";
import type { EventData } from "../components/app_context.js";
const TPL = `
<div class="search-result-widget">
@ -11,32 +13,37 @@ const TPL = `
min-height: 0;
overflow: auto;
}
.search-result-widget .note-list {
padding: 10px;
}
.search-no-results, .search-not-executed-yet {
margin: 20px;
padding: 20px;
}
</style>
<div class="search-no-results alert alert-info">
${t("search_result.no_notes_found")}
</div>
<div class="search-not-executed-yet alert alert-info">
${t("search_result.search_not_executed")}
</div>
<div class="search-result-widget-content">
</div>
</div>`;
export default class SearchResultWidget extends NoteContextAwareWidget {
private $content!: JQuery<HTMLElement>;
private $noResults!: JQuery<HTMLElement>;
private $notExecutedYet!: JQuery<HTMLElement>;
isEnabled() {
return super.isEnabled() && this.note.type === "search";
return super.isEnabled() && this.note?.type === "search";
}
doRender() {
@ -47,7 +54,7 @@ export default class SearchResultWidget extends NoteContextAwareWidget {
this.$notExecutedYet = this.$widget.find(".search-not-executed-yet");
}
async refreshWithNote(note) {
async refreshWithNote(note: FNote) {
this.$content.empty();
this.$noResults.toggle(note.getChildNoteIds().length === 0 && !!note.searchResultsLoaded);
this.$notExecutedYet.toggle(!note.searchResultsLoaded);
@ -56,7 +63,7 @@ export default class SearchResultWidget extends NoteContextAwareWidget {
await noteListRenderer.renderList();
}
searchRefreshedEvent({ ntxId }) {
searchRefreshedEvent({ ntxId }: EventData<"searchRefreshed">) {
if (!this.isNoteContext(ntxId)) {
return;
}
@ -64,8 +71,8 @@ export default class SearchResultWidget extends NoteContextAwareWidget {
this.refresh();
}
notesReloadedEvent({ noteIds }) {
if (noteIds.includes(this.noteId)) {
notesReloadedEvent({ noteIds }: EventData<"notesReloaded">) {
if (this.noteId && noteIds.includes(this.noteId)) {
this.refresh();
}
}

View File

@ -2,6 +2,8 @@ import NoteContextAwareWidget from "./note_context_aware_widget.js";
import options from "../services/options.js";
import attributeService from "../services/attributes.js";
import { t } from "../services/i18n.js";
import type FNote from "../entities/fnote.js";
import type { EventData } from "../components/app_context.js";
const TPL = `
<div class="shared-info-widget alert alert-warning use-tn-links">
@ -18,8 +20,12 @@ const TPL = `
</div>`;
export default class SharedInfoWidget extends NoteContextAwareWidget {
private $sharedLink!: JQuery<HTMLElement>;
private $sharedText!: JQuery<HTMLElement>;
isEnabled() {
return super.isEnabled() && this.noteId !== "_share" && this.note.hasAncestor("_share");
return super.isEnabled() && this.noteId !== "_share" && this.note?.hasAncestor("_share");
}
doRender() {
@ -29,7 +35,7 @@ export default class SharedInfoWidget extends NoteContextAwareWidget {
this.contentSized();
}
async refreshWithNote(note) {
async refreshWithNote(note: FNote) {
const syncServerHost = options.get("syncServerHost");
let link;
@ -53,7 +59,7 @@ export default class SharedInfoWidget extends NoteContextAwareWidget {
this.$sharedLink.attr("href", link).text(link);
}
getShareId(note) {
getShareId(note: FNote) {
if (note.hasOwnedLabel("shareRoot")) {
return "";
}
@ -61,8 +67,8 @@ export default class SharedInfoWidget extends NoteContextAwareWidget {
return note.getOwnedLabelValue("shareAlias") || note.noteId;
}
entitiesReloadedEvent({ loadResults }) {
if (loadResults.getAttributeRows().find((attr) => attr.name.startsWith("_share") && attributeService.isAffecting(attr, this.note))) {
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (loadResults.getAttributeRows().find((attr) => attr.name?.startsWith("_share") && attributeService.isAffecting(attr, this.note))) {
this.refresh();
} else if (loadResults.getBranchRows().find((branch) => branch.noteId === this.noteId)) {
this.refresh();

View File

@ -5,10 +5,15 @@ import utils from "../services/utils.js";
import syncService from "../services/sync.js";
import dialogService from "../services/dialog.js";
import { t } from "../services/i18n.js";
import type FNote from "../entities/fnote.js";
import type { EventData } from "../components/app_context.js";
export default class SharedSwitchWidget extends SwitchWidget {
isEnabled() {
return super.isEnabled() && !["root", "_share", "_hidden"].includes(this.noteId) && !this.noteId.startsWith("_options");
return super.isEnabled()
&& !["root", "_share", "_hidden"].includes(this.noteId ?? "")
&& !this.noteId?.startsWith("_options");
}
doRender() {
@ -25,19 +30,23 @@ export default class SharedSwitchWidget extends SwitchWidget {
}
async switchOn() {
if (!this.noteId) {
return;
}
await branchService.cloneNoteToParentNote(this.noteId, "_share");
syncService.syncNow(true);
}
async switchOff() {
const shareBranch = this.note.getParentBranches().find((b) => b.parentNoteId === "_share");
const shareBranch = this.note?.getParentBranches().find((b) => b.parentNoteId === "_share");
if (!shareBranch) {
return;
}
if (this.note.getParentBranches().length === 1) {
if (this.note?.getParentBranches().length === 1) {
if (!(await dialogService.confirm(t("shared_switch.shared-branch")))) {
return;
}
@ -48,7 +57,7 @@ export default class SharedSwitchWidget extends SwitchWidget {
syncService.syncNow(true);
}
async refreshWithNote(note) {
async refreshWithNote(note: FNote) {
const isShared = note.hasAncestor("_share");
const canBeUnshared = isShared && note.getParentBranches().find((b) => b.parentNoteId === "_share");
const switchDisabled = isShared && !canBeUnshared;
@ -64,7 +73,7 @@ export default class SharedSwitchWidget extends SwitchWidget {
}
}
entitiesReloadedEvent({ loadResults }) {
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (loadResults.getBranchRows().find((b) => b.noteId === this.noteId)) {
this.refresh();
}

View File

@ -1,3 +1,4 @@
import type { EventData } from "../components/app_context.js";
import { t } from "../services/i18n.js";
import NoteContextAwareWidget from "./note_context_aware_widget.js";
@ -21,6 +22,10 @@ const TPL = `
</div>`;
export default class SqlResultWidget extends NoteContextAwareWidget {
private $resultContainer!: JQuery<HTMLElement>;
private $noRowsAlert!: JQuery<HTMLElement>;
isEnabled() {
return this.note && this.note.mime === "text/x-sqlite;schema=trilium" && super.isEnabled();
}
@ -32,7 +37,7 @@ export default class SqlResultWidget extends NoteContextAwareWidget {
this.$noRowsAlert = this.$widget.find(".sql-query-no-rows");
}
async sqlQueryResultsEvent({ ntxId, results }) {
async sqlQueryResultsEvent({ ntxId, results }: EventData<"sqlQueryResults">) {
if (!this.isNoteContext(ntxId)) {
return;
}

View File

@ -1,6 +1,7 @@
import { t } from "../services/i18n.js";
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import server from "../services/server.js";
import type FNote from "../entities/fnote.js";
const TPL = `
<div class="sql-table-schemas-widget">
@ -38,7 +39,19 @@ const TPL = `
<span class="sql-table-schemas"></span>
</div>`;
interface SchemaResponse {
name: string;
columns: {
name: string;
type: string;
}[];
}
export default class SqlTableSchemasWidget extends NoteContextAwareWidget {
private tableSchemasShown?: boolean;
private $sqlConsoleTableSchemas!: JQuery<HTMLElement>;
isEnabled() {
return this.note && this.note.mime === "text/x-sqlite;schema=trilium" && super.isEnabled();
}
@ -50,14 +63,14 @@ export default class SqlTableSchemasWidget extends NoteContextAwareWidget {
this.$sqlConsoleTableSchemas = this.$widget.find(".sql-table-schemas");
}
async refreshWithNote(note) {
async refreshWithNote(note: FNote) {
if (this.tableSchemasShown) {
return;
}
this.tableSchemasShown = true;
const tableSchema = await server.get("sql/schema");
const tableSchema = await server.get<SchemaResponse[]>("sql/schema");
for (const table of tableSchema) {
const $tableLink = $('<button class="btn">').text(table.name);
@ -73,7 +86,6 @@ export default class SqlTableSchemasWidget extends NoteContextAwareWidget {
$tableLink.tooltip({
html: true,
placement: "bottom",
boundary: "window",
title: $table[0].outerHTML,
sanitize: false
});

View File

@ -131,7 +131,7 @@ export default class SwitchWidget extends NoteContextAwareWidget {
protected switchOffName = "";
protected switchOffTooltip = "";
private disabledTooltip = "";
protected disabledTooltip = "";
private currentState = false;

View File

@ -3,6 +3,7 @@ import TypeWidget from "./type_widget.js";
import appContext from "../../components/app_context.js";
import searchService from "../../services/search.js";
import { t } from "../../services/i18n.js";
import type FNote from "../../entities/fnote.js";
const TPL = `
<div class="note-detail-empty note-detail-printable">
@ -13,7 +14,7 @@ const TPL = `
flex-wrap: wrap;
justify-content: space-evenly;
}
.workspace-notes .workspace-note {
width: 130px;
text-align: center;
@ -40,7 +41,7 @@ const TPL = `
.empty-tab-search .input-clearer-button {
border-bottom-right-radius: 0;
}
.workspace-icon {
text-align: center;
font-size: 500%;
@ -58,6 +59,11 @@ const TPL = `
</div>`;
export default class EmptyTypeWidget extends TypeWidget {
private $autoComplete!: JQuery<HTMLElement>;
private $results!: JQuery<HTMLElement>;
private $workspaceNotes!: JQuery<HTMLElement>;
static getType() {
return "empty";
}
@ -74,7 +80,7 @@ export default class EmptyTypeWidget extends TypeWidget {
hideGoToSelectedNoteButton: true,
allowCreatingNotes: true,
allowJumpToSearchNotes: true,
container: this.$results
container: this.$results[0]
})
.on("autocomplete:noteselected", function (event, suggestion, dataset) {
if (!suggestion.notePath) {
@ -90,7 +96,7 @@ export default class EmptyTypeWidget extends TypeWidget {
super.doRender();
}
async doRefresh(note) {
async doRefresh(note: FNote) {
const workspaceNotes = await searchService.searchForNotes("#workspace #!template");
this.$workspaceNotes.empty();

View File

@ -3,6 +3,8 @@ import TypeWidget from "./type_widget.js";
import libraryLoader from "../../services/library_loader.js";
import imageContextMenuService from "../../menus/image_context_menu.js";
import imageService from "../../services/image.js";
import type FNote from "../../entities/fnote.js";
import type { EventData } from "../../components/app_context.js";
const TPL = `
<div class="note-detail-image note-detail-printable">
@ -10,11 +12,11 @@ const TPL = `
.type-image .note-detail {
height: 100%;
}
.note-detail-image {
height: 100%;
height: 100%;
}
.note-detail-image-wrapper {
position: relative;
display: flex;
@ -23,7 +25,7 @@ const TPL = `
justify-content: center;
height: 100%;
}
.note-detail-image-view {
display: block;
width: auto;
@ -39,6 +41,10 @@ const TPL = `
</div>`;
class ImageTypeWidget extends TypeWidget {
private $imageWrapper!: JQuery<HTMLElement>;
private $imageView!: JQuery<HTMLElement>;
static getType() {
return "image";
}
@ -61,11 +67,11 @@ class ImageTypeWidget extends TypeWidget {
super.doRender();
}
async doRefresh(note) {
async doRefresh(note: FNote) {
this.$imageView.prop("src", utils.createImageSrcUrl(note));
}
copyImageReferenceToClipboardEvent({ ntxId }) {
copyImageReferenceToClipboardEvent({ ntxId }: EventData<"copyImageReferenceToClipboard">) {
if (!this.isNoteContext(ntxId)) {
return;
}
@ -73,7 +79,7 @@ class ImageTypeWidget extends TypeWidget {
imageService.copyImageReferenceToClipboard(this.$imageWrapper);
}
async entitiesReloadedEvent({ loadResults }) {
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (loadResults.isNoteReloaded(this.noteId)) {
this.refresh();
}

View File

@ -1,6 +1,8 @@
import renderService from "../../services/render.js";
import TypeWidget from "./type_widget.js";
import { t } from "../../services/i18n.js";
import type FNote from "../../entities/fnote.js";
import type { EventData } from "../../components/app_context.js";
const TPL = `
<div class="note-detail-render note-detail-printable">
@ -20,6 +22,10 @@ const TPL = `
</div>`;
export default class RenderTypeWidget extends TypeWidget {
private $noteDetailRenderHelp!: JQuery<HTMLElement>;
private $noteDetailRenderContent!: JQuery<HTMLElement>;
static getType() {
return "render";
}
@ -32,7 +38,7 @@ export default class RenderTypeWidget extends TypeWidget {
super.doRender();
}
async doRefresh(note) {
async doRefresh(note: FNote) {
this.$widget.show();
this.$noteDetailRenderHelp.hide();
@ -48,12 +54,12 @@ export default class RenderTypeWidget extends TypeWidget {
}
renderActiveNoteEvent() {
if (this.noteContext.isActive()) {
if (this.noteContext?.isActive()) {
this.refresh();
}
}
async executeWithContentElementEvent({ resolve, ntxId }) {
async executeWithContentElementEvent({ resolve, ntxId }: EventData<"executeWithContentElement">) {
if (!this.isNoteContext(ntxId)) {
return;
}

View File

@ -3,6 +3,8 @@ import NoteContextAwareWidget from "./note_context_aware_widget.js";
import server from "../services/server.js";
import fileWatcher from "../services/file_watcher.js";
import dayjs from "dayjs";
import type { EventData } from "../components/app_context.js";
import type FNote from "../entities/fnote.js";
const TPL = `
<div class="dropdown watched-file-update-status-widget alert alert-warning">
@ -12,21 +14,30 @@ const TPL = `
contain: none;
}
</style>
<p>${t("watched_file_update_status.file_last_modified", { count: "" })}</p>
<p>${t("watched_file_update_status.file_last_modified")}</p>
<div style="display: flex; flex-direction: row; justify-content: space-evenly;">
<button class="btn btn-sm file-upload-button">${t("watched_file_update_status.upload_modified_file")}</button>
<button class="btn btn-sm ignore-this-change-button">${t("watched_file_update_status.ignore_this_change")}</button>
</div>
</div>`;
export default class WatchedFileUpdateStatusWidget extends NoteContextAwareWidget {
private $filePath!: JQuery<HTMLElement>;
private $fileLastModified!: JQuery<HTMLElement>;
private $fileUploadButton!: JQuery<HTMLElement>;
private $ignoreThisChangeButton!: JQuery<HTMLElement>;
isEnabled() {
const { entityType, entityId } = this.getEntity();
return super.isEnabled() && !!fileWatcher.getFileModificationStatus(entityType, entityId);
return super.isEnabled()
&& !!entityType
&& !!entityId
&& !!fileWatcher.getFileModificationStatus(entityType, entityId);
}
doRender() {
@ -43,7 +54,9 @@ export default class WatchedFileUpdateStatusWidget extends NoteContextAwareWidge
filePath: this.$filePath.text()
});
fileWatcher.fileModificationUploaded(entityType, entityId);
if (entityType && entityId) {
fileWatcher.fileModificationUploaded(entityType, entityId);
}
this.refresh();
});
@ -51,13 +64,18 @@ export default class WatchedFileUpdateStatusWidget extends NoteContextAwareWidge
this.$ignoreThisChangeButton.on("click", () => {
const { entityType, entityId } = this.getEntity();
fileWatcher.ignoreModification(entityType, entityId);
if (entityType && entityId) {
fileWatcher.ignoreModification(entityType, entityId);
}
this.refresh();
});
}
async refreshWithNote(note) {
async refreshWithNote(note: FNote) {
const { entityType, entityId } = this.getEntity();
if (!entityType || !entityId) {
return;
}
const status = fileWatcher.getFileModificationStatus(entityType, entityId);
this.$filePath.text(status.filePath);
@ -71,7 +89,7 @@ export default class WatchedFileUpdateStatusWidget extends NoteContextAwareWidge
const { viewScope } = this.noteContext;
if (viewScope.viewMode === "attachments" && viewScope.attachmentId) {
if (viewScope?.viewMode === "attachments" && viewScope.attachmentId) {
return {
entityType: "attachments",
entityId: viewScope.attachmentId
@ -84,7 +102,7 @@ export default class WatchedFileUpdateStatusWidget extends NoteContextAwareWidge
}
}
openedFileUpdatedEvent(data) {
openedFileUpdatedEvent(data: EventData<"openedFileUpdated">) {
console.log(data);
const { entityType, entityId } = this.getEntity();