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

Add Feature: Enable Double-Click to Open Links in Built-in Browser #619

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions src/bridges/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ const settingsBridge = {
ipcRenderer.invoke("set-proxy", address)
},

getDoubleClickStatus: (): boolean => {
return ipcRenderer.sendSync("get-double-click-status")
},
toggleDoubleClickStatus: () => {
ipcRenderer.send("toggle-double-click-status")
},

getDefaultView: (): ViewType => {
return ipcRenderer.sendSync("get-view")
},
Expand Down
20 changes: 10 additions & 10 deletions src/components/article.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type ArticleProps = {
item: RSSItem
source: RSSSource
locale: string
openTarget: SourceOpenTarget
shortcuts: (item: RSSItem, e: KeyboardEvent) => void
dismiss: () => void
offsetItem: (offset: number) => void
Expand Down Expand Up @@ -57,11 +58,12 @@ class Article extends React.Component<ArticleProps, ArticleState> {

constructor(props: ArticleProps) {
super(props)
let openTarget = props.openTarget ?? props.source.openTarget;
this.state = {
fontFamily: window.settings.getFont(),
fontSize: window.settings.getFontSize(),
loadWebpage: props.source.openTarget === SourceOpenTarget.Webpage,
loadFull: props.source.openTarget === SourceOpenTarget.FullContent,
loadWebpage: openTarget === SourceOpenTarget.Webpage,
loadFull: openTarget === SourceOpenTarget.FullContent,
fullContent: "",
loaded: false,
error: false,
Expand All @@ -70,7 +72,7 @@ class Article extends React.Component<ArticleProps, ArticleState> {
window.utils.addWebviewContextListener(this.contextMenuHandler)
window.utils.addWebviewKeydownListener(this.keyDownHandler)
window.utils.addWebviewErrorListener(this.webviewError)
if (props.source.openTarget === SourceOpenTarget.FullContent)
if (openTarget === SourceOpenTarget.FullContent)
this.loadFull()
}

Expand Down Expand Up @@ -283,15 +285,13 @@ class Article extends React.Component<ArticleProps, ArticleState> {
}
}
componentDidUpdate = (prevProps: ArticleProps) => {
if (prevProps.item._id != this.props.item._id) {
if (prevProps.item._id != this.props.item._id || prevProps.openTarget != this.props.openTarget) {
let openTarget = this.props.openTarget ?? this.props.source.openTarget;
this.setState({
loadWebpage:
this.props.source.openTarget === SourceOpenTarget.Webpage,
loadFull:
this.props.source.openTarget ===
SourceOpenTarget.FullContent,
loadWebpage: openTarget === SourceOpenTarget.Webpage,
loadFull: openTarget === SourceOpenTarget.FullContent,
})
if (this.props.source.openTarget === SourceOpenTarget.FullContent)
if (openTarget === SourceOpenTarget.FullContent)
this.loadFull()
}
this.componentDidMount()
Expand Down
36 changes: 28 additions & 8 deletions src/components/cards/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,24 @@ export namespace Card {
markRead: (item: RSSItem) => void
contextMenu: (feedId: string, item: RSSItem, e) => void
showItem: (fid: string, item: RSSItem) => void
showItemOnTarget: (fid: string, item: RSSItem, openTarget: SourceOpenTarget) => void
}

const openInBrowser = (props: Props, e: React.MouseEvent) => {
props.markRead(props.item)
window.utils.openExternal(props.item.link, platformCtrl(e))
}

export const bindEventsToProps = (props: Props) => ({
onClick: (e: React.MouseEvent) => onClick(props, e),
onMouseUp: (e: React.MouseEvent) => onMouseUp(props, e),
onKeyDown: (e: React.KeyboardEvent) => onKeyDown(props, e),
})
export const bindEventsToProps = (props: Props) => {
const timeouts: Array<NodeJS.Timeout> = [null]
return ({
onClick: (e: React.MouseEvent) => onClick(props, e, timeouts),
onMouseUp: (e: React.MouseEvent) => onMouseUp(props, e),
onKeyDown: (e: React.KeyboardEvent) => onKeyDown(props, e),
})
}

const onClick = (props: Props, e: React.MouseEvent) => {
const onClick = (props: Props, e: React.MouseEvent, timeouts: Array<NodeJS.Timeout>) => {
e.preventDefault()
e.stopPropagation()
switch (props.source.openTarget) {
Expand All @@ -39,8 +43,24 @@ export namespace Card {
break
}
default: {
props.markRead(props.item)
props.showItem(props.feedId, props.item)
if (!window.settings.getDoubleClickStatus()) {
props.markRead(props.item)
props.showItem(props.feedId, props.item)
break
}
if (timeouts[0] == null) {
timeouts[0] = setTimeout(() => {
clearTimeout(timeouts[0])
timeouts[0] = null
props.markRead(props.item)
props.showItem(props.feedId, props.item)
}, 200)
} else {
clearTimeout(timeouts[0])
timeouts[0] = null
props.markRead(props.item)
props.showItemOnTarget(props.feedId, props.item, SourceOpenTarget.Webpage)
}
break
}
}
Expand Down
1 change: 1 addition & 0 deletions src/components/feeds/cards-feed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class CardsFeed extends React.Component<FeedProps> {
markRead={this.props.markRead}
contextMenu={this.props.contextMenu}
showItem={this.props.showItem}
showItemOnTarget={this.props.showItemOnTarget}
/>
) : (
<div className="flex-fix" key={"f-" + index}></div>
Expand Down
2 changes: 2 additions & 0 deletions src/components/feeds/feed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { RSSFeed, FeedFilter } from "../../scripts/models/feed"
import { ViewType, ViewConfigs } from "../../schema-types"
import CardsFeed from "./cards-feed"
import ListFeed from "./list-feed"
import { SourceOpenTarget } from "../../scripts/models/source"

export type FeedProps = FeedReduxProps & {
feed: RSSFeed
Expand All @@ -19,6 +20,7 @@ export type FeedProps = FeedReduxProps & {
contextMenu: (feedId: string, item: RSSItem, e) => void
loadMore: (feed: RSSFeed) => void
showItem: (fid: string, item: RSSItem) => void
showItemOnTarget: (fid: string, item: RSSItem, openTarget: SourceOpenTarget) => void
}

export class Feed extends React.Component<FeedProps> {
Expand Down
1 change: 1 addition & 0 deletions src/components/feeds/list-feed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class ListFeed extends React.Component<FeedProps> {
markRead: this.props.markRead,
contextMenu: this.props.contextMenu,
showItem: this.props.showItem,
showItemOnTarget: this.props.showItemOnTarget,
} as Card.Props
if (
this.props.viewType === ViewType.List &&
Expand Down
12 changes: 10 additions & 2 deletions src/components/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AnimationClassNames, Icon, FocusTrapZone } from "@fluentui/react"
import ArticleContainer from "../containers/article-container"
import { ViewType } from "../schema-types"
import ArticleSearch from "./utils/article-search"
import { SourceOpenTarget } from "../scripts/models/source"

type PageProps = {
menuOn: boolean
Expand All @@ -12,6 +13,7 @@ type PageProps = {
feeds: string[]
itemId: number
itemFromFeed: boolean
openTarget: SourceOpenTarget
viewType: ViewType
dismissItem: () => void
offsetItem: (offset: number) => void
Expand Down Expand Up @@ -54,7 +56,10 @@ class Page extends React.Component<PageProps> {
<div
className="article-wrapper"
onClick={e => e.stopPropagation()}>
<ArticleContainer itemId={this.props.itemId} />
<ArticleContainer
itemId={this.props.itemId}
openTarget={this.props.openTarget}
/>
</div>
{this.props.itemFromFeed && (
<>
Expand Down Expand Up @@ -93,7 +98,10 @@ class Page extends React.Component<PageProps> {
</div>
{this.props.itemId ? (
<div className="side-article-wrapper">
<ArticleContainer itemId={this.props.itemId} />
<ArticleContainer
itemId={this.props.itemId}
openTarget={this.props.openTarget}
/>
</div>
) : (
<div className="side-logo-wrapper">
Expand Down
24 changes: 24 additions & 0 deletions src/components/settings/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type AppTabProps = {
}

type AppTabState = {
doubleClickStatus: boolean
pacStatus: boolean
pacUrl: string
themeSettings: ThemeSettings
Expand All @@ -46,6 +47,7 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
constructor(props) {
super(props)
this.state = {
doubleClickStatus: window.settings.getDoubleClickStatus(),
pacStatus: window.settings.getProxyStatus(),
pacUrl: window.settings.getProxy(),
themeSettings: getThemeSettings(),
Expand Down Expand Up @@ -156,6 +158,13 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
})
}

toggleDoubleClickStatus = () => {
window.settings.toggleDoubleClickStatus()
this.setState({
doubleClickStatus: window.settings.getDoubleClickStatus(),
})
}

handleInputChange = event => {
const name: string = event.target.name
// @ts-ignore
Expand Down Expand Up @@ -262,6 +271,21 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
</form>
)}

<Stack horizontal verticalAlign="baseline">
<Stack.Item grow>
<Label>{intl.get("app.enableDoubleClick")}</Label>
</Stack.Item>
<Stack.Item>
<Toggle
checked={this.state.doubleClickStatus}
onChange={this.toggleDoubleClickStatus}
/>
</Stack.Item>
</Stack>
<span className="settings-hint up">
{intl.get("app.doubleClickHint")}
</span>

<Label>{intl.get("app.cleanup")}</Label>
<Stack horizontal>
<Stack.Item grow>
Expand Down
8 changes: 6 additions & 2 deletions src/containers/article-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,31 @@ import {
} from "../scripts/models/app"
import {
RSSSource,
SourceOpenTarget,
SourceTextDirection,
updateSource,
} from "../scripts/models/source"

type ArticleContainerProps = {
itemId: number
openTarget: SourceOpenTarget
}

const getItem = (state: RootState, props: ArticleContainerProps) =>
state.items[props.itemId]
const getSource = (state: RootState, props: ArticleContainerProps) =>
state.sources[state.items[props.itemId].source]
const getLocale = (state: RootState) => state.app.locale
const getOpenTarget = (state: RootState, props: ArticleContainerProps) => props.openTarget

const makeMapStateToProps = () => {
return createSelector(
[getItem, getSource, getLocale],
(item, source, locale) => ({
[getItem, getSource, getLocale, getOpenTarget],
(item, source, locale, openTarget) => ({
item: item,
source: source,
locale: locale,
openTarget: openTarget,
})
)
}
Expand Down
4 changes: 3 additions & 1 deletion src/containers/feed-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { RootState } from "../scripts/reducer"
import { markRead, RSSItem, itemShortcuts } from "../scripts/models/item"
import { openItemMenu } from "../scripts/models/app"
import { loadMore, RSSFeed } from "../scripts/models/feed"
import { showItem } from "../scripts/models/page"
import { showItem, showItemOnTarget } from "../scripts/models/page"
import { ViewType } from "../schema-types"
import { Feed } from "../components/feeds/feed"
import { SourceOpenTarget } from "../scripts/models/source"

interface FeedContainerProps {
feedId: string
Expand Down Expand Up @@ -53,6 +54,7 @@ const mapDispatchToProps = dispatch => {
dispatch(openItemMenu(item, feedId, e)),
loadMore: (feed: RSSFeed) => dispatch(loadMore(feed)),
showItem: (fid: string, item: RSSItem) => dispatch(showItem(fid, item)),
showItemOnTarget: (fid: string, item: RSSItem, openTarget: SourceOpenTarget) => dispatch(showItemOnTarget(fid, item, openTarget)),
}
}

Expand Down
1 change: 1 addition & 0 deletions src/containers/page-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const mapStateToProps = createSelector(
contextOn: contextOn,
itemId: page.itemId,
itemFromFeed: page.itemFromFeed,
openTarget: page.openTarget,
viewType: page.viewType,
})
)
Expand Down
14 changes: 14 additions & 0 deletions src/main/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,20 @@ ipcMain.handle("set-proxy", (_, address = null) => {
setProxy(address)
})

const DOUBLE_CLICK_STATUS_KEY = "doubleClickStatus"
export function getDoubleClickStatus() {
return store.get(DOUBLE_CLICK_STATUS_KEY, false)
}
function toggleDoubleClickStatus() {
store.set(DOUBLE_CLICK_STATUS_KEY, !getDoubleClickStatus())
}
ipcMain.on("get-double-click-status", event => {
event.returnValue = getDoubleClickStatus()
})
ipcMain.on("toggle-double-click-status", () => {
toggleDoubleClickStatus()
})

const VIEW_STORE_KEY = "view"
ipcMain.on("get-view", event => {
event.returnValue = store.get(VIEW_STORE_KEY, ViewType.Cards)
Expand Down
1 change: 1 addition & 0 deletions src/schema-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export type SchemaTypes = {
theme: ThemeSettings
pac: string
pacOn: boolean
doubleClickStatus: boolean
view: ViewType
locale: string
sourceGroups: SourceGroup[]
Expand Down
2 changes: 2 additions & 0 deletions src/scripts/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@
"pac": "PAC Address",
"setPac": "Set PAC",
"pacHint": "For Socks proxies, it is recommended for PAC to return \"SOCKS5\" for proxy-side DNS. Turning off proxy requires restart.",
"enableDoubleClick": "Open in the built-in browser on double-click",
"doubleClickHint": "Enabling this may introduce a 200-millisecond delay for single clicks.",
"fetchInterval": "Automatic fetch interval",
"never": "Never"
}
Expand Down
2 changes: 2 additions & 0 deletions src/scripts/i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@
"pac": "PAC地址",
"setPac": "设置PAC",
"pacHint": "对于Socks代理建议PAC返回“SOCKS5”以启用代理端解析。关闭代理需重启应用后生效。",
"enableDoubleClick": "卡片双击直接用内置浏览器打开",
"doubleClickHint": "开启会导致单击存在200毫秒延迟。",
"fetchInterval": "自动抓取频率",
"never": "从不"
}
Expand Down
Loading