Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introducing a map note type #1017

Merged
merged 43 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
e1952fe
feat(geomap): create geomap note type
eliandoran Jan 20, 2025
94a0403
feat(geomap): load leaflet
eliandoran Jan 20, 2025
eca3955
feat(geomap): add basic layer
eliandoran Jan 20, 2025
2b8ee31
refactor(geomap): skip module loader for JS
eliandoran Jan 20, 2025
5cefd4f
feat(mindmap): save view center coordinates
eliandoran Jan 20, 2025
f66f437
feat(geomap): restore view coordinates
eliandoran Jan 20, 2025
4d5e04f
feat(geomap): save & restore zoom
eliandoran Jan 20, 2025
f803b9f
feat(geomap): add floating button section
eliandoran Jan 20, 2025
2582924
feat(geomap): add prompt for creating child note
eliandoran Jan 20, 2025
a3f257f
feat(geomap): set location after creating a note
eliandoran Jan 20, 2025
f76b454
feat(geomap): load markers at startup
eliandoran Jan 20, 2025
986a1c2
feat(geomap): reload markers after adding a new note
eliandoran Jan 20, 2025
ef5b2d6
fix(geomap): multiple clicks creating multiple markers
eliandoran Jan 20, 2025
fed0598
feat(geomap): adjust cursor when adding new note
eliandoran Jan 21, 2025
3281bb8
feat(geomap): allow dragging
eliandoran Jan 21, 2025
04367de
fix(geomap): duplicate markers
eliandoran Jan 21, 2025
b2a5f06
feat(geomap): enable autopan for dragging markers
eliandoran Jan 21, 2025
087d479
feat(geomap): setup marker based on note icon
eliandoran Jan 21, 2025
016a9e4
fix(geomap): pixel perfect marker positioning
eliandoran Jan 21, 2025
1be3492
style(geomap): improve alignment for marker icon
eliandoran Jan 21, 2025
08722d5
feat(geomap): add shadow to marker
eliandoran Jan 21, 2025
6b906a9
feat(geomap): add labels to markers
eliandoran Jan 21, 2025
259dcdb
feat(geomap): set custom icon for notes created from within the map
eliandoran Jan 21, 2025
d1aa0e5
feat(geomap): invert note creation workflow
eliandoran Jan 21, 2025
c2cb07e
feat(geomap): dismiss creation if dialog is dismissed
eliandoran Jan 21, 2025
be4ee4c
feat(geomap): dismiss adding with escape
eliandoran Jan 21, 2025
dc7dd51
refactor(geomap): simplify changing state
eliandoran Jan 21, 2025
b813b10
chore(geomap): change attribute to `geolocation`
eliandoran Jan 21, 2025
31491b9
feat(geomap): use persistent notification
eliandoran Jan 21, 2025
6555325
fix(geomap): dismissing add with any key
eliandoran Jan 21, 2025
ac26222
feat(geomap): note preview on tooltip
eliandoran Jan 21, 2025
dbd38ec
fix(geo_map): markers no longer rendering after clicking on a link
eliandoran Jan 22, 2025
2e1ad24
feat(geo_map): add option to remove from map
eliandoran Jan 22, 2025
47b02da
feat(geo_map): add back open context menu
eliandoran Jan 22, 2025
9b1279c
feat(geo_map): add option to open location
eliandoran Jan 22, 2025
e06db00
fix(geomap): display again note tooltip
eliandoran Jan 22, 2025
5a40d3f
fix(build): build errors
eliandoran Jan 22, 2025
d814a4d
chore(i18n): translate geo map messages
eliandoran Jan 22, 2025
0288ebc
feat(context_menu): dismiss note tooltip when a context menu is shown
eliandoran Jan 22, 2025
474ae48
Merge branch 'develop' into feature/map_note_type
eliandoran Jan 22, 2025
7a3a514
fix(geomap): not working on electron
eliandoran Jan 22, 2025
a8e2c29
fix(geomap): error in creating empty map
eliandoran Jan 22, 2025
9e2c592
feat(geomap): set default position
eliandoran Jan 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bin/copy-dist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ const copy = async () => {
"node_modules/codemirror/keymap/",
"node_modules/mind-elixir/dist/",
"node_modules/@highlightjs/cdn-assets/languages",
"node_modules/@highlightjs/cdn-assets/styles"
"node_modules/@highlightjs/cdn-assets/styles",
"node_modules/leaflet/dist"
];

