diff --git a/src/components/article.tsx b/src/components/article.tsx index 6917c58..6ff46f5 100644 --- a/src/components/article.tsx +++ b/src/components/article.tsx @@ -114,11 +114,22 @@ class Article extends React.Component { case "ArrowRight": this.props.offsetItem(input.key === "ArrowLeft" ? -1 : 1) break - case "l": + case "l": case "L": this.toggleWebpage() break default: this.props.shortcuts(this.props.item, input.key) + const keyboardEvent = new KeyboardEvent("keydown", { + code: input.code, + key: input.key, + shiftKey: input.shift, + altKey: input.alt, + ctrlKey: input.control, + metaKey: input.meta, + repeat: input.isAutoRepeat, + bubbles: true + }) + document.dispatchEvent(keyboardEvent) break } } diff --git a/src/components/nav.tsx b/src/components/nav.tsx index 699f829..f589150 100644 --- a/src/components/nav.tsx +++ b/src/components/nav.tsx @@ -7,14 +7,15 @@ import { ProgressIndicator } from "@fluentui/react" import { getWindowBreakpoint } from "../scripts/utils" type NavProps = { - state: AppState, - itemShown: boolean, - fetch: () => void, - menu: () => void, - logs: () => void, - views: () => void, - settings: () => void, + state: AppState + itemShown: boolean + menu: () => void + search: () => void markAllRead: () => void + fetch: () => void + logs: () => void + views: () => void + settings: () => void } type NavState = { @@ -44,6 +45,9 @@ class Nav extends React.Component { case "F1": this.props.menu() break + case "F2": + this.props.search() + break case "F5": this.fetch() break diff --git a/src/components/page.tsx b/src/components/page.tsx index 0ae354c..4befe09 100644 --- a/src/components/page.tsx +++ b/src/components/page.tsx @@ -7,6 +7,7 @@ import ArticleSearch from "./utils/article-search" type PageProps = { menuOn: boolean + contextOn: boolean settingsOn: boolean feeds: string[] itemId: string @@ -35,6 +36,7 @@ class Page extends React.Component { } {this.props.itemId && ( { componentDidUpdate(prevProps: SearchProps) { if (this.props.searchOn && !prevProps.searchOn) { + this.setState({ query: this.props.initQuery }) this.inputRef.current.focus() } } diff --git a/src/containers/nav-container.tsx b/src/containers/nav-container.tsx index 5048ba9..f40c231 100644 --- a/src/containers/nav-container.tsx +++ b/src/containers/nav-container.tsx @@ -5,7 +5,7 @@ import { createSelector } from "reselect" import { RootState } from "../scripts/reducer" import { fetchItems, markAllRead } from "../scripts/models/item" import { toggleMenu, toggleLogMenu, toggleSettings, openViewMenu } from "../scripts/models/app" -import { ViewType } from "../scripts/models/page" +import { ViewType, toggleSearch } from "../scripts/models/page" import Nav from "../components/nav" const getState = (state: RootState) => state.app @@ -25,6 +25,7 @@ const mapDispatchToProps = (dispatch) => ({ logs: () => dispatch(toggleLogMenu()), views: () => dispatch(openViewMenu()), settings: () => dispatch(toggleSettings()), + search: () => dispatch(toggleSearch()), markAllRead: () => { remote.dialog.showMessageBox(remote.getCurrentWindow(), { title: intl.get("nav.markAllRead"), diff --git a/src/containers/page-container.tsx b/src/containers/page-container.tsx index 15cfeed..548a3e7 100644 --- a/src/containers/page-container.tsx +++ b/src/containers/page-container.tsx @@ -4,17 +4,20 @@ import { RootState } from "../scripts/reducer" import Page from "../components/page" import { AppDispatch } from "../scripts/utils" import { dismissItem, showOffsetItem } from "../scripts/models/page" +import { ContextMenuType } from "../scripts/models/app" const getPage = (state: RootState) => state.page const getSettings = (state: RootState) => state.app.settings.display const getMenu = (state: RootState) => state.app.menu +const getContext = (state: RootState) => state.app.contextMenu.type != ContextMenuType.Hidden const mapStateToProps = createSelector( - [getPage, getSettings, getMenu], - (page, settingsOn, menuOn) => ({ + [getPage, getSettings, getMenu, getContext], + (page, settingsOn, menuOn, contextOn) => ({ feeds: [page.feedId], settingsOn: settingsOn, menuOn: menuOn, + contextOn: contextOn, itemId: page.itemId, viewType: page.viewType }) diff --git a/src/electron.ts b/src/electron.ts index 1d1c029..1659624 100644 --- a/src/electron.ts +++ b/src/electron.ts @@ -66,8 +66,12 @@ if (process.platform === 'darwin') { { label: "Edit", submenu: [ + { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" }, + { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" }, + { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" }, { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" }, { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" }, + { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" } ] } ] diff --git a/src/scripts/models/app.ts b/src/scripts/models/app.ts index 3f286a8..30f1214 100644 --- a/src/scripts/models/app.ts +++ b/src/scripts/models/app.ts @@ -266,10 +266,7 @@ export function appReducer( } case INIT_FEEDS: switch (action.status) { - case ActionStatus.Request: return { - ...state, - feedInit: false - } + case ActionStatus.Request: return state default: return { ...state, feedInit: true diff --git a/src/scripts/models/item.ts b/src/scripts/models/item.ts index e689943..c5f9c88 100644 --- a/src/scripts/models/item.ts +++ b/src/scripts/models/item.ts @@ -266,17 +266,18 @@ export function toggleHidden(item: RSSItem): AppThunk { export function itemShortcuts(item: RSSItem, key: string): AppThunk { return (dispatch) => { switch (key) { - case "m": + case "m": case "M": if (item.hasRead) dispatch(markUnread(item)) else dispatch(markRead(item)) break - case "b": + case "b": case "B": + if (!item.hasRead) dispatch(markRead(item)) openExternal(item.link) break - case "s": + case "s": case "S": dispatch(toggleStarred(item)) break - case "h": + case "h": case "H": dispatch(toggleHidden(item)) break } diff --git a/src/scripts/models/page.ts b/src/scripts/models/page.ts index 7b9696f..6f3a0a4 100644 --- a/src/scripts/models/page.ts +++ b/src/scripts/models/page.ts @@ -1,5 +1,5 @@ -import { ALL, SOURCE, loadMore, FeedFilter, FilterType, initFeeds } from "./feed" -import { getWindowBreakpoint, AppThunk } from "../utils" +import { ALL, SOURCE, loadMore, FeedFilter, FilterType, initFeeds, FeedActionTypes, INIT_FEED } from "./feed" +import { getWindowBreakpoint, AppThunk, ActionStatus } from "../utils" import { getDefaultView } from "../settings" import { RSSItem, markRead } from "./item" import { SourceActionTypes, DELETE_SOURCE } from "./source" @@ -104,7 +104,7 @@ export const toggleSearch = (): AppThunk => { return (dispatch, getState) => { let state = getState() dispatch(({ type: TOGGLE_SEARCH })) - if (!getWindowBreakpoint()) { + if (!getWindowBreakpoint() && state.app.menu) { dispatch(toggleMenu()) } if (state.page.searchOn) { @@ -214,7 +214,7 @@ export class PageState { export function pageReducer( state = new PageState(), - action: PageActionTypes | SourceActionTypes + action: PageActionTypes | SourceActionTypes | FeedActionTypes ): PageState { switch (action.type) { case SELECT_PAGE: @@ -244,6 +244,14 @@ export function pageReducer( ...state, itemId: action.item._id } + case INIT_FEED: switch (action.status) { + case ActionStatus.Success: return { + ...state, + itemId: (action.feed._id === state.feedId && action.items.filter(i => i._id === state.itemId).length === 0) + ? null : state.itemId + } + default: return state + } case DELETE_SOURCE: case DISMISS_ITEM: return { ...state,