mirror of
https://github.com/yang991178/fluent-reader.git
synced 2025-02-27 08:57:44 +01:00
image context menu
This commit is contained in:
parent
7a5ad6deb6
commit
ebb85847fe
@ -1,4 +1,5 @@
|
||||
import { ipcRenderer } from "electron"
|
||||
import { ImageCallbackTypes } from "../schema-types"
|
||||
|
||||
const utilsBridge = {
|
||||
platform: process.platform,
|
||||
@ -54,6 +55,9 @@ const utilsBridge = {
|
||||
callback(pos, text)
|
||||
})
|
||||
},
|
||||
imageCallback: (type: ImageCallbackTypes) => {
|
||||
ipcRenderer.invoke("image-callback", type)
|
||||
},
|
||||
|
||||
addWebviewKeydownListener: (callback: (event: Electron.Input) => any) => {
|
||||
ipcRenderer.removeAllListeners("webview-keydown")
|
||||
|
@ -19,6 +19,7 @@ type ArticleProps = {
|
||||
toggleStarred: (item: RSSItem) => void
|
||||
toggleHidden: (item: RSSItem) => void
|
||||
textMenu: (text: string, position: [number, number]) => void
|
||||
imageMenu: (position: [number, number]) => void
|
||||
dismissContextMenu: () => void
|
||||
}
|
||||
|
||||
@ -95,7 +96,8 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
||||
|
||||
contextMenuHandler = (pos: [number, number], text: string) => {
|
||||
if (pos) {
|
||||
this.props.textMenu(text, pos)
|
||||
if (text) this.props.textMenu(text, pos)
|
||||
else this.props.imageMenu(pos)
|
||||
} else {
|
||||
this.props.dismissContextMenu()
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import { ContextualMenu, IContextualMenuItem, ContextualMenuItemType, Directiona
|
||||
import { ContextMenuType } from "../scripts/models/app"
|
||||
import { RSSItem } from "../scripts/models/item"
|
||||
import { ContextReduxProps } from "../containers/context-menu-container"
|
||||
import { ViewType } from "../schema-types"
|
||||
import { ViewType, ImageCallbackTypes } from "../schema-types"
|
||||
import { FilterType } from "../scripts/models/feed"
|
||||
|
||||
export type ContextMenuProps = ContextReduxProps & {
|
||||
@ -152,6 +152,32 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
},
|
||||
getSearchItem(this.props.text)
|
||||
]
|
||||
case ContextMenuType.Image: return [
|
||||
{
|
||||
key: "openInBrowser",
|
||||
text: intl.get("openExternal"),
|
||||
iconProps: { iconName: "NavigateExternalInline" },
|
||||
onClick: () => { window.utils.imageCallback(ImageCallbackTypes.OpenExternal) }
|
||||
},
|
||||
{
|
||||
key: "saveImageAs",
|
||||
text: intl.get("context.saveImageAs"),
|
||||
iconProps: { iconName: "SaveTemplate" },
|
||||
onClick: () => { window.utils.imageCallback(ImageCallbackTypes.SaveAs) }
|
||||
},
|
||||
{
|
||||
key: "copyImage",
|
||||
text: intl.get("context.copyImage"),
|
||||
iconProps: { iconName: "FileImage" },
|
||||
onClick: () => { window.utils.imageCallback(ImageCallbackTypes.Copy) }
|
||||
},
|
||||
{
|
||||
key: "copyImageURL",
|
||||
text: intl.get("context.copyImageURL"),
|
||||
iconProps: { iconName: "Link" },
|
||||
onClick: () => { window.utils.imageCallback(ImageCallbackTypes.CopyLink) }
|
||||
}
|
||||
]
|
||||
case ContextMenuType.View: return [
|
||||
{
|
||||
key: "section_1",
|
||||
|
@ -5,7 +5,7 @@ import { RSSItem, markUnread, markRead, toggleStarred, toggleHidden, itemShortcu
|
||||
import { AppDispatch } from "../scripts/utils"
|
||||
import { dismissItem, showOffsetItem } from "../scripts/models/page"
|
||||
import Article from "../components/article"
|
||||
import { openTextMenu, closeContextMenu } from "../scripts/models/app"
|
||||
import { openTextMenu, closeContextMenu, openImageMenu } from "../scripts/models/app"
|
||||
|
||||
type ArticleContainerProps = {
|
||||
itemId: string
|
||||
@ -35,6 +35,7 @@ const mapDispatchToProps = (dispatch: AppDispatch) => {
|
||||
toggleStarred: (item: RSSItem) => dispatch(toggleStarred(item)),
|
||||
toggleHidden: (item: RSSItem) => dispatch(toggleHidden(item)),
|
||||
textMenu: (text: string, position: [number, number]) => dispatch(openTextMenu(text, position)),
|
||||
imageMenu: (position: [number, number]) => dispatch(openImageMenu(position)),
|
||||
dismissContextMenu: () => dispatch(closeContextMenu())
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,10 @@ const mapStateToProps = createSelector(
|
||||
event: context.event,
|
||||
sids: context.target
|
||||
}
|
||||
case ContextMenuType.Image: return {
|
||||
type: context.type,
|
||||
position: context.position
|
||||
}
|
||||
default: return { type: ContextMenuType.Hidden }
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { ipcMain, shell, dialog, app, session, webContents, clipboard } from "electron"
|
||||
import { WindowManager } from "./window"
|
||||
import fs = require("fs")
|
||||
import { ImageCallbackTypes } from "../schema-types"
|
||||
|
||||
export function openExternal(url: string) {
|
||||
if (url.startsWith("https://") || url.startsWith("http://"))
|
||||
@ -88,8 +89,29 @@ export function setUtilsListeners(manager: WindowManager) {
|
||||
}
|
||||
})
|
||||
contents.on("context-menu", (_, params) => {
|
||||
if (params.selectionText && manager.hasWindow()) {
|
||||
if ((params.hasImageContents || params.selectionText) && manager.hasWindow()) {
|
||||
if (params.hasImageContents) {
|
||||
ipcMain.removeHandler("image-callback")
|
||||
ipcMain.handleOnce("image-callback", (_, type: ImageCallbackTypes) => {
|
||||
switch (type) {
|
||||
case ImageCallbackTypes.OpenExternal:
|
||||
openExternal(params.srcURL)
|
||||
break
|
||||
case ImageCallbackTypes.SaveAs:
|
||||
contents.session.downloadURL(params.srcURL)
|
||||
break
|
||||
case ImageCallbackTypes.Copy:
|
||||
contents.copyImageAt(params.x, params.y)
|
||||
break
|
||||
case ImageCallbackTypes.CopyLink:
|
||||
clipboard.writeText(params.srcURL)
|
||||
break
|
||||
}
|
||||
})
|
||||
manager.mainWindow.webContents.send("webview-context-menu", [params.x, params.y])
|
||||
} else {
|
||||
manager.mainWindow.webContents.send("webview-context-menu", [params.x, params.y], params.selectionText)
|
||||
}
|
||||
contents.executeJavaScript(`new Promise(resolve => {
|
||||
const dismiss = () => {
|
||||
document.removeEventListener("mousedown", dismiss)
|
||||
|
@ -32,6 +32,10 @@ export const enum SearchEngines {
|
||||
Google, Bing, Baidu, DuckDuckGo
|
||||
}
|
||||
|
||||
export const enum ImageCallbackTypes {
|
||||
OpenExternal, SaveAs, Copy, CopyLink
|
||||
}
|
||||
|
||||
export type SchemaTypes = {
|
||||
version: string
|
||||
theme: ThemeSettings
|
||||
|
@ -83,7 +83,10 @@
|
||||
"starredOnly": "Starred only",
|
||||
"fullSearch": "Search in full text",
|
||||
"showHidden": "Show hidden articles",
|
||||
"manageSources": "Manage sources"
|
||||
"manageSources": "Manage sources",
|
||||
"saveImageAs": "Save image as …",
|
||||
"copyImage": "Copy image",
|
||||
"copyImageURL": "Copy image link"
|
||||
},
|
||||
"searchEngine": {
|
||||
"name": "Search engine",
|
||||
|
@ -83,7 +83,10 @@
|
||||
"starredOnly": "仅星标文章",
|
||||
"fullSearch": "在正文中搜索",
|
||||
"showHidden": "显示隐藏文章",
|
||||
"manageSources": "管理订阅源"
|
||||
"manageSources": "管理订阅源",
|
||||
"saveImageAs": "将图像另存为",
|
||||
"copyImage": "复制图像",
|
||||
"copyImageURL": "复制图像链接"
|
||||
},
|
||||
"searchEngine": {
|
||||
"name": "搜索引擎",
|
||||
|
@ -10,7 +10,7 @@ import locales from "../i18n/_locales"
|
||||
import * as db from "../db"
|
||||
|
||||
export const enum ContextMenuType {
|
||||
Hidden, Item, Text, View, Group
|
||||
Hidden, Item, Text, View, Group, Image
|
||||
}
|
||||
|
||||
export const enum AppLogType {
|
||||
@ -74,6 +74,7 @@ export const OPEN_ITEM_MENU = "OPEN_ITEM_MENU"
|
||||
export const OPEN_TEXT_MENU = "OPEN_TEXT_MENU"
|
||||
export const OPEN_VIEW_MENU = "OPEN_VIEW_MENU"
|
||||
export const OPEN_GROUP_MENU = "OPEN_GROUP_MENU"
|
||||
export const OPEN_IMAGE_MENU = "OPEN_IMAGE_MENU"
|
||||
|
||||
interface CloseContextMenuAction {
|
||||
type: typeof CLOSE_CONTEXT_MENU
|
||||
@ -102,8 +103,13 @@ interface OpenGroupMenuAction {
|
||||
sids: number[]
|
||||
}
|
||||
|
||||
interface OpenImageMenuAction {
|
||||
type: typeof OPEN_IMAGE_MENU
|
||||
position: [number, number]
|
||||
}
|
||||
|
||||
export type ContextMenuActionTypes = CloseContextMenuAction | OpenItemMenuAction
|
||||
| OpenTextMenuAction | OpenViewMenuAction | OpenGroupMenuAction
|
||||
| OpenTextMenuAction | OpenViewMenuAction | OpenGroupMenuAction | OpenImageMenuAction
|
||||
|
||||
export const TOGGLE_LOGS = "TOGGLE_LOGS"
|
||||
export const PUSH_NOTIFICATION = "PUSH_NOTIFICATION"
|
||||
@ -163,6 +169,13 @@ export function openGroupMenu(sids: number[], event: React.MouseEvent): ContextM
|
||||
}
|
||||
}
|
||||
|
||||
export function openImageMenu(position: [number, number]): ContextMenuActionTypes {
|
||||
return {
|
||||
type: OPEN_IMAGE_MENU,
|
||||
position: position
|
||||
}
|
||||
}
|
||||
|
||||
export function toggleMenu(): AppThunk {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({ type: TOGGLE_MENU })
|
||||
@ -422,6 +435,13 @@ export function appReducer(
|
||||
target: action.sids
|
||||
}
|
||||
}
|
||||
case OPEN_IMAGE_MENU: return {
|
||||
...state,
|
||||
contextMenu: {
|
||||
type: ContextMenuType.Image,
|
||||
position: action.position
|
||||
}
|
||||
}
|
||||
case TOGGLE_MENU: return {
|
||||
...state,
|
||||
menu: !state.menu
|
||||
|
Loading…
x
Reference in New Issue
Block a user