for (const folder of nodeModulesFolder) {
Expand Down
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"@mermaid-js/layout-elk": "0.1.7",
"@mind-elixir/node-menu": "1.0.3",
"@triliumnext/express-partial-content": "1.0.1",
"@types/leaflet": "1.9.16",
"@types/react-dom": "18.3.5",
"archiver": "7.0.1",
"async-mutex": "0.5.0",
Expand Down Expand Up @@ -112,6 +113,7 @@
"jsplumb": "2.15.6",
"katex": "0.16.21",
"knockout": "3.5.1",
"leaflet": "1.9.4",
"mark.js": "8.11.1",
"marked": "15.0.6",
"mermaid": "11.4.1",
Expand Down
3 changes: 2 additions & 1 deletion src/becca/entities/rows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ export const ALLOWED_NOTE_TYPES = [
"book",
"webView",
"code",
"mindMap"
"mindMap",
"geoMap"
] as const;
export type NoteType = (typeof ALLOWED_NOTE_TYPES)[number];

Expand Down
25 changes: 22 additions & 3 deletions src/public/app/components/app_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import type LoadResults from "../services/load_results.js";
import type { Attribute } from "../services/attribute_parser.js";
import type NoteTreeWidget from "../widgets/note_tree.js";
import type { default as NoteContext, GetTextEditorCallback } from "./note_context.js";
import type { ContextMenuEvent } from "../menus/context_menu.js";

interface Layout {
getRootWidget: (appContext: AppContext) => RootWidget;
Expand Down Expand Up @@ -69,6 +70,7 @@ export interface ExecuteCommandData extends CommandData {
*/
export type CommandMappings = {
"api-log-messages": CommandData;
focusTree: CommandData,
focusOnDetail: Required<CommandData>;
focusOnSearchDefinition: Required<CommandData>;
searchNotes: CommandData & {
Expand Down Expand Up @@ -193,6 +195,10 @@ export type CommandMappings = {
setZoomFactorAndSave: {
zoomFactor: string;
}

// Geomap
deleteFromMap: { noteId: string },
openGeoLocation: { noteId: string, event: JQuery.MouseDownEvent }
};

type EventMappings = {
Expand Down Expand Up @@ -227,9 +233,12 @@ type EventMappings = {
activeContextChanged: {
noteContext: NoteContext;
};
beforeNoteSwitch: {
noteContext: NoteContext;
};
noteSwitched: {
noteContext: NoteContext;
notePath: string;
notePath: string | null;
};
noteSwitchedAndActivatedEvent: {
noteContext: NoteContext;
Expand All @@ -248,12 +257,16 @@ type EventMappings = {
noteId: string;
};
hoistedNoteChanged: {
ntxId: string;
noteId: string;
ntxId: string | null;
};
contextsReopenedEvent: {
mainNtxId: string;
tabPosition: number;
};
noteDetailRefreshed: {
ntxId?: string | null;
};
noteContextReorderEvent: {
oldMainNtxId: string;
newMainNtxId: string;
Expand All @@ -266,7 +279,13 @@ type EventMappings = {
};
exportSvg: {
ntxId: string;
}
};
geoMapCreateChildNote: {
ntxId: string | null | undefined; // TODO: deduplicate ntxId
};
tabReorder: {
ntxIdsInOrder: string[]
};
};

export type EventListener<T extends EventNames> = {
Expand Down
2 changes: 1 addition & 1 deletion src/public/app/components/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> {
}
}

triggerEvent(name: string, data = {}): Promise<unknown> | undefined | null {
triggerEvent<T extends EventNames>(name: T, data: EventData<T>): Promise<unknown> | undefined | null {
return this.parent?.triggerEvent(name, data);
}

Expand Down
5 changes: 3 additions & 2 deletions src/public/app/entities/fnote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,16 @@ const NOTE_TYPE_ICONS = {
launcher: "bx bx-link",
doc: "bx bxs-file-doc",
contentWidget: "bx bxs-widget",
mindMap: "bx bx-sitemap"
mindMap: "bx bx-sitemap",
geoMap: "bx bx-map-alt"
};

/**
* There are many different Note types, some of which are entirely opaque to the
* end user. Those types should be used only for checking against, they are
* not for direct use.
*/
type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap";
type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap" | "geoMap";

interface NotePathRecord {
isArchived: boolean;
Expand Down
2 changes: 2 additions & 0 deletions src/public/app/layouts/desktop_layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import ScrollPaddingWidget from "../widgets/scroll_padding.js";
import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolbar.js";
import options from "../services/options.js";
import utils from "../services/utils.js";
import GeoMapButtons from "../widgets/floating_buttons/geo_map_button.js";

export default class DesktopLayout {
constructor(customWidgets) {
Expand Down Expand Up @@ -200,6 +201,7 @@ export default class DesktopLayout {
.child(new ShowHighlightsListWidgetButton())
.child(new CodeButtonsWidget())
.child(new RelationMapButtons())
.child(new GeoMapButtons())
.child(new CopyImageReferenceButton())
.child(new SvgExportButton())
.child(new BacklinksWidget())
Expand Down
4 changes: 4 additions & 0 deletions src/public/app/menus/context_menu.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { CommandNames } from "../components/app_context.js";
import keyboardActionService from "../services/keyboard_actions.js";
import note_tooltip from "../services/note_tooltip.js";
import utils from "../services/utils.js";

interface ContextMenuOptions<T extends CommandNames> {
Expand Down Expand Up @@ -31,6 +32,7 @@ export interface MenuCommandItem<T extends CommandNames> {

export type MenuItem<T extends CommandNames> = MenuCommandItem<T> | MenuSeparatorItem;
export type MenuHandler<T extends CommandNames> = (item: MenuCommandItem<T>, e: JQuery.MouseDownEvent<HTMLElement, undefined, HTMLElement, HTMLElement>) => void;
export type ContextMenuEvent = PointerEvent | MouseEvent | JQuery.ContextMenuEvent;

class ContextMenu {
private $widget: JQuery<HTMLElement>;
Expand All @@ -56,6 +58,8 @@ class ContextMenu {
async show<T extends CommandNames>(options: ContextMenuOptions<T>) {
this.options = options;

note_tooltip.dismissAllTooltips();

if (this.$widget.hasClass("show")) {
// The menu is already visible. Hide the menu then open it again
// at the new location to re-trigger the opening animation.
Expand Down
54 changes: 31 additions & 23 deletions src/public/app/menus/link_context_menu.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,44 @@
import { t } from "../services/i18n.js";
import contextMenu from "./context_menu.js";
import appContext from "../components/app_context.js";
import contextMenu, { type ContextMenuEvent, type MenuItem } from "./context_menu.js";
import appContext, { type CommandNames } from "../components/app_context.js";
import type { ViewScope } from "../services/link.js";

function openContextMenu(notePath: string, e: PointerEvent | MouseEvent | JQuery.ContextMenuEvent, viewScope: ViewScope = {}, hoistedNoteId: string | null = null) {
function openContextMenu(notePath: string, e: ContextMenuEvent, viewScope: ViewScope = {}, hoistedNoteId: string | null = null) {
contextMenu.show({
x: e.pageX,
y: e.pageY,
items: [
{ title: t("link_context_menu.open_note_in_new_tab"), command: "openNoteInNewTab", uiIcon: "bx bx-link-external" },
{ title: t("link_context_menu.open_note_in_new_split"), command: "openNoteInNewSplit", uiIcon: "bx bx-dock-right" },
{ title: t("link_context_menu.open_note_in_new_window"), command: "openNoteInNewWindow", uiIcon: "bx bx-window-open" }
],
selectMenuItemHandler: ({ command }) => {
if (!hoistedNoteId) {
hoistedNoteId = appContext.tabManager.getActiveContext().hoistedNoteId;
}
items: getItems(),
selectMenuItemHandler: ({ command }) => handleLinkContextMenuItem(command, notePath, viewScope, hoistedNoteId)
});
}

function getItems(): MenuItem<CommandNames>[] {
return [
{ title: t("link_context_menu.open_note_in_new_tab"), command: "openNoteInNewTab", uiIcon: "bx bx-link-external" },
{ title: t("link_context_menu.open_note_in_new_split"), command: "openNoteInNewSplit", uiIcon: "bx bx-dock-right" },
{ title: t("link_context_menu.open_note_in_new_window"), command: "openNoteInNewWindow", uiIcon: "bx bx-window-open" }
];
}

if (command === "openNoteInNewTab") {
appContext.tabManager.openContextWithNote(notePath, { hoistedNoteId, viewScope });
} else if (command === "openNoteInNewSplit") {
const subContexts = appContext.tabManager.getActiveContext().getSubContexts();
const { ntxId } = subContexts[subContexts.length - 1];
function handleLinkContextMenuItem(command: string | undefined, notePath: string, viewScope = {}, hoistedNoteId: string | null = null) {
if (!hoistedNoteId) {
hoistedNoteId = appContext.tabManager.getActiveContext().hoistedNoteId;
}

appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath, hoistedNoteId, viewScope });
} else if (command === "openNoteInNewWindow") {
appContext.triggerCommand("openInWindow", { notePath, hoistedNoteId, viewScope });
}
}
});
if (command === "openNoteInNewTab") {
appContext.tabManager.openContextWithNote(notePath, { hoistedNoteId, viewScope });
} else if (command === "openNoteInNewSplit") {
const subContexts = appContext.tabManager.getActiveContext().getSubContexts();
const { ntxId } = subContexts[subContexts.length - 1];

appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath, hoistedNoteId, viewScope });
} else if (command === "openNoteInNewWindow") {
appContext.triggerCommand("openInWindow", { notePath, hoistedNoteId, viewScope });
}
}

export default {
getItems,
handleLinkContextMenuItem,
openContextMenu
};
7 changes: 6 additions & 1 deletion src/public/app/services/library_loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ const HIGHLIGHT_JS: Library = {
}
};

const LEAFLET: Library = {
css: [ "node_modules/leaflet/dist/leaflet.css" ],
}

async function requireLibrary(library: Library) {
if (library.css) {
library.css.map((cssUrl) => requireCss(cssUrl));
Expand Down Expand Up @@ -196,5 +200,6 @@ export default {
MERMAID,
MARKJS,
I18NEXT,
HIGHLIGHT_JS
HIGHLIGHT_JS,
LEAFLET
};
2 changes: 1 addition & 1 deletion src/public/app/services/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ function goToLink(evt: MouseEvent | JQuery.ClickEvent) {
return goToLinkExt(evt, hrefLink, $link);
}

function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | React.PointerEvent<HTMLCanvasElement>, hrefLink: string | undefined, $link: JQuery<HTMLElement> | null) {
function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent | React.PointerEvent<HTMLCanvasElement>, hrefLink: string | undefined, $link?: JQuery<HTMLElement> | null) {
if (hrefLink?.startsWith("data:")) {
return true;
}
Expand Down
13 changes: 7 additions & 6 deletions src/public/app/services/note_tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ function setupGlobalTooltip() {
return;
}

cleanUpTooltips();
dismissAllTooltips();
});
}

function cleanUpTooltips() {
function dismissAllTooltips() {
$(".note-tooltip").remove();
}

Expand Down Expand Up @@ -102,12 +102,12 @@ async function mouseEnterHandler(this: HTMLElement) {
customClass: linkId
});

cleanUpTooltips();
dismissAllTooltips();
$(this).tooltip("show");

// Dismiss the tooltip immediately if a link was clicked inside the tooltip.
$(`.${tooltipClass} a`).on("click", (e) => {
cleanUpTooltips();
dismissAllTooltips();
});

// the purpose of the code below is to:
Expand All @@ -117,7 +117,7 @@ async function mouseEnterHandler(this: HTMLElement) {
const checkTooltip = () => {
if (!$(this).filter(":hover").length && !$(`.${linkId}:hover`).length) {
// cursor is neither over the link nor over the tooltip, user likely is not interested
cleanUpTooltips();
dismissAllTooltips();
} else {
setTimeout(checkTooltip, 1000);
}
Expand Down Expand Up @@ -172,5 +172,6 @@ function renderFootnote($link: JQuery<HTMLElement>, url: string) {

export default {
setupGlobalTooltip,
setupElementTooltip
setupElementTooltip,
dismissAllTooltips
};
3 changes: 2 additions & 1 deletion src/public/app/services/note_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ async function getNoteTypeItems(command?: NoteTypeCommandNames) {
{ title: t("note_types.mermaid-diagram"), command, type: "mermaid", uiIcon: "bx bx-selection" },
{ title: t("note_types.canvas"), command, type: "canvas", uiIcon: "bx bx-pen" },
{ title: t("note_types.web-view"), command, type: "webView", uiIcon: "bx bx-globe-alt" },
{ title: t("note_types.mind-map"), command, type: "mindMap", uiIcon: "bx bx-sitemap" }
{ title: t("note_types.mind-map"), command, type: "mindMap", uiIcon: "bx bx-sitemap" },
{ title: t("note_types.geo-map"), command, type: "geoMap", uiIcon: "bx bx-map-alt" },
];

const templateNoteIds = await server.get<string[]>("search-templates");
Expand Down
Loading
Loading