From d31af2ddc2ccfa8fe55d13a0dde982812169cadf Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 6 Jul 2025 23:34:07 +0300 Subject: [PATCH] feat(views/geomap): add a context menu for empty area --- .../view_widgets/geo_view/context_menu.ts | 21 +++++++++++++ .../widgets/view_widgets/geo_view/editing.ts | 28 ++++++++++++++++- .../widgets/view_widgets/geo_view/index.ts | 30 ++++--------------- 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/apps/client/src/widgets/view_widgets/geo_view/context_menu.ts b/apps/client/src/widgets/view_widgets/geo_view/context_menu.ts index 1e1a4b3d7..410064f0f 100644 --- a/apps/client/src/widgets/view_widgets/geo_view/context_menu.ts +++ b/apps/client/src/widgets/view_widgets/geo_view/context_menu.ts @@ -1,8 +1,10 @@ +import { LeafletMouseEvent } from "leaflet"; import appContext from "../../../components/app_context.js"; import type { ContextMenuEvent } from "../../../menus/context_menu.js"; import contextMenu from "../../../menus/context_menu.js"; import linkContextMenu from "../../../menus/link_context_menu.js"; import { t } from "../../../services/i18n.js"; +import { createNewNote } from "./editing.js"; export default function openContextMenu(noteId: string, e: ContextMenuEvent) { contextMenu.show({ @@ -30,3 +32,22 @@ export default function openContextMenu(noteId: string, e: ContextMenuEvent) { } }); } + +export function openMapContextMenu(noteId: string, e: LeafletMouseEvent) { + contextMenu.show({ + x: e.originalEvent.pageX, + y: e.originalEvent.pageY, + items: [ + { title: t("geo-map-context.add-note"), command: "addNoteToMap", uiIcon: "bx bx-plus" } + ], + selectMenuItemHandler: ({ command }) => { + switch (command) { + case "addNoteToMap": + createNewNote(noteId, e); + break; + default: + appContext.triggerCommand(command); + } + } + }); +} diff --git a/apps/client/src/widgets/view_widgets/geo_view/editing.ts b/apps/client/src/widgets/view_widgets/geo_view/editing.ts index 863305ebc..7d70ba370 100644 --- a/apps/client/src/widgets/view_widgets/geo_view/editing.ts +++ b/apps/client/src/widgets/view_widgets/geo_view/editing.ts @@ -1,8 +1,34 @@ -import { LatLng } from "leaflet"; +import { LatLng, LeafletMouseEvent } from "leaflet"; import attributes from "../../../services/attributes"; import { LOCATION_ATTRIBUTE } from "./index.js"; +import dialog from "../../../services/dialog"; +import server from "../../../services/server"; +import { t } from "../../../services/i18n"; + +const CHILD_NOTE_ICON = "bx bx-pin"; + +// TODO: Deduplicate +interface CreateChildResponse { + note: { + noteId: string; + }; +} export async function moveMarker(noteId: string, latLng: LatLng | null) { const value = latLng ? [latLng.lat, latLng.lng].join(",") : ""; await attributes.setLabel(noteId, LOCATION_ATTRIBUTE, value); } + +export async function createNewNote(noteId: string, e: LeafletMouseEvent) { + const title = await dialog.prompt({ message: t("relation_map.enter_title_of_new_note"), defaultValue: t("relation_map.default_new_note_title") }); + + if (title?.trim()) { + const { note } = await server.post(`notes/${noteId}/children?target=into`, { + title, + content: "", + type: "text" + }); + attributes.setLabel(note.noteId, "iconClass", CHILD_NOTE_ICON); + moveMarker(note.noteId, e.latlng); + } +} diff --git a/apps/client/src/widgets/view_widgets/geo_view/index.ts b/apps/client/src/widgets/view_widgets/geo_view/index.ts index 5791ecf38..e67768fce 100644 --- a/apps/client/src/widgets/view_widgets/geo_view/index.ts +++ b/apps/client/src/widgets/view_widgets/geo_view/index.ts @@ -7,18 +7,9 @@ import processNoteWithMarker, { processNoteWithGpxTrack } from "./markers.js"; import { hasTouchBar } from "../../../services/utils.js"; import toast from "../../../services/toast.js"; import { CommandListenerData, EventData } from "../../../components/app_context.js"; -import dialog from "../../../services/dialog.js"; -import server from "../../../services/server.js"; -import attributes from "../../../services/attributes.js"; -import { moveMarker } from "./editing.js"; +import { createNewNote, moveMarker } from "./editing.js"; import link from "../../../services/link.js"; - -// TODO: Deduplicate -interface CreateChildResponse { - note: { - noteId: string; - }; -} +import { openMapContextMenu } from "./context_menu.js"; const TPL = /*html*/`
@@ -102,7 +93,6 @@ interface MapData { const DEFAULT_COORDINATES: [number, number] = [3.878638227135724, 446.6630455551659]; const DEFAULT_ZOOM = 2; export const LOCATION_ATTRIBUTE = "geolocation"; -const CHILD_NOTE_ICON = "bx bx-pin"; enum State { Normal, @@ -166,7 +156,8 @@ export default class GeoView extends ViewMode { const updateFn = () => this.spacedUpdate.scheduleUpdate(); map.on("moveend", updateFn); map.on("zoomend", updateFn); - map.on("click", (e) => this.#onMapClicked(e)); + map.on("click", (e) => this.#onMapClicked(e)) + map.on("contextmenu", (e) => openMapContextMenu(this.parentNote.noteId, e)); this.#reloadMarkers(); @@ -299,18 +290,7 @@ export default class GeoView extends ViewMode { } toast.closePersistent("geo-new-note"); - const title = await dialog.prompt({ message: t("relation_map.enter_title_of_new_note"), defaultValue: t("relation_map.default_new_note_title") }); - - if (title?.trim()) { - const { note } = await server.post(`notes/${this.parentNote.noteId}/children?target=into`, { - title, - content: "", - type: "text" - }); - attributes.setLabel(note.noteId, "iconClass", CHILD_NOTE_ICON); - moveMarker(note.noteId, e.latlng); - } - + await createNewNote(this.parentNote.noteId, e); this.#changeState(State.Normal); }