fix(desktop): preserve native image context menu
This commit is contained in:
parent
444f8e05a5
commit
431ab0e0be
|
|
@ -32,6 +32,10 @@ interface SaveToEagleInput {
|
|||
mediaUrls: string[]
|
||||
}
|
||||
|
||||
interface SetEagleContextMenuEnabledInput {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
interface LoginToQBittorrentInput {
|
||||
host: string
|
||||
username: string
|
||||
|
|
@ -55,6 +59,29 @@ interface CustomFetchInput {
|
|||
timeout?: number
|
||||
}
|
||||
|
||||
export async function saveMediaToEagle(input: SaveToEagleInput): Promise<any> {
|
||||
try {
|
||||
const res = await fetch("http://localhost:41595/api/item/addFromURLs", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
items: input.mediaUrls?.map((media) => ({
|
||||
url: media,
|
||||
website: input.url,
|
||||
headers: {
|
||||
referer: input.url,
|
||||
},
|
||||
})),
|
||||
}),
|
||||
})
|
||||
return await res.json()
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export class IntegrationService extends IpcService {
|
||||
static override readonly groupName = "integration"
|
||||
|
||||
|
|
@ -125,26 +152,12 @@ ${content}
|
|||
|
||||
@IpcMethod()
|
||||
async saveToEagle(context: IpcContext, input: SaveToEagleInput): Promise<any> {
|
||||
try {
|
||||
const res = await fetch("http://localhost:41595/api/item/addFromURLs", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
items: input.mediaUrls?.map((media) => ({
|
||||
url: media,
|
||||
website: input.url,
|
||||
headers: {
|
||||
referer: input.url,
|
||||
},
|
||||
})),
|
||||
}),
|
||||
})
|
||||
return await res.json()
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
return saveMediaToEagle(input)
|
||||
}
|
||||
|
||||
@IpcMethod()
|
||||
setEagleContextMenuEnabled(context: IpcContext, input: SetEagleContextMenuEnabledInput): void {
|
||||
store.set("eagleContextMenuEnabled", input.enabled)
|
||||
}
|
||||
|
||||
@IpcMethod()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ type StoreData = {
|
|||
"notifications-persistent-ids"?: string[] | null
|
||||
appearance?: "light" | "dark" | "system" | null
|
||||
cacheSizeLimit?: number | null
|
||||
eagleContextMenuEnabled?: boolean | null
|
||||
minimizeToTray?: boolean | null
|
||||
proxy?: string | null
|
||||
qbittorrentSID?: string | null
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { WindowManager } from "~/manager/window"
|
|||
|
||||
import { getIconPath } from "../helper"
|
||||
import { initializeIpcServices } from "../ipc"
|
||||
import { saveMediaToEagle } from "../ipc/services/integration"
|
||||
import { checkAndCleanCodeCache, clearCacheCronJob } from "../lib/cleaner"
|
||||
import { getSessionTokenFromCookies, syncSessionToCliConfig } from "../lib/cli-session-sync"
|
||||
import { t } from "../lib/i18n"
|
||||
|
|
@ -213,6 +214,19 @@ class AppManagerStatic {
|
|||
|
||||
prepend: (_defaultActions, params) => {
|
||||
return [
|
||||
{
|
||||
label: t("contextMenu.saveMediaToEagle"),
|
||||
visible:
|
||||
params.mediaType === "image" &&
|
||||
params.srcURL !== "" &&
|
||||
!!store.get("eagleContextMenuEnabled"),
|
||||
click: () => {
|
||||
void saveMediaToEagle({
|
||||
url: params.pageURL || params.srcURL,
|
||||
mediaUrls: [params.srcURL],
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t("contextMenu.openImageInBrowser"),
|
||||
visible: params.mediaType === "image",
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { tracker } from "@follow/tracker"
|
|||
import { useMutation, useQuery } from "@tanstack/react-query"
|
||||
import type { FetchError } from "ofetch"
|
||||
import { ofetch } from "ofetch"
|
||||
import { useMemo } from "react"
|
||||
import { useEffect, useMemo } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { toast } from "sonner"
|
||||
|
||||
|
|
@ -72,6 +72,14 @@ const useRegisterEagleCommands = () => {
|
|||
|
||||
const isEagleAvailable = enableEagle && (checkEagle.isLoading ? false : !!checkEagle.data)
|
||||
|
||||
useEffect(() => {
|
||||
if (!IN_ELECTRON) return
|
||||
|
||||
void ipcServices?.integration.setEagleContextMenuEnabled({
|
||||
enabled: isEagleAvailable,
|
||||
})
|
||||
}, [isEagleAvailable])
|
||||
|
||||
useRegisterCommandEffect(
|
||||
!isEagleAvailable
|
||||
? []
|
||||
|
|
|
|||
|
|
@ -1,54 +1,13 @@
|
|||
import { SimpleIconsEagle } from "@follow/components/ui/platform-icon/icons.js"
|
||||
import { IN_ELECTRON } from "@follow/shared/constants"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { toast } from "sonner"
|
||||
import { useEventCallback } from "usehooks-ts"
|
||||
|
||||
import { MenuItemText, useShowContextMenu } from "~/atoms/context-menu"
|
||||
import { useIntegrationSettingKey } from "~/atoms/settings/integration"
|
||||
import { ipcServices } from "~/lib/client"
|
||||
|
||||
import type { MarkdownRenderActions } from "../../../components/ui/markdown/types"
|
||||
|
||||
export const useImageContextMenu = (
|
||||
entryUrl?: Nullable<string>,
|
||||
): NonNullable<MarkdownRenderActions["onImageContextMenu"]> => {
|
||||
const { t } = useTranslation()
|
||||
const showContextMenu = useShowContextMenu()
|
||||
const enableEagle = useIntegrationSettingKey("enableEagle")
|
||||
void entryUrl
|
||||
|
||||
return useEventCallback(async (event, imageUrl) => {
|
||||
if (!IN_ELECTRON || !enableEagle || !ipcServices?.integration?.saveToEagle) {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
await showContextMenu(
|
||||
[
|
||||
new MenuItemText({
|
||||
label: t("entry_actions.save_image_to_eagle"),
|
||||
icon: <SimpleIconsEagle />,
|
||||
click: async () => {
|
||||
const response = await ipcServices?.integration.saveToEagle({
|
||||
url: entryUrl || imageUrl,
|
||||
mediaUrls: [imageUrl],
|
||||
})
|
||||
|
||||
if (response?.status === "success") {
|
||||
toast.success(t("entry_actions.saved_to_eagle"), {
|
||||
duration: 3000,
|
||||
})
|
||||
} else {
|
||||
toast.error(t("entry_actions.failed_to_save_to_eagle"), {
|
||||
duration: 3000,
|
||||
})
|
||||
}
|
||||
},
|
||||
}),
|
||||
],
|
||||
event,
|
||||
)
|
||||
return useEventCallback(() => {
|
||||
// Keep Chromium's native image context menu. Electron appends custom media actions in main.
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
"contextMenu.saveImage": "Save Image",
|
||||
"contextMenu.saveImageAs": "Save Image As...",
|
||||
"contextMenu.saveLinkAs": "Save Link As...",
|
||||
"contextMenu.saveMediaToEagle": "Save Media to Eagle",
|
||||
"contextMenu.saveVideo": "Save Video",
|
||||
"contextMenu.saveVideoAs": "Save Video As...",
|
||||
"contextMenu.searchWithGoogle": "Search with Google",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
"contextMenu.saveImage": "Enregistrer l'image",
|
||||
"contextMenu.saveImageAs": "Enregistrer l'image sous...",
|
||||
"contextMenu.saveLinkAs": "Enregistrer le lien sous...",
|
||||
"contextMenu.saveMediaToEagle": "Enregistrer le média dans Eagle",
|
||||
"contextMenu.saveVideo": "Enregistrer la vidéo",
|
||||
"contextMenu.saveVideoAs": "Enregistrer la vidéo sous...",
|
||||
"contextMenu.searchWithGoogle": "Rechercher avec Google",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
"contextMenu.saveImage": "画像を保存",
|
||||
"contextMenu.saveImageAs": "画像を名前を付けて保存...",
|
||||
"contextMenu.saveLinkAs": "名前をつけてリンクを保存...",
|
||||
"contextMenu.saveMediaToEagle": "メディアを Eagle に保存",
|
||||
"contextMenu.saveVideo": "動画を保存",
|
||||
"contextMenu.saveVideoAs": "動画を名前を付けて保存...",
|
||||
"contextMenu.searchWithGoogle": "Google で検索",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
"contextMenu.saveImage": "保存图片",
|
||||
"contextMenu.saveImageAs": "图片另存为...",
|
||||
"contextMenu.saveLinkAs": "链接另存为...",
|
||||
"contextMenu.saveMediaToEagle": "保存到 Eagle",
|
||||
"contextMenu.saveVideo": "保存视频",
|
||||
"contextMenu.saveVideoAs": "视频另存为...",
|
||||
"contextMenu.searchWithGoogle": "在谷歌中搜索",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
"contextMenu.saveImage": "儲存圖片",
|
||||
"contextMenu.saveImageAs": "圖片另存為...",
|
||||
"contextMenu.saveLinkAs": "連結另存為...",
|
||||
"contextMenu.saveMediaToEagle": "儲存媒體至 Eagle",
|
||||
"contextMenu.saveVideo": "儲存影片",
|
||||
"contextMenu.saveVideoAs": "影片另存為...",
|
||||
"contextMenu.searchWithGoogle": "使用 Google 搜尋",
|
||||
|
|
|
|||
Loading…
Reference in New Issue