refactor context menu as function component
This commit is contained in:
parent
ba9edbd65b
commit
5ecf189ed6
@ -13,38 +13,25 @@ import {
|
||||
ContextualMenuItemType,
|
||||
DirectionalHint,
|
||||
} from "office-ui-fabric-react/lib/ContextualMenu"
|
||||
import { ContextMenuType } from "../scripts/models/app"
|
||||
import { RSSItem } from "../scripts/models/item"
|
||||
import { ContextReduxProps } from "../containers/context-menu-container"
|
||||
import { closeContextMenu, ContextMenuType } from "../scripts/models/app"
|
||||
import {
|
||||
markAllRead,
|
||||
markRead,
|
||||
markUnread,
|
||||
RSSItem,
|
||||
toggleHidden,
|
||||
toggleStarred,
|
||||
} from "../scripts/models/item"
|
||||
import { ViewType, ImageCallbackTypes, ViewConfigs } from "../schema-types"
|
||||
import { FilterType } from "../scripts/models/feed"
|
||||
|
||||
export type ContextMenuProps = ContextReduxProps & {
|
||||
type: ContextMenuType
|
||||
event?: MouseEvent | string
|
||||
position?: [number, number]
|
||||
item?: RSSItem
|
||||
feedId?: string
|
||||
text?: string
|
||||
url?: string
|
||||
viewType?: ViewType
|
||||
viewConfigs?: ViewConfigs
|
||||
filter?: FilterType
|
||||
sids?: number[]
|
||||
showItem: (feedId: string, item: RSSItem) => void
|
||||
markRead: (item: RSSItem) => void
|
||||
markUnread: (item: RSSItem) => void
|
||||
toggleStarred: (item: RSSItem) => void
|
||||
toggleHidden: (item: RSSItem) => void
|
||||
switchView: (viewType: ViewType) => void
|
||||
setViewConfigs: (configs: ViewConfigs) => void
|
||||
switchFilter: (filter: FilterType) => void
|
||||
toggleFilter: (filter: FilterType) => void
|
||||
markAllRead: (sids?: number[], date?: Date, before?: boolean) => void
|
||||
fetchItems: (sids: number[]) => void
|
||||
settings: (sids: number[]) => void
|
||||
close: () => void
|
||||
}
|
||||
import { useAppDispatch, useAppSelector } from "../scripts/reducer"
|
||||
import {
|
||||
setViewConfigs,
|
||||
showItem,
|
||||
switchFilter,
|
||||
switchView,
|
||||
toggleFilter,
|
||||
} from "../scripts/models/page"
|
||||
|
||||
export const shareSubmenu = (item: RSSItem): IContextualMenuItem[] => [
|
||||
{ key: "qr", url: item.link, onRender: renderShareQR },
|
||||
@ -69,21 +56,42 @@ function getSearchItem(text: string): IContextualMenuItem {
|
||||
}
|
||||
}
|
||||
|
||||
export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
getItems = (): IContextualMenuItem[] => {
|
||||
switch (this.props.type) {
|
||||
export function ContextMenu() {
|
||||
const { type } = useAppSelector(state => state.app.contextMenu)
|
||||
|
||||
switch (type) {
|
||||
case ContextMenuType.Hidden:
|
||||
return null
|
||||
case ContextMenuType.Item:
|
||||
return [
|
||||
return <ItemContextMenu />
|
||||
case ContextMenuType.Text:
|
||||
return <TextContextMenu />
|
||||
case ContextMenuType.Image:
|
||||
return <ImageContextMenu />
|
||||
case ContextMenuType.View:
|
||||
return <ViewContextMenu />
|
||||
case ContextMenuType.Group:
|
||||
return <GroupContextMenu />
|
||||
case ContextMenuType.MarkRead:
|
||||
return <MarkReadContextMenu />
|
||||
}
|
||||
}
|
||||
|
||||
function ItemContextMenu() {
|
||||
const dispatch = useAppDispatch()
|
||||
const viewConfigs = useAppSelector(state => state.page.viewConfigs)
|
||||
const target = useAppSelector(state => state.app.contextMenu.target)
|
||||
const item = target[0] as RSSItem
|
||||
const feedId = target[1] as string
|
||||
|
||||
const menuItems: IContextualMenuItem[] = [
|
||||
{
|
||||
key: "showItem",
|
||||
text: intl.get("context.read"),
|
||||
iconProps: { iconName: "TextDocument" },
|
||||
onClick: () => {
|
||||
this.props.markRead(this.props.item)
|
||||
this.props.showItem(
|
||||
this.props.feedId,
|
||||
this.props.item
|
||||
)
|
||||
dispatch(markRead(item))
|
||||
dispatch(showItem(feedId, item))
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -91,28 +99,27 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
text: intl.get("openExternal"),
|
||||
iconProps: { iconName: "NavigateExternalInline" },
|
||||
onClick: e => {
|
||||
this.props.markRead(this.props.item)
|
||||
window.utils.openExternal(
|
||||
this.props.item.link,
|
||||
platformCtrl(e)
|
||||
)
|
||||
dispatch(markRead(item))
|
||||
window.utils.openExternal(item.link, platformCtrl(e))
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "markAsRead",
|
||||
text: this.props.item.hasRead
|
||||
text: item.hasRead
|
||||
? intl.get("article.markUnread")
|
||||
: intl.get("article.markRead"),
|
||||
iconProps: this.props.item.hasRead
|
||||
iconProps: item.hasRead
|
||||
? {
|
||||
iconName: "RadioBtnOn",
|
||||
style: { fontSize: 14, textAlign: "center" },
|
||||
}
|
||||
: { iconName: "StatusCircleRing" },
|
||||
onClick: () => {
|
||||
if (this.props.item.hasRead)
|
||||
this.props.markUnread(this.props.item)
|
||||
else this.props.markRead(this.props.item)
|
||||
if (item.hasRead) {
|
||||
dispatch(markUnread(item))
|
||||
} else {
|
||||
dispatch(markRead(item))
|
||||
}
|
||||
},
|
||||
split: true,
|
||||
subMenuProps: {
|
||||
@ -124,11 +131,9 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
iconName: "Down",
|
||||
style: { fontSize: 14 },
|
||||
},
|
||||
onClick: () =>
|
||||
this.props.markAllRead(
|
||||
null,
|
||||
this.props.item.date
|
||||
),
|
||||
onClick: () => {
|
||||
dispatch(markAllRead(null, item.date))
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "markAbove",
|
||||
@ -137,40 +142,35 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
iconName: "Up",
|
||||
style: { fontSize: 14 },
|
||||
},
|
||||
onClick: () =>
|
||||
this.props.markAllRead(
|
||||
null,
|
||||
this.props.item.date,
|
||||
false
|
||||
),
|
||||
onClick: () => {
|
||||
dispatch(markAllRead(null, item.date, false))
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "toggleStarred",
|
||||
text: this.props.item.starred
|
||||
text: item.starred
|
||||
? intl.get("article.unstar")
|
||||
: intl.get("article.star"),
|
||||
iconProps: {
|
||||
iconName: this.props.item.starred
|
||||
? "FavoriteStar"
|
||||
: "FavoriteStarFill",
|
||||
iconName: item.starred ? "FavoriteStar" : "FavoriteStarFill",
|
||||
},
|
||||
onClick: () => {
|
||||
this.props.toggleStarred(this.props.item)
|
||||
dispatch(toggleStarred(item))
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "toggleHidden",
|
||||
text: this.props.item.hidden
|
||||
text: item.hidden
|
||||
? intl.get("article.unhide")
|
||||
: intl.get("article.hide"),
|
||||
iconProps: {
|
||||
iconName: this.props.item.hidden ? "View" : "Hide3",
|
||||
iconName: item.hidden ? "View" : "Hide3",
|
||||
},
|
||||
onClick: () => {
|
||||
this.props.toggleHidden(this.props.item)
|
||||
dispatch(toggleHidden(item))
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -182,24 +182,24 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
text: intl.get("context.share"),
|
||||
iconProps: { iconName: "Share" },
|
||||
subMenuProps: {
|
||||
items: shareSubmenu(this.props.item),
|
||||
items: shareSubmenu(item),
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "copyTitle",
|
||||
text: intl.get("context.copyTitle"),
|
||||
onClick: () => {
|
||||
window.utils.writeClipboard(this.props.item.title)
|
||||
window.utils.writeClipboard(item.title)
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "copyURL",
|
||||
text: intl.get("context.copyURL"),
|
||||
onClick: () => {
|
||||
window.utils.writeClipboard(this.props.item.link)
|
||||
window.utils.writeClipboard(item.link)
|
||||
},
|
||||
},
|
||||
...(this.props.viewConfigs !== undefined
|
||||
...(viewConfigs !== undefined
|
||||
? [
|
||||
{
|
||||
key: "divider_2",
|
||||
@ -212,50 +212,46 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
items: [
|
||||
{
|
||||
key: "showCover",
|
||||
text: intl.get(
|
||||
"context.showCover"
|
||||
),
|
||||
text: intl.get("context.showCover"),
|
||||
canCheck: true,
|
||||
checked: Boolean(
|
||||
this.props.viewConfigs &
|
||||
ViewConfigs.ShowCover
|
||||
viewConfigs & ViewConfigs.ShowCover
|
||||
),
|
||||
onClick: () =>
|
||||
this.props.setViewConfigs(
|
||||
this.props.viewConfigs ^
|
||||
dispatch(
|
||||
setViewConfigs(
|
||||
viewConfigs ^
|
||||
ViewConfigs.ShowCover
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "showSnippet",
|
||||
text: intl.get(
|
||||
"context.showSnippet"
|
||||
),
|
||||
text: intl.get("context.showSnippet"),
|
||||
canCheck: true,
|
||||
checked: Boolean(
|
||||
this.props.viewConfigs &
|
||||
ViewConfigs.ShowSnippet
|
||||
viewConfigs & ViewConfigs.ShowSnippet
|
||||
),
|
||||
onClick: () =>
|
||||
this.props.setViewConfigs(
|
||||
this.props.viewConfigs ^
|
||||
dispatch(
|
||||
setViewConfigs(
|
||||
viewConfigs ^
|
||||
ViewConfigs.ShowSnippet
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "fadeRead",
|
||||
text: intl.get(
|
||||
"context.fadeRead"
|
||||
),
|
||||
text: intl.get("context.fadeRead"),
|
||||
canCheck: true,
|
||||
checked: Boolean(
|
||||
this.props.viewConfigs &
|
||||
ViewConfigs.FadeRead
|
||||
viewConfigs & ViewConfigs.FadeRead
|
||||
),
|
||||
onClick: () =>
|
||||
this.props.setViewConfigs(
|
||||
this.props.viewConfigs ^
|
||||
ViewConfigs.FadeRead
|
||||
dispatch(
|
||||
setViewConfigs(
|
||||
viewConfigs ^ ViewConfigs.FadeRead
|
||||
)
|
||||
),
|
||||
},
|
||||
],
|
||||
@ -264,26 +260,35 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
]
|
||||
: []),
|
||||
]
|
||||
case ContextMenuType.Text: {
|
||||
const items: IContextualMenuItem[] = this.props.text
|
||||
return <ContextMenuBase menuItems={menuItems} />
|
||||
}
|
||||
|
||||
function TextContextMenu() {
|
||||
const target = useAppSelector(state => state.app.contextMenu.target) as [
|
||||
string,
|
||||
string
|
||||
]
|
||||
const text = target[0]
|
||||
const url = target[1]
|
||||
const menuItems: IContextualMenuItem[] = text
|
||||
? [
|
||||
{
|
||||
key: "copyText",
|
||||
text: intl.get("context.copy"),
|
||||
iconProps: { iconName: "Copy" },
|
||||
onClick: () => {
|
||||
window.utils.writeClipboard(this.props.text)
|
||||
window.utils.writeClipboard(text)
|
||||
},
|
||||
},
|
||||
getSearchItem(this.props.text),
|
||||
getSearchItem(text),
|
||||
]
|
||||
: []
|
||||
if (this.props.url) {
|
||||
items.push({
|
||||
if (url) {
|
||||
menuItems.push({
|
||||
key: "urlSection",
|
||||
itemType: ContextualMenuItemType.Section,
|
||||
sectionProps: {
|
||||
topDivider: items.length > 0,
|
||||
topDivider: menuItems.length > 0,
|
||||
items: [
|
||||
{
|
||||
key: "openInBrowser",
|
||||
@ -292,10 +297,7 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
iconName: "NavigateExternalInline",
|
||||
},
|
||||
onClick: e => {
|
||||
window.utils.openExternal(
|
||||
this.props.url,
|
||||
platformCtrl(e)
|
||||
)
|
||||
window.utils.openExternal(url, platformCtrl(e))
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -303,19 +305,18 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
text: intl.get("context.copyURL"),
|
||||
iconProps: { iconName: "Link" },
|
||||
onClick: () => {
|
||||
window.utils.writeClipboard(
|
||||
this.props.url
|
||||
)
|
||||
window.utils.writeClipboard(url)
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
}
|
||||
return items
|
||||
}
|
||||
case ContextMenuType.Image:
|
||||
return [
|
||||
return <ContextMenuBase menuItems={menuItems} />
|
||||
}
|
||||
|
||||
function ImageContextMenu() {
|
||||
const menuItems: IContextualMenuItem[] = [
|
||||
{
|
||||
key: "openInBrowser",
|
||||
text: intl.get("openExternal"),
|
||||
@ -326,9 +327,7 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
ImageCallbackTypes.OpenExternalBg
|
||||
)
|
||||
} else {
|
||||
window.utils.imageCallback(
|
||||
ImageCallbackTypes.OpenExternal
|
||||
)
|
||||
window.utils.imageCallback(ImageCallbackTypes.OpenExternal)
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -337,9 +336,7 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
text: intl.get("context.saveImageAs"),
|
||||
iconProps: { iconName: "SaveTemplate" },
|
||||
onClick: () => {
|
||||
window.utils.imageCallback(
|
||||
ImageCallbackTypes.SaveAs
|
||||
)
|
||||
window.utils.imageCallback(ImageCallbackTypes.SaveAs)
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -355,14 +352,19 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
text: intl.get("context.copyImageURL"),
|
||||
iconProps: { iconName: "Link" },
|
||||
onClick: () => {
|
||||
window.utils.imageCallback(
|
||||
ImageCallbackTypes.CopyLink
|
||||
)
|
||||
window.utils.imageCallback(ImageCallbackTypes.CopyLink)
|
||||
},
|
||||
},
|
||||
]
|
||||
case ContextMenuType.View:
|
||||
return [
|
||||
return <ContextMenuBase menuItems={menuItems} />
|
||||
}
|
||||
|
||||
function ViewContextMenu() {
|
||||
const dispatch = useAppDispatch()
|
||||
const viewType = useAppSelector(state => state.page.viewType)
|
||||
const filter = useAppSelector(state => state.page.filter.type)
|
||||
|
||||
const menuItems: IContextualMenuItem[] = [
|
||||
{
|
||||
key: "section_1",
|
||||
itemType: ContextualMenuItemType.Section,
|
||||
@ -375,44 +377,32 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
text: intl.get("context.cardView"),
|
||||
iconProps: { iconName: "GridViewMedium" },
|
||||
canCheck: true,
|
||||
checked:
|
||||
this.props.viewType === ViewType.Cards,
|
||||
onClick: () =>
|
||||
this.props.switchView(ViewType.Cards),
|
||||
checked: viewType === ViewType.Cards,
|
||||
onClick: () => dispatch(switchView(ViewType.Cards)),
|
||||
},
|
||||
{
|
||||
key: "listView",
|
||||
text: intl.get("context.listView"),
|
||||
iconProps: { iconName: "BacklogList" },
|
||||
canCheck: true,
|
||||
checked:
|
||||
this.props.viewType === ViewType.List,
|
||||
onClick: () =>
|
||||
this.props.switchView(ViewType.List),
|
||||
checked: viewType === ViewType.List,
|
||||
onClick: () => dispatch(switchView(ViewType.List)),
|
||||
},
|
||||
{
|
||||
key: "magazineView",
|
||||
text: intl.get("context.magazineView"),
|
||||
iconProps: { iconName: "Articles" },
|
||||
canCheck: true,
|
||||
checked:
|
||||
this.props.viewType ===
|
||||
ViewType.Magazine,
|
||||
onClick: () =>
|
||||
this.props.switchView(
|
||||
ViewType.Magazine
|
||||
),
|
||||
checked: viewType === ViewType.Magazine,
|
||||
onClick: () => dispatch(switchView(ViewType.Magazine)),
|
||||
},
|
||||
{
|
||||
key: "compactView",
|
||||
text: intl.get("context.compactView"),
|
||||
iconProps: { iconName: "BulletedList" },
|
||||
canCheck: true,
|
||||
checked:
|
||||
this.props.viewType ===
|
||||
ViewType.Compact,
|
||||
onClick: () =>
|
||||
this.props.switchView(ViewType.Compact),
|
||||
checked: viewType === ViewType.Compact,
|
||||
onClick: () => dispatch(switchView(ViewType.Compact)),
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -430,13 +420,10 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
iconProps: { iconName: "ClearFilter" },
|
||||
canCheck: true,
|
||||
checked:
|
||||
(this.props.filter &
|
||||
~FilterType.Toggles) ==
|
||||
(filter & ~FilterType.Toggles) ==
|
||||
FilterType.Default,
|
||||
onClick: () =>
|
||||
this.props.switchFilter(
|
||||
FilterType.Default
|
||||
),
|
||||
dispatch(switchFilter(FilterType.Default)),
|
||||
},
|
||||
{
|
||||
key: "unreadOnly",
|
||||
@ -450,13 +437,10 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
},
|
||||
canCheck: true,
|
||||
checked:
|
||||
(this.props.filter &
|
||||
~FilterType.Toggles) ==
|
||||
(filter & ~FilterType.Toggles) ==
|
||||
FilterType.UnreadOnly,
|
||||
onClick: () =>
|
||||
this.props.switchFilter(
|
||||
FilterType.UnreadOnly
|
||||
),
|
||||
dispatch(switchFilter(FilterType.UnreadOnly)),
|
||||
},
|
||||
{
|
||||
key: "starredOnly",
|
||||
@ -464,13 +448,10 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
iconProps: { iconName: "FavoriteStarFill" },
|
||||
canCheck: true,
|
||||
checked:
|
||||
(this.props.filter &
|
||||
~FilterType.Toggles) ==
|
||||
(filter & ~FilterType.Toggles) ==
|
||||
FilterType.StarredOnly,
|
||||
onClick: () =>
|
||||
this.props.switchFilter(
|
||||
FilterType.StarredOnly
|
||||
),
|
||||
dispatch(switchFilter(FilterType.StarredOnly)),
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -493,28 +474,18 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
children: "Aa",
|
||||
},
|
||||
canCheck: true,
|
||||
checked: !(
|
||||
this.props.filter &
|
||||
FilterType.CaseInsensitive
|
||||
),
|
||||
checked: !(filter & FilterType.CaseInsensitive),
|
||||
onClick: () =>
|
||||
this.props.toggleFilter(
|
||||
FilterType.CaseInsensitive
|
||||
),
|
||||
dispatch(toggleFilter(FilterType.CaseInsensitive)),
|
||||
},
|
||||
{
|
||||
key: "fullSearch",
|
||||
text: intl.get("context.fullSearch"),
|
||||
iconProps: { iconName: "Breadcrumb" },
|
||||
canCheck: true,
|
||||
checked: Boolean(
|
||||
this.props.filter &
|
||||
FilterType.FullSearch
|
||||
),
|
||||
checked: Boolean(filter & FilterType.FullSearch),
|
||||
onClick: () =>
|
||||
this.props.toggleFilter(
|
||||
FilterType.FullSearch
|
||||
),
|
||||
dispatch(toggleFilter(FilterType.FullSearch)),
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -523,36 +494,52 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
key: "showHidden",
|
||||
text: intl.get("context.showHidden"),
|
||||
canCheck: true,
|
||||
checked: Boolean(
|
||||
this.props.filter & FilterType.ShowHidden
|
||||
),
|
||||
onClick: () =>
|
||||
this.props.toggleFilter(FilterType.ShowHidden),
|
||||
checked: Boolean(filter & FilterType.ShowHidden),
|
||||
onClick: () => dispatch(toggleFilter(FilterType.ShowHidden)),
|
||||
},
|
||||
]
|
||||
case ContextMenuType.Group:
|
||||
return [
|
||||
return <ContextMenuBase menuItems={menuItems} />
|
||||
}
|
||||
|
||||
function GroupContextMenu() {
|
||||
const dispatch = useAppDispatch()
|
||||
const sids = useAppSelector(
|
||||
state => state.app.contextMenu.target
|
||||
) as number[]
|
||||
|
||||
const menuItems: IContextualMenuItem[] = [
|
||||
{
|
||||
key: "markAllRead",
|
||||
text: intl.get("nav.markAllRead"),
|
||||
iconProps: { iconName: "CheckMark" },
|
||||
onClick: () => this.props.markAllRead(this.props.sids),
|
||||
onClick: () => {
|
||||
dispatch(markAllRead(sids))
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "refresh",
|
||||
text: intl.get("nav.refresh"),
|
||||
iconProps: { iconName: "Sync" },
|
||||
onClick: () => this.props.fetchItems(this.props.sids),
|
||||
onClick: () => {
|
||||
dispatch(markAllRead(sids))
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "manage",
|
||||
text: intl.get("context.manageSources"),
|
||||
iconProps: { iconName: "Settings" },
|
||||
onClick: () => this.props.settings(this.props.sids),
|
||||
onClick: () => {
|
||||
dispatch(markAllRead(sids))
|
||||
},
|
||||
},
|
||||
]
|
||||
case ContextMenuType.MarkRead:
|
||||
return [
|
||||
return <ContextMenuBase menuItems={menuItems} />
|
||||
}
|
||||
|
||||
function MarkReadContextMenu() {
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const menuItems: IContextualMenuItem[] = [
|
||||
{
|
||||
key: "section_1",
|
||||
itemType: ContextualMenuItemType.Section,
|
||||
@ -563,7 +550,9 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
key: "all",
|
||||
text: intl.get("allArticles"),
|
||||
iconProps: { iconName: "ReceiptCheck" },
|
||||
onClick: () => this.props.markAllRead(),
|
||||
onClick: () => {
|
||||
dispatch(markAllRead())
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "1d",
|
||||
@ -571,7 +560,7 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
onClick: () => {
|
||||
let date = new Date()
|
||||
date.setTime(date.getTime() - 86400000)
|
||||
this.props.markAllRead(null, date)
|
||||
dispatch(markAllRead(null, date))
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -579,10 +568,8 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
text: intl.get("app.daysAgo", { days: 3 }),
|
||||
onClick: () => {
|
||||
let date = new Date()
|
||||
date.setTime(
|
||||
date.getTime() - 3 * 86400000
|
||||
)
|
||||
this.props.markAllRead(null, date)
|
||||
date.setTime(date.getTime() - 3 * 86400000)
|
||||
dispatch(markAllRead(null, date))
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -590,35 +577,35 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
||||
text: intl.get("app.daysAgo", { days: 7 }),
|
||||
onClick: () => {
|
||||
let date = new Date()
|
||||
date.setTime(
|
||||
date.getTime() - 7 * 86400000
|
||||
)
|
||||
this.props.markAllRead(null, date)
|
||||
date.setTime(date.getTime() - 7 * 86400000)
|
||||
dispatch(markAllRead(null, date))
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
default:
|
||||
return []
|
||||
}
|
||||
}
|
||||
return <ContextMenuBase menuItems={menuItems} />
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.type == ContextMenuType.Hidden ? null : (
|
||||
function ContextMenuBase({
|
||||
menuItems,
|
||||
}: Readonly<{ menuItems: IContextualMenuItem[] }>) {
|
||||
const { event, position } = useAppSelector(state => state.app.contextMenu)
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
return (
|
||||
<ContextualMenu
|
||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||
items={this.getItems()}
|
||||
items={menuItems}
|
||||
target={
|
||||
this.props.event ||
|
||||
(this.props.position && {
|
||||
left: this.props.position[0],
|
||||
top: this.props.position[1],
|
||||
event ||
|
||||
(position && {
|
||||
left: position[0],
|
||||
top: position[1],
|
||||
})
|
||||
}
|
||||
onDismiss={this.props.close}
|
||||
onDismiss={() => dispatch(closeContextMenu())}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import * as React from "react"
|
||||
import { connect } from "react-redux"
|
||||
import { ContextMenuContainer } from "../containers/context-menu-container"
|
||||
import { closeContextMenu } from "../scripts/models/app"
|
||||
import PageContainer from "../containers/page-container"
|
||||
import MenuContainer from "../containers/menu-container"
|
||||
@ -8,6 +7,7 @@ import NavContainer from "../containers/nav-container"
|
||||
import LogMenuContainer from "../containers/log-menu-container"
|
||||
import SettingsContainer from "../containers/settings-container"
|
||||
import { RootState } from "../scripts/reducer"
|
||||
import { ContextMenu } from "./context-menu"
|
||||
|
||||
const Root = ({ locale, dispatch }) =>
|
||||
locale && (
|
||||
@ -20,7 +20,7 @@ const Root = ({ locale, dispatch }) =>
|
||||
<LogMenuContainer />
|
||||
<MenuContainer />
|
||||
<SettingsContainer />
|
||||
<ContextMenuContainer />
|
||||
<ContextMenu />
|
||||
</div>
|
||||
)
|
||||
|
||||
|
@ -1,115 +0,0 @@
|
||||
import { connect } from "react-redux"
|
||||
import { createSelector } from "reselect"
|
||||
import { RootState } from "../scripts/reducer"
|
||||
import {
|
||||
ContextMenuType,
|
||||
closeContextMenu,
|
||||
toggleSettings,
|
||||
} from "../scripts/models/app"
|
||||
import { ContextMenu } from "../components/context-menu"
|
||||
import {
|
||||
RSSItem,
|
||||
markRead,
|
||||
markUnread,
|
||||
toggleStarred,
|
||||
toggleHidden,
|
||||
markAllRead,
|
||||
fetchItems,
|
||||
} from "../scripts/models/item"
|
||||
import {
|
||||
showItem,
|
||||
switchView,
|
||||
switchFilter,
|
||||
toggleFilter,
|
||||
setViewConfigs,
|
||||
} from "../scripts/models/page"
|
||||
import { ViewType, ViewConfigs } from "../schema-types"
|
||||
import { FilterType } from "../scripts/models/feed"
|
||||
|
||||
const getContext = (state: RootState) => state.app.contextMenu
|
||||
const getViewType = (state: RootState) => state.page.viewType
|
||||
const getFilter = (state: RootState) => state.page.filter
|
||||
const getViewConfigs = (state: RootState) => state.page.viewConfigs
|
||||
|
||||
const mapStateToProps = createSelector(
|
||||
[getContext, getViewType, getFilter, getViewConfigs],
|
||||
(context, viewType, filter, viewConfigs) => {
|
||||
switch (context.type) {
|
||||
case ContextMenuType.Item:
|
||||
return {
|
||||
type: context.type,
|
||||
event: context.event,
|
||||
viewConfigs: viewConfigs,
|
||||
item: context.target[0],
|
||||
feedId: context.target[1],
|
||||
}
|
||||
case ContextMenuType.Text:
|
||||
return {
|
||||
type: context.type,
|
||||
position: context.position,
|
||||
text: context.target[0],
|
||||
url: context.target[1],
|
||||
}
|
||||
case ContextMenuType.View:
|
||||
return {
|
||||
type: context.type,
|
||||
event: context.event,
|
||||
viewType: viewType,
|
||||
filter: filter.type,
|
||||
}
|
||||
case ContextMenuType.Group:
|
||||
return {
|
||||
type: context.type,
|
||||
event: context.event,
|
||||
sids: context.target,
|
||||
}
|
||||
case ContextMenuType.Image:
|
||||
return {
|
||||
type: context.type,
|
||||
position: context.position,
|
||||
}
|
||||
case ContextMenuType.MarkRead:
|
||||
return {
|
||||
type: context.type,
|
||||
event: context.event,
|
||||
}
|
||||
default:
|
||||
return { type: ContextMenuType.Hidden }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
showItem: (feedId: string, item: RSSItem) =>
|
||||
dispatch(showItem(feedId, item)),
|
||||
markRead: (item: RSSItem) => dispatch(markRead(item)),
|
||||
markUnread: (item: RSSItem) => dispatch(markUnread(item)),
|
||||
toggleStarred: (item: RSSItem) => dispatch(toggleStarred(item)),
|
||||
toggleHidden: (item: RSSItem) => {
|
||||
if (!item.hasRead) {
|
||||
dispatch(markRead(item))
|
||||
item.hasRead = true // get around chaining error
|
||||
}
|
||||
dispatch(toggleHidden(item))
|
||||
},
|
||||
switchView: (viewType: ViewType) => {
|
||||
window.settings.setDefaultView(viewType)
|
||||
dispatch(switchView(viewType))
|
||||
},
|
||||
setViewConfigs: (configs: ViewConfigs) =>
|
||||
dispatch(setViewConfigs(configs)),
|
||||
switchFilter: (filter: FilterType) => dispatch(switchFilter(filter)),
|
||||
toggleFilter: (filter: FilterType) => dispatch(toggleFilter(filter)),
|
||||
markAllRead: (sids?: number[], date?: Date, before?: boolean) => {
|
||||
dispatch(markAllRead(sids, date, before))
|
||||
},
|
||||
fetchItems: (sids: number[]) => dispatch(fetchItems(false, sids)),
|
||||
settings: (sids: number[]) => dispatch(toggleSettings(true, sids)),
|
||||
close: () => dispatch(closeContextMenu()),
|
||||
}
|
||||
}
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps)
|
||||
export type ContextReduxProps = typeof connector
|
||||
export const ContextMenuContainer = connector(ContextMenu)
|
@ -1,29 +1,21 @@
|
||||
import * as React from "react"
|
||||
import * as ReactDOM from "react-dom"
|
||||
import { Provider } from "react-redux"
|
||||
import { createStore, applyMiddleware } from "redux"
|
||||
import thunkMiddleware from "redux-thunk"
|
||||
import { initializeIcons } from "@fluentui/react/lib/Icons"
|
||||
import { rootReducer, RootState } from "./scripts/reducer"
|
||||
import Root from "./components/root"
|
||||
import { AppDispatch } from "./scripts/utils"
|
||||
import { applyThemeSettings } from "./scripts/settings"
|
||||
import { initApp, openTextMenu } from "./scripts/models/app"
|
||||
import { rootStore } from "./scripts/reducer"
|
||||
|
||||
window.settings.setProxy()
|
||||
|
||||
applyThemeSettings()
|
||||
initializeIcons("icons/")
|
||||
|
||||
const store = createStore(
|
||||
rootReducer,
|
||||
applyMiddleware<AppDispatch, RootState>(thunkMiddleware)
|
||||
)
|
||||
|
||||
store.dispatch(initApp())
|
||||
rootStore.dispatch(initApp())
|
||||
|
||||
window.utils.addMainContextListener((pos, text) => {
|
||||
store.dispatch(openTextMenu(pos, text))
|
||||
rootStore.dispatch(openTextMenu(pos, text))
|
||||
})
|
||||
|
||||
window.fontList = [""]
|
||||
@ -32,7 +24,7 @@ window.utils.initFontList().then(fonts => {
|
||||
})
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<Provider store={rootStore}>
|
||||
<Root />
|
||||
</Provider>,
|
||||
document.getElementById("app")
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { combineReducers } from "redux"
|
||||
import { applyMiddleware, combineReducers, createStore } from "redux"
|
||||
import thunkMiddleware from "redux-thunk"
|
||||
|
||||
import { sourceReducer } from "./models/source"
|
||||
import { itemReducer } from "./models/item"
|
||||
@ -7,6 +8,13 @@ import { appReducer } from "./models/app"
|
||||
import { groupReducer } from "./models/group"
|
||||
import { pageReducer } from "./models/page"
|
||||
import { serviceReducer } from "./models/service"
|
||||
import { AppDispatch } from "./utils"
|
||||
import {
|
||||
TypedUseSelectorHook,
|
||||
useDispatch,
|
||||
useSelector,
|
||||
useStore,
|
||||
} from "react-redux"
|
||||
|
||||
export const rootReducer = combineReducers({
|
||||
sources: sourceReducer,
|
||||
@ -18,4 +26,14 @@ export const rootReducer = combineReducers({
|
||||
app: appReducer,
|
||||
})
|
||||
|
||||
export const rootStore = createStore(
|
||||
rootReducer,
|
||||
applyMiddleware<AppDispatch, RootState>(thunkMiddleware)
|
||||
)
|
||||
|
||||
export type AppStore = typeof rootStore
|
||||
export type RootState = ReturnType<typeof rootReducer>
|
||||
|
||||
export const useAppDispatch: () => AppDispatch = useDispatch
|
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
|
||||
export const useAppStore: () => AppStore = useStore
|
||||
|
Loading…
x
Reference in New Issue
Block a